操作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 文件,上面的代码略显粗糙,这是我脱敏业务构建的代码,可以直接借鉴,后续有时间再整理整理

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

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

相关推荐
雕刻刀38 分钟前
Latex 转换为 Word(使用GrindEQ )(英文转中文,毕业论文)
word
觅远12 小时前
python实现word转html
python·html·word
养个小橘猫12 小时前
Word使用分隔符实现页面部分分栏
word
神色自若14 小时前
Net9为PDF文字替换,使用Spire.PDF版本10.12.4.1360
pdf
机器懒得学习17 小时前
解析交通事故报告:利用 PDF、AI 与数据标准化技术构建智能分析系统
pdf
SEO-狼术1 天前
Document Solutions for Word CRACK
word
合合技术团队1 天前
高效准确的PDF解析工具,赋能企业非结构化数据治理
人工智能·科技·pdf·aigc·文档
m0_748237151 天前
前端:纯前端快速实现html导出word和pdf
前端·html·word
jingling5551 天前
如何使用免费资源--知网篇
开发语言·经验分享·搜索引擎·pdf·开源