欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot生成和操作PDF的代碼詳解

 更新時(shí)間:2025年01月13日 10:05:09   作者:笑小楓  
本文主要介紹了在SpringBoot項(xiàng)目下,通過(guò)代碼和操作步驟,詳細(xì)的介紹了如何操作PDF,希望可以幫助到準(zhǔn)備通過(guò)JAVA操作PDF的你,項(xiàng)目框架用的SpringBoot,但在JAVA中代碼都是通用的,需要的朋友可以參考下

本文簡(jiǎn)介

本文涉及pdf操作,如下:

  • PDF模板制作
  • 基于PDF模板生成,并支持下載
  • 自定義中文字體
  • 完全基于代碼生成,并保存到指定目錄
  • 合并PDF,并保存到指定目錄
  • 合并PDF,并支持下載

基于PDF模板生成:適用于固定格式的PDF模板,基于內(nèi)容進(jìn)行填空,例如:合同信息生成、固定格式表格等等

完全基于代碼生成:適用于不固定的PDF,例如:動(dòng)態(tài)表格、動(dòng)態(tài)添加某塊內(nèi)容、不確定的內(nèi)容大小等不確定的場(chǎng)景

PDF文件簡(jiǎn)介

PDF是可移植文檔格式,是一種電子文件格式,具有許多其他電子文檔格式無(wú)法相比的優(yōu)點(diǎn)。PDF文件格式可以將文字、字型、格式、顏色及獨(dú)立于設(shè)備和分辨率的圖形圖像等封裝在一個(gè)文件中。該格式文件還可以包含超文本鏈接、聲音和動(dòng)態(tài)影像等電子信息,支持特長(zhǎng)文件,集成度和安全可靠性都較高。在系統(tǒng)開發(fā)中通常用來(lái)生成比較正式的報(bào)告或者合同類的電子文檔。

代碼實(shí)現(xiàn)PDF操作

首先需要引入我們的依賴,這里通過(guò)maven管理依賴

在pom.xml文件添加以下依賴

<!--pdf操作-->
<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.6</version>
</dependency>
<!-- 這個(gè)主要用來(lái)設(shè)置樣式什么的 -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>

基于PDF模板生成,并下載

PDF模板制作

首先在word或者其他軟件里面制作模板,挑選你熟悉的軟件即可,前提是可生成pdf。

image-20220815134838696

將word文件轉(zhuǎn)為pdf文件。

image-20220815135121685

使用Adobe Acrobat軟件操作pdf,這里用的是這個(gè)軟件,只要能實(shí)現(xiàn)這個(gè)功能,其他的軟件也可~

選擇表單編輯哈,我們要在對(duì)應(yīng)的坑上添加表單占位

image-20220815135323496

在表單上添加文本域即可,所有的格式都用文本域即可,這里只是占坑。

對(duì)應(yīng)的域名稱要與程序的名稱對(duì)應(yīng),方便后面數(shù)據(jù)填充,不然后面需要手動(dòng)處理賦值。

image-20220815135431811

image-20220815135520626

創(chuàng)建個(gè)簡(jiǎn)單的模板吧,要注意填充的空間要充足,不然會(huì)出現(xiàn)數(shù)據(jù)展示不全呦~

效果如下:

image-20220815135622485

好了,到這里模板就生成好了,我們保存一下,然后放在我們的/resources/templates目錄下

PDF生成代碼編寫

util包下創(chuàng)建PdfUtil.java工具類,代碼如下:

package com.maple.demo.util;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;

import javax.servlet.ServletOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;


/**
 * @author 笑小楓
 * @date 2022/8/15
 * @see <a  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >https://www.xiaoxiaofeng.com</a>
 */
public class PdfUtil {

    private PdfUtil() {

    }

    /**
     * 利用模板生成pdf
     *
     * @param data         寫入的數(shù)據(jù)
     * @param photoMap     圖片信息
     * @param out          自定義保存pdf的文件流
     * @param templatePath pdf模板路徑
     */
    public static void fillTemplate(Map<String, Object> data, Map<String, String> photoMap, ServletOutputStream out, String templatePath) {
        PdfReader reader;
        ByteArrayOutputStream bos;
        PdfStamper stamper;
        try {
            // 讀取pdf模板
            reader = new PdfReader(templatePath);
            bos = new ByteArrayOutputStream();
            stamper = new PdfStamper(reader, bos);
            AcroFields acroFields = stamper.getAcroFields();
            // 賦值
            for (String name : acroFields.getFields().keySet()) {
                String value = data.get(name) != null ? data.get(name).toString() : null;
                acroFields.setField(name, value);
            }

            // 圖片賦值
            for (Map.Entry<String, String> entry : photoMap.entrySet()) {
                if (Objects.isNull(entry.getKey())) {
                    continue;
                }
                String key = entry.getKey();
                String url = entry.getValue();
                // 根據(jù)地址讀取需要放入pdf中的圖片
                Image image = Image.getInstance(url);
                // 設(shè)置圖片在哪一頁(yè)
                PdfContentByte overContent = stamper.getOverContent(acroFields.getFieldPositions(key).get(0).page);
                // 獲取模板中圖片域的大小
                Rectangle signRect = acroFields.getFieldPositions(key).get(0).position;
                float x = signRect.getLeft();
                float y = signRect.getBottom();
                // 圖片等比縮放
                image.scaleAbsolute(signRect.getWidth(), signRect.getHeight());
                // 圖片位置
                image.setAbsolutePosition(x, y);
                // 在該頁(yè)加入圖片
                overContent.addImage(image);
            }

            // 如果為false那么生成的PDF文件還能編輯,一定要設(shè)為true
            stamper.setFormFlattening(true);
            stamper.close();
            Document doc = new Document();
            PdfCopy copy = new PdfCopy(doc, out);
            doc.open();
            PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);
            copy.addPage(importPage);
            doc.close();
            bos.close();
        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        }
    }
}

controller包下創(chuàng)建TestPdfController.java類,并i代碼如下:

package com.maple.demo.controller;

import com.maple.demo.util.PdfUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 笑小楓
 * @date 2022/8/15
 * @see <a  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >https://www.xiaoxiaofeng.com</a>
 */
@Slf4j
@RestController
@RequestMapping("/example")
@Api(tags = "實(shí)例演示-PDF操作接口")
public class TestPdfController {

    @ApiOperation(value = "根據(jù)PDF模板導(dǎo)出PDF")
    @GetMapping("/exportPdf")
    public void exportPdf(HttpServletResponse response) {
        Map<String, Object> dataMap = new HashMap<>(16);
        dataMap.put("nickName", "笑小楓");
        dataMap.put("age", 18);
        dataMap.put("sex", "男");
        dataMap.put("csdnUrl", "https://zhangfz.blog.csdn.net/");
        dataMap.put("siteUrl", "https://www.xiaoxiaofeng.com/");
        dataMap.put("desc", "大家好,我是笑小楓。");

        Map<String, String> photoMap = new HashMap<>(16);
        photoMap.put("logo", "https://profile.csdnimg.cn/C/9/4/2_qq_34988304");

        // 設(shè)置response參數(shù),可以打開下載頁(yè)面
        response.reset();
        response.setCharacterEncoding("UTF-8");
        // 定義輸出類型
        response.setContentType("application/PDF;charset=utf-8");
        // 設(shè)置名稱
        response.setHeader("Content-Disposition", "attachment; filename=" + "xiaoxiaofeng.pdf");
        try (ServletOutputStream out = response.getOutputStream()) {
            // 模板路徑記
            PdfUtil.fillTemplate(dataMap, photoMap, out, "src/main/resources/templates/xiaoxiaofeng.pdf");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

重啟項(xiàng)目,在瀏覽器訪問(wèn):http://localhost:6666/example/exportPdf

導(dǎo)出的文件效果如下:

image-20220816165204811

完全基于代碼生成,并保存

完全基于代碼生成PDF文件,這個(gè)就比較定制化了,這里只講常見(jiàn)的操作,大家可以用作參考:

自定義字體

PDF生成的時(shí)候,有沒(méi)有遇到過(guò)丟失中文字體的問(wèn)題呢,唉~解決一下

先下載字體,這里用黑體演示哈,下載地址:https://www.zitijia.com/downloadpage?itemid=281258939050380345

下載完會(huì)得到一個(gè)Simhei.ttf文件,對(duì)我們就是要它

先在util包下創(chuàng)建一個(gè)字體工具類吧,代碼如下:

注意:代碼中相關(guān)的絕對(duì)路徑要替換成自己的

package com.maple.demo.util;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPTable;

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

/**
 * @author 笑小楓
 * @date 2022/8/15
 * @see <a  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >https://www.xiaoxiaofeng.com</a>
 */
public class PdfFontUtil {

    private PdfFontUtil() {
    }

    /**
     * 基礎(chǔ)配置,可以放相對(duì)路徑,這里演示絕對(duì)路徑,因?yàn)樽煮w文件過(guò)大,這里不傳到項(xiàng)目里面了,需要的自己下載
     * 下載地址:https://www.zitijia.com/downloadpage?itemid=281258939050380345
     */
    public static final String FONT = "D:\\font/Simhei.ttf";

    /**
     * 基礎(chǔ)樣式
     */
    public static final Font TITLE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 20, Font.BOLD);
    public static final Font NODE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 15, Font.BOLD);
    public static final Font BLOCK_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 13, Font.BOLD, BaseColor.BLACK);
    public static final Font INFO_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 12, Font.NORMAL, BaseColor.BLACK);
    public static final Font CONTENT_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);


    /**
     * 段落樣式獲取
     */
    public static Paragraph getParagraph(String content, Font font, Integer alignment) {
        Paragraph paragraph = new Paragraph(content, font);
        if (alignment != null && alignment >= 0) {
            paragraph.setAlignment(alignment);
        }
        return paragraph;
    }

    /**
     * 圖片樣式
     */
    public static Image getImage(String imgPath, float width, float height) throws IOException, BadElementException {
        Image image = Image.getInstance(imgPath);
        image.setAlignment(Image.MIDDLE);
        if (width > 0 && height > 0) {
            image.scaleAbsolute(width, height);
        }
        return image;
    }

    /**
     * 表格生成
     */
    public static PdfPTable getPdfTable(int numColumns, float totalWidth) {
        // 表格處理
        PdfPTable table = new PdfPTable(numColumns);
        // 設(shè)置表格寬度比例為%100
        table.setWidthPercentage(100);
        // 設(shè)置寬度:寬度平均
        table.setTotalWidth(totalWidth);
        // 鎖住寬度
        table.setLockedWidth(true);
        // 設(shè)置表格上面空白寬度
        table.setSpacingBefore(10f);
        // 設(shè)置表格下面空白寬度
        table.setSpacingAfter(10f);
        // 設(shè)置表格默認(rèn)為無(wú)邊框
        table.getDefaultCell().setBorder(0);
        table.setPaddingTop(50);
        table.setSplitLate(false);
        return table;
    }

    /**
     * 表格內(nèi)容帶樣式
     */
    public static void addTableCell(PdfPTable dataTable, Font font, List<String> cellList) {
        for (String content : cellList) {
            dataTable.addCell(getParagraph(content, font, -1));
        }
    }
}

pdf生成工具類

繼續(xù)在util包下的PdfUtil.java工具類里添加,因?yàn)楸疚纳婕暗降牟僮鞅容^多,這里只貼相關(guān)代碼,最后統(tǒng)一貼一個(gè)完成的文件

注意:代碼中相關(guān)的絕對(duì)路徑要替換成自己的

	/**
     * 創(chuàng)建PDF,并保存到指定位置
     *
     * @param filePath 保存路徑
     */
    public static void createPdfPage(String filePath) {
        // FileOutputStream 需要關(guān)閉,釋放資源
        try (FileOutputStream outputStream = new FileOutputStream(filePath)) {
            // 創(chuàng)建文檔
            Document document = new Document();
            PdfWriter writer = PdfWriter.getInstance(document, outputStream);
            document.open();
            // 報(bào)告標(biāo)題
            document.add(PdfFontUtil.getParagraph("笑小楓的網(wǎng)站介紹", TITLE_FONT, 1));
            document.add(PdfFontUtil.getParagraph("\n網(wǎng)站名稱:笑小楓(www.xiaoxiaofeng.com)", INFO_FONT, -1));
            document.add(PdfFontUtil.getParagraph("\n生成時(shí)間:2022-07-02\n\n", INFO_FONT, -1));
            // 報(bào)告內(nèi)容
            // 段落標(biāo)題 + 報(bào)表圖
            document.add(PdfFontUtil.getParagraph("文章數(shù)據(jù)統(tǒng)計(jì)", NODE_FONT, -1));
            document.add(PdfFontUtil.getParagraph("\n· 網(wǎng)站首頁(yè)圖\n\n", BLOCK_FONT, -1));
            // 設(shè)置圖片寬高
            float documentWidth = document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin();
            float documentHeight = documentWidth / 580 * 320;
            document.add(PdfFontUtil.getImage("D:\\xiaoxiaofeng.jpg", documentWidth - 80, documentHeight - 80));
            // 數(shù)據(jù)表格
            document.add(PdfFontUtil.getParagraph("\n· 數(shù)據(jù)詳情\n\n", BLOCK_FONT, -1));
            // 生成6列的表格
            PdfPTable dataTable = PdfFontUtil.getPdfTable(6, 500);
            // 設(shè)置表格
            List<String> tableHeadList = tableHead();
            List<List<String>> tableDataList = getTableData();
            PdfFontUtil.addTableCell(dataTable, CONTENT_FONT, tableHeadList);
            for (List<String> tableData : tableDataList) {
                PdfFontUtil.addTableCell(dataTable, CONTENT_FONT, tableData);
            }
            document.add(dataTable);
            document.add(PdfFontUtil.getParagraph("\n· 報(bào)表描述\n\n", BLOCK_FONT, -1));
            document.add(PdfFontUtil.getParagraph("數(shù)據(jù)報(bào)告可以監(jiān)控每天的推廣情況," +
                    "可以針對(duì)不同的數(shù)據(jù)表現(xiàn)進(jìn)行分析,以提升推廣效果。", CONTENT_FONT, -1));
            document.newPage();
            document.close();
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 模擬數(shù)據(jù)
     */
    private static List<String> tableHead() {
        List<String> tableHeadList = new ArrayList<>();
        tableHeadList.add("省份");
        tableHeadList.add("城市");
        tableHeadList.add("數(shù)量");
        tableHeadList.add("百分比1");
        tableHeadList.add("百分比2");
        tableHeadList.add("百分比3");
        return tableHeadList;
    }

    /**
     * 模擬數(shù)據(jù)
     */
    private static List<List<String>> getTableData() {
        List<List<String>> tableDataList = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            List<String> tableData = new ArrayList<>();
            tableData.add("浙江" + i);
            tableData.add("杭州" + i);
            tableData.add("276" + i);
            tableData.add("33.3%");
            tableData.add("34.3%");
            tableData.add("35.3%");
            tableDataList.add(tableData);
        }
        return tableDataList;
    }

controller包下的TestPdfController.java類中添加代碼,因?yàn)楸疚纳婕暗降牟僮鞅容^多,這里只貼相關(guān)代碼,最后統(tǒng)一貼一個(gè)完成的文件,相關(guān)代碼如下:

注意:代碼中相關(guān)的絕對(duì)路徑要替換成自己的

	@ApiOperation(value = "測(cè)試純代碼生成PDF到指定目錄")
    @GetMapping("/createPdfLocal")
    public void create() {
        PdfUtil.createPdfPage("D:\\xxf.pdf");
    }

重啟項(xiàng)目,在瀏覽器訪問(wèn):http://localhost:6666/example/createPdfLocal

可以在D:\\test目錄下看到xxf.pdf文件

image-20220816170702824

我們打開看一下效果:

image-20220816170841371

合并PDF,并保存

繼續(xù)在util包下的PdfUtil.java工具類里添加,因?yàn)楸疚纳婕暗降牟僮鞅容^多,這里只貼相關(guān)代碼,最后統(tǒng)一貼一個(gè)完成的文件。

/**
     * 合并pdf文件
     *
     * @param files   要合并文件數(shù)組(絕對(duì)路徑如{ "D:\\test\\1.pdf", "D:\\test\\2.pdf" , "D:\\test\\3.pdf"})
     * @param newFile 合并后存放的目錄D:\\test\\xxf-merge.pdf
     * @return boolean 生成功返回true, 否則返回false
     */
    public static boolean mergePdfFiles(String[] files, String newFile) {
        boolean retValue = false;
        Document document;
        try (FileOutputStream fileOutputStream = new FileOutputStream(newFile)) {
            document = new Document(new PdfReader(files[0]).getPageSize(1));
            PdfCopy copy = new PdfCopy(document, fileOutputStream);
            document.open();
            for (String file : files) {
                PdfReader reader = new PdfReader(file);
                int n = reader.getNumberOfPages();
                for (int j = 1; j <= n; j++) {
                    document.newPage();
                    PdfImportedPage page = copy.getImportedPage(reader, j);
                    copy.addPage(page);
                }
            }
            retValue = true;
            document.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retValue;
    }

controller包下的TestPdfController.java類中添加代碼,因?yàn)楸疚纳婕暗降牟僮鞅容^多,這里只貼相關(guān)代碼,最后統(tǒng)一貼一個(gè)完成的文件,相關(guān)代碼如下:

注意:代碼中相關(guān)的絕對(duì)路徑要替換成自己的

    @ApiOperation(value = "測(cè)試合并PDF到指定目錄")
    @GetMapping("/mergePdf")
    public Boolean mergePdf() {
        String[] files = {"D:\\test\\1.pdf", "D:\\test\\2.pdf"};
        String newFile = "D:\\test\\xxf-merge.pdf";
        return PdfUtil.mergePdfFiles(files, newFile);
    }

我們首先要準(zhǔn)備兩個(gè)文件D:\\test\\1.pdf,D:\\test\\2.pdf,這里可以指定文件,也可以是生成的pdf文件。

如果是處理生成的文件,這里說(shuō)下思想:程序創(chuàng)建一個(gè)臨時(shí)目錄,注意要唯一命名,然后將生成PDF文件保存到這個(gè)目錄,然后從這個(gè)目錄下拿到pdf進(jìn)行處理,最后處理完成,保存到對(duì)應(yīng)的目錄下,刪除這個(gè)臨時(shí)目錄和下面的文件。這里不做演示?!逗喜DF,并下載》里面略有涉及,但是處理的單個(gè)文件,稍微改造即可

重啟項(xiàng)目,在瀏覽器訪問(wèn):http://localhost:6666/example/mergePdf

可以在D:\\test目錄下看到xxf-merge文件

image-20220816172150022

打開看一下效果:

image-20220816172236016

合并PDF,并下載

繼續(xù)在util包下的PdfUtil.java工具類里添加,這里只貼將文件轉(zhuǎn)為輸出流,并刪除文件的相關(guān)代碼,合并相關(guān)代碼見(jiàn)《合并PDF,并保存》的util類。

    /**
     * 讀取PDF,讀取后刪除PDF,適用于生成后需要導(dǎo)出PDF,創(chuàng)建臨時(shí)文件
     */
    public static void readDeletePdf(String fileName, ServletOutputStream outputStream) {
        File file = new File(fileName);
        if (!file.exists()) {
            System.out.println(fileName + "文件不存在");
        }
        try (InputStream in = new FileInputStream(fileName)) {
            IOUtils.copy(in, outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                Files.delete(file.toPath());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

controller包下的TestPdfController.java類中添加代碼,因?yàn)楸疚纳婕暗降牟僮鞅容^多,這里只貼相關(guān)代碼,最后統(tǒng)一貼一個(gè)完成的文件,相關(guān)代碼如下:

注意:代碼中相關(guān)的絕對(duì)路徑要替換成自己的

@ApiOperation(value = "測(cè)試合并PDF后并導(dǎo)出")
    @GetMapping("/exportMergePdf")
    public void createPdf(HttpServletResponse response) {
        // 設(shè)置response參數(shù),可以打開下載頁(yè)面
        response.reset();
        response.setCharacterEncoding("UTF-8");
        // 定義輸出類型
        response.setContentType("application/PDF;charset=utf-8");
        // 設(shè)置名稱
        response.setHeader("Content-Disposition", "attachment; filename=" + "xiaoxiaofeng.pdf");
        try (ServletOutputStream out = response.getOutputStream()) {
            String[] files = {"D:\\test\\1.pdf", "D:\\test\\2.pdf"};
            // 生成為臨時(shí)文件,轉(zhuǎn)換為流后,再刪除該文件
            String newFile = "src\\main\\resources\\templates\\" + UUID.randomUUID() + ".pdf";
            boolean isOk = PdfUtil.mergePdfFiles(files, newFile);
            if (isOk) {
                PdfUtil.readDeletePdf(newFile, out);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

重啟項(xiàng)目,在瀏覽器訪問(wèn):http://localhost:6666/example/exportMergePdf會(huì)提示我們下載。

image-20220816172814426

完整代碼

PdfUtil.java

package com.maple.demo.util;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import org.apache.commons.compress.utils.IOUtils;

import javax.servlet.ServletOutputStream;
import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.maple.demo.util.PdfFontUtil.*;

/**
 * @author 笑小楓
 * @date 2022/8/15
 * @see <a  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >https://www.xiaoxiaofeng.com</a>
 */
public class PdfUtil {

    private PdfUtil() {

    }

    /**
     * 利用模板生成pdf
     *
     * @param data         寫入的數(shù)據(jù)
     * @param photoMap     圖片信息
     * @param out          自定義保存pdf的文件流
     * @param templatePath pdf模板路徑
     */
    public static void fillTemplate(Map<String, Object> data, Map<String, String> photoMap, ServletOutputStream out, String templatePath) {
        PdfReader reader;
        ByteArrayOutputStream bos;
        PdfStamper stamper;
        try {
            // 讀取pdf模板
            reader = new PdfReader(templatePath);
            bos = new ByteArrayOutputStream();
            stamper = new PdfStamper(reader, bos);
            AcroFields acroFields = stamper.getAcroFields();
            // 賦值
            for (String name : acroFields.getFields().keySet()) {
                String value = data.get(name) != null ? data.get(name).toString() : null;
                acroFields.setField(name, value);
            }

            // 圖片賦值
            for (Map.Entry<String, String> entry : photoMap.entrySet()) {
                if (Objects.isNull(entry.getKey())) {
                    continue;
                }
                String key = entry.getKey();
                String url = entry.getValue();
                // 根據(jù)地址讀取需要放入pdf中的圖片
                Image image = Image.getInstance(url);
                // 設(shè)置圖片在哪一頁(yè)
                PdfContentByte overContent = stamper.getOverContent(acroFields.getFieldPositions(key).get(0).page);
                // 獲取模板中圖片域的大小
                Rectangle signRect = acroFields.getFieldPositions(key).get(0).position;
                float x = signRect.getLeft();
                float y = signRect.getBottom();
                // 圖片等比縮放
                image.scaleAbsolute(signRect.getWidth(), signRect.getHeight());
                // 圖片位置
                image.setAbsolutePosition(x, y);
                // 在該頁(yè)加入圖片
                overContent.addImage(image);
            }

            // 如果為false那么生成的PDF文件還能編輯,一定要設(shè)為true
            stamper.setFormFlattening(true);
            stamper.close();
            Document doc = new Document();
            PdfCopy copy = new PdfCopy(doc, out);
            doc.open();
            PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);
            copy.addPage(importPage);
            doc.close();
            bos.close();
        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 創(chuàng)建PDF,并保存到指定位置
     *
     * @param filePath 保存路徑
     */
    public static void createPdfPage(String filePath) {
        // FileOutputStream 需要關(guān)閉,釋放資源
        try (FileOutputStream outputStream = new FileOutputStream(filePath)) {
            // 創(chuàng)建文檔
            Document document = new Document();
            PdfWriter writer = PdfWriter.getInstance(document, outputStream);
            document.open();
            // 報(bào)告標(biāo)題
            document.add(PdfFontUtil.getParagraph("笑小楓的網(wǎng)站介紹", TITLE_FONT, 1));
            document.add(PdfFontUtil.getParagraph("\n網(wǎng)站名稱:笑小楓(www.xiaoxiaofeng.com)", INFO_FONT, -1));
            document.add(PdfFontUtil.getParagraph("\n生成時(shí)間:2022-07-02\n\n", INFO_FONT, -1));
            // 報(bào)告內(nèi)容
            // 段落標(biāo)題 + 報(bào)表圖
            document.add(PdfFontUtil.getParagraph("文章數(shù)據(jù)統(tǒng)計(jì)", NODE_FONT, -1));
            document.add(PdfFontUtil.getParagraph("\n· 網(wǎng)站首頁(yè)圖\n\n", BLOCK_FONT, -1));
            // 設(shè)置圖片寬高
            float documentWidth = document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin();
            float documentHeight = documentWidth / 580 * 320;
            document.add(PdfFontUtil.getImage("D:\\xiaoxiaofeng.jpg", documentWidth - 80, documentHeight - 80));
            // 數(shù)據(jù)表格
            document.add(PdfFontUtil.getParagraph("\n· 數(shù)據(jù)詳情\n\n", BLOCK_FONT, -1));
            // 生成6列的表格
            PdfPTable dataTable = PdfFontUtil.getPdfTable(6, 500);
            // 設(shè)置表格
            List<String> tableHeadList = tableHead();
            List<List<String>> tableDataList = getTableData();
            PdfFontUtil.addTableCell(dataTable, CONTENT_FONT, tableHeadList);
            for (List<String> tableData : tableDataList) {
                PdfFontUtil.addTableCell(dataTable, CONTENT_FONT, tableData);
            }
            document.add(dataTable);
            document.add(PdfFontUtil.getParagraph("\n· 報(bào)表描述\n\n", BLOCK_FONT, -1));
            document.add(PdfFontUtil.getParagraph("數(shù)據(jù)報(bào)告可以監(jiān)控每天的推廣情況," +
                    "可以針對(duì)不同的數(shù)據(jù)表現(xiàn)進(jìn)行分析,以提升推廣效果。", CONTENT_FONT, -1));
            document.newPage();
            document.close();
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 模擬數(shù)據(jù)
     */
    private static List<String> tableHead() {
        List<String> tableHeadList = new ArrayList<>();
        tableHeadList.add("省份");
        tableHeadList.add("城市");
        tableHeadList.add("數(shù)量");
        tableHeadList.add("百分比1");
        tableHeadList.add("百分比2");
        tableHeadList.add("百分比3");
        return tableHeadList;
    }

    /**
     * 模擬數(shù)據(jù)
     */
    private static List<List<String>> getTableData() {
        List<List<String>> tableDataList = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            List<String> tableData = new ArrayList<>();
            tableData.add("浙江" + i);
            tableData.add("杭州" + i);
            tableData.add("276" + i);
            tableData.add("33.3%");
            tableData.add("34.3%");
            tableData.add("35.3%");
            tableDataList.add(tableData);
        }
        return tableDataList;
    }

    /**
     * 合并pdf文件
     *
     * @param files   要合并文件數(shù)組(絕對(duì)路徑如{ "D:\\test\\1.pdf", "D:\\test\\2.pdf" , "D:\\test\\3.pdf"})
     * @param newFile 合并后存放的目錄D:\\test\\xxf-merge.pdf
     * @return boolean 生成功返回true, 否則返回false
     */
    public static boolean mergePdfFiles(String[] files, String newFile) {
        boolean retValue = false;
        Document document;
        try (FileOutputStream fileOutputStream = new FileOutputStream(newFile)) {
            document = new Document(new PdfReader(files[0]).getPageSize(1));
            PdfCopy copy = new PdfCopy(document, fileOutputStream);
            document.open();
            for (String file : files) {
                PdfReader reader = new PdfReader(file);
                int n = reader.getNumberOfPages();
                for (int j = 1; j <= n; j++) {
                    document.newPage();
                    PdfImportedPage page = copy.getImportedPage(reader, j);
                    copy.addPage(page);
                }
            }
            retValue = true;
            document.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retValue;
    }

    /**
     * 讀取PDF,讀取后刪除PDF,適用于生成后需要導(dǎo)出PDF,創(chuàng)建臨時(shí)文件
     */
    public static void readDeletePdf(String fileName, ServletOutputStream outputStream) {
        File file = new File(fileName);
        if (!file.exists()) {
            System.out.println(fileName + "文件不存在");
        }
        try (InputStream in = new FileInputStream(fileName)) {
            IOUtils.copy(in, outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                Files.delete(file.toPath());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

PdfFontUtil.java

package com.maple.demo.util;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPTable;

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

/**
 * @author 笑小楓
 * @date 2022/8/15
 * @see <a  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >https://www.xiaoxiaofeng.com</a>
 */
public class PdfFontUtil {

    private PdfFontUtil() {
    }

    /**
     * 基礎(chǔ)配置,可以放相對(duì)路徑,這里演示絕對(duì)路徑,因?yàn)樽煮w文件過(guò)大,這里不傳到項(xiàng)目里面了,需要的自己下載
     * 下載地址:https://www.zitijia.com/downloadpage?itemid=281258939050380345
     */
    public static final String FONT = "D:\\font/Simhei.ttf";

    /**
     * 基礎(chǔ)樣式
     */
    public static final Font TITLE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 20, Font.BOLD);
    public static final Font NODE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 15, Font.BOLD);
    public static final Font BLOCK_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 13, Font.BOLD, BaseColor.BLACK);
    public static final Font INFO_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, 12, Font.NORMAL, BaseColor.BLACK);
    public static final Font CONTENT_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);


    /**
     * 段落樣式獲取
     */
    public static Paragraph getParagraph(String content, Font font, Integer alignment) {
        Paragraph paragraph = new Paragraph(content, font);
        if (alignment != null && alignment >= 0) {
            paragraph.setAlignment(alignment);
        }
        return paragraph;
    }

    /**
     * 圖片樣式
     */
    public static Image getImage(String imgPath, float width, float height) throws IOException, BadElementException {
        Image image = Image.getInstance(imgPath);
        image.setAlignment(Image.MIDDLE);
        if (width > 0 && height > 0) {
            image.scaleAbsolute(width, height);
        }
        return image;
    }

    /**
     * 表格生成
     */
    public static PdfPTable getPdfTable(int numColumns, float totalWidth) {
        // 表格處理
        PdfPTable table = new PdfPTable(numColumns);
        // 設(shè)置表格寬度比例為%100
        table.setWidthPercentage(100);
        // 設(shè)置寬度:寬度平均
        table.setTotalWidth(totalWidth);
        // 鎖住寬度
        table.setLockedWidth(true);
        // 設(shè)置表格上面空白寬度
        table.setSpacingBefore(10f);
        // 設(shè)置表格下面空白寬度
        table.setSpacingAfter(10f);
        // 設(shè)置表格默認(rèn)為無(wú)邊框
        table.getDefaultCell().setBorder(0);
        table.setPaddingTop(50);
        table.setSplitLate(false);
        return table;
    }

    /**
     * 表格內(nèi)容帶樣式
     */
    public static void addTableCell(PdfPTable dataTable, Font font, List<String> cellList) {
        for (String content : cellList) {
            dataTable.addCell(getParagraph(content, font, -1));
        }
    }
}

TestPdfController.java

package com.maple.demo.controller;

import com.maple.demo.util.PdfUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author 笑小楓
 * @date 2022/8/15
 * @see <a  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >https://www.xiaoxiaofeng.com</a>
 */
@Slf4j
@RestController
@RequestMapping("/example")
@Api(tags = "實(shí)例演示-PDF操作接口")
public class TestPdfController {

    @ApiOperation(value = "根據(jù)PDF模板導(dǎo)出PDF")
    @GetMapping("/exportPdf")
    public void exportPdf(HttpServletResponse response) {
        Map<String, Object> dataMap = new HashMap<>(16);
        dataMap.put("nickName", "笑小楓");
        dataMap.put("age", 18);
        dataMap.put("sex", "男");
        dataMap.put("csdnUrl", "https://zhangfz.blog.csdn.net/");
        dataMap.put("siteUrl", "https://www.xiaoxiaofeng.com/");
        dataMap.put("desc", "大家好,我是笑小楓。");

        Map<String, String> photoMap = new HashMap<>(16);
        photoMap.put("logo", "https://profile.csdnimg.cn/C/9/4/2_qq_34988304");

        // 設(shè)置response參數(shù),可以打開下載頁(yè)面
        response.reset();
        response.setCharacterEncoding("UTF-8");
        // 定義輸出類型
        response.setContentType("application/PDF;charset=utf-8");
        // 設(shè)置名稱
        response.setHeader("Content-Disposition", "attachment; filename=" + "xiaoxiaofeng.pdf");
        try {
            ServletOutputStream out = response.getOutputStream();
            // 模板路徑記
            PdfUtil.fillTemplate(dataMap, photoMap, out, "src/main/resources/templates/xiaoxiaofeng.pdf");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @ApiOperation(value = "測(cè)試純代碼生成PDF到指定目錄")
    @GetMapping("/createPdfLocal")
    public void create() {
        PdfUtil.createPdfPage("D:\\test\\xxf.pdf");
    }

    @ApiOperation(value = "測(cè)試合并PDF到指定目錄")
    @GetMapping("/mergePdf")
    public Boolean mergePdf() {
        String[] files = {"D:\\test\\1.pdf", "D:\\test\\2.pdf"};
        String newFile = "D:\\test\\xxf-merge.pdf";
        return PdfUtil.mergePdfFiles(files, newFile);
    }

    @ApiOperation(value = "測(cè)試合并PDF后并導(dǎo)出")
    @GetMapping("/exportMergePdf")
    public void createPdf(HttpServletResponse response) {
        // 設(shè)置response參數(shù),可以打開下載頁(yè)面
        response.reset();
        response.setCharacterEncoding("UTF-8");
        // 定義輸出類型
        response.setContentType("application/PDF;charset=utf-8");
        // 設(shè)置名稱
        response.setHeader("Content-Disposition", "attachment; filename=" + "xiaoxiaofeng.pdf");
        try (ServletOutputStream out = response.getOutputStream()) {
            String[] files = {"D:\\test\\1.pdf", "D:\\test\\2.pdf"};
            // 生成為臨時(shí)文件,轉(zhuǎn)換為流后,再刪除該文件
            String newFile = "src\\main\\resources\\templates\\" + UUID.randomUUID() + ".pdf";
            boolean isOk = PdfUtil.mergePdfFiles(files, newFile);
            if (isOk) {
                PdfUtil.readDeletePdf(newFile, out);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

以上就是SpringBoot生成和操作PDF的代碼詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot生成和操作PDF的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何在JDK 9中更簡(jiǎn)潔使用 try-with-resources 語(yǔ)句

    如何在JDK 9中更簡(jiǎn)潔使用 try-with-resources 語(yǔ)句

    本文詳細(xì)介紹了自 JDK 7 引入的 try-with-resources 語(yǔ)句的原理和用法,以及介紹了 JDK 9 對(duì) try-with-resources 的改進(jìn),使得用戶可以更加方便、簡(jiǎn)潔的使用 try-with-resources 語(yǔ)句。,需要的朋友可以參考下
    2019-06-06
  • Mybatis中通用Mapper的InsertList()用法

    Mybatis中通用Mapper的InsertList()用法

    文章介紹了通用Mapper中的insertList()方法在批量新增時(shí)的使用方式,包括自增ID和自定義ID的情況,對(duì)于自增ID,使用tk.mybatis.mapper.additional.insert.InsertListMapper包下的insertList()方法;對(duì)于自定義ID,需要重寫insertList()方法
    2025-02-02
  • Windows系統(tǒng)下Eclipse搭建ESP32編譯環(huán)境及安裝過(guò)程

    Windows系統(tǒng)下Eclipse搭建ESP32編譯環(huán)境及安裝過(guò)程

    Ecppse 使用了 ESP-IDF 中的 Makefile 支持。這意味著您需要從創(chuàng)建 ESP-IDF 項(xiàng)目開始。您可以使用 github 中的 idf-template 項(xiàng)目,接下來(lái)通過(guò)本文給大家介紹Windows系統(tǒng)下Eclipse搭建ESP32編譯環(huán)境及安裝過(guò)程,感興趣的朋友一起看看吧
    2021-10-10
  • 基于Spring框架由ConditionalOnMissingBean注解引發(fā)的問(wèn)題

    基于Spring框架由ConditionalOnMissingBean注解引發(fā)的問(wèn)題

    這篇文章主要介紹了基于Spring框架由ConditionalOnMissingBean注解引發(fā)的問(wèn)題,具有很好
    2023-11-11
  • Java這個(gè)名字的來(lái)歷與優(yōu)勢(shì)

    Java這個(gè)名字的來(lái)歷與優(yōu)勢(shì)

    Java是Sun公司開發(fā)的一種編程語(yǔ)言,Sun公司最初的方向是讓Java來(lái)開發(fā)一些電器裝置程序,Java名字的由來(lái),實(shí)際上是一個(gè)有趣的故事。
    2014-10-10
  • mybatis注入Date日期值為null的解決方法

    mybatis注入Date日期值為null的解決方法

    這篇文章主要給大家介紹了關(guān)于mybatis注入Date日期值為null的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 200行Java代碼編寫一個(gè)計(jì)算器程序

    200行Java代碼編寫一個(gè)計(jì)算器程序

    本篇文章給大家分享的只用200行java代碼,實(shí)現(xiàn)一個(gè)計(jì)算器程序,不僅能夠計(jì)算加減乘除,還能夠匹配小括號(hào)。實(shí)現(xiàn)代碼超簡(jiǎn)單,需要的朋友參考下吧
    2017-12-12
  • java中的靜態(tài)代碼塊、構(gòu)造代碼塊、構(gòu)造方法詳解

    java中的靜態(tài)代碼塊、構(gòu)造代碼塊、構(gòu)造方法詳解

    下面小編就為大家?guī)?lái)一篇java中的靜態(tài)代碼塊、構(gòu)造代碼塊、構(gòu)造方法詳解。小編覺(jué)得挺好的,現(xiàn)在分享給大家。給大家一個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-03-03
  • SpringBoot+MyBatisPlus+MySQL8實(shí)現(xiàn)樹形結(jié)構(gòu)查詢

    SpringBoot+MyBatisPlus+MySQL8實(shí)現(xiàn)樹形結(jié)構(gòu)查詢

    這篇文章主要為大家詳細(xì)介紹了SpringBoot+MyBatisPlus+MySQL8實(shí)現(xiàn)樹形結(jié)構(gòu)查詢,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • maven <repositories>標(biāo)簽和<pluginRepositories>標(biāo)簽的使用

    maven <repositories>標(biāo)簽和<pluginRepositories>標(biāo)簽的使用

    這篇文章主要介紹了maven <repositories>標(biāo)簽和<pluginRepositories>標(biāo)簽的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07

最新評(píng)論