【Java】通过poi给word首页添加水印图片

news2024/11/24 13:48:00

背景:

poi并没有提供直接插入水印图片的方法,目前需要再word的首页插入一张水印图片,于是就需要通过另一种方式,插入透明图片(png格式)并将图片设置为“浮于文字上方”的方式实现该需求。

所需jar:

poi-3.8.jar
poi-ooxml-3.8.jar
poi-ooxml-schemas-3.8.jar
xmlbeans-2.6.0.jar
dom4j-1.6.1.jar

示例代码:

创建CustomXWPFDocument类

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;

import java.io.IOException;
import java.io.InputStream;

/**
 * 文档对象
 */
public class CustomXWPFDocument extends XWPFDocument {
	
	public CustomXWPFDocument() {
		super();
	}

	public CustomXWPFDocument(InputStream in) throws IOException {
		super(in);
	}

	public CustomXWPFDocument(OPCPackage pkg) throws IOException {
		super(pkg);
	}

	/**
	 * 创建一个包含图片的XWPFParagraph,将图片插入到指定或新建的段落中。
	 *
	 * @param blipId 图片在文档关系中的唯一标识(r:embed属性值),用于链接到图片数据。
	 * @param id     图片在文档中的唯一标识(cNvPr元素的id属性值)。
	 * @param width  图片的宽度,单位为像素。内部转换为EMU(English Metric Unit)单位。
	 * @param height 图片的高度,单位为像素。内部转换为EMU单位。
	 * @param paragraph 可选参数,要插入图片的XWPFParagraph。如果为null,则会创建一个新的段落。
	 */
	public void createPicture(String blipId, int id, int width, int height,
									 XWPFParagraph paragraph) {
		// 定义EMU(English Metric Unit)常量,1EMU等于1/914400米,是OpenXML中使用的长度单位。
		final int EMU = 9525;

		// 将像素宽度和高度转换为EMU单位。
		width *= EMU;
		height *= EMU;

		// 如果传入的段落为空,则创建一个新的段落。
		if (paragraph == null) {
			paragraph = createParagraph();
		}

		// 在段落中创建一个新的运行(Run),并在运行的底层XML结构(CTR)中添加一个新的内联图形(Inline)元素。
		CTInline inline = paragraph.createRun().getCTR().addNewDrawing()
				.addNewInline();

		// 构建包含图片详细信息的XML字符串,遵循OpenXML DrawingML规范。
		StringBuffer picXml = new StringBuffer();
		// 开始图形元素
		picXml.append("<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">")
				// 描述图形类型为图片
				.append( "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">")
				// 开始图片元素
				.append( "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">")
				// 包含非视觉属性的图片属性(名称、ID等)
				.append( "         <pic:nvPicPr>" )
				// 非视觉属性主元素(通用非视觉属性)设置图片ID
				.append( "            <pic:cNvPr id=\"")
				.append(id)
				.append( "\" name=\"img_")
				.append(id)
				.append( "\"/>")
				.append( "            <pic:cNvPicPr/>")
				.append( "         </pic:nvPicPr>")
				// 填充图片的属性(图片源、缩放方式等)
				.append( "         <pic:blipFill>")
				// 插入图片的Blip(位图)元素,引用图片数据
				.append( "            <a:blip r:embed=\"")
				.append(blipId)
				// 引用关系命名空间
				.append( "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>")
				// 设置图片填充方式为拉伸
				.append( "            <a:stretch>")
				.append( "               <a:fillRect/>")
				.append( "            </a:stretch>")
				.append( "         </pic:blipFill>")
				// 图片的形状属性(位置、尺寸、旋转等)
				.append( "         <pic:spPr>")
				// 图片的变换属性(平移、缩放)
				.append( "            <a:xfrm>")
				// 图片相对于容器的偏移(此处为原点)
				.append( "               <a:off x=\"0\" y=\"0\"/>")
				// 图片的扩展尺寸(宽度)
				.append( "               <a:ext cx=\"")
				.append(width)
				// 图片的扩展尺寸(高度)
				.append( "\" cy=\"")
				.append(height)
				.append( "\"/>")
				.append( "            </a:xfrm>")
				// 使用预定义形状(矩形)
				.append( "            <a:prstGeom prst=\"rect\">")
				// 空的可变几何形状属性列表(保留结构完整性)
				.append( "               <a:avLst/>")
				.append( "            </a:prstGeom>")
				.append( "         </pic:spPr>")
				.append( "      </pic:pic>")
				.append( "   </a:graphicData>")
				.append( "</a:graphic>");

		// 将构造的XML字符串解析为XmlToken对象
		XmlToken xmlToken = null;
		try {
			xmlToken = XmlToken.Factory.parse(picXml.toString());
		} catch (XmlException xe) {
			xe.printStackTrace();
		}

		// 将XmlToken对象设置到之前创建的CTInline元素中。
		inline.set(xmlToken);

		// 设置内联图形与周围文本的间距(顶部、底部、左侧、右侧均为0EMU)
		inline.setDistT(0);
		inline.setDistB(0);
		inline.setDistL(0);
		inline.setDistR(0);

		// 设置内联图形的实际尺寸(与构造的XML中一致,可能不是必需的,因为已经包含在XML中)
		CTPositiveSize2D extent = inline.addNewExtent();
		extent.setCx(width);
		extent.setCy(height);

		// 设置非视觉文档属性(名称、ID、描述等)
		CTNonVisualDrawingProps docPr = inline.addNewDocPr();
		docPr.setId(id);
		docPr.setName("docx_img_ " + id);
		docPr.setDescr("docx Picture");
	}

	/**
	 * 创建一个带有图形对象(如图片)的CTAnchor(锚点),用于将图形放置于Word文档中特定位置,并可设定其浮于文字上方或下方。
	 *
	 * @param ctGraphicalObject 要插入的图形对象,例如CTPicture(图片)对象。
	 * @param deskFileName 图片的描述性文件名,用于设置锚点的描述属性。
	 * @param width 图形对象的宽度,单位为EMU(English Metric Unit)。
	 * @param height 图形对象的高度,单位为EMU。
	 * @param leftOffset 图形相对于所在列左侧的偏移距离,单位为EMU。
	 * @param topOffset 图形相对于所在段落顶部的偏移距离,单位为EMU。
	 * @param behind 指定图形是否应位于文字下方。如果为true,则图形位于文字下方;否则位于文字上方。
	 * @return 生成的CTAnchor对象,包含了指定图形对象及定位信息。
	 */
	public static CTAnchor getAnchorWithGraphic(CTGraphicalObject ctGraphicalObject,
												String deskFileName, int width, int height,
												int leftOffset, int topOffset, boolean behind) {
		// 构建锚点的XML字符串,遵循WordProcessingML DrawingML规范。
		String anchorXML =
				// 开始wp:anchor元素,定义命名空间
				"<wp:anchor  xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" "
				// 设置锚点属性(简单定位、相对高度、文字前后关系、锁定状态、布局方式、重叠允许)
						+ "simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"" + ((behind) ? 1 : 0) + "\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">"
						// 设置简单定位信息(此处为原点)
						+ "<wp:simplePos x=\"0\" y=\"0\"/>"
						// 设置水平位置,相对于列
						+ "<wp:positionH relativeFrom=\"column\">"
						// 设置水平偏移距离
						+ "<wp:posOffset>" + leftOffset + "</wp:posOffset>"
						+ "</wp:positionH>"
						// 设置垂直位置,相对于段落
						+ "<wp:positionV relativeFrom=\"paragraph\">"
						+ "<wp:posOffset>" + topOffset + "</wp:posOffset>" +
						"</wp:positionV>"
						// 设置图形对象的尺寸
						+ "<wp:extent cx=\"" + width + "\" cy=\"" + height + "\"/>"
						// 设置图形边缘效果扩展距离(此处为默认值)
						+ "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"3175\"/>"
						// 设置无环绕模式,即图形不随文字换行而移动
						+ "<wp:wrapNone/>"
						// 设置文档属性(ID、名称、描述)
						+ "<wp:docPr id=\"0\" name=\"Drawing 0\" descr=\"" + deskFileName + "\"/><wp:cNvGraphicFramePr/>"
						+ "</wp:anchor>";

		// 将构造的XML字符串解析为CTDrawing对象。
		CTDrawing drawing = null;
		try {
			drawing = CTDrawing.Factory.parse(anchorXML);
		} catch (XmlException e) {
			e.printStackTrace();
		}
		
		// 从解析得到的CTDrawing对象中获取第一个(也是唯一一个)CTAnchor对象。
		CTAnchor anchor = drawing.getAnchorArray(0);
		
		// 将提供的图形对象(如CTPicture)设置到锚点中。
		anchor.setGraphic(ctGraphicalObject);
		return anchor;
	}
}

创建poitest类进行验证:

import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class poitest {

    public static void main(String[] args) throws IOException, InvalidFormatException {
        insertWatermark("D:\\document_ls\\bb.docx","1231321","D:\\document_ls\\");
    }


    /**
     * 向指定Word文档中插入水印图片,并将其设置为浮于文字之上。
     *
     * @param wdlj 输入Word文档路径,待插入水印的源文件路径。
     * @param bgxh 水印图片背景编号或其他标识,用于生成临时输出文件的名称。
     * @param parentPath 输出文件所在的父目录路径,用于保存临时文件和最终修改后的Word文档。
     * @throws IOException 在文件读写过程中发生I/O错误时抛出。
     * @throws InvalidFormatException 当处理的文件格式不符合预期或无法解析时抛出。
     */
    private static void insertWatermark(String wdlj, String bgxh, String parentPath) throws IOException, InvalidFormatException {
        FileOutputStream fos = null ;

        // 定义水印图片文件路径
        String pic = "D:\\document_ls\\reportPic.png";

        // 生成临时输出文件路径
        String outputPath = parentPath + bgxh + "_temp.docx";

        // 打开并解析输入Word文档,获得OPCPackage对象和CustomXWPFDocument对象
        OPCPackage pk = POIXMLDocument.openPackage(wdlj);
        CustomXWPFDocument doc = new CustomXWPFDocument(pk);

        // 获取文档的第一个段落
        XWPFParagraph para = doc.getParagraphs().get(0);

        // 将水印图片添加到文档的图片数据集中,并获取其在文档中的Blip ID
        String blipId = para.getDocument().addPictureData(new FileInputStream(new File(pic)), XWPFDocument.PICTURE_TYPE_PNG);

        // 在指定段落中创建图片,设置尺寸为350px x 350px,并关联到当前段落
        doc.createPicture(blipId, XWPFDocument.PICTURE_TYPE_PNG, 350, 350, para);

        // 从段落的第一个运行中获取对应的CTR对象,并从中找到包含图形对象的CTDrawing对象
        CTDrawing drawing = para.getRuns().get(0).getCTR().getDrawingArray(0);

        // 从CTDrawing对象中提取出CTGraphicalObject对象,即插入的图片
        CTGraphicalObject graphicalobject = drawing.getInlineArray(0).getGraphic();

        // 使用getAnchorWithGraphic方法将图形对象转换为浮于文字之上的CTAnchor对象,设置相关属性
        CTAnchor anchor = doc.getAnchorWithGraphic(graphicalobject, "Generated",
                Units.toEMU(350), Units.toEMU(350),
                Units.toEMU(70), Units.toEMU(200), false);

        // 替换CTDrawing中的内联图形对象为新创建的浮点锚点对象
        drawing.setAnchorArray(new CTAnchor[]{anchor});

        // 删除原有的内联图形对象,确保仅保留浮点锚点
        drawing.removeInline(0);

        // 创建文件输出流,将修改后的文档写入临时输出文件
        fos = new FileOutputStream(new File(outputPath));
        doc.write(fos);

        // 关闭OPCPackage以释放资源
        pk.close();

        // 移动临时文件至源文件路径,替换原有文档
        Files.move(Paths.get(outputPath), Paths.get(wdlj), StandardCopyOption.REPLACE_EXISTING);
    }
}

效果展示:

插入前:

插入后:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1600762.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI智能体技术突破:引领科技新浪潮

AI智能体技术突破&#xff1a;引领科技新浪潮 基于大模型的 AI Agent 工作流基于大模型的 AI Agent 工作流效果AI Agent 的四种设计模式Reflection 反思设计模式Tool use 工具使用设计模式Planning 规划设计模式Multiagent collaboration 多智能体协作设计模式 吴恩达在红杉美国…

吃透2000-2024年600道真题和解析,科学高效通过2025年AMC8竞赛

为帮助孩子科学、有效备考AMC8竞赛&#xff0c;我整理了2000-2004年的全部AMC8真题&#xff08;完整版共600道&#xff0c;且修正了官方发布的原试卷中的少量bug&#xff09;&#xff0c;并且独家制作成多种在线练习&#xff0c;利用碎片化时间&#xff0c;8个多月的时间足以通…

【C++题解】1317. 正多边形每个内角的度数?

问题&#xff1a;1317. 正多边形每个内角的度数&#xff1f; 类型&#xff1a;基本运算、小数运算 题目描述&#xff1a; 根据多边形内角和定理&#xff0c;正多边形内角和等于&#xff1a;&#xff08; n&#xff0d;2 &#xff09; 180∘ ( n 大于等于 3 且 n 为整数&#…

Backend - Django Swagger

目录 一、安装依赖 二、配置环境 三、路由&#xff08;urls&#xff09; 四、swagger UI 界面 &#xff08;一&#xff09;UI 界面 &#xff08;二&#xff09;单引号问题&#xff1a;Expecting property name enclosed in double quotes 1. 原因 2. 解决 五、自定义s…

Go 单元测试基本介绍

文章目录 引入一、单元测试基本介绍1.1 什么是单元测试&#xff1f;1.2 如何写好单元测试1.3 单元测试的优点1.4 单元测试的设计原则 二、Go语言测试2.1 Go单元测试概要2.2 Go单元测试基本规范2.3 一个简单例子2.3.1 使用Goland 生成测试文件2.3.2 运行单元测试2.3.3 完善测试用…

基于Adaboost模型的数据预测和分类matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 AdaBoost&#xff08;Adaptive Boosting&#xff09;是一种集成学习方法&#xff0c;由Yoav Freund和Robert Schapire于1995年提出&#xff0c;主要用于提高弱分类…

Docker 学习笔记(九):Docker 网络原理,理解 docker0,虚拟网卡,容器互联,以及跨网络连通

一、前言 记录时间 [2024-4-16] 系列文章简摘&#xff1a; Docker 学习笔记&#xff08;六&#xff09;&#xff1a;挑战容器数据卷技术一文通&#xff0c;实战多个 MySQL 数据同步&#xff0c;能懂会用&#xff0c;初学必备 Docker 学习笔记&#xff08;七&#xff09;&#x…

引领智能互联时代,紫光展锐赋能百业创新发展

随着5G技术的快速发展&#xff0c;各行各业对通信技术的需求也在不断升级。紫光展锐持续深耕5G垂直行业&#xff0c;不断推进5G标准演进&#xff0c;从R15到R16&#xff0c;再到R17&#xff0c;展锐携手生态合作伙伴&#xff0c;不断推出创新性解决方案&#xff0c;在5G RedCap…

FMEA赋能可穿戴设备:打造安全可靠的未来科技新宠!

在科技日新月异的今天&#xff0c;可穿戴设备已成为我们生活中不可或缺的一部分。它们以其便携性、智能化和个性化的特点&#xff0c;深受消费者喜爱。然而&#xff0c;随着可穿戴设备市场的快速扩张&#xff0c;其安全性和可靠性问题也日益凸显。为了确保产品质量&#xff0c;…

【Git】常用命令速查

目录 一、创建版本 二、修改和提交 三、查看提交历史 四、撤销 五、分支与标签 六、合并与衍合 七、远程操作 一、创建版本 命令简要说明注意事项git clone <url>克隆远程版本库 二、修改和提交 命令简要说明注意事项 三、查看提交历史 命令简要说明注意事项 …

Adobe AE(After Effects)2017下载地址及安装教程

Adobe After Effects是一款专业级别的视觉效果和动态图形处理软件&#xff0c;由Adobe Systems开发。它被广泛用于电影、电视节目、广告和其他多媒体项目的制作。 After Effects提供了强大的合成和特效功能&#xff0c;可以让用户创建出令人惊艳的动态图形和视觉效果。用户可以…

OpenCV轻松入门(八)——图片卷积

对图像和滤波矩阵进行逐个元素相乘再求和的操作就相当于将一个二维的函数移动到另一个二维函数的所有位置&#xff0c;这个操作就叫卷积。 卷积需要4个嵌套循环&#xff0c;所以它并不快&#xff0c;除非我们使用很小的卷积核。这里一般使用3x3或者5x5 图像滤波 图像滤波是尽…

Map与Set的模拟实现封装

目录 一. 底层原理 二. 红黑树节点的定义 三. 仿函数封装 四. 基本函数的封装 五. 迭代器的封装 5.1 迭代器的基本定义 5.2 *与->操作 5.3 迭代器的操作 5.3.1 右子树不为空 5.3.2 右子树为空 5.4 迭代器的--操作 5.4.1 当前节点的父节点…

CentOS 7开机启动过程,引导和服务,密码的修改

开机启动过程&#xff1a; 引导过程&#xff1a;1.开机自检(BIOS)->2.MBR引导->GRUB菜单->加载内核kernel->systemd进程初始化 程序&#xff1a;执行特定任务的一串代码&#xff0c;静态&#xff0c;存在硬盘中。 进程&#xff1a;运行中的程序叫进程&#xff0…

第十一章数据仓库和商务智能10分

【数据仓库-后端&#xff0c;商务智能-前端】 基本算法&#xff1a;关联关系&#xff08;牵手-谈恋爱&#xff09;&#xff0c;集群关系&#xff08;杭州人爱吃酸甜口&#xff09;&#xff0c;决策树&#xff0c;线性回归&#xff0c;贝叶斯&#xff0c;神经网络&#xff0c;时…

【Linux】磁盘扩容到根目录逻辑卷(LVM)

目录 一、物理卷和逻辑卷 1.物理卷和逻辑卷的区别 2.在Linux系统中查看所有物理卷的信息 3.在Linux系统中查看所有逻辑卷的信息 二、文件系统 三、实操-对root&#xff08;/&#xff09;目录进行扩容 1.使用lsblk命令查看新加入的磁盘信息 2.fdisk -l命令查看系统中磁盘…

景区导览系统平台|智能导览|数字人导游|VR游园

随着人工智能、元宇宙等技术的飞速发展&#xff0c;文旅行业正迎来一场前所未有的变革。道可云文旅元宇宙平台以其独特的智慧景区导览系统、元宇宙空间以及数字人导游等创新应用&#xff0c;为景区和游客带来了全新的旅游体验&#xff0c;也标志着文旅行业正式步入了元宇宙时代…

含多种需求响应及电动汽车的微网/虚拟电厂日前优化调度

1 主要内容 程序主要建立一个微网/虚拟电厂的日前优化调度模型&#xff0c;以燃气轮机运行成本、购售电费用、电动汽车电池损耗成本以及需求响应费用之和为目标&#xff0c;在日前经济调度模型中&#xff0c;加入了电动汽车模型&#xff0c;考虑了电动汽车出行规律以及充放电规…

华硕ROG幻16笔记本电脑模式切换管理工具完美替代华硕奥创中心管理工具

文章目录 华硕ROG幻16笔记本电脑模式切换管理工具完美替代华硕奥创中心管理工具1. 介绍2. 下载3. 静音模式、平衡模式、增强模式配置4. 配置电源方案与模式切换绑定5. 启动Ghelper控制面板6. 目前支持的设备型号 华硕ROG幻16笔记本电脑模式切换管理工具完美替代华硕奥创中心管理…

环境搭建创建项目_使用DevEco开发工具进行开发_创建项目_认识项目结构---HarmonyOS4.0+鸿蒙NEXT工作笔记001

首先去下载DevEco Studio然后再去安装就可以了 安装下一步下一步非常简单 首先去安装nodejs,可以看到,有两个安装方法,左边是自己安装的制定文件夹就可以了,然后 右边是使用鸿蒙自带的,我们选择第二个 然后我们看这个ohpm其实就跟npm是一个意思,用来管理鸿蒙的包的. 这里我们…