Springboot 整合 itext 实现PDF文件合并,识别图片则转成PDF拼接

目录


前言

本文实现 Springboot 整合 itext 实现PDF文件合并,图片转PDF拼接。


一、引用依赖

xml 复制代码
<dependency>
     <groupId>com.itextpdf</groupId>
     <artifactId>itext7-core</artifactId>
     <version>8.0.5</version>
     <type>pom</type>
</dependency>

二、使用步骤

1.Controller

代码如下(示例):

java 复制代码
import com.ruoyi.tools.service.IPdfHandlerService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/pdf")
public class PdfHandlerController {

private final IPdfHandlerService pdfHandlerService;

@PostMapping(value = "/generateFromFiles", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void uploadFiles(@RequestPart("files") List<MultipartFile> files, HttpServletResponse response) throws Exception {
    byte[] pdfBytes = pdfHandlerService.generatePdfFromFiles(files);
    response.setContentType("application/pdf");
    response.setContentLength(pdfBytes.length);
    //把字节数组写入输出流中
    response.getOutputStream().write(pdfBytes);
}

2.Service接口

java 复制代码
import org.springframework.web.multipart.MultipartFile;
import java.util.List;

public interface IPdfHandlerService {

    byte[] generatePdfFromFiles(List<MultipartFile> files);
}

3.实现类

java 复制代码
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.ruoyi.tools.service.IPdfHandlerService;
import lombok.SneakyThrows;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.List;

@Service
public class PdfHandlerServiceImpl implements IPdfHandlerService {

    @SneakyThrows
    public byte[] generatePdfFromFiles(List<MultipartFile> files) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PdfWriter writer = new PdfWriter(baos);
        PdfDocument pdfDoc = new PdfDocument(writer);
        Document document = new Document(pdfDoc);

        for (MultipartFile file : files) {
            if (isPdf(file)) {
                PdfDocument sourcePdf = new PdfDocument(new PdfReader(file.getInputStream()));
                int n = sourcePdf.getNumberOfPages();
                for (int i = 1; i <= n; i++) {
                    PdfPage page = sourcePdf.getPage(i);
                    PdfFormXObject pageCopy = page.copyAsFormXObject(pdfDoc);
                    Rectangle pageSize = page.getPageSize();

                    PdfPage newPage = pdfDoc.addNewPage();
                    newPage.setMediaBox(pageSize);
                    new PdfCanvas(newPage).addXObjectAt(pageCopy, 0, 0);
                    newPage.setRotation(page.getRotation());
                }
                sourcePdf.close();
                continue;
            }

            if (isImage(file)) {
                // 创建一个新的 A4 页面
                PdfPage newPage = pdfDoc.addNewPage(PageSize.A4);
                Image img = new Image(ImageDataFactory.create(file.getBytes()));
                // 设置图片大小以适应 A4 页面
                img.scaleToFit(PageSize.A4.getWidth(), PageSize.A4.getHeight());
                // 获取当前页面编号
                int pageNumber = pdfDoc.getNumberOfPages();
                // 设置图片位置为页面中心
                img.setFixedPosition(pageNumber,
                    (PageSize.A4.getWidth() - img.getImageScaledWidth()) / 2,
                    (PageSize.A4.getHeight() - img.getImageScaledHeight()) / 2);
                // 添加图片到文档
                document.add(img);
                continue;
            }

            //可以删掉此提示
            document.add(new Paragraph(file.getOriginalFilename() + "仅支持PDF和图片的拼接"));

        }

        document.close();
        return baos.toByteArray();
    }

    @SneakyThrows
    private boolean isPdf(MultipartFile file) {
        String contentType = file.getContentType();
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());

        if (contentType != null && contentType.equals("application/pdf")) {
            return true;
        }

        if ("pdf".equalsIgnoreCase(extension)) {
            byte[] fileHeader = new byte[4];
            try (ByteArrayInputStream bis = new ByteArrayInputStream(file.getBytes())) {
                int bytesRead = bis.read(fileHeader);
                if (bytesRead != fileHeader.length) {
                    return false;
                }
            }
            return new String(fileHeader).startsWith("%PDF");
        }

        return false;
    }

    @SneakyThrows
    private boolean isImage(MultipartFile file) {
        String contentType = file.getContentType();
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());

        if (contentType != null && contentType.startsWith("image")) {
            return true;
        }

        if (extension != null) {
            switch (extension.toLowerCase()) {
                case "jpg":
                case "jpeg":
                case "png":
                case "gif":
                case "bmp":
                    return true;
                default:
                    return false;
            }
        }

        return false;
    }

}

三、请求接口及结果

http://localhost:8080/pdf/generateFromFiles

相关推荐
石榴树下3 分钟前
00. 马里奥的 OAuth 2 和 OIDC 历险记
后端
uhakadotcom4 分钟前
开源:subdomainpy快速高效的 Python 子域名检测工具
前端·后端·面试
似水流年流不尽思念20 分钟前
容器化技术了解吗?主要解决什么问题?原理是什么?
后端
Java水解21 分钟前
Java中的四种引用类型详解:强引用、软引用、弱引用和虚引用
java·后端
i听风逝夜22 分钟前
看好了,第二遍,SpringBoot单体应用真正的零停机无缝更新代码
后端
柏油1 小时前
可视化 MySQL binlog 监听方案
数据库·后端·mysql
舒一笑2 小时前
Started TttttApplication in 0.257 seconds (没有 Web 依赖导致 JVM 正常退出)
jvm·spring boot·后端
M1A12 小时前
Java Enum 类:优雅的常量定义与管理方式(深度解析)
后端
AAA修煤气灶刘哥2 小时前
别再懵了!Spring、Spring Boot、Spring MVC 的区别,一篇讲透
后端·面试
柏油3 小时前
MySQL 字符集 utf8 与 utf8mb4
数据库·后端·mysql