当前位置: 首页 > article >正文

Java实现 itext PDF文件打印水印(文字和图片水印)

itext PDF文件打印水印

前言:公司有个需求,打印的合同模板要加上水印,但是由于itext版本问题,网上千篇一律的方法基本都没办法有效使用,然后自己琢磨下,记录下实现水印的功能的笔记。多页上都加上水印,核心就是加上自定义处理器实现水印功能.

PS: 这个也不是适用于itextpdf的所有版本,项目中引用的版本是: com.itextpdf:kernel:7.2.3,如其他版本不适用请自行调整代码

1.实现文字水印

实现的效果:水印加上透明度,不影响原先的内容,文字45度倾斜,文体是自定义字体,然后就是平铺真个整个页面。

TextWatermarkEventHandler
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.extgstate.PdfExtGState;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TextWatermarkEventHandler implements IEventHandler {

    private String watermarkText;
    private PdfFont font;
    private float fontSize;
    private static final float WIDTH_SPACING = 20;
    private static final float HEIGHT_SPACING = 130;
    private static final float ANGLE = 45;
    private static final float TRANSPARENCY = 0.3f;

    // 构造函数,传入水印文字、字体、大小、水平间距、垂直间距和旋转角度
    public TextWatermarkEventHandler(String text, PdfFont font, float fontSize) {
        this.watermarkText = text;
        this.font = font;
        this.fontSize = fontSize;
    }

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdfDoc = docEvent.getDocument();
        PdfPage page = docEvent.getPage();

        // 获取页面大小
        Rectangle pageSize = page.getPageSize();
        PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);

        // 计算可用的页面宽度和高度
        float availableWidth = pageSize.getWidth();
        float availableHeight = pageSize.getHeight();

        // 创建图形状态并设置透明度
        PdfExtGState gState = new PdfExtGState();
        gState.setFillOpacity(TRANSPARENCY); // 设置透明度为 0.3

        // 文字的宽度
        float textWidth = font.getWidth(watermarkText, fontSize);

        // 调整初始绘制位置
        float startXOffset = -textWidth; // 从负数开始
        float startYOffset = -availableWidth;  // 从负数开始

        // 绘制每一行水印文字
        for (float startY = startYOffset; startY < availableHeight + fontSize; startY += fontSize + HEIGHT_SPACING) {
            // 保存当前画布状态,确保透明度只影响水印
            canvas.saveState();

            // 设置透明度,仅影响水印
            canvas.setExtGState(gState);

            // 设置字体和大小
            canvas.setFontAndSize(font, fontSize);

            // 设置文字颜色(灰色水印)
            canvas.setFillColor(ColorConstants.LIGHT_GRAY);

            // 整行旋转
            canvas.concatMatrix((float) Math.cos(Math.toRadians(ANGLE)), (float) Math.sin(Math.toRadians(ANGLE)),
                    (float) -Math.sin(Math.toRadians(ANGLE)), (float) Math.cos(Math.toRadians(ANGLE)),
                    0, startY);
            // 绘制这一行的文字水印
            for (float x = startXOffset; x < availableWidth + textWidth; x += textWidth + WIDTH_SPACING) {
                canvas.beginText();
                canvas.moveText(x, 0);
                canvas.showText(watermarkText);
                canvas.endText();
            }
            // 恢复画布状态,确保透明度只影响水印
            canvas.restoreState();
        }
        // 释放画布资源
        canvas.release();
    }
}
调用TextWatermarkEventHandler监听器
// 自定义字体
String fontPath = WebUtils.getClasspath()+ File.separator+ "static" + File.separator + "font" + File.separator+"simsun.ttf";
PdfFont watermarkFont = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
TextWatermarkEventHandler watermarkEventHandler = new TextWatermarkEventHandler("你是个大聪明呀",watermarkFont,30);
pdf.addEventHandler(PdfDocumentEvent.END_PAGE, watermarkEventHandler);
实现的效果(截取部分)

在这里插入图片描述

2.实现图片水印

实现的效果:水印加上透明度,不影响原先的内容,图片45度倾斜,图片进行缩放处理,然后就是平铺真个整个页面。(ps:此处的图片是经过缩放处理后的图片了)

ImageWatermarkEventHandler
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.extgstate.PdfExtGState;
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ImageWatermarkEventHandler implements IEventHandler {

    private String imagePath;

    private static final float SCALE = 1;
    private static final float WIDTH_SPACING = 20;
    private static final float HEIGHT_SPACING = 130;
    private static final float ANGLE = 45;

    private static final float TRANSPARENCY = 0.3f;
    // 构造函数,传入水印图片路径
    public ImageWatermarkEventHandler(String imagePath) {
        this.imagePath = imagePath;
    }

    @Override
    public void handleEvent(Event event) {
        // 获取事件类型
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdfDoc = docEvent.getDocument();
        PdfPage page = docEvent.getPage();

        // 获取页面尺寸
        Rectangle pageSize = page.getPageSize();
        PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);

        ImageData imageData = null;
        try {
            imageData = ImageDataFactory.create(imagePath);
            PdfImageXObject watermarkImage = new PdfImageXObject(imageData);
            // 获取图片的原始宽高,按比例缩放
            float imgWidth = watermarkImage.getWidth() ;
            float imgHeight = watermarkImage.getHeight() ;
            // 计算可用的页面宽度和高度
            float availableWidth = pageSize.getWidth();
//            float availableHeight = pageSize.getHeight();
            // 创建图形状态并设置透明度
            PdfExtGState gState = new PdfExtGState();
            gState.setFillOpacity(TRANSPARENCY); // 设置透明度为 0.3

            // 在页面上平铺水印图片
            for (float y = -availableWidth; y < pageSize.getHeight()*2; y += imgHeight + HEIGHT_SPACING) {
                for (float x = 0; x < pageSize.getWidth()*2; x += imgWidth + WIDTH_SPACING) {
                    // 保存当前的画布状态
                    canvas.saveState();

                    // 设置水印的透明度
                    canvas.setExtGState(gState);

                    // 将图片水印旋转
                    canvas.concatMatrix(Math.cos(Math.toRadians(ANGLE)),Math.sin(Math.toRadians(ANGLE)),
                            -Math.sin(Math.toRadians(ANGLE)), Math.cos(Math.toRadians(ANGLE)),
                            0, 0); // 旋转
                    canvas.addXObjectAt(watermarkImage, x, y); // x, y 位置
                    // 恢复画布状态
                    canvas.restoreState();
                }
            }

            // 结束页面修改
            canvas.release();
        } catch (Exception e) {
            log.error("pdf添加水印报错:{}",e.getMessage());
        }
    }
}
调用TextWatermarkEventHandler监听器
String watermarkImagePath = WebUtils.getClasspath()+ File.separator+ "static" + File.separator + "image" + File.separator+"xxxx.jpg";
ImageWatermarkEventHandler watermarkEventHandler = new ImageWatermarkEventHandler(watermarkImagePath);
pdf.addEventHandler(PdfDocumentEvent.END_PAGE, watermarkEventHandler);
实现的效果(截取部分)

在这里插入图片描述


http://www.kler.cn/news/368334.html

相关文章:

  • 1024——视触觉传感器GelSight的前世今生
  • GoogleChrome的安装和使用
  • vue面试题+wx-open-launch-app开放标签唤醒app方法
  • 服务器虚拟化实践
  • esp32学习:语音识别教程esp-skainet库的使用
  • MyBatis 配置详解
  • Vue前端开发:双向数据绑定之v-model与修饰符
  • 基于STM32的水产品运输监测系统设计与实现
  • 湖南(满意度调查)源点咨询 市场调研中定量调研方式的运用技巧
  • 使用ceph-csi把ceph-fs做为k8s的storageclass使用
  • 基于vite和vue3、 eslint、prettier、stylelint、husky规范
  • Python实现贝叶斯优化器(Bayes_opt)优化简单循环神经网络分类模型(SimpleRNN分类算法)项目实战
  • count(1)、count(*)、count(主键)、count(字段)区别
  • openlayers 封装加载本地geojson数据 - vue3
  • git stash和git stash pop
  • Linux之nginx离线安装
  • 图文详解ChatGPT-o1完成论文写作的全流程
  • android openGL ES详解——缓冲区VBO/VAO/EBO/FBO/离屏渲染
  • 使用Node.js和Express构建RESTful API
  • 机器学习介绍
  • QT 信号重载时的处理方法
  • jmeter自定义函数
  • 浅谈网络 | 通信协议
  • Leetcode刷题笔记13
  • 16天自制CppServer-day05
  • apply,call,bind手写