开源PDF工具 Apache PDFBox 认识及使用(知识点+案例)

源码获取

项目源码:GiteeGithub

本篇文档的视频系列讲解:Java实现自动化pdf打水印工具 开源PDF工具PDFBoxWord、Word转PDF开源工具Documents4j


一、认识PDFBox

Apache PDFBox库是一个开源的Java工具,专门用于处理PDF文档。它允许用户创建全新的PDF文件,编辑现有的PDF文档,以及从PDF文件中提取内容。

功能:创建、渲染、打印、合并、拆分、加密、解密、签名等多种操作PDF文件的功能,包括一个命令行工具,可以用于执行各种PDF处理任务。支持文本提取和搜索,以及将PDF转换为其他格式,如图片和文本。

应用场景:广泛应用于企业和开发者构建PDF处理相关的应用程序和工具。

Apache PDFBox具备以下主要功能:

  • 从PDF文件中提取Unicode文本。
  • 将单个PDF文件拆分成多个文件,或将多个PDF文件合并成一个。
  • 从PDF表单中提取数据,或填写PDF表单。
  • 验证PDF文件是否符合PDF/A-1b标准。
  • 使用标准的Java打印API打印PDF文件。
  • 将PDF文件另存为图像格式,如PNG或JPEG。
  • 从零开始创建PDF文件,包括嵌入字体和图像。
  • 对PDF文件进行数字签名。

二、导入依赖

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.apache.pdfbox</groupId>
        <artifactId>pdfbox</artifactId>
        <version>2.0.28</version>
    </dependency>
</dependencies>

三、基础功能

demo1:读取pdf所有内容

java 复制代码
package com.changlu.demos;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;

import java.io.File;
import java.net.URLDecoder;

/**
 * @Description:
 * @Author: changlu
 * @Date: 1:28 PM
 */
public class Demo1 {
    public static void main(String[] args) throws Exception{
        //读取resources目录下input.pdf文件
        String inputFile = URLDecoder.decode(Demo1.class.getClassLoader().getResource("input.pdf").getFile(), "UTF-8");
        PDDocument pdDocument = PDDocument.load(new File(inputFile));
        PDFTextStripper pdfTextStripper = new PDFTextStripper();
        //读取pdf中所有的文件
        String fullText = pdfTextStripper.getText(pdDocument);
        System.out.println(fullText);
    }
}

demo2:读取所有页内容(分页)

java 复制代码
package com.changlu;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;

import java.io.InputStream;

/**
 * @Description:
 * @Author: changlu
 * @Date: 11:19 AM
 */
public class Main {
    public static void main(String[] args) throws Exception{
        //读取resources目录下input.pdf文件
        InputStream is = Main.class.getClassLoader().getResourceAsStream("input.pdf");
        PDDocument pdDocument = PDDocument.load(is);
        PDFTextStripper pdfTextStripper = new PDFTextStripper();
        //读取所有的分页
        for (int i = 1; i <= pdDocument.getNumberOfPages(); i++) {
            //设置起始-结束页  这里设置指定某页
            pdfTextStripper.setStartPage(i);
            pdfTextStripper.setEndPage(i);
            //读取每一页
            String pageText = pdfTextStripper.getText(pdDocument);
            System.out.println(String.format("第%s页读取内容:", i));
            System.out.println(pageText);
        }
    }
}

demo3:添加页眉、页脚

要求:页眉页脚居中显示。

java 复制代码
package com.changlu.demos;

import com.changlu.Main;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @Description: 添加页眉、页脚
 * @Author: changlu
 * @Date: 1:38 PM
 */
public class Demo3 {
    public static void main(String[] args) throws Exception{
        //读取resources目录下input.pdf文件
        InputStream is = Main.class.getClassLoader().getResourceAsStream("input.pdf");
        PDDocument pdDocument = PDDocument.load(is);

        //自定义字体 C:\Users\93997\Desktop\watermark tools\watermarkTools\target\classes\ttfs
        //URLDecoder.decode() 方法来解码 URL 编码的路径,将 %20 转换回空格
        String fontFile = URLDecoder.decode(Main.class.getClassLoader().getResource(File.separator + "ttfs" + File.separator + "Alibaba_PuHuiTi_2.0_65_Medium_65_Medium.ttf").getFile(), "UTF-8");
        PDType0Font font = PDType0Font.load(pdDocument, new File(fontFile));
        float fontSize = 10; // 设置字体大小为12

        // 设置透明度状态对象
        PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
        graphicsState.setNonStrokingAlphaConstant(0.2f);
        graphicsState.setAlphaSourceFlag(true);
        graphicsState.setStrokingAlphaConstant(0.2f);

        //设置新的页眉
        String headerText = "咨询专转本默默学课程联系官方报名处QQ:3503851091,更多资料可加群828303961";
        String footerText = "江苏专转本公众号:专转本智慧树";

        //遍历原先的pdf文档
        for (PDPage page : pdDocument.getPages()) {
            float pageWidth = page.getMediaBox().getWidth();
            //计算页眉的居中位置
            float headerTextWidth = font.getStringWidth(headerText) / 1000 * fontSize;
            float headerCenteredX = (pageWidth - headerTextWidth) / 2; // 计算水平居中位置
            //计算页脚的居中位置
            float footerTextWidth = font.getStringWidth(footerText) / 1000 * fontSize;
            float footerCenteredX = (pageWidth - footerTextWidth) / 2; // 计算水平居中位置

            // 创建用于页眉的内容流
            PDPageContentStream headerContentStream = new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true, true);
            headerContentStream.beginText(); // 开始文本操作
            headerContentStream.setFont(font, fontSize); // 设置字体和字号
            headerContentStream.newLineAtOffset(headerCenteredX, page.getMediaBox().getHeight() - 30); // 设置文本起始位置
            headerContentStream.showText(headerText); // 绘制页眉内容
            headerContentStream.endText(); // 结束文本操作
            headerContentStream.close(); // 关闭内容流

            // 添加页脚
            PDPageContentStream footerContentStream = new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true, true);
            footerContentStream.beginText(); // 开始文本操作
            footerContentStream.setFont(font, fontSize); // 设置字体和字号
            footerContentStream.newLineAtOffset(footerCenteredX, 30); // 设置文本起始位置
            footerContentStream.showText(footerText); // 绘制页脚内容
            footerContentStream.endText(); // 结束文本操作
            footerContentStream.close(); // 关闭内容流
        }

        //目标目录 Thread.currentThread().getContextClassLoader().getResource("").getPath()  当前工程目录路径
        //String targetPDFPath = URLDecoder.decode(Demo3.class.getClassLoader().getResource("resources").getPath() + File.separator + "output.pdf", "UTF-8");
//        String targetPDFPath = URLDecoder.decode(Main.class.getClassLoader().getResource("output.pdf").getFile(), "UTF-8");
        String targetPDFPath = "F:\\00核心知识、成果、视频产出区\\技术视频\\2024.2.15 自制默默学打水印工具 watermark tools\\watermarkTools\\src\\main\\resources\\output.pdf";
        File outputFile = new File(targetPDFPath);
        // 若是文件存在先进行删除
        Files.deleteIfExists(Paths.get(outputFile.toURI()));
        // 保存修改后的文档
        pdDocument.save(outputFile);
        System.out.println("转换任务:" + targetPDFPath + " 成功!");
        // 关闭文档
        pdDocument.close(); // 关闭文档
    }
}

效果:


demo4:添加居中45°文字水印

要求:对pdf每页都添加上旋转45°水印,透明度为20%。

java 复制代码
package com.changlu.demos;

import com.changlu.Main;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;

import java.io.File;
import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @Description: Apache PDFBox案例:对pdf每页都添加上旋转45°水印。
 * @Author: changlu
 * @Date: 1:38 PM
 */
public class Demo4 {
    public static void main(String[] args) throws Exception{
        //读取resources目录下input.pdf文件
        InputStream is = Main.class.getClassLoader().getResourceAsStream("input.pdf");
        PDDocument pdDocument = PDDocument.load(is);

        //自定义字体 C:\Users\93997\Desktop\watermark tools\watermarkTools\target\classes\ttfs
        //URLDecoder.decode() 方法来解码 URL 编码的路径,将 %20 转换回空格
        String fontFile = URLDecoder.decode(Main.class.getClassLoader().getResource(File.separator + "ttfs" + File.separator + "Alibaba_PuHuiTi_2.0_65_Medium_65_Medium.ttf").getFile(), "UTF-8");
        PDType0Font font = PDType0Font.load(pdDocument, new File(fontFile));

        // 设置透明度状态对象
        PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
        graphicsState.setNonStrokingAlphaConstant(0.2f);
        graphicsState.setAlphaSourceFlag(true);
        graphicsState.setStrokingAlphaConstant(0.2f);

        //设置水印名
        String waterText = "江苏专转本网课报名vx:mmxchanglu";

        //遍历原先的pdf文档
        for (PDPage page : pdDocument.getPages()) {
            float pageWidth = page.getMediaBox().getWidth();
            float pageHeight = page.getMediaBox().getHeight();
            //添加水印   要求:旋转45°,不透明度30%
            float waterTextWidth = font.getStringWidth(waterText) / 1000 * 30;
            float waterCenteredX = (pageWidth - waterTextWidth) / 2;
            float waterCenteredY = pageHeight / 2;
            //创建一个水印内容流
            PDPageContentStream waterContentStream = new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true, true);
            waterContentStream.beginText();
            waterContentStream.setFont(font, 30);
            // 设置不透明度
            waterContentStream.setNonStrokingColor(0, 0, 0); // black color
            waterContentStream.setStrokingColor(0, 0, 0); // black color
            waterContentStream.setGraphicsStateParameters(graphicsState);//设置透明度
            //设置旋转文本 45° 对于tx、ty是以左下角为偏移位置中心来进行旋转角度
            waterContentStream.setTextRotation(Math.toRadians(45), 400, -50);
            //设置文本
            waterContentStream.newLineAtOffset(waterCenteredX, waterCenteredY);
            waterContentStream.showText(waterText);
            waterContentStream.endText();
            waterContentStream.close();
        }

        //目标目录 Thread.currentThread().getContextClassLoader().getResource("").getPath()  当前工程目录路径
        String targetPDFPath = "F:\\00核心知识、成果、视频产出区\\技术视频\\2024.2.15 自制默默学打水印工具 watermark tools\\watermarkTools\\src\\main\\resources\\output.pdf";
        File outputFile = new File(targetPDFPath);
        // 若是文件存在先进行删除
        Files.deleteIfExists(Paths.get(outputFile.toURI()));
        // 保存修改后的文档
        pdDocument.save(outputFile);
        System.out.println("转换任务:" + targetPDFPath + " 成功!");
        // 关闭文档
        pdDocument.close(); // 关闭文档
    }
}

效果:


demo5:添加图片到右上角

要求:将图片缩小25%后插入到右上角。

java 复制代码
package com.changlu.demos;

import com.changlu.Main;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;

import java.io.File;
import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @Description: Apache PDFBox案例:将图片缩小25%后插入到右上角。
 * @Author: changlu
 * @Date: 1:38 PM
 */
public class Demo5 {
    public static void main(String[] args) throws Exception{
        //读取resources目录下input.pdf文件
        InputStream is = Main.class.getClassLoader().getResourceAsStream("input.pdf");
        PDDocument pdDocument = PDDocument.load(is);

        //自定义字体 C:\Users\93997\Desktop\watermark tools\watermarkTools\target\classes\ttfs
        //URLDecoder.decode() 方法来解码 URL 编码的路径,将 %20 转换回空格
        String fontFile = URLDecoder.decode(Main.class.getClassLoader().getResource(File.separator + "ttfs" + File.separator + "Alibaba_PuHuiTi_2.0_65_Medium_65_Medium.ttf").getFile(), "UTF-8");
        PDType0Font font = PDType0Font.load(pdDocument, new File(fontFile));

        //遍历原先的pdf文档
        for (PDPage page : pdDocument.getPages()) {
            float pageWidth = page.getMediaBox().getWidth();
            float pageHeight = page.getMediaBox().getHeight();
            //添加图片水印
            //创建一个水印内容流
            PDPageContentStream imageContentStream = new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true, true);
            // 创建图像对象
//            PDImageXObject image = PDImageXObject.createFromFile("C:\\Users\\93997\\Desktop\\watermark tools\\watermarkTools\\src\\main\\resources\\images\\ConsultationGroupQRCode.jpg", pdDocument);
            String pictureFile = URLDecoder.decode(Main.class.getClassLoader().getResource(File.separator + "images" + File.separator + "ConsultationGroupQRCode.jpg").getFile(), "UTF-8");
            PDImageXObject image = PDImageXObject.createFromFile(pictureFile, pdDocument);
            // 计算图像的宽度和高度(缩小比例为0.3)
            float imageWidth = (float) (image.getWidth() * 0.25);
            float imageHeight = (float) (image.getHeight() * 0.25);
            //具体图片位置
            float imageX = pageWidth - imageWidth - 10;
            float imageY = pageHeight - imageHeight - 10;

            // 在指定位置绘制图像
            imageContentStream.drawImage(image, imageX, imageY, imageWidth, imageHeight);
            imageContentStream.close();
        }

        //目标目录 Thread.currentThread().getContextClassLoader().getResource("").getPath()  当前工程目录路径
        String targetPDFPath = "F:\\00核心知识、成果、视频产出区\\技术视频\\2024.2.15 自制默默学打水印工具 watermark tools\\watermarkTools\\src\\main\\resources\\output.pdf";
        File outputFile = new File(targetPDFPath);
        // 若是文件存在先进行删除
        Files.deleteIfExists(Paths.get(outputFile.toURI()));
        // 保存修改后的文档
        pdDocument.save(outputFile);
        System.out.println("转换任务:" + targetPDFPath + " 成功!");
        // 关闭文档
        pdDocument.close(); // 关闭文档
    }
}

效果:


参考文章

1\]. [使用 Apache PDFBox 操作PDF文件](https://link.juejin.cn?target=https%3A%2F%2Fblog.csdn.net%2Fheihaozi%2Farticle%2Fdetails%2F130245373 "https://blog.csdn.net/heihaozi/article/details/130245373") \[2\]. [Java使用pdfbox将已有的pdf添加页眉](https://link.juejin.cn?target=https%3A%2F%2Fblog.51cto.com%2Fu_16213397%2F8976931 "https://blog.51cto.com/u_16213397/8976931") \[3\]. [基于pdfbox实现的pdf添加文字水印工具](https://link.juejin.cn?target=https%3A%2F%2Fwww.cnblogs.com%2Fyywf%2Fp%2F17305449.html "https://www.cnblogs.com/yywf/p/17305449.html")

相关推荐
鬼火儿21 小时前
SpringBoot】Spring Boot 项目的打包配置
java·后端
cr7xin1 天前
缓存三大问题及解决方案
redis·后端·缓存
间彧1 天前
Kubernetes的Pod与Docker Compose中的服务在概念上有何异同?
后端
间彧1 天前
从开发到生产,如何将Docker Compose项目平滑迁移到Kubernetes?
后端
间彧1 天前
如何结合CI/CD流水线自动选择正确的Docker Compose配置?
后端
间彧1 天前
在多环境(开发、测试、生产)下,如何管理不同的Docker Compose配置?
后端
间彧1 天前
如何为Docker Compose中的服务配置健康检查,确保服务真正可用?
后端
间彧1 天前
Docker Compose和Kubernetes在编排服务时有哪些核心区别?
后端
间彧1 天前
如何在实际项目中集成Arthas Tunnel Server实现Kubernetes集群的远程诊断?
后端
brzhang1 天前
读懂 MiniMax Agent 的设计逻辑,然后我复刻了一个MiniMax Agent
前端·后端·架构