提供一个pdf水印生成工具类

提供一个pdf水印生成工具类


/**
 * pdf水印工具类
 *
 */
public class PdfWaterMarkUtil {

    private PdfWaterMarkUtil() {
    }

    /**
     * 水印的横向间隔
     */
    private static final float LEN_OFFSET = 100;
    /**
     * 平铺水印的,上下水印的错位比例
     */
    private static final float TILE_MALPOSITION = 0.3f;

    /**
     * 平铺
     */
    public static final int TILE = 0;
    /**
     * 左上
     */
    public static final int TOP_LEFT = 1;
    /**
     * 中上
     */
    public static final int TOP_CENTER = 2;
    /**
     * 右上
     */
    public static final int TOP_RIGHT = 3;
    /**
     * 左中
     */
    public static final int CENTER_LEFT = 4;
    /**
     * 正中
     */
    public static final int CENTER = 5;
    /**
     * 右中
     */
    public static final int CENTER_RIGHT = 6;
    /**
     * 左下
     */
    public static final int BOTTOM_LEFT = 7;
    /**
     * 中下
     */
    public static final int BOTTOM_CENTER = 8;
    /**
     * 右下
     */
    public static final int BOTTOM_RIGHT = 9;

    /**
     * 字体缓存
     */
    private static final Map<String, BaseFont> FONT_MAP = new HashMap<>();
    private static final Map<Integer, PdfGState> GS_MAP = new HashMap<>();

    /**
     * 长度(厘米)到像素(px)的换算系数
     */
    private static final float LEN_TO_PX = 28.347618466331845f;

    public static void addWaterMark(String srcPdfFile, OutputStream out, List<MarkInfo> markInfoList) throws Exception {
        addWaterMark(new PdfReader(srcPdfFile), out, markInfoList);
    }

    public static void addWaterMark(File pdfFile, OutputStream out, List<MarkInfo> markInfoList) throws Exception {
        addWaterMark(new PdfReader(new FileInputStream(pdfFile)), out, markInfoList);
    }

    public static void addWaterMark(URL pdfUrl, OutputStream out, List<MarkInfo> markInfoList) throws Exception {
        addWaterMark(new PdfReader(pdfUrl), out, markInfoList);
    }


    private static void addWaterMark(PdfReader reader, OutputStream out, List<MarkInfo> markInfoList) throws Exception {
        // 加完水印的文件
        PdfStamper pdfStamper = new PdfStamper(reader, out);
        int pageNumber = reader.getNumberOfPages() + 1;

        // 循环对每页插入水印
        for (int i = 1; i < pageNumber; i++) {
            Rectangle pageSize = reader.getPageSize(i);
            float height = pageSize.getHeight();
            float width = pageSize.getWidth();
            // 水印在之前文本上
            PdfContentByte content = pdfStamper.getOverContent(i);
            // 开始
            content.beginText();
            for (MarkInfo markInfo : markInfoList) {
                // 设置透明度
                content.setGState(getPdfGState(markInfo.getPellucidity()));
                // 设置字体及大小
                content.setFontAndSize(getFont(markInfo.getFontFamily()), markInfo.getFontSize());
                // 设置字体颜色
                content.setColorFill(Color.get(markInfo.getFontColor()));
                // 水印类型
                switch (markInfo.getPositionType()) {
                    case TILE:
                        tile(markInfo, height, width, content);
                        break;
                    case TOP_LEFT:
                        topLeft(markInfo, height, width, content);
                        break;
                    case TOP_CENTER:
                        topCenter(markInfo, height, width, content);
                        break;
                    case TOP_RIGHT:
                        topRight(markInfo, height, width, content);
                        break;
                    case CENTER_LEFT:
                        centerLeft(markInfo, height, width, content);
                        break;
                    case CENTER:
                        center(markInfo, height, width, content);
                        break;
                    case CENTER_RIGHT:
                        centerRight(markInfo, height, width, content);
                        break;
                    case BOTTOM_LEFT:
                        bottomLeft(markInfo, height, width, content);
                        break;
                    case BOTTOM_CENTER:
                        bottomCenter(markInfo, height, width, content);
                        break;
                    case BOTTOM_RIGHT:
                        bottomRight(markInfo, height, width, content);
                        break;
                    default:
                }
            }
            //结束
            content.endText();
        }
        pdfStamper.close();
    }

    private static PdfGState getPdfGState(int pellucidity) {
        PdfGState gs = GS_MAP.get(pellucidity);
        if (gs == null) {
            gs = new PdfGState();
            gs.setFillOpacity((100 - pellucidity) / 100f);
            GS_MAP.put(pellucidity, gs);
        }
        return gs;
    }

    private static void bottomRight(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        final float rotation = markInfo.getRotation();
        float x = getRightX(markInfo, width, content);
        float y = getBottomY(markInfo, height, content);
        content.showTextAligned(Element.ALIGN_LEFT, markText, x, y, rotation);
    }

    private static void bottomCenter(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        final float rotation = markInfo.getRotation();
        float x = getCenterX(markInfo, width, content);
        float y = getBottomY(markInfo, height, content);
        content.showTextAligned(Element.ALIGN_LEFT, markText, x, y, rotation);
    }

    private static void bottomLeft(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        final float rotation = markInfo.getRotation();
        float x = getLeftX(markInfo, width, content);
        float y = getBottomY(markInfo, height, content);
        content.showTextAligned(Element.ALIGN_LEFT, markText, x, y, rotation);
    }

    private static void centerRight(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        final float rotation = markInfo.getRotation();
        float x = getRightX(markInfo, width, content);
        float y = getCenterY(markInfo, height, content);
        content.showTextAligned(Element.ALIGN_LEFT, markText, x, y, rotation);
    }

    private static void centerLeft(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        final float rotation = markInfo.getRotation();
        float x = getLeftX(markInfo, width, content);
        float y = getCenterY(markInfo, height, content);
        content.showTextAligned(Element.ALIGN_LEFT, markText, x, y, rotation);
    }

    private static void topRight(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        final float rotation = markInfo.getRotation();
        float x = getRightX(markInfo, width, content);
        float y = getTopY(markInfo, height, content);
        content.showTextAligned(Element.ALIGN_LEFT, markText, x, y, rotation);
    }

    private static void topCenter(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        final float rotation = markInfo.getRotation();
        float x = getCenterX(markInfo, width, content);
        float y = getTopY(markInfo, height, content);
        content.showTextAligned(Element.ALIGN_LEFT, markText, x, y, rotation);
    }

    private static void topLeft(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        final float rotation = markInfo.getRotation();
        float x = getLeftX(markInfo, width, content);
        float y = getTopY(markInfo, height, content);
        content.showTextAligned(Element.ALIGN_LEFT, markText, x, y, rotation);
    }

    private static void center(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        final float rotation = markInfo.getRotation();
        float x = getCenterX(markInfo, width, content);
        float y = getCenterY(markInfo, height, content);
        content.showTextAligned(Element.ALIGN_LEFT, markText, x, y, rotation);
    }

    private static float getBottomY(MarkInfo markInfo, float height, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        float textLength = content.getEffectiveStringWidth(markText, false);
        final float rotation = markInfo.getRotation();
        float bottomMargin = markInfo.getYMargin() * LEN_TO_PX;
        if (rotation >= 0) {
            return bottomMargin;
        }
        return bottomMargin - textLength * sin(rotation);
    }


    private static float getCenterY(MarkInfo markInfo, float height, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        float textLength = content.getEffectiveStringWidth(markText, false);
        final float rotation = markInfo.getRotation();
        final float yLen = textLength * sin(rotation);

        final float halfSize = markInfo.getFontSize() / 2f;
        return (height - yLen) / 2 + halfSize * (1 - cos(rotation));
    }

    private static float getTopY(MarkInfo markInfo, float height, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        float textLength = content.getEffectiveStringWidth(markText, false);
        final int fontSize = markInfo.getFontSize();
        final float rotation = markInfo.getRotation();
        float topMargin = markInfo.getYMargin() * LEN_TO_PX;
        if (rotation >= 0) {
            return height - topMargin - fontSize * cos(rotation) - textLength * sin(rotation);
        }

        return height - topMargin - fontSize * cos(rotation);
    }


    private static float getRightX(MarkInfo markInfo, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        float textLength = content.getEffectiveStringWidth(markText, false);
        final int fontSize = markInfo.getFontSize();
        final float rotation = markInfo.getRotation();
        final float rightMargin = markInfo.getXMargin() * LEN_TO_PX;
        if (rotation >= 0) {
            return width - rightMargin - textLength * cos(rotation);
        }

        return width - rightMargin - textLength * cos(rotation) + fontSize * sin(rotation);
    }

    private static float getCenterX(MarkInfo markInfo, float width, PdfContentByte content) {
        final String markText = markInfo.getMarkText();
        float textLength = content.getEffectiveStringWidth(markText, false);
        final float halfSize = markInfo.getFontSize() / 2f;
        final float rotation = markInfo.getRotation();
        final float xLen = textLength * cos(rotation);
        return (width - xLen) / 2 + halfSize * sin(rotation);
    }

    private static float getLeftX(MarkInfo markInfo, float width, PdfContentByte content) {
        final int fontSize = markInfo.getFontSize();
        final float rotation = markInfo.getRotation();
        float leftMargin = markInfo.getXMargin() * LEN_TO_PX;
        if (rotation <= 0) {
            return leftMargin;
        }
        return leftMargin + fontSize * sin(rotation);
    }


    /**
     * 平铺
     * 1、先找出为旋转的时候平铺的点坐标,再绕中心点旋转
     * 2、文档正中心始终有一个水印
     */
    private static void tile(MarkInfo markInfo, float height, float width, PdfContentByte content) {
        float textLength = content.getEffectiveStringWidth(markInfo.getMarkText(), false);
        final int fontSize = markInfo.getFontSize();

        // 正中心的条水印的坐标,所有其他水印的坐标由该坐标推算出来
        float x = (width - textLength) / 2;
        float y = (height - fontSize) / 2;

        // 文档外切圆的半径
        final float radius = (float) Math.sqrt(height * height + width * width) / 2f;
        // 相邻水印的x坐标偏移量
        final float xOffset = textLength + LEN_OFFSET;
        // 相邻水印的y坐标偏移量
        final float yOffset = markInfo.getLineSpacing() * fontSize;
        final float centerX = width / 2;
        float textCenterX = x + textLength / 2;
        float relativeX = Math.abs(textCenterX - centerX);
        while (relativeX < radius) {
            x -= xOffset;
            textCenterX = x + textLength / 2;
            relativeX = Math.abs(textCenterX - centerX);
        }

        diffusion(x, y, 1, 1, markInfo, height, width, content);
        diffusion(x - xOffset * TILE_MALPOSITION, y - yOffset, 1, -1, markInfo, height, width, content);
    }

    private static void diffusion(float x, float y, int xDirection, int yDirection, MarkInfo markInfo, float height, float width, PdfContentByte content) {
        final float rotation = markInfo.getRotation();
        final float sin = sin(rotation);
        final float cos = cos(rotation);
        float textLength = content.getEffectiveStringWidth(markInfo.getMarkText(), false);
        final int fontSize = markInfo.getFontSize();
        final float radius = (float) Math.sqrt(height * height + width * width) / 2f;
        final float xOffset = textLength + LEN_OFFSET;
        final float yOffset = markInfo.getLineSpacing() * fontSize;
        final float centerX = width / 2;
        final float centerY = height / 2;

        final float xLen = textLength * cos;
        final float yLen = textLength * sin;

        float textCenterX = x + textLength / 2;
        float textCenterY = y + fontSize / 2f;
        float relativeY = Math.abs(textCenterY - centerY);
        if (relativeY > radius) {
            return;
        }
        float relativeX = Math.abs(textCenterX - centerX);
        float preRelativeX = relativeX;
        while (relativeX > radius) {
            x += xOffset * xDirection;
            textCenterX = x + textLength / 2;
            relativeX = Math.abs(textCenterX - centerX);
            if (relativeX > preRelativeX) {
                break;
            }
            preRelativeX = relativeX;
        }
        while (relativeX < radius) {
            float newX = (x - centerX) * cos - (y - centerY) * sin + centerX;
            float newY = (x - centerX) * sin + (y - centerY) * cos + centerY;

            x += xOffset * xDirection;
            textCenterX = x + textLength / 2;
            relativeX = Math.abs(textCenterX - centerX);

            boolean outOfRange = (newX < 0 && newX + xLen < 0)
                    || (newX > width && (newX + xLen) > width)
                    || (newY < 0 && (newY + yLen < 0))
                    || (newY > height && ((newY + yLen) > height));
            if (outOfRange) {
                continue;
            }
            content.showTextAligned(Element.ALIGN_LEFT, markInfo.getMarkText(), newX, newY, rotation);
        }

        float offset = Math.min(xOffset * TILE_MALPOSITION, 200);
        x += offset * yDirection;
        xDirection *= -1;
        y += yOffset * yDirection;

        diffusion(x, y, xDirection, yDirection, markInfo, height, width, content);
    }

    private static float sin(float rotation) {
        return (float) Math.sin(Math.toRadians(rotation));
    }

    private static float cos(float rotation) {
        return (float) Math.cos(Math.toRadians(rotation));
    }

    private static BaseFont getFont(String fontFamily) throws IOException, DocumentException {
        BaseFont baseFont = FONT_MAP.get(fontFamily);
        if (baseFont == null) {
            String fontName = "fonts/SimSun.ttf";
            switch (fontFamily) {
                case "NSimSun":
                    fontName = "fonts/simsun_new.ttf";
                    break;
                case "SimHei":
                    fontName = "fonts/SIMHEI.TTF";
                    break;
                case "KaiTi":
                    fontName = "fonts/simkai.ttf";
                    break;
                default:
            }
            if (SystemUtils.isLinux()) {
                fontName = "/usr/share/" + fontName;
                baseFont = BaseFont.createFont(fontName, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            } else {
                baseFont = BaseFont.createFont(PdfWaterMarkUtil.class.getClassLoader().getResource(fontName).getPath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            }

            FONT_MAP.put(fontFamily, baseFont);
        }

        return baseFont;
    }

  

    public static class Color {
        public static final int BLANK = 1;
        public static final int RED = 2;
        public static final int BLUE = 3;

        public static BaseColor get(int fontColor) {
            switch (fontColor) {
                case BLANK:
                    return BaseColor.BLACK;
                case RED:
                    return BaseColor.RED;
                default:
                    return BaseColor.BLUE;
            }

        }
    }

    @Data
    public static class MarkInfo {

        /**
         * 水印文字
         */
        private String markText;

        /**
         * 字体
         */
        private String fontFamily = "SimSun";

        /**
         * 字号
         */
        private int fontSize = 12;

        /**
         * 字体颜色
         */
        private int fontColor = 1;

        /**
         * 透明度(0-100,0全完不透明,100完全透明)
         */
        private int pellucidity = 30;

        /**
         * 水印位置类型
         */
        private int positionType = 0;

        /**
         * 倾斜度(-90至90)
         */
        private float rotation = 45;

        /**
         * 左/右边距(厘米),最大15
         */
        private float xMargin = 3;
        /**
         * 上/下边距(厘米),最大15
         */
        private float yMargin = 2;

        /**
         * 行距(字体倍数)
         */
        private float lineSpacing = 5f;

    }

}

测试,使用这个加水印的工具类,需要保证项目里面已有对应的字体

 public static void main(String[] args) throws Exception {
        try (final FileInputStream is = new FileInputStream(new File("1.pdf"));
             FileOutputStream out = new FileOutputStream(new File("test444.pdf"))
        ) {
            String s = "中国";
            final PdfReader reader = new PdfReader(is);

            MarkInfo m1 = new MarkInfo();
            m1.setPellucidity(45);
            m1.setPositionType(TILE);
            m1.setFontColor(Color.BLUE);
            m1.setFontFamily("KaiTi");
            m1.setMarkText(s);
            m1.setFontSize(18);
            m1.setRotation(30);
            m1.setXMargin(1f);
            m1.setYMargin(0.1f);

            final MarkInfo m2 = WrappedBeanCopier.copyProperties(m1, MarkInfo.class);
            m2.setRotation(30);
            m2.setFontFamily("NSimSun");
            m2.setPositionType(TOP_CENTER);
            final MarkInfo m3 = WrappedBeanCopier.copyProperties(m1, MarkInfo.class);
            m3.setRotation(45);
            m3.setFontFamily("SimHei");
            m2.setPositionType(TOP_CENTER);
            final MarkInfo m4 = WrappedBeanCopier.copyProperties(m1, MarkInfo.class);
            m4.setRotation(60);
            final MarkInfo m5 = WrappedBeanCopier.copyProperties(m1, MarkInfo.class);
            m5.setRotation(90);
            final MarkInfo m6 = WrappedBeanCopier.copyProperties(m1, MarkInfo.class);
            m6.setRotation(-30);
            final MarkInfo m7 = WrappedBeanCopier.copyProperties(m1, MarkInfo.class);
            m7.setRotation(-45);
            final MarkInfo m8 = WrappedBeanCopier.copyProperties(m1, MarkInfo.class);
            m8.setRotation(-60);
            final MarkInfo m9 = WrappedBeanCopier.copyProperties(m1, MarkInfo.class);
            m9.setRotation(-90);

            List<MarkInfo> list = new ArrayList<>();
            list.add(m1);
//            list.add(m2);
//            list.add(m3);
//            list.add(m4);
//            list.add(m5);
//            list.add(m6);
//            list.add(m7);
//            list.add(m8);
//            list.add(m9);

            addWaterMark(reader, out, list);
        }
    }```
## 效果图
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ef7014d2b7eb4e519d244e5da35745ca.png)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值