JAVA 实现给PDF的图像批量添加超链接

需求

给如下PDF中的所有图片添加超链接
在这里插入图片描述

实现原理

org.apache.pdfbox +e-iceblue的spire.pdf 实现pdf中图片解析,获取图片在pdf画布中的坐标点,根据图片的长宽绘制图片区域,添加超链接。

实现方式

springboot+vue3

vue前端代码

代码结构

在这里插入图片描述

前端运行之后的样子

在这里插入图片描述

对应代码

request.js
import axios from 'axios';
//import QS from 'qs';

const api = axios.create({
    baseURL: '/api', // 所有请求的公共地址部分(因为服务器解析了域名,方便转发)
    timeout: 12000 // 请求超时时间 这里的意思是当请求时间超过5秒还未取得结果时 提示用户请求超时
})

axios.interceptors.request.use(
    config => {
        config.headers.token = sessionStorage.getItem('token');
        return config;
    },
    error => {
        return Promise.reject(error);
    }
);

axios.interceptors.response.use(
    response => {
        if (response.status === 200) {
            return Promise.resolve(response);
        } else {
            return Promise.reject(response);
        }
    },
    error => {
        if (error.response && error.response.status) {
            switch (error.response.status) {
                case 401:
                    //异常处理
                    break;
                case 403:
                    break;
                case 404:
                    break;
                default:
                    return Promise.reject(error.response);
            }
        }
        return Promise.reject(error);
    }
);

const $get = (url, params) => {
    return new Promise((resolve, reject) => {
        api.get(url, { params })
            .then(res => {
                resolve(res.data);
            })
            .catch(err => {
                reject(err);
            });
    });
};

const $post = (url, params) => {
    return new Promise((resolve, reject) => {
        api.post(url,  params ).then(res => {
            resolve(res.data);
        })
            .catch(err => {
                reject(err);
            });
    });
};

/**
 * 文件上传
 * @param {String} url 地址
 * @param {File} file 文件
 */
const $methodUpload = (url, file) => {
    return new Promise((resolve, reject) => {
        const formData = new FormData()
        formData.append('file', file)
        formData.append('currentDate', Date.now())
        api.post(url, formData, {
            headers: {
                'Content-Type': 'multipart/form-data'
            }
        }).then((response) => {
            resolve(response.data)
        }).catch((error) => {
            reject(error)
        })
    })
};
export default {
    install: (app) => {
        app.config.globalProperties.$get = $get;
        app.config.globalProperties.$post = $post;
        app.config.globalProperties.$axios = axios;
        app.config.globalProperties.$methodUpload = $methodUpload;
    }
};


PdfChange.vue
<template>
  <div>
    Excel表格:<input type="file" id="ExcelPath" accept=".xlsx" :v-model="ExcelPath">
    <button id="myButtonExcel" @click="uploadFileExcel" :disabled="EV">上传</button><span v-show="ExcelShow">✔️</span><br/>
    PDF文件:<input type="file" id="PdfPath" accept=".pdf" :v-model="PdfPath">
    <button id="myButtonPdf" @click="uploadFilePdf" :disabled="PV">上传</button><span v-show="PdfShow">✔️</span><br/>
    <form id="myForm">
      <label>
        <input type="checkbox" name="option1" value="Y" :disabled="checkV"> 转换成英文
      </label>
      <label>
        <input type="checkbox" name="option2" value="N" :disabled="checkV"> 转换成中文</label>
    </form>
    <button id="submit" @click="submit" v-loading="isLoading">开始添加超链接</button>
    <div v-if="isLoading">Loading...</div>
    <a v-show="linksShow" :href="links" target="_blank">点击此链接下载附件</a>
    <button id="refresh" @click="refreshPage">刷新界面</button><br/>
    <div id="loadingSpinner"><span id="loadingText">处理中...</span></div>
  </div>
</template>

<style scoped>
.icon1 {
  display: none; /* 初始时隐藏图标 */
}
.icon2 {
  display: none; /* 初始时隐藏图标 */
}
.icon3 {
  display: none; /* 初始时隐藏图标 */
}
#loadingSpinner {
  border: 16px solid #f3f3f3;
  border-top: 16px solid #3498db;
  border-radius: 50%;
  width: 120px;
  height: 120px;
  animation: spin 2s linear infinite;
  display: none; /* 初始状态下隐藏等待框 */
}
#loadingText {
  position: absolute; /* 添加绝对定位 */
  top: 50%; /* 将文字垂直居中 */
  left: 50%; /* 将文字水平居中 */
  transform: translate(-50%, -50%); /* 将文字居中 */
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.hidden-file-input {
  display: none;
}
</style>

<script>
export default {
  name:"PdfChange",
  data() {
    return {
      linksShow:false,
      links:"",
      isLoading:false,
      checkV:false,
      PV:false,
      EV:false,
      ExcelPath:"",
      ExcelShow:false,
      PdfPath:"",
      PdfShow:false
    };
  },
  methods: {
    refreshPage(){
      location.reload()
    },
    uploadFileExcel(){
          var fileInput = document.getElementById('ExcelPath');
          var file = fileInput.files[0];
          this.$methodUpload('/upload',file).then((res) => {
            console.log(res)
            this.ExcelPath=res
            this.ExcelShow=true
            this.EV=true
          }).catch((err) => {
             console.log(err)
          })
          },
    uploadFilePdf(){
          var fileInput = document.getElementById('PdfPath');
          var file = fileInput.files[0];
          this.$methodUpload('/upload',file).then((res) => {
            console.log(res)
            this.PdfPath=res
            this.PdfShow=true
            this.PV=true
          }).catch((err) => {
            console.log(err)
          })
    },
    submit(){
      var form = document.getElementById("myForm");
      var checkboxes = form.querySelectorAll('input[type="checkbox"]:checked');
      // eslint-disable-next-line no-unused-vars
      let English="";
      // eslint-disable-next-line no-unused-vars
      var selectedValues = Array.from(checkboxes).map(function(checkbox) {
        English=checkbox.value
      });
      if(this.ExcelPath==""||this.PdfPath==""||English==""){
        alert("必要参数没有录入,无法进行转换!")
        return;
      }
      var formData = new FormData();
      formData.append('ExcelPath', this.ExcelPath);
      formData.append('PDFPath', this.PdfPath);
      formData.append('English', English);
      this.isLoading=true
      this.$post("/operate",formData).then((res) => {
        console.log(res)
        this.links=res
        this.showlinks()
      }).catch((err) => {
        console.log(err)
      })

    },
    showlinks(){
      this.isLoading=false
      this.linksShow=true
    }
  }
}
</script>

App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <PdfChange msg="Welcome to Your Vue.js App"/>
</template>

<script>
import PdfChange from "@/components/PdfChange.vue";
export default {
  name: 'App',
  components: {
    // eslint-disable-next-line vue/no-unused-components
    PdfChange
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

main.js
import { createApp } from 'vue'
import App from './App.vue'
import http from './utils/request'
import VLoading from 'v-loading';
const app = createApp(App)
app.use(http) //axios封装
app.use(VLoading) //Loading
app.mount('#app')


java后端代码

代码结构

在这里插入图片描述

具体代码

首先需要下载 spire.pdf-9.6.2.jar 放在/src/main/resources/lib/路径下

这个可以在绑定资源中下载

pom依赖
 <dependency>
            <groupId>e-iceblue</groupId>
            <artifactId>spire.pdf</artifactId>
            <version>9.6.2</version>
            <scope>system</scope>
            <systemPath>${pom.basedir}/src/main/resources/lib/spire.pdf-9.6.2.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>fontbox</artifactId>
            <version>2.0.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>jempbox</artifactId>
            <version>1.8.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>xmpbox</artifactId>
            <version>2.0.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>preflight</artifactId>
            <version>2.0.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox-tools</artifactId>
            <version>2.0.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.9</version>
        </dependency>
        <!--xlsx(07)-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.9</version>
        </dependency>
application.properties文件添加
#生产环境保存上传文件的路径
property.OutPutFilSavePathDev=/usr/share/nginx/html/files/
#测试环境保存文件的路径
property.OutPutFilSavePathTest=/Users/gwx/Desktop/java/pdf/
#nginx配置的资源下载路径
property.StaticResourcelink=http://81.71.97.165:80/downloads
#生成文件之后需要执行的权限脚本,以便nginx有权限访问
property.CommandChangeRight=sudo chmod -R 755 /usr/share/nginx/html/files
#0是生产环境,1是测试环境
property.test=0
ReadExcelData.java 读取Excel文件
package com.boot.springboot.utils;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ReadExcelData {
    /**
     * 读取数据转换为list
     * @param filePath
     * @param i
     */
    public static List<String> ReadExcelToList(String filePath, Integer i) {
        List<String> finalList=new ArrayList<String>();
        try {
            FileInputStream file = new FileInputStream(filePath);
            Workbook workbook = new XSSFWorkbook(file);
            Sheet sheet = workbook.getSheetAt(i);

            List<List<String>> dataList = new ArrayList<>();
            Iterator<Row> rowIterator = sheet.iterator();
            while (rowIterator.hasNext()) {
                Row row = rowIterator.next();
                List<String> rowData = new ArrayList<>();
                Iterator<Cell> cellIterator = row.cellIterator();
                String cellValue = "";
                while (cellIterator.hasNext()) {
                    Cell cell = cellIterator.next();
                    if (cell.getCellType() == 1) {
                        cellValue = cell.getStringCellValue();
                    }
                    rowData.add(cellValue);
                }
                dataList.add(rowData);
            }


            // 输出结果
            for (List<String> rowData : dataList) {
                finalList.addAll(rowData);
                for (String cellValue : rowData) {
                    // System.out.print(cellValue + "\t");
                }
            }
            file.close();
        } catch (IOException e) {
            e.printStackTrace();
            finalList=null;
        }
        return finalList;
    }

    public static Map<String, Object> ReadExcelToListMap(String filePath, String English) {
        int startRow = 2; // 数据起始行(从1开始计数)
        int endRow = 6373; // 数据结束行(从1开始计数)
        int column1Index = 0; // 第一列的索引(从0开始计数)
        int column2Index = 1; // 第二列的索引(从0开始计数)

        Map<String, Object> dataList = new HashMap<>();

        try {
            FileInputStream fis = new FileInputStream(filePath);
            Workbook workbook = new XSSFWorkbook(fis) ;
            //获取所有的sheet页数
            int numberOfSheets = workbook.getNumberOfSheets();
            int sheetNumber=0;
            if(English=="Y"){
                sheetNumber=numberOfSheets-1;
            }else{
                sheetNumber=numberOfSheets-2;
            }
            Sheet sheet = workbook.getSheetAt(sheetNumber);
            for (int rowNum = startRow; rowNum <=endRow ; rowNum++) {
                Row row = sheet.getRow(rowNum - 1);
                if (row == null) {
                    continue;
                }

                Cell cell1 = row.getCell(column1Index);
                Cell cell2 = row.getCell(column2Index);

                if (cell1 == null || cell2 == null) {
                    continue;
                }

                dataList.put(cell1.getStringCellValue(), cell2.getStringCellValue());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return dataList;
    }

    public static List<String>  getFinalList(String ExcelfilePath,Integer pageNum,String English){
        List<String> listSheet1= ReadExcelData.ReadExcelToList(ExcelfilePath,pageNum-2);
        Map<String, Object>  sum= ReadExcelData.ReadExcelToListMap(ExcelfilePath,English);
        List<String> listSheet1_r=ListUtils.listCom(listSheet1,sum);
        return listSheet1_r;
    }
}

PDFReader.java 解析pdf文件
package com.boot.springboot.utils;

import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.io.RandomAccessFile;
import org.apache.pdfbox.pdfparser.PDFParser;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.text.PDFTextStripper;
import com.boot.springboot.pojo.Point;
import javax.imageio.ImageIO;
import java.io.File;
import java.util.Iterator;
import java.util.List;

public class PDFReader {

    static String fileName = "/Users/gwx/Desktop/pythonFiles/读取PDF/416--添加链接.pdf";

    /**
     * 一次获取整个文件的内容
     *
     * @throws Exception
     */
    public static void readFile() throws Exception {
        File file = new File(fileName);
        RandomAccessFile is = new RandomAccessFile(file, "r");
        PDFParser parser = new PDFParser(is);
        parser.parse();
        PDDocument doc = parser.getPDDocument();
        PDFTextStripper textStripper = new PDFTextStripper();
        String s = textStripper.getText(doc);
        System.out.println("总页数:" + doc.getNumberOfPages());
        System.out.println("输出内容:");
        System.out.println(s);
        doc.close();
    }

    /**
     * 分页获取文字的内容
     *
     * @throws Exception
     */
    public static void readPage() throws Exception {
        File file = new File(fileName);
        RandomAccessFile is = new RandomAccessFile(file, "r");
        PDFParser parser = new PDFParser(is);
        parser.parse();
        PDDocument doc = parser.getPDDocument();
        PDFTextStripper textStripper = new PDFTextStripper();
        for (int i = 1; i <= doc.getNumberOfPages(); i++) {
            textStripper.setStartPage(i);
            textStripper.setEndPage(i);
            // 一次输出多个页时,按顺序输出
            textStripper.setSortByPosition(true);
            String s = textStripper.getText(doc);
            System.out.println("当前页:" + i);
            System.out.println("输出内容:");
            System.out.println(s);
        }
        doc.close();
    }

    /**
     * 读取文本内容和图片
     *
     * @throws Exception
     */
    public static void readTextImage() throws Exception {
        File file = new File(fileName);
        PDDocument doc = PDDocument.load(file);
        PDFTextStripper textStripper = new PDFTextStripper();
        for (int i = 1; i <= doc.getNumberOfPages(); i++) {
            textStripper.setStartPage(i);
            textStripper.setEndPage(i);
            String s = textStripper.getText(doc);
            System.out.println("第 " + i + " 页 :" + s);
            // 读取图片
            PDPage page = doc.getPage(i - 1);
            PDResources resources = page.getResources();
            // 获取页中的对象
            Iterable<COSName> xobjects = resources.getXObjectNames();
            if (xobjects != null) {
                Iterator<COSName> imageIter = xobjects.iterator();
                while (imageIter.hasNext()) {
                    COSName cosName = imageIter.next();
                    boolean isImageXObject = resources.isImageXObject(cosName);
                    if (isImageXObject) {
                        // 获取每页资源的图片
                        PDImageXObject ixt = (PDImageXObject) resources.getXObject(cosName);
                        File outputfile = new File("第 " + (i) + " 页" + cosName.getName() + ".jpg");
                        ImageIO.write(ixt.getImage(), "jpg", outputfile);
                    }
                }
            }
        }
        doc.close();
    }


    /**
     * 读取某一页文本内容和图片
     *
     * @throws Exception
     */
    public static void readImage(PDPage page, Integer num, PDDocument document, List<Point> lists, String ExcelPath, String English) throws Exception {

        PDResources resources = page.getResources();
        // 获取页中的对象
        AddLinks link = new AddLinks();
        //添加超链接
        link.addLink(document, num, lists, ExcelPath, English);
    }
}

ListUtils.java
package com.boot.springboot.utils;

import com.boot.springboot.pojo.Point;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

public class ListUtils {

    /**
     * 去重,降序排列
     * @param list
     * @return
     */
    public static List<Integer> duplicateListBySet(List<Integer> list) {
        HashSet h = new HashSet(list);
        List newList = new ArrayList();
        newList.addAll(h);
        newList.sort(null);
        return  newList;
    }

    /**
     * LIST反转
     * @param originalList
     * @return
     */
    public static List<Integer> reverse(List<Integer> originalList) {
        List<Integer> reversedList = new ArrayList<>();
        for (int i = originalList.size() - 1; i >= 0; i--) {
            reversedList.add(originalList.get(i));
        }
        return reversedList;
    }

    /**
     * LIST去除杂数据(相邻坐标差<5的剔除一个)
     * @param list
     * @return
     */
    public static List<Integer> deviation(List<Integer> list) {

        for(int i = list.size()-1;i>0; i--){
            if( Math.abs(list.get(i)- list.get(i-1))<5){
                list.remove(i);
            }
        }
        return  list;
    }

    /**
     * 自定义封装:将每页坐标点转换为每个画布点的实体LIST
     * @param x
     * @param y
     * @return
     */
    public static List<Point> listPoint(List<Integer> x, List<Integer> y, int num){
        List<Point> pointMap=new ArrayList<>();
        for(int i = 0; i < y.size(); i++){
            for(int j = 0; j < x.size(); j++){
                Point point=new Point();
                point.setStartX(x.get(j));
                point.setStartY(y.get(i));
                pointMap.add(point);
            }
        }
        if(pointMap.size()-num>0){
            pointMap = pointMap.subList(0, pointMap.size() - (pointMap.size()-num));
        }
        return pointMap;
    }

    public static List<Point> listPointTrans(List<Point> a,int num){
        List<Point> pointMap=new ArrayList<>();
        if(pointMap.size()-num>0){
            pointMap = pointMap.subList(0, pointMap.size() - (pointMap.size()-num));
        }
        return pointMap;
    }

    /**
     * 合并LIST
     * @param sheet1
     * @param map
     * @return
     */
    public static List<String> listCom(List<String> sheet1, Map<String, Object> map) {
        List<String> result=new ArrayList<>();
        for(int i=0;i< sheet1.size();i++){
            result.add((String) map.get(sheet1.get(i)));
        }
        return result;
    }

    public static void compareAndAssign(List<Integer> list1, List<Integer> list2) {
        int size = Math.min(list1.size(), list2.size());

        for (int i = 0; i < size; i++) {
            int diff = Math.abs(list1.get(i) - list2.get(i));
            if (diff > 10) {
                if (list1.get(i) < list2.get(i)) {
                    list1.set(i, list2.get(i));
                } else {
                    list2.set(i, list1.get(i));
                }
            }
        }
    }
}

getCoordinateOfImage.java 获取图片所在坐标
package com.boot.springboot.utils;

import com.boot.springboot.pojo.Point;
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.exporting.PdfImageInfo;

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

public class GetCoordinateOfImage {
    /**
     * 获取当页所有图片坐标位置,输出List实类
     * @param doc PdfDocument ==》PDF文件
     * @param num PDF文件循环的页码
     * @return
     * @throws Exception
     */
    public List<Point> getCoordinateOfImage(PdfDocument doc, Integer num) throws Exception {
        List<Point> listPoint=new ArrayList<>();
        //获取页的内容
        PdfPageBase page = doc.getPages().get(num);
        //获取当页图片List
        PdfImageInfo[] imageInfo = page.getImagesInfo();
        //获取图片大致的X坐标点和Y坐标点
        List<Integer> listX=new ArrayList<>();
        List<Integer> listY=new ArrayList<>();
        Integer boxHigh= (int) page.getCropBox().getHeight();
        for (int i = 0; i < imageInfo.length; i++) {
            //获取指定图片的边界属性
            Rectangle2D rect = imageInfo[i].getBounds();
            listX.add((int) rect.getX());
            //pdfbox获取的坐标点在左上角,需要转换成左下角的坐标
            listY.add(boxHigh-(int) rect.getY()-28);

            //如下方法可以保存PDF的图片到本地
            //File outputfile = new File("第 " + (i) + "张" + i + ".jpg");
            //ImageIO.write(imageInfo[i].getImage(), "jpg", outputfile);
            //获取左上角坐标
            // System.out.println(
            // String.format("第%d张图片的左下角坐标为:(%f, %f),右上角坐标为:( %f, %f),页面宽高:%f, %f",
            //       i+1,rect.getX(), rect.getY()
            //       ,rect.getX()+rect.getWidth(), rect.getY()+rect.getHeight(),
            //      page.getCropBox().getWidth(),page.getCropBox().getHeight()));
        }
        //当前页码(用于打印)
        Integer pageNum=num+1;
        //XY用于去重+去杂+反转
        List<Integer> listXFinal=new ArrayList<>();
        List<Integer> listYFinal=new ArrayList<>();
        //坐标处理
        listXFinal=ListUtils.duplicateListBySet(listX);
        listYFinal=ListUtils.reverse(ListUtils.duplicateListBySet(listY));
        System.out.println( "第"+pageNum+"页的X坐标处理后的数据为:"+ListUtils.deviation(listXFinal));
        System.out.println( "第"+pageNum+"页的Y坐标处理后的数据为:"+ListUtils.deviation(listYFinal));
        System.out.println( "第"+pageNum+"页的数据量为:"+ListUtils.listPoint(ListUtils.deviation(listXFinal),ListUtils.deviation(listYFinal),imageInfo.length).size());
        listPoint=ListUtils.listPoint(ListUtils.deviation(listXFinal), ListUtils.deviation(listYFinal),imageInfo.length);
        return listPoint;
    }

}

CommandOperate.java 执行Linux脚本
package com.boot.springboot.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class CommandOperate {
    /**
     * JAVA执行指令
     * @param command
     * @throws IOException
     */
    public static void executeRootCommand(String command) throws IOException {
        Process process = Runtime.getRuntime().exec(command);
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        reader.close();
    }
}

AddLinks.java 添加链接的主要实现方法
package com.boot.springboot.utils;

import com.boot.springboot.pojo.Point;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;

import java.io.IOException;
import java.util.List;

public class AddLinks {
    /**
     * 每页PDF添加超链接
     * @param document
     * @param pageNumber
     * @param lists
     * @param ExcelPath
     * @return
     * @throws IOException
     */
    public String addLink(PDDocument document, int pageNumber, List<Point> lists, String ExcelPath, String English) throws IOException {

        Integer pagenum=pageNumber+1;
        System.out.println("AddLinks===页码"+pagenum+"开始添加link任务!");
        String tips="";
        PDPage page = document.getDocumentCatalog().getPages().get(pageNumber);
        List<String> picUrls=ReadExcelData.getFinalList(ExcelPath,pageNumber,English);
        if(pageNumber==document.getDocumentCatalog().getPages().getCount()) {
            tips="AddLinks===PDF任务全部完成!!!!";
            return tips;
        }
        else{
            for(int i = 0; i < lists.size(); i++){
                if(i==lists.size()){
                    tips="页码"+pagenum+"完成添加link任务!";
                    return tips;
                }
                try {
                    //添加链接区域
                    PDAnnotationLink txtLink = new PDAnnotationLink();
                    PDRectangle position = new PDRectangle(0, 0, page.getCropBox().getWidth(), page.getCropBox().getHeight());

                    //设置区域坐标点
                    position.setLowerLeftX(lists.get(i).getStartX());
                    position.setUpperRightX(lists.get(i).getStartX() + lists.get(i).getWidth());
                    position.setLowerLeftY(lists.get(i).getStartY());
                    position.setUpperRightY(lists.get(i).getStartY()+ lists.get(i).getHight());
                    txtLink.setRectangle(position);
                    // 去除区域边框
                    PDBorderStyleDictionary borderStyle = new PDBorderStyleDictionary();
                    borderStyle.setWidth(0);
                    txtLink.setBorderStyle(borderStyle);

                    PDActionURI action = new PDActionURI();
                    //循环插入excel读取的顺序链接
                    action.setURI(picUrls.get(i));
                    txtLink.setAction(action);
                    page.getAnnotations().add(txtLink);

                    tips= "Finished!";
                } catch (IOException e) {
                    tips="页码"+pagenum+"添加失败==》"+e.getMessage();
                }
            }
        }
        tips="页码"+pagenum+"完成添加link任务!";
        return tips;
    }
}

Point.java 图片坐标实体
package com.boot.springboot.pojo;

public class Point {

    private Integer startX;
    private Integer startY;
    private Integer startXMax;
    private Integer startYMin;

    public Point() {
    }

    @Override
    public String toString() {
        return "Point{" +
                "startX=" + startX +
                ", startY=" + startY +
                ", startXMax=" + startXMax +
                ", startYMin=" + startYMin +
                ", width=" + width +
                ", hight=" + hight +
                '}';
    }

    public Integer getStartXMax() {
        return startXMax;
    }

    public void setStartXMax(Integer startXMax) {
        this.startXMax = startXMax;
    }

    public Integer getStartYMin() {
        return startYMin;
    }

    public void setStartYMin(Integer startYMin) {
        this.startYMin = startYMin;
    }

    private final float width= 56.000000F;
    private final float hight= 28.000000F;

    public float getStartX() {
        return startX;
    }

    public void setStartX(Integer startX) {
        this.startX = startX;
    }

    public float getStartY() {
        return startY;
    }

    public void setStartY(Integer startY) {
        this.startY = startY;
    }

    public float getWidth() {
        return width;
    }

    public float getHight() {
        return hight;
    }
}

PdfOperation.java 添加链接controller
package com.boot.springboot.controller;

import com.boot.springboot.pojo.Point;
import com.boot.springboot.utils.GetCoordinateOfImage;
import com.boot.springboot.utils.PDFReader;
import com.spire.pdf.PdfDocument;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import static com.boot.springboot.utils.CommandOperate.executeRootCommand;

@Controller
public class PdfOperation {

    @Value("${property.test}")
    private String test;
    @Value("${property.OutPutFilSavePathDev}")
    private String OutPutFilSavePathDev;
    @Value("${property.OutPutFilSavePathTest}")
    private String OutPutFilSavePathTest;
    @Value("${property.StaticResourcelink}")
    private String StaticResourcelink;
    @Value("${property.CommandChangeRight}")
    private String CommandChangeRight;

    @RequestMapping(value = "/operate",method = RequestMethod.POST,produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String  operate(@RequestParam("ExcelPath") String ExcelPath,
                           @RequestParam("PDFPath") String PDFPath,
                           @RequestParam("English") String English) throws Exception {
        String FileSavePath="";
        if(this.test.equals("1")){
            FileSavePath=OutPutFilSavePathTest;
        }else{
            FileSavePath=OutPutFilSavePathDev;
        }
        //创建PdfDocument对象
        PdfDocument doc = new PdfDocument();
        //加载一个PDF文档
        doc.loadFromFile(PDFPath);
        File file = new File(PDFPath);
        PDDocument doc1 = PDDocument.load(file);
        GetCoordinateOfImage test=new GetCoordinateOfImage();
        System.out.println("Main==PDF文件总共"+doc1.getNumberOfPages()+"页!");

        for (int i = 2; i <doc1.getNumberOfPages(); i++) {
            //int i=4;
            Integer pageNum=i+1;
            if(pageNum==doc1.getNumberOfPages()){
                System.out.println("Main==PDF文件执行完成!");
                break;
            }
            System.out.println("Main页码任务"+pageNum+"开始喽~");
            List<Point> list = test.getCoordinateOfImage(doc,i);
            PDPage page=doc1.getPage(i);
            PDFReader.readImage(page,i,doc1,list,ExcelPath,English);
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        String dateStr = simpleDateFormat.format(new Date());
        System.out.println("文件名:"+file.getName());
        doc1.save(FileSavePath+ file.getName().substring(0,file.getName().length()-4)+"-"+dateStr+"版本.pdf");
        String FileOutPath=FileSavePath+file.getName().substring(0,file.getName().length()-4)+"-"+dateStr+"版本.pdf";
        System.out.println("Main==已经输出文件,输出文件路径为:"+FileOutPath);
        doc1.close();
        doc.close();
        String link="";
        if(this.test.equals("1")){
        }else{
            //Linux服务器需要配置文件夹的运行用户组和文件夹权限
            executeRootCommand(CommandChangeRight);
            System.out.print("linux指令执行完成!");
            link=StaticResourcelink;
        }
        // 返回解析后的结果
        return link+"/"+file.getName().substring(0,file.getName().length()-4)+"-"+dateStr+"版本.pdf";
    }

}

FileUploadController.java 文件上传
package com.boot.springboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
@RestController
@CrossOrigin("*")
public class FileUploadController {


    @PostMapping("/upload")
    @ResponseBody
    public String handleFileUpload(@RequestParam("file") MultipartFile file) throws IOException {
        String filePath="";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
        String dateStr = simpleDateFormat.format(new Date());
        if (file.isEmpty()) {
        }

        try {
            // 获取文件名
            String fileName = file.getOriginalFilename();
            // 指定文件保存路径
            filePath= "/Users/gwx/Desktop/"+dateStr+"/"+ fileName;
            // 创建文件对象
            File dest = new File(filePath);
            // 检查父目录是否存在,不存在则创建
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            // 保存文件
            file.transferTo(dest);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.print("文件上传失败:"+e.getMessage());
            filePath= "";
        }
        System.out.print(filePath+",UPLOADED SUCCESSFUL!!");
        return filePath;
    }
}

项目运行结果

生成的文件:http://81.71.97.165/downloads/416-2023-12-07-20-43-40版本.pdf
执行完成后的界面
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值