欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

docx转pdf

程序员文章站 2024-01-18 23:03:10
...

最近被word逼疯,不仅要导出各种报告,还要附带表格,所以写了一个docx转pdf以供参考。


之前用XWPFDocument生成的docx在转pdf的时候总是会报java.lang.IllegalStateException: Expecting one Styles document part, but found 0。
转出来的pdf总是会损坏,给我气够呛,网上找了办法使用doc.createStyles();
然后又给我报什么文件提前结束?或者SAXParseException; Premature end of file.
一直以为是样式的问题,最后试验发现,手动创建的docx文档是可以完美转换成pdf的,我就突然有一种想法,能不能用手动创建docx的模板进行操作呢?比如说我替换掉里面的内容再导出,其实就跟我自己创建的docx没有什么区别呢?说干就干,接下来就是正文了。
该方法不限制页数,带图片的应该也可以,大家可以试试看。

创建docx

1、先自己手动创建一个docx模板,内容是什么不重要。
docx转pdf
2、其次读取该模板并删除模板里面的内容,所以里面是什么内容并不重要。

File file = new File("E:/模板.docx");
XWPFDocument doc = new XWPFDocument(new FileInputStream(file));
XWPFParagraph paragraph = doc.getLastParagraph();
paragraph.removeRun(0);

3、写自己需要的内容在word里。

XWPFParagraph paragraph= doc.createParagraph(); // 新建一个标题段落对象(就是一段文字)
paragraph.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun titleFun = paragraph.createRun(); // 创建文本对象
titleFun.setText(titleText); //设置标题的名字
fun.setTextPosition(20); // 设置两行之间的行间距
fun.setBold(isBold); // 加粗
fun.setColor("000000");// 设置颜色
fun.setFontSize(fontSize); // 字体大小
fun.setFontFamily("宋体");//设置字体

4、导出pdf。

OutputStream os = new FileOutputStream(new File("E:/转换结果.pdf"));
PdfOptions options = PdfOptions.create();
PdfConverter.getInstance().convert(doc, os, options);

doc.write(os);
os.flush();
os.close();

导出表格的时候遇到的问题

1、第一个问题

Caused by: java.lang.NullPointerException
	at fr.opensagres.poi.xwpf.converter.core.utils.XWPFTableUtil.getGridColList(XWPFTableUtil.java:184)
	at fr.opensagres.poi.xwpf.converter.core.utils.XWPFTableUtil.computeColWidths(XWPFTableUtil.java:117)
	at fr.opensagres.poi.xwpf.converter.core.XWPFDocumentVisitor.visitTable(XWPFDocumentVisitor.java:970)
	at fr.opensagres.poi.xwpf.converter.core.XWPFDocumentVisitor.visitBodyElements(XWPFDocumentVisitor.java:267)
	at fr.opensagres.poi.xwpf.converter.core.XWPFDocumentVisitor.start(XWPFDocumentVisitor.java:215)
	at fr.opensagres.poi.xwpf.converter.pdf.PdfConverter.doConvert(PdfConverter.java:57)
	... 4 more

跟着断点知道是grid为null,所以取不到值。
docx转pdf
所以在创建表格的时候加上:

CTTblGrid grid = table.getCTTbl().getTblGrid();
if (grid == null) {
	table.getCTTbl().addNewTblGrid();
}

2、紧接着出现第二个问题


Caused by: java.lang.NullPointerException
	at fr.opensagres.poi.xwpf.converter.core.utils.XWPFTableUtil.getWidth(XWPFTableUtil.java:274)
	at fr.opensagres.poi.xwpf.converter.core.utils.XWPFTableUtil.computeColWidths(XWPFTableUtil.java:285)
	at fr.opensagres.poi.xwpf.converter.core.utils.XWPFTableUtil.computeColWidths(XWPFTableUtil.java:128)
	at fr.opensagres.poi.xwpf.converter.core.XWPFDocumentVisitor.visitTable(XWPFDocumentVisitor.java:970)
	at fr.opensagres.poi.xwpf.converter.core.XWPFDocumentVisitor.visitBodyElements(XWPFDocumentVisitor.java:267)
	at fr.opensagres.poi.xwpf.converter.core.XWPFDocumentVisitor.start(XWPFDocumentVisitor.java:215)
	at fr.opensagres.poi.xwpf.converter.pdf.PdfConverter.doConvert(PdfConverter.java:57)
	... 4 more

跟着断点知道TcPr是null,所以取值的时候就会报null。
docx转pdf
所以在创建表格样式的时候遍历一下列,创建出TcPr,顺便创建TcTw,因为如果不创建的话,还是会报错。

for (XWPFTableRow row : table.getRows()) {
    for(XWPFTableCell cell : row.getTableCells()) {
        CTTblWidth ctTblWidth = cell.getCTTc().addNewTcPr().addNewTcW();
        ctTblWidth.setW(new BigInteger(width));
    }
}

3、本以为这样就好了,结果又报错了。

Caused by: java.lang.NullPointerException
	at fr.opensagres.poi.xwpf.converter.core.utils.XWPFTableUtil.getTableWidth(XWPFTableUtil.java:347)
	at fr.opensagres.poi.xwpf.converter.core.utils.XWPFTableUtil.getTableWidth(XWPFTableUtil.java:331)
	at fr.opensagres.poi.xwpf.converter.core.utils.XWPFTableUtil.computeColWidths(XWPFTableUtil.java:290)
	at fr.opensagres.poi.xwpf.converter.core.utils.XWPFTableUtil.computeColWidths(XWPFTableUtil.java:128)
	at fr.opensagres.poi.xwpf.converter.core.XWPFDocumentVisitor.visitTable(XWPFDocumentVisitor.java:970)
	at fr.opensagres.poi.xwpf.converter.core.XWPFDocumentVisitor.visitBodyElements(XWPFDocumentVisitor.java:267)
	at fr.opensagres.poi.xwpf.converter.core.XWPFDocumentVisitor.start(XWPFDocumentVisitor.java:215)
	at fr.opensagres.poi.xwpf.converter.pdf.PdfConverter.doConvert(PdfConverter.java:57)
	... 4 more

再打断点看到是Type为空。
docx转pdf
在上一行代码的setW()下增加一行

ctTblWidth.setType(STTblWidth.DXA);

运行结果完成,虽然还会报一个OpenXML4JRuntimeException 但是不影响结果就不管了。。。累了。
docx转pdf

完整的代码

1、Maven依赖(里面自带poi-4.0.1)

<dependency>
    <groupId>fr.opensagres.xdocreport</groupId>
    <artifactId>fr.opensagres.poi.xwpf.converter.pdf-gae</artifactId>
    <version>2.0.2</version>
</dependency>

2、WordOutputUtil 工具类


import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.math.BigInteger;
import java.util.List;

public class WordOutputUtil {

    public static void setBigTitle(XWPFDocument doc, String bigTitle, String authorName) {
        XWPFParagraph bigTitleParagraph = doc.createParagraph(); // 新建一个标题段落对象(就是一段文字)
        bigTitleParagraph.setAlignment(ParagraphAlignment.CENTER);// 样式居中
        XWPFRun titleFun = bigTitleParagraph.createRun(); // 创建文本对象
        titleFun.setText(bigTitle); //设置标题的名字
        setBigTitleFontStyle(titleFun);
        titleFun.addBreak(); // 换行
        titleFun = bigTitleParagraph.createRun(); // 创建文本对象
        titleFun.setText(authorName); //设置标题的名字
        setBigTitleTextFontStyle(titleFun);
    }

    public static void setTitleText(XWPFParagraph paragraph, String titleText) {
        XWPFRun titleFun = paragraph.createRun(); // 创建文本对象
        titleFun.setText(titleText); //设置标题的名字
        setTitleFontStyle(titleFun);
    }

    public static void setTableTitle(XWPFDocument doc, String tableTitleName) {
        XWPFParagraph bigTitleParagraph = doc.createParagraph(); // 新建一个标题段落对象(就是一段文字)
        bigTitleParagraph.setAlignment(ParagraphAlignment.CENTER);// 样式居中
        XWPFRun titleFun = bigTitleParagraph.createRun(); // 创建文本对象
        titleFun.addBreak();
        titleFun.setText(tableTitleName); //设置标题的名字
        setTableTitleFontStyle(titleFun);
    }

    public static void setGroupTitle(XWPFParagraph paragraph, String tableTitleName) {
        XWPFRun titleFun = paragraph.createRun(); // 创建文本对象
        titleFun.addBreak();
        titleFun.addTab();
        titleFun.setText(tableTitleName); //设置标题的名字
        setTitleFontStyle(titleFun);
    }


    public static void setText(XWPFParagraph paragraph, String text) {
        XWPFRun textFun = paragraph.createRun(); // 创建文本对象

        textFun.addBreak();
        textFun.addTab();
        textFun.setText(text); //设置标题的名字
        setTextFontStyle(textFun);
    }

    public static XWPFTable createTable(XWPFDocument doc, int rows, int cols) {
        XWPFTable table = doc.createTable(rows, cols);
        // 校验一下grid是否为空,如果为空就创建。转pdf的时候如果为空会报空指针
        CTTblGrid grid = table.getCTTbl().getTblGrid();
        if (grid == null) {
            table.getCTTbl().addNewTblGrid();
        }
        setTableWidthAndHAlign(table, "8288", STJc.CENTER);
        return table;
    }

    /**
     * @Description: 设置表格总宽度与水平对齐方式
     */
    public static void setTableWidthAndHAlign(XWPFTable table, String width, STJc.Enum enumValue) {
        CTTblPr tblPr = getTableCTTblPr(table);
        // 表格宽度
        CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
        if (enumValue != null) {
            CTJc cTJc = tblPr.addNewJc();
            cTJc.setVal(enumValue);
        }
        // 设置宽度
        tblWidth.setW(new BigInteger(width));
        tblWidth.setType(STTblWidth.DXA);

        // 转换pdf的时候如果没有这个可能会报空指针
        for (XWPFTableRow row : table.getRows()) {
            for(XWPFTableCell cell : row.getTableCells()) {
                CTTblWidth ctTblWidth = cell.getCTTc().addNewTcPr().addNewTcW();
                ctTblWidth.setW(new BigInteger(width));
                ctTblWidth.setType(STTblWidth.DXA);
            }
        }
    }

    /**
     * @Description: 得到Table的CTTblPr, 不存在则新建
     */
    public static CTTblPr getTableCTTblPr(XWPFTable table) {
        CTTbl ttbl = table.getCTTbl();
        // 表格属性
        CTTblPr tblPr = ttbl.getTblPr() == null ? ttbl.addNewTblPr() : ttbl.getTblPr();
        return tblPr;
    }

    /**
     * 合并表格单元格
     *
     * @param table    表格对象
     * @param startRow 开始行
     * @param endRow   结束行
     * @param startCol 开始列
     * @param endCol   结束列
     */
    public static void mergeTableCellsAndRow(XWPFTable table, int startRow, int endRow, int startCol, int endCol) {

        for (int rowIndex = startRow; rowIndex <= endRow; rowIndex++) {
            XWPFTableRow row = table.getRow(rowIndex);
            for (int cellIndex = startCol; cellIndex <= endCol; cellIndex++) {
                XWPFTableCell cell = row.getCell(cellIndex);
                // 第一个合并单元格用重启合并值设置
                if (cellIndex == startCol) {
                    cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
                } else {
                    // 合并第一个单元格的单元被设置为“继续”
                    cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
                }
            }
        }
        for (int rowIndex = startRow; rowIndex <= endRow; rowIndex++) {
            XWPFTableRow row = table.getRow(rowIndex);
            XWPFTableCell cell = row.getCell(startCol);
            // 第一个合并单元格用重启合并值设置
            if (rowIndex == startRow) {
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
            } else {
                // 合并第一个单元格的单元被设置为“继续”
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    /**
     * 往表格中填充数据
     *
     * @param table
     * @param tableData
     * @Author Huangxiaocong 2018年12月16日
     */
    public static void setTableData(XWPFTable table, List<List<Object>> tableData) {
        List<XWPFTableRow> rowList = table.getRows();
        for (int i = 0; i < rowList.size(); i++) {
            if (i >= tableData.size())
                break;
            List<Object> list = tableData.get(i);
            List<XWPFTableCell> cellList = rowList.get(i).getTableCells();
            for (int j = 0; j < cellList.size(); j++) {
                if (j >= list.size())
                    break;
                XWPFParagraph cellParagraph = cellList.get(j).getParagraphArray(0);
                XWPFRun cellParagraphRun = cellParagraph.createRun();
                setTableTextFontStyle(cellParagraphRun);
                cellParagraphRun.setText(String.valueOf(list.get(j)));
            }
        }
    }

    public static void setBigTitleFontStyle(XWPFRun fun) {
        fun.setBold(true); // 加粗
        fun.setColor("000000");// 设置颜色
        fun.setFontSize(20); // 字体大小
        fun.setFontFamily("黑体");//设置字体
    }

    public static void setBigTitleTextFontStyle(XWPFRun fun) {
        setFontStyle(fun, 12, false);
    }

    public static void setTitleFontStyle(XWPFRun fun) {
        fun.setTextPosition(20); // 设置两行之间的行间距
        setFontStyle(fun, 12, true);
    }

    public static void setTableTitleFontStyle(XWPFRun fun) {
        fun.setBold(true); // 加粗
        fun.setColor("000000");// 设置颜色
        fun.setFontSize(12); // 字体大小
        fun.setFontFamily("黑体");//设置字体
    }

    public static void setTableTextFontStyle(XWPFRun fun) {
        fun.setBold(false); // 加粗
        fun.setColor("000000");// 设置颜色
        fun.setFontSize(10); // 字体大小
        fun.setFontFamily("仿宋");//设置字体
    }

    public static void setTextFontStyle(XWPFRun fun) {
        setFontStyle(fun, 12, false);
    }

    public static void setFontStyle(XWPFRun fun, int fontSize, boolean isBold) {
        fun.setBold(isBold); // 加粗
        fun.setColor("000000");// 设置颜色
        fun.setFontSize(fontSize); // 字体大小
        fun.setFontFamily("宋体");//设置字体
    }

3、测试代码


import com.top.ckdemo.ck.common.WordOutputUtil;
import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter;
import fr.opensagres.poi.xwpf.converter.pdf.PdfOptions;
import org.apache.poi.xwpf.usermodel.*;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * docx转pdf
 */
public class Docx2Pdf {
    public static void main(String[] args) {
        String modelPath = "E:/模板.docx";
        String outPath = "E:/转换结果.pdf";
        docx2Pdf(modelPath, outPath);
    }

    public static void docx2Pdf(String modelPath, String outPath) {
        try {
            File file = new File(modelPath);
            XWPFDocument doc = new XWPFDocument(new FileInputStream(file));
            XWPFParagraph paragraph = doc.getLastParagraph();
            paragraph.removeRun(0);

            String bigTitle = "这是大标题";
            String authorName = "这是作者小标题";
            WordOutputUtil.setBigTitle(doc, bigTitle, authorName); // 大标题

            XWPFParagraph paragraph1 = doc.createParagraph(); // 新建一个标题段落对象(就是一段文字)
            paragraph1.setVerticalAlignment(TextAlignment.CENTER);
            XWPFParagraph paragraph2 = doc.createParagraph(); // 新建一个标题段落对象(就是一段文字)
            paragraph2.setVerticalAlignment(TextAlignment.CENTER);
            XWPFParagraph paragraph3 = doc.createParagraph(); // 新建一个标题段落对象(就是一段文字)
            paragraph3.setVerticalAlignment(TextAlignment.CENTER);
            String titleText = "一、内容1";
            WordOutputUtil.setTitleText(paragraph1, titleText); // 段落标题
            titleText = "二、内容2";
            WordOutputUtil.setTitleText(paragraph2, titleText); // 段落标题
            titleText = "三、内容3";
            WordOutputUtil.setTitleText(paragraph3, titleText); // 段落标题

            List<List<Object>> list = new ArrayList<List<Object>>();
            List<Object> rowList = new ArrayList<>();
            rowList.add("1");
            rowList.add("2");
            rowList.add("3");
            list.add(rowList);
            // 创建表格,
            WordOutputUtil.setTableTitle(doc, "表1-1");
            XWPFTable table = WordOutputUtil.createTable(doc, 3, 10); // 数据行数+1行标题,列数
            WordOutputUtil.setTableData(table, list);

            OutputStream os = new FileOutputStream(new File(outPath));

            PdfOptions options = PdfOptions.create();
            PdfConverter.getInstance().convert(doc, os, options);

            doc.write(os);
            os.flush();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

需要注意的是,表格合并的时候,如果是横向合并的话,字数太多或者列数太多会出现段落不齐的情况,竖向合并不会,所以请自行选择是否横向合并。