当前位置: 首页 > 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;收集正利润的区间…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...