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

背景:

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);
    }
}

效果展示:

插入前:

插入后:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux解压4GB以上zip文件

Linux使用unzip解压大于4GB文件&#xff0c;会出现以下错误&#xff1a; 解决方法 安装p7zip yum -y install p7zip执行命令&#xff1a; 7za x MSRVTT.zip

Spark-机器学习(2)特征工程之特征提取

在之前的文章中&#xff0c;我们了解我们的机器学习&#xff0c;了解我们spark机器学习中的MLIib算法库&#xff0c;知道它大概的模型&#xff0c;熟悉并认识它。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&a…

HackMyVM-Connection

目录 信息收集 arp nmap WEB web信息收集 dirsearch smbclient put shell 提权 系统信息收集 suid gdb提权 信息收集 arp ┌─[rootparrot]─[~/HackMyVM] └──╼ #arp-scan -l Interface: enp0s3, type: EN10MB, MAC: 08:00:27:16:3d:f8, IPv4: 192.168.9.115 S…

js打印页面源码 ,打印选取的容器里的内容,打印指定内容

js打印页面源码 &#xff0c;打印选取的容器里的内容&#xff0c;打印指定内容 效果 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge&…

FreeRTOS时间管理

FreeRTOS时间管理 主要要了解延时函数&#xff1a; 相对延时&#xff1a;指每次延时都是从执行函数vTaskDelay()开始&#xff0c;直到延时指定的时间结束。 绝对延时&#xff1a;指将整个任务的运行周期看成一个整体&#xff0c;适用于需要按照一定频率运行的任务。 函数 vTa…

PTA图论的搜索题

目录 7-1 列出连通集 题目 输入格式: 输出格式: 输入样例: 输出样例: AC代码 7-2 六度空间 题目 输入格式: 输出格式: 输入样例: 输出样例: 思路 AC代码 7-3 地下迷宫探索 题目 输入格式: 输出格式: 输入样例1: 输出样例1: 输入样例2: 输出样例2: 思路 …

MySQL 试图

视图功能在 5.0 以后的版本启用 视图是一张虚表。数据表确实包含了具体数据并且保存到硬盘中的实表。视图使用数据检索语句动态生 成的一张虚表。每一次数据服务重启或者系统重启之后&#xff0c;在数据库服务启动期间&#xff0c;会使用创建视图的语 句重新生成视图中的数据&…

这家物流装备公司突破天际:销售额飙升至10亿美元,引领仓储机器人革命!...

导语 大家好&#xff0c;我是智能仓储物流技术研习社的社长&#xff0c;老K。专注分享智能仓储物流技术、智能制造等内容。 新书《智能物流系统构成与技术实践》 法国的Exotec公司在仓储自动化领域取得了显著成就&#xff0c;其销售额已超过10亿美元&#xff0c;成为全球物料搬…

考研数学|《1800》《1000》《660》《880》如何搭配❓

这几本书都是不同阶段对应的习题册 我觉得最舒服的使用就是方式就是基础阶段用《1800题基础部分》然后强化阶段主要刷《880题》并且强化阶段带着刷《660题》 上面是我的使用方式。之所以没有刷《1000题》是因为这本习题册的难度对我来说还是太大了&#xff0c;并且计算量很大…

上海计算机学会 2023年10月月赛 乙组T3 树的连通子图(树、树形dp)

第三题&#xff1a;T3树的连通子图 标签&#xff1a;树、树形 d p dp dp题意&#xff1a;给定一棵 n n n个结点的树&#xff0c; 1 1 1号点为这棵树的根。计算这棵树连通子图的个数&#xff0c;答案对 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007取余数。题解&#xff1…

HTML内联框架

前言&#xff1a; 我们有时候打开网页时会有广告窗的出现&#xff0c;而这些窗口并不是来自于本站的&#xff0c;而是来自于外部网页&#xff0c;只是被引用到了自己网页中而已。这一种技术可以通过内联来实现。 标签介绍&#xff1a; HTML 内联框架元素 (<iframe>) 表示…

基于Springboot的影城管理系统

基于SpringbootVue的影城管理系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页展示 电影信息 电影资讯 后台登录页 后台首页 用户管理 电影类型管理 放映…

RAG (Retrieval Augmented Generation) 结合 LlamaIndex、Elasticsearch 和 Mistral

作者&#xff1a;Srikanth Manvi 在这篇文章中&#xff0c;我们将讨论如何使用 RAG 技术&#xff08;检索增强生成&#xff09;和 Elasticsearch 作为向量数据库来实现问答体验。我们将使用 LlamaIndex 和本地运行的 Mistral LLM。 在开始之前&#xff0c;我们将先了解一些术…

vue3 生命周期(生命周期钩子 vs 生命周期选项 vs 缓存实例的生命周期)

vue3 支持两种风格书写&#xff1a;选项式 API 和组合式 API 若采用组合式 API &#xff0c;则使用生命周期钩子若采用选项式 API &#xff0c;则使用生命周期选项两者选用一种即可&#xff0c;不建议同时使用&#xff0c;避免逻辑紊乱。 生命周期钩子 在 setup 中使用 onBefo…

Vue 阶段练习:记事本

将 Vue快速入门 和 Vue 指令的学习成果应用到实际场景中&#xff08;如该练习 记事本&#xff09;&#xff0c;我们能够解决实际问题并提升对 Vue 的技能掌握。 目录 功能展示 需求分析 我的代码 案例代码 知识点总结 功能展示 需求分析 列表渲染删除功能添加功能底部统计…

3D目标检测实用技巧(二)- 实现点云(or 体素)向图像平面的投影并可视化

一、引言 受Focals Conv的启发&#xff0c;该论文中通过将点云投影到图片中清晰展现出点云学习后的情况&#xff1a; 本次实现的是体素向图像投影并显示&#xff0c;实现出来的效果如下&#xff1a; 二、 实现细节 1、体素投影到图像坐标系 这里我们参考的是VirConv的投影函…

通过matlab分别对比PSO,反向学习PSO,多策略改进反向学习PSO三种优化算法

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 粒子群优化算法 (PSO) 4.2 反向学习粒子群优化算法 (OPSO) 4.3 多策略改进反向学习粒子群优化算法 (MSO-PSO) 5.完整程序 1.程序功能描述 分别对比PSO,反向学习PSO,多策略改进反向学…

百货商场用户画像描绘与价值分析

目录 内容概述数据说明实现目标技术点主要内容导入模块1.项目背景1.1 项目背景与挖掘目标 2.数据探索与预处理2.1 结合业务对数据进行探索并进行预处理2.2 将会员信息表和销售流水表关联与合并 3 统计分析3.1 分析会员的年龄构成、男女比例等基本信息3.2 分析会员的总订单占比&…

Python 入门指南(四)

原文&#xff1a;zh.annas-archive.org/md5/97bc15629f1b51a0671040c56db61b92 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十章&#xff1a;哈希和符号表 我们之前看过列表&#xff0c;其中项目按顺序存储并通过索引号访问。索引号对计算机来说很有效。它们是整…

使用 Docker 部署 SurveyKing 调查问卷系统

1&#xff09;SurveyKing 介绍 SurveyKing 是一款功能强大、操作简便的开源问卷系统。它不仅满足了用户对问卷调查的基本需求&#xff0c;还提供了丰富的逻辑设置和灵活的问题设置&#xff0c;使得问卷制作更加智能化和个性化。此外&#xff0c;SurveyKing 还具有快速部署和安全…