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

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;}@Overridepublic 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;}@Overridepublic 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);
实现的效果(截取部分)

在这里插入图片描述

相关文章:

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

itext PDF文件打印水印 前言&#xff1a;公司有个需求&#xff0c;打印的合同模板要加上水印&#xff0c;但是由于itext版本问题&#xff0c;网上千篇一律的方法基本都没办法有效使用&#xff0c;然后自己琢磨下&#xff0c;记录下实现水印的功能的笔记。多页上都加上水印&…...

面经之一:Synchronized与ReentrantLock区别

Synchronized与ReentrantLock是Java中用于实现线程同步的两种主要机制&#xff0c;它们各有特点和适用场景。以下是它们的主要区别&#xff1a; 实现方式&#xff1a; Synchronized&#xff1a;是Java语言内置的关键字&#xff0c;通过JVM层面的监视器&#xff08;Monitor&…...

论文速读:面向单阶段跨域检测的域自适应YOLO(ACML2021)

原文标题&#xff1a;Domain Adaptive YOLO for One-Stage Cross-Domain Detection 中文标题&#xff1a;面向单阶段跨域检测的域自适应YOLO 1、Abstract 域转移是目标检测器在实际应用中推广的主要挑战。两级检测器的域自适应新兴技术有助于解决这个问题。然而&#xff0c;两…...

React中在map遍历中,给虚拟标签(<></>)加key

有时我们可能会需要在遍历时使用虚拟标签包裹内容&#xff0c;而不使用实际标签 &#xff0c;这种时候会有一个问题&#xff0c;就是虚拟标签无法加key&#xff0c;这样控制台会一直有警告。 {[1,2,3,4].map(v><><div></div><div></div><…...

大数据生态守护:Hadoop的深度保护策略

PART 1 从Hadoop运行原理透视数据保护需求 1、Hadoop的定义与范畴 Hadoop&#xff0c;狭义而言&#xff0c;是一个专为大数据设计的分布式存储与计算平台&#xff0c;其核心组件包括HDFS&#xff08;Hadoop分布式文件系统&#xff09;、MapReduce&#xff08;分布式计算框架&a…...

代码欣赏之:此题易错在 a+b 非要写成 a-fabs(b).因为这样就成了浮点值了,得不到准确数

代码欣赏之&#xff1a;此题易错在 ab 非要写成 a-fabs(b).因为这样就成了浮点值了&#xff0c;得不到准确数 7-23 小孩子才做选择&#xff0c;大人全都要 #include<stdio.h> #include<math.h> int main() {int a,b;scanf("%d %d",&a,&b);if(a&…...

ECharts饼图-环形图,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个饼图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供详…...

arcgis js 怎么加载geoserver发布的wms服务

arcgis js api加载wms服务&#xff0c;官方的参考样例&#xff1a; WMSLayer | Sample Code | ArcGIS Maps SDK for JavaScript 4.30 | Esri Developer 按照官方样例加载比较奇怪&#xff0c;我们平常习惯用url或者json的方式加载&#xff0c;稍微改一下就行&#xff0c;如下…...

前端_006_Vue2

文章目录 vue常用属性生命周期模版语法自定义组件全局注册 单文件组件路由 本文全部参考Vue2 简介&#xff1a;Vue是一个数据响应式&#xff0c;MVVM模型的JS框架 官网&#xff1a;https://v2.cn.vuejs.org/v2/guide/ API&#xff1a;https://v2.cn.vuejs.org/v2/api/#method…...

论多端数据互通网游的架构评估

摘要 在2023年&#xff0c;笔者参与了一款多端数据互通网络游戏的架构评估工作&#xff0c;并担任评估团队的核心成员。该游戏支持PC、移动设备和游戏机等多种终端&#xff0c;实现了数据的实时互通。本文通过该项目的评估实践&#xff0c;探讨了多端数据互通网游架构评估的关…...

网页HTML编写练习:华语榜中榜

网页效果 HTML代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice…...

C++ 编程基础:深入理解 `pair`(键值对) 和 `unordered_map`(无序映射)

C 编程基础&#xff1a;深入理解 pair&#xff08;键值对&#xff09; 和 unordered_map&#xff08;无序映射&#xff09; 在 C 标准库中&#xff0c;pair&#xff08;键值对&#xff09;和 unordered_map&#xff08;无序映射&#xff09;是两种常用的数据结构&#xff0c;它…...

高德动态地图

1.搭建页面结构 <div class"dataAllBorder02" style"position: relative; overflow: hidden;"><div class"map_title_box" style"height: 6%"><div class"map_title_innerbox"><div class"map_t…...

springboot集成camunda学习与使用

springboot集成camunda学习与使用.md 0、前言一、Spring Boot 集成camunda流程引擎1.新建全新的springboot工程2.添加pom.xml依赖3.启动Spring Boot工程4.切换成mysql数据库5.设计并部署一个BPMN流程6.camunda流程引擎测试6.1 通过camunda web控制台测试6.2 通过camunda rest接…...

微服务架构学习笔记

#1024程序员节|征文# 微服务架构作为现代软件开发中的热门技术架构&#xff0c;因其灵活性和可扩展性&#xff0c;逐渐成为许多企业系统设计的首选。以下是关于微服务的一些学习笔记&#xff0c;涵盖微服务的核心概念、优缺点、设计原则以及常用工具等方面。 1. 微服务是什么&…...

代码优化之简化if臃肿的判断条件

简化if判断条件 方法1&#xff1a; #include <iostream> #include <vector> #include <functional>// 封装参数的结构体 struct ConditionParams {int facenum;double zoomRatio;int iso;double facelv;int face_w;double qualityScore;int xx;int yy; };//…...

【OpenAI】第六节(语音生成与语音识别技术)从 ChatGPT 到 Whisper 的全方位指南

前言 在人工智能的浪潮中&#xff0c;语音识别技术正逐渐成为我们日常生活中不可或缺的一部分。随着 OpenAI 的 Whisper 模型的推出&#xff0c;语音转文本的过程变得前所未有的简单和高效。无论是从 YouTube 视频中提取信息&#xff0c;还是将播客内容转化为文本&#xff0c;…...

Docker 下备份恢复oracle

1.docker导出容器镜像 ##docker save -o 导出后的镜像名称.tar 容器名称|镜像id docker save -o oracle_11g.tar 3fa112fd3642 2.下载镜像上传镜像略 3.加载镜像 ##docker load -i <archive_file> docker load -i oracle11g11201.tar 4.添加版本号…...

oneplus3t-android_framework

0.确认oneplus6 root正常 oneplus6 root材料 oneplus6手机恢复出厂设置 &#xff0c; 或者 线刷 enchilada_22_K.52_210716_repack--HOS-10.0.11.zip &#xff1a; https://gitee.com/OnePlus6-brick-enchilada_22_K_52_210716_repack-HOS-10_0_11-zip OnePlus6Hydrogen_22…...

偷懒总结篇|贪心算法|动态规划|单调栈|图论

由于这周来不及了&#xff0c;先过一遍后面的思路&#xff0c;具体实现等下周再开始详细写。 贪心算法 这个图非常好 122.买卖股票的最佳时机 II(妙&#xff0c;拆分利润) 把利润分解为每天为单位的维度&#xff0c;需要收集每天的正利润就可以&#xff0c;收集正利润的区间…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

基于单片机的宠物屋智能系统设计与实现(论文+源码)

本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢&#xff0c;连接红外测温传感器&#xff0c;可实时精准捕捉宠物体温变化&#xff0c;以便及时发现健康异常&#xff1b;水位检测传感器时刻监测饮用水余量&#xff0c;防止宠物…...

13.10 LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析

LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析 LanguageMentor 对话式训练系统架构与实现 关键词:多轮对话系统设计、场景化提示工程、情感识别优化、LangGraph 状态管理、Ollama 私有化部署 1. 对话训练系统技术架构 采用四层架构实现高扩展性的对话训练…...