Use poi to generate word documents based on templates and convert them into PDF files

1. First make a word template (it should be noted here that the file suffix is docx and not doc). ${xxxx} is the content that will be replaced later.
Documentation example

Regarding why the docx suffix must be, you can read this article https://www.cnblogs.com/ct-csu/p/8178932.html

2. Add the jar package files required by poi. I use maven to manage the jar package.
poi dependency
3. Due to poi’s own bug, there will be a problem that the picture cannot be displayed. Here we need to customize a class to inherit the XWPFDocument class. Next, we will use this class created by ourselves to operate the word object. This

The class inherits XWPFDocument, so you don’t have to worry about any problems.

package com.newdo.base;

import java.io.IOException;
import java.io.InputStream;

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

/**
 * word export tool class
 */
public class CustomXWPFDocument extends XWPFDocument{<!-- -->
public CustomXWPFDocument(InputStream in) throws IOException {<!-- -->
super(in);
}

public CustomXWPFDocument() {<!-- -->
super();
}

public CustomXWPFDocument(OPCPackage pkg) throws IOException {<!-- -->
super(pkg);
}
}

4. Next is the tool class for exporting word

package com.newdo.base;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletResponse;

import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;

/**
 * word export tool class
 */
public class WordUtils {<!-- -->

/**
* Used to determine whether the exported word is converted to pdf
*/
public static int judgment = 0;

public WordUtils() {<!-- -->
judgment = 0;
}

/**
* Generate word based on template
*
* @param path template path
* @param params Parameters that need to be replaced
* @param tableList Parameters to be inserted
* @param fileName The file name of the generated word file
* @param response
*/
public void getWord(String path, Map<String, Object> params, List<String[]> tableList, String fileName,
HttpServletResponse response) throws Exception {<!-- -->
String saveRoute = path.substring(0, path.indexOf(""));

File file = new File(path);
InputStream is = new FileInputStream(file);
CustomXWPFDocument doc = new CustomXWPFDocument(is);

this.replaceInPara(doc, params); // Replace variables in text
this.replaceInTable(doc, params, tableList); // Replace variables in the table
OutputStream os = null;
if (judgment == 0) {<!-- -->
fileName = java.net.URLDecoder.decode(fileName, "UTF-8");
os = new FileOutputStream(saveRoute + "\word" + fileName);

doc.write(os);

String pdf = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf"; // Truncate
wToPdfChange(saveRoute + "\word" + fileName, saveRoute + "\word" + pdf);

try {<!-- -->
System.out.println("========================pdf download starts================ ========");
// path refers to the path of the file to be downloaded.
file = new File(saveRoute + "\word" + pdf);
// Get the file name.
String filename = URLEncoder.encode(file.getName(), "utf-8");
// Get the file extension.
String ext = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase();
//Download the file as a stream.
is = new BufferedInputStream(new FileInputStream(saveRoute + "\word" + pdf));
byte[] buffer = new byte[is.available()];
is.read(buffer);
// Clear response
response.reset();
//Set the header of the response
response.addHeader("Content-Disposition", "attachment;filename=" + filename);
response.addHeader("Content-Length", "" + file.length());
os = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
os.write(buffer);
os.flush();
System.out.println("========================pdf download ends================ ========");
} catch (IOException ex) {<!-- -->
ex.printStackTrace();
}
} else {<!-- -->
//os = response.getOutputStream();
            fileName = java.net.URLDecoder.decode(fileName, "UTF-8");
            os = new FileOutputStream(saveRoute + "\word" + fileName);
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
doc.write(os);
judgment = 0;
}

this.close(os);
this.close(is);
}

/**
* word to pdf
*
* @param wordFile The path of word The path of word
* @param pdfFile pdf path
*/
public static void wToPdfChange(String wordFile, String pdfFile) {<!-- -->
ActiveXComponent app = null;
Dispatch document = null;
System.out.println("========================Start conversion================== =======");
try {<!-- -->
//Open word
System.out.println("Start opening word");
app = new ActiveXComponent("Word.Application");

// Get all open documents in word
Dispatch documents = app.getProperty("Documents").toDispatch();
System.out.println("Open file: " + wordFile);
//Open document
document = Dispatch.call(documents, "Open", wordFile, false, true).toDispatch();
// If the file exists, it will not be overwritten and an error will be reported directly, so we need to determine whether the file exists.
File target = new File(pdfFile);
if (target.exists()) {<!-- -->
target.delete();
}
System.out.println("Save as: " + pdfFile);
Dispatch.call(document, "SaveAs", pdfFile, 17);
} catch (Exception e) {<!-- -->
System.out.println("Conversion failed" + e.getMessage());
} finally {<!-- -->
//Close office
// app.invoke("Quit", 0);

if (document != null) {<!-- -->
//Close document
Dispatch.call(document, "Close", false);
}
if (app != null) {<!-- -->
app.invoke("Quit", 0);
}
\t\t\t
// Get system type
String osName = System.getProperty("os.name");
// Determine whether it is a system type
if (osName.toLowerCase().startsWith("win")) {<!-- -->
System.out.println(osName);
// window system
String killCmd = "taskkill /f /im wps.exe";
String killCmd1 = "taskkill /f /im wpscenter.exe";
Process p;
try {<!-- -->
p = Runtime.getRuntime().exec(killCmd);
p = Runtime.getRuntime().exec(killCmd1);
int runnngStatus = p.waitFor();
System.out.println("Killed" + runnngStatus);
} catch (IOException e) {<!-- -->
e.printStackTrace();
System.out.println("Conversion failed" + e.getMessage());
} catch (InterruptedException e) {<!-- -->
e.printStackTrace();
System.out.println("Conversion failed" + e.getMessage());
}
}
}
System.out.println("========================Conversion ends================== =======");
}

/**
* Replace variables in paragraphs
*
* @param doc The document to be replaced
* @param params parameters
*/
private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) {<!-- -->
Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
XWPFParagraph para;
while (iterator.hasNext()) {<!-- -->
para = iterator.next();
this.replaceInPara(para, params, doc);
}
}

/**
* Replace variables in paragraphs
*
* @param para The paragraph to be replaced
* @param params parameters
*/
private void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {<!-- -->
List<XWPFRun> runs;
Matcher matcher;
if (this.matcher(para.getParagraphText()).find()) {<!-- -->
runs = para.getRuns();
int start = -1;
int end = -1;
String runsIndex = "";
String str = "";
for (int i = 0; i < runs.size(); i + + ) {<!-- -->
XWPFRun run = runs.get(i);
String runText = run.toString().trim();
if (!runText.equals("") & amp; & amp; (runText.indexOf("${")!=-1 || runText.equals("}"))){<!-- -->
                    if ('$' == runText.charAt(0) & amp; & amp; '{' == runText.charAt(1)) {<!-- -->
                        start = i;
                        runsIndex + = i + ",";
                    }
                    if ((start != -1)) {<!-- -->
                        str + = runText;
                    }
                    if ('}' == runText.charAt(runText.length() - 1)) {<!-- -->
                        if (start != -1) {<!-- -->
                            end = i;

                            for (int k = start; k <= end; k + + ) {<!-- -->
                                para.removeRun(k);
                                k--;
                                end--;
                            }
                        }
                    }
                }

}

// for (int i = start; i <= end; i + + ) {<!-- -->
// para.removeRun(i);
//i--;
// end--;
// }
            int doInt = 0;
            String[] runsIns = runsIndex.split(",");
            for (int k=0;k<runsIns.length;k + + ){<!-- -->
                if (!"".equals(str)) {<!-- -->
                    for (Map.Entry<String, Object> entry : params.entrySet()) {<!-- -->
                        String key = entry.getKey();
                        if (str.indexOf(key) != -1) {<!-- -->

                            Object value = entry.getValue();
                            if (value instanceof String) {<!-- -->
                                str = str.replace(key, value.toString());
                                if (((String) str).indexOf("\r") > 0) {<!-- -->
                                    //Set newline
                                    String[] text = str.toString().split("\r");
                                    para.removeRun(0);
                                    for (int f = 0; f < text.length; f + + ) {<!-- -->
                                        if (f == 0) {<!-- -->
                                            // There is no indentation here because the word template is already indented.
                                            para.createRun().setText(text[f].trim());
                                        } else {<!-- -->
                                            para.createRun().addCarriageReturn();//hard carriage return
                                            // Note: The indentation of the first line of wps line break is three spaces. You can use it if the office requires it.
                                            // run.addTab(); indent or four spaces
                                            para.createRun().setText(text[f]);
                                        }
                                    }
                                } else {<!-- -->
// para.createRun().setText((String) value);
                                    int num = 0;
                                    if (doInt==0){<!-- -->
                                        num = CFunc.ToInt(runsIns[k]);
                                    }else{<!-- -->
                                        num = CFunc.ToInt(runsIns[k]) + doInt;
                                    }
                                    para.insertNewRun(num).setText((String) value);
                                    doInt + + ;
                                }
                                // para.createRun().setText(str, 0);
                                break;
                            } else if (value instanceof Map) {<!-- -->
                                str = str.replace(key, "");
                                Map pic = (Map) value;
                                int width = Integer.parseInt(pic.get("width").toString());
                                int height = Integer.parseInt(pic.get("height").toString());
                                int picType = getPictureType(pic.get("type").toString());
                                byte[] byteArray = (byte[]) pic.get("content");
                                ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                                try {<!-- -->
                                    // int ind =
                                    // doc.addPicture(byteInputStream,picType);
                                    // doc.createPicture(ind, width, height,para);
                                    doc.addPictureData(byteInputStream, picType);
                                    para.createRun().setText(str, 0);
                                    break;
                                } catch (Exception e) {<!-- -->
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }

}
}

/**
* Insert data into the table, add new rows if the number of rows is not enough
*
* @param table table into which data needs to be inserted
* @param tableList insert data collection
*/
private static void insertTable(XWPFTable table, List<String[]> tableList) {<!-- -->
// Create rows, add new rows according to the data that needs to be inserted, and do not process the header
for (int i = 0; i < tableList.size(); i + + ) {<!-- -->
XWPFTableRow row = table.createRow();
}
// Traverse the table and insert data
List<XWPFTableRow> rows = table.getRows();
int length = table.getRows().size();
for (int i = 1; i < length - 1; i + + ) {<!-- -->
XWPFTableRow newRow = table.getRow(i);
List<XWPFTableCell> cells = newRow.getTableCells();
for (int j = 0; j < cells.size(); j + + ) {<!-- -->
XWPFTableCell cell = cells.get(j);
String s = tableList.get(i - 1)[j];
cell.setText(s);
}
}
}

/**
* Replace variables in the table
*
* @param doc The document to be replaced
* @param params parameters
*/
private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<String[]> tableList) {<!-- -->
Iterator<XWPFTable> iterator = doc.getTablesIterator();
XWPFTable table;
List<XWPFTableRow> rows;
List<XWPFTableCell> cells;
List<XWPFParagraph> paras;
while (iterator.hasNext()) {<!-- -->
table = iterator.next();
// System.out.println("---------------->" + table.getRows().size());
if (table.getRows().size() > 1) {<!-- -->
// Determine whether the table needs to be replaced or inserted. If the judgment logic has $, it means replacement, if the table does not have $, it means insert.
if (this.matcher(table.getText()).find()) {<!-- -->
rows = table.getRows();
for (XWPFTableRow row : rows) {<!-- -->
cells = row.getTableCells();
for (XWPFTableCell cell : cells) {<!-- -->
paras = cell.getParagraphs();
for (XWPFParagraph para : paras) {<!-- -->
this.replaceInPara(para, params, doc);
}
}
}
} else {<!-- -->
insertTable(table, tableList); // Insert data
}
}
}
}

/**
* Regular match string
*
* @param str
* @return
*/
private Matcher matcher(String str) {<!-- -->
Pattern pattern = Pattern.compile("\$\{(. + ?)\}", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(str);
return matcher;
}

/**
* According to the image type, obtain the corresponding image type code
*
* @param picType
* @return int
*/
private static int getPictureType(String picType) {<!-- -->
int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
if (picType != null) {<!-- -->
if (picType.equalsIgnoreCase("png")) {<!-- -->
res = CustomXWPFDocument.PICTURE_TYPE_PNG;
} else if (picType.equalsIgnoreCase("dib")) {<!-- -->
res = CustomXWPFDocument.PICTURE_TYPE_DIB;
} else if (picType.equalsIgnoreCase("emf")) {<!-- -->
res = CustomXWPFDocument.PICTURE_TYPE_EMF;
} else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {<!-- -->
res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
} else if (picType.equalsIgnoreCase("wmf")) {<!-- -->
res = CustomXWPFDocument.PICTURE_TYPE_WMF;
}
}
return res;
}

/**
* Write the data in the input stream to the byte array
*
* @param in
* @return
*/
public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {<!-- -->
byte[] byteArray = null;
try {<!-- -->
int total = in.available();
byteArray = new byte[total];
in.read(byteArray);
} catch (IOException e) {<!-- -->
e.printStackTrace();
} finally {<!-- -->
if (isClose) {<!-- -->
try {<!-- -->
in.close();
} catch (Exception e2) {<!-- -->
e2.getStackTrace();
}
}
}
return byteArray;
}

/**
* Close the input stream
*
* @param is
*/
public void close(InputStream is) {<!-- -->
if (is != null) {<!-- -->
try {<!-- -->
is.close();
} catch (IOException e) {<!-- -->
e.printStackTrace();
}
}
}

/**
* Close the output stream
*
* @param os
*/
public void close(OutputStream os) {<!-- -->
if (os != null) {<!-- -->
try {<!-- -->
os.close();
} catch (IOException e) {<!-- -->
e.printStackTrace();
}
}
}
}

5. To convert word to pdf file, we need to use a package called jacob.jar (the download address is at the bottom of the article). Put the jar package into the lib under the resources file in the project.

Import local jar packages into maven

At the same time, you need to put the dll file in jacob into your own jdk environment, copy the dll file to C:\Program Files\Java\jdk1.8.0_191\jre\bin (this is my own jdk location), or copy Go to the jre/bin directory in the jdk directory where the current project is running.

The number of bits (×64 or ×86) of the file here depends on your computer.

Complete the above operations before word can be converted to pdf files normally

6. The last step is to test. I am using the springboot framework. Here is the test code.

 @Override
    public void contractPrintDetail(HttpServletRequest request, HttpServletResponse response, Map<String, Object> mapArgu) {<!-- -->
    //Queryed data
Map<String,Object> map = contractPrintMapper.mySelect(mapArgu);
        WordUtils wordUtil = new WordUtils();
        Map<String, Object> params = new LinkedHashMap<String, Object>();
        List<String[]> testList = new ArrayList<String[]>();
        params.put("${DWMC}",map.get("DWMC"));
        params.put("${DWDZ}",map.get("DWDZ"));
        params.put("${FDDBR}",map.get("FDDBR"));
        params.put("${XM}",map.get("XM"));
        params.put("${XBMC}",map.get("XBMC"));
        params.put("${LXDH}",map.get("LXDH"));
        params.put("${JG}",map.get("JG"));
        params.put("${ZS}",map.get("ZS"));
        params.put("${SFZHM}",map.get("SFZHM"));
        params.put("${GWMC}",map.get("GWMC"));
        try{<!-- -->
            String path = CFunc.clobToString((Clob) map.get("HTMB"));
            String name = CFunc.ToString(map.get("XM")) + "_" + CFunc.ToString(map.get("HTLX")) + "Contract.docx";
            String filename = URLEncoder.encode(name, "utf-8");
            response.setContentType(request.getServletContext().getMimeType(filename));
            wordUtil.getWord(path, params, testList, filename, response);
        }catch (Exception e){<!-- -->
            e.printStackTrace();
        }

7. The final generated pdf document

POI official website: https://poi.apache.org/
Apache POI Word simple sample document: https://my.oschina.net/skymozn/blog/3189732
JACOB:https://sourceforge.net/projects/jacob-project/?source=typ_redirect

Reference links:
https://www.cnblogs.com/duanrantao/p/8682897.html