操作word 以及pdf 记录以及踩坑总结

操作word 以及pdf 记录

场景如下,需求是给了一个Word 模板,word 中有一个表格,需要填充这个表格,填充完毕之后,将其转换为PDF,因为之前下载的文件是word,产品tmd让转PDF

实现的思路

问了下AI,常见的有几种方式

1)用POI操作

2)用aspose实现word转PDF

操作word 使用org.apache.poi.xwpf.usermodel.XWPFDocument

对象,填充word

模板word内容如下

填充之后内容如下

填充的代码如下

java 复制代码
package com.htb.haigangpdf6.test;

import com.htb.haigangpdf6.utils.AposeWordPDFUtil;
import com.htb.haigangpdf6.utils.WordPDFUtil;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

import java.io.IOException;

public class Main3 {

    public static void main(String[] args) {
        String templatePath = "C:\\work\\file\\写文章\\自己画的模板.docx";
        String wordAfterFillPath = "C:\\work\\file\\写文章\\自己画的模板_填充内容之后.docx";
        String pdfFilePath = "C:\\work\\file\\写文章\\自己画的模板_填充内容之后转PDF.pdf";
        // 填充得到Word文件对象
        XWPFDocument docFile = WordPDFUtil.getDocFile(templatePath);
        try {
            // Word文件对象 转 bytes,得到的是 Word文件的byte[]
            byte[] bytes = WordPDFUtil.xwpfDocumentToBytes(docFile);
            // 方式一、Word文件的byte[] 转为PDF 文件
            // AposeWordPDFUtil.wordBytes2PdfFile(bytes, pdfFilePath);
            // 方式二、也可以调用方法 wordBytes2PdfBytes 得到PDF 文件 byte[],方便返回文件流让前端下载
            // byte[] pdfBytes = AposeWordPDFUtil.wordBytes2PdfBytes(bytes);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

如果需要返回文件流给前端,只需要改造main方法中 byte[] bytes 对象,写入到response 即可,下面会提到

上面的代码是在读取word 模板,进行填充内容,得到的是填充好的 XWPFDocument 对象,(此时如果你的需求是Word 那直接就结束了,保存为文件或者是下载文件流),但是产品的最终需求是转为PDF,需要把 得到的XWPFDocument对象转化为PDF

使用下面的方式实现转化word 为PDF

使用apose实现word转PDF

参考github 工程

https://github.com/zhengqingya/file-convert-util?tab=readme-ov-file

这个项目中使用是apose 相关的技术,实现的各种文件之间的转换

参考如下的内容

先引入所需要的第三方的jar

放百度云

链接:https://pan.baidu.com/s/1Dtdz9cK5TBcMt1e9MQsUbw

提取码:ef67

aspose-cells-8.5.2.jar

aspose-words-18.6-jdk16.jar

导入工程之后,引入jar

工程结构如下

xml 复制代码
         <dependency>
            <groupId>com.aspose</groupId>
            <artifactId>aspose-words</artifactId>
            <version>18.6</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/aspose-words-18.6-jdk16.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.aspose</groupId>
            <artifactId>aspose-cells</artifactId>
            <version>8.5.2</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/aspose-cells-8.5.2.jar</systemPath>
        </dependency>

添加对应的依赖之后 转换PDF的方法

java 复制代码
package com.htb.haigangpdf6.test;

import com.htb.haigangpdf6.utils.AposeWordPDFUtil;
import com.htb.haigangpdf6.utils.WordPDFUtil;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

import java.io.IOException;

public class Main3 {
    public static void main(String[] args) {
        String templatePath = "C:\\work\\file\\写文章\\自己画的模板.docx";
        String wordAfterFillPath = "C:\\work\\file\\写文章\\自己画的模板_填充内容之后.docx";
        String pdfFilePath = "C:\\work\\file\\写文章\\自己画的模板_填充内容之后转PDF.pdf";
        // 填充得到Word文件对象
        XWPFDocument docFile = WordPDFUtil.getDocFile(templatePath);
        try {
            // Word文件对象 转 bytes,得到的是 Word文件的byte[]
            byte[] bytes = WordPDFUtil.xwpfDocumentToBytes(docFile);
            // 方式一、Word文件的byte[] 转为PDF 文件
            AposeWordPDFUtil.wordBytes2PdfFile(bytes, pdfFilePath);
            // 方式二、也可以调用方法 wordBytes2PdfBytes 得到PDF 文件 byte[],方便返回文件流让前端下载
            // byte[] pdfBytes = AposeWordPDFUtil.wordBytes2PdfBytes(bytes);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上面的方法转换之后得到的文件如下,下面的截图是PDF文件

上面的图片转化失败,是因为图片本身的问题,换了一张图片后解决,如果遇到,可以考虑换一下图片

注意:

我找到的这个版本的Apose 转PDF 有一个弊端,不能转换超过11页的文件,如果超过,可能还要考虑PDF 拆分合并什么的 可以考虑使用 PDFBox

还有一个问题就是字体的问题,上述的截图是我在window 系统中转化的结果,使用的字体是宋体,放到服务器之后,运行在docker 容器中的时候,会有一个问题中文字都变成了 框,崩溃...我猜测是缺失字体,但是目前的镜像是基于jdk 构建的docker 镜像,没有根据操作也行构建镜像,实在不知道怎么解决,欢迎大佬提供方法

为了避免这个问题,又产生了下面的一种方式,使用itextpdf 直接操作PDF,直接绘制PDF

需要引入的依赖

xml 复制代码
<properties>
      <poi-ooxml.version>4.1.2</poi-ooxml.version>
        <poi-ooxml-schemas.version>4.1.2</poi-ooxml-schemas.version>
      </properties>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>${poi-ooxml.version}</version>
        </dependency>
  <!--绘制PDF所需依赖 start-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <!--绘制PDF所需依赖 end-->
java 复制代码
package com.htb.haigangpdf6.test;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import org.springframework.http.HttpHeaders;

import javax.servlet.ServletOutputStream;
import java.io.*;
import java.nio.file.Files;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

public class Main5 {
    private static final String imagePath = "C:\\work\\file\\写文章\\logo.png";
    private static final String pdfPath = "C:\\work\\file\\写文章\\直接绘制的.pdf";

    public static void main(String[] args) throws DocumentException, IOException {
        //新建一个A4大小的文档
        Document document = new Document(PageSize.A4);

//创建一个ByteArrayOutputStream绑定document,后续只需要将数据流写入baos即可
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PdfWriter.getInstance(document, baos);

//打开文档
        document.open();

        // 设置中文字体
        BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

//文档标题字体样式
        Font titleFontChinese = new Font(bfChinese, 15, Font.BOLD);

//小标题字体样式
        Font smallTitleFontChinese = new Font(bfChinese, 8, Font.BOLD);

//表头字体样式
        Font headFontChinese = new Font(bfChinese, 8, Font.BOLD);

//内容字体样式
        Font contentFontChinese = new Font(bfChinese, 9, Font.NORMAL);

        // 画表头以上的信息
        drawHead(document, titleFontChinese, contentFontChinese);
        String title = "淘宝订单";
        String bidNo = "no20240602xxxxx";
        drawTableOne(document, contentFontChinese, title, bidNo);
        drawTableTwo(document, contentFontChinese);

        //关闭文档
        document.close();

        //将PDF内容写入响应输出流并关闭
        OutputStream outputStream = new FileOutputStream(new File(pdfPath));
        outputStream.write(baos.toByteArray());
        outputStream.flush();
        outputStream.close();

    }


    private static void drawHead(Document document, Font titleFontChinese, Font contentFontChinese) throws DocumentException, IOException {
        //保持美观,空一行
        document.add(new Paragraph(" "));

        //创建一个包含两列的表格,这个表格里面左列是图片,右列是表格
        PdfPTable table = new PdfPTable(2);
        //设置表格宽度
        table.setWidthPercentage(100);
        //希望最终图片的大小小于右边的表格,需要划分两列比例
        table.setWidths(new int[]{1, 2});
        //第一列添加图片
        PdfPCell imageCell = new PdfPCell();

        File imageFile = new File(imagePath);
        byte[] imageBytes = Files.readAllBytes(imageFile.toPath());

        Image img = Image.getInstance(imageBytes);
        img.scaleToFit(100, 52);
        //设置内部表格宽度为100%
        img.setWidthPercentage(100);
        imageCell.addElement(img);
        //设置单元格无边框
        imageCell.setBorder(Rectangle.NO_BORDER);
        //将带图片的单元格设置进入表格中
        table.addCell(imageCell);

        //第一列添加图片
        PdfPCell cell2 = new PdfPCell();
        Paragraph paragraph = new Paragraph("天珩集团", contentFontChinese);
        paragraph.setAlignment(Element.ALIGN_RIGHT);

        cell2.addElement(paragraph);
        cell2.setBorder(Rectangle.NO_BORDER);
        table.addCell(cell2);
        document.add(table);
        //这一步将step2中的titleFontChinese设置进来即可解决中文字体不显示问题
        Paragraph title = new Paragraph("出价详情", titleFontChinese);
        //标题居中
        title.setAlignment(Element.ALIGN_CENTER);
        //将标题添加到文档中
        document.add(title);
    }


    private static void drawTableOne(Document document, Font contentFontChinese,
                                     String title,
                                     String bidNo) throws DocumentException {
        //创建一个包含两列的表格,这个表格里面左列是图片,右列是表格
        PdfPTable table = new PdfPTable(2);
        //设置表格宽度
        table.setWidthPercentage(100);
        //希望最终图片的大小小于右边的表格,需要划分两列比例
        table.setWidths(new int[]{5, 3});
        PdfPCell titleCell = new PdfPCell();
        titleCell.addElement(new Paragraph("采购标题:  " + title, contentFontChinese));
        //设置单元格无边框
        titleCell.setBorder(Rectangle.NO_BORDER);
        table.addCell(titleCell);

        PdfPCell bidNoCell = new PdfPCell();
        bidNoCell.addElement(new Paragraph("采购招标单号:  " + bidNo, contentFontChinese));
        //设置单元格无边框
        bidNoCell.setBorder(Rectangle.NO_BORDER);
        table.addCell(bidNoCell);
        document.add(table);
    }


    private static void drawTableTwo(Document document, Font contentFontChinese) throws DocumentException {

        PdfPTable table = new PdfPTable(9);
        //设置表格宽度
        table.setWidthPercentage(100);

        // 添加表头信息
        addTableHead(table, contentFontChinese);

        // 获取数据
        List<BidsBuyMaterial> dataList = getDataSource();

        // 添加表格主体内容
        addTableBody(table, contentFontChinese, dataList);
        document.add(table);
        String userName = "小甜甜";
        addTableFoot(document, contentFontChinese, userName);

    }

    private static void addTableFoot(Document document, Font contentFontChinese, String userName) throws DocumentException {
        PdfPTable table = new PdfPTable(3);
        //设置表格宽度
        table.setWidthPercentage(100);
        //希望最终图片的大小小于右边的表格,需要划分两列比例
        table.setWidths(new int[]{5, 2, 2});
        PdfPCell cell1 = new PdfPCell();
        cell1.addElement(new Paragraph("数据来源:天珩集团", contentFontChinese));
        cell1.setBorder(Rectangle.NO_BORDER);
        table.addCell(cell1);

        PdfPCell cell2 = new PdfPCell();
        cell2.addElement(new Paragraph("打印人: " + userName, contentFontChinese));
        cell2.setBorder(Rectangle.NO_BORDER);
        table.addCell(cell2);

        PdfPCell cell3 = new PdfPCell();
        Paragraph paragraphCell4 = new Paragraph("打印时间:" + localDate2DateString(LocalDate.now()), contentFontChinese);
        paragraphCell4.setAlignment(Element.ALIGN_RIGHT);
        cell3.addElement(paragraphCell4);
        cell3.setBorder(Rectangle.NO_BORDER);
        table.addCell(cell3);

        document.add(table);
    }


    private static void addTableHead(PdfPTable table,
                                     Font contentFontChinese) {
        List<String> headList = new ArrayList<>(10);
        headList.add("投标公司");
        headList.add("商品名称");
        headList.add("港口");
        headList.add("数量");
        headList.add("价格");
        headList.add("成分");
        headList.add("单位(%)");
        headList.add("交货日期");
        headList.add("附件");
        for (String txt : headList) {
            PdfPCell titleCell = new PdfPCell();
            titleCell.addElement(new Paragraph(txt, contentFontChinese));
            table.addCell(titleCell);
        }
    }

    private static List<BidsBuyMaterial> getDataSource() {
        List<BidsBuyMaterial> dataList = new ArrayList<>(10);
        BidsBuyMaterial bidsBuyMaterial1 = new BidsBuyMaterial();
        bidsBuyMaterial1.setBidsCompanyName("天珩集团1");
        bidsBuyMaterial1.setMaterialName("冷轧板卷");
        bidsBuyMaterial1.setPortLocation("矿石公司");
        bidsBuyMaterial1.setBidsNum(100.11D);
        bidsBuyMaterial1.setTaxInclude(1000.222D);
        bidsBuyMaterial1.setMaterialAttrFe("10");
        bidsBuyMaterial1.setMaterialAttrSi("20");
        bidsBuyMaterial1.setMaterialAttrAi("30");
        bidsBuyMaterial1.setMaterialAttrP("40");
        bidsBuyMaterial1.setMaterialAttrSulfur("50");
        bidsBuyMaterial1.setMaterialAttrWater("");
        bidsBuyMaterial1.setMaterialAttrOther("60");
        bidsBuyMaterial1.setDeliveryDateBegin(LocalDate.now());
        bidsBuyMaterial1.setAcc("质量报告.pdf");

        BidsBuyMaterial bidsBuyMaterial2 = new BidsBuyMaterial();
        bidsBuyMaterial2.setBidsCompanyName("天珩集团2");
        bidsBuyMaterial2.setMaterialName("冷轧板卷22222");
        bidsBuyMaterial2.setPortLocation("矿石公司22222");
        bidsBuyMaterial2.setBidsNum(100.11D);
        bidsBuyMaterial2.setTaxInclude(1000.222D);
        bidsBuyMaterial2.setMaterialAttrFe("10");
        bidsBuyMaterial2.setMaterialAttrSi("20");
        bidsBuyMaterial2.setMaterialAttrAi("30");
        bidsBuyMaterial2.setMaterialAttrP("40");
        bidsBuyMaterial2.setMaterialAttrSulfur("50");
        bidsBuyMaterial2.setMaterialAttrWater("");
        bidsBuyMaterial2.setMaterialAttrOther("60");
        bidsBuyMaterial2.setDeliveryDateBegin(LocalDate.now());
        bidsBuyMaterial2.setAcc("质量报告.pdf");


        dataList.add(bidsBuyMaterial1);
        dataList.add(bidsBuyMaterial2);
        return dataList;
    }


    private static void addTableBody(PdfPTable table, Font contentFontChinese, List<BidsBuyMaterial> dataList) {

        LinkedList<LinkedList<String>> dataToCell = getDataToCell(dataList);
        for (LinkedList<String> linkedList : dataToCell) {
            for (String txt : linkedList) {

                PdfPCell titleCell = new PdfPCell();
                titleCell.addElement(new Paragraph(txt, contentFontChinese));
                table.addCell(titleCell);
            }
        }
    }

    private static LinkedList<LinkedList<String>> getDataToCell(List<BidsBuyMaterial> dataList) {
        LinkedList<LinkedList<String>> result = new LinkedList<>();
        for (BidsBuyMaterial bidsBuyMaterial : dataList) {
            LinkedList<String> row = new LinkedList<>();
            row.add(bidsBuyMaterial.getBidsCompanyName());
            row.add(bidsBuyMaterial.getMaterialName());
            row.add(bidsBuyMaterial.getPortLocation());
            row.add(bidsBuyMaterial.getBidsNum().toString());
            row.add(bidsBuyMaterial.getTaxInclude().toString());
            List<String> valuesList1 = new ArrayList<>(10);
            valuesList1.add("铁");
            valuesList1.add("硅");
            valuesList1.add("铝");
            valuesList1.add("磷");
            valuesList1.add("硫");
            valuesList1.add("其他");
            String component = String.join("\n", valuesList1);
            row.add(component);

            List<String> valuesList2 = new ArrayList<>(10);
            valuesList2.add(Optional.ofNullable(bidsBuyMaterial.getMaterialAttrFe()).orElse(""));
            valuesList2.add(Optional.ofNullable(bidsBuyMaterial.getMaterialAttrSi()).orElse(""));
            valuesList2.add(Optional.ofNullable(bidsBuyMaterial.getMaterialAttrAi()).orElse(""));
            valuesList2.add(Optional.ofNullable(bidsBuyMaterial.getMaterialAttrP()).orElse(""));
            valuesList2.add(Optional.ofNullable(bidsBuyMaterial.getMaterialAttrSulfur()).orElse(""));
            valuesList2.add(Optional.ofNullable(bidsBuyMaterial.getMaterialAttrOther()).orElse(""));
            String componentNum = String.join("\n", valuesList2);
            row.add(componentNum);
            row.add(localDate2DateString(bidsBuyMaterial.getDeliveryDateBegin()));
            row.add(bidsBuyMaterial.getAcc());

            result.add(row);
        }
        return result;
    }

    private static String localDate2DateString(LocalDate localDate) {
        return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(localDate);
    }
}

绘制PDF的效果

这种方式一步到位,且没有页数限制,直接转为PDF 文件,上面的代码略显粗糙,这是我脱敏业务构建的代码,可以直接借鉴,后续有时间再整理整理

用这种方式还可以避免缺失字体的问题,超级推荐

很久没写文章了,欢迎指正

相关推荐
一个处女座的程序猿2 小时前
LLMs之PDF:zeroX(一款PDF到Markdown 的视觉模型转换工具)的简介、安装和使用方法、案例应用之详细攻略
pdf·markdown·zerox
Dxy12393102162 小时前
python下载pdf
数据库·python·pdf
周亚鑫2 小时前
vue3 pdf base64转成文件流打开
前端·javascript·pdf
一名技术极客3 小时前
Vue2 doc、excel、pdf、ppt、txt、图片以及视频等在线预览
pdf·powerpoint·excel·文件在线预览
Jacob程序员6 小时前
java导出word文件(手绘)
java·开发语言·word
q2498596936 小时前
前端预览word、excel、ppt
前端·word·excel
flashman9116 小时前
python在word中插入图片
python·microsoft·自动化·word
hairenjing112313 小时前
使用 Mac 数据恢复从 iPhoto 图库中恢复照片
windows·stm32·嵌入式硬件·macos·word
S. Dylan17 小时前
Edge浏览器打开PDF无法显示电子签章
edge·pdf
一马平川的大草原17 小时前
如何基于pdf2image实现pdf批量转换为图片
计算机视觉·pdf·文件拆分