开发者

Springboot如何根据docx填充生成word文件并导出pdf

目录
  • 将docx模板填充数据生成doc文件
    • 1、依赖引入
    • 2、doc文件转换docx,并标注别名
    • 3、编写Java代码实现数据填充
  • docx文件填充数据导出pdf(web)
    • 1、依赖引入
    • 2、字体文件
    • 3、编写工具类
    • 4、编写测试接口
  • 请求测试
    • docx4j 复杂docx文件转pdf碰见的坑总结

      在项目中碰见一个需求,需要将.doc的合同,转换为pdf实现打印与预览功能。

      将docx模板填充数据生成doc文件

      1、依赖引入

      填充docx模板,只需要引入一个pom依赖即可实现。

      <dependency>
          <groupId>com.deepoove</groupId>
          <artifactId>poi-tl</artifactId>
          <version>1.5.0</version>
      </dependency>

      2、doc文件转换docx,并标注别名

      用office或者wps,创建一个001.doc文件,绘制表格,保存。

      更改后缀为.docx,确定后,在指定的位置,表示数据接受变量名称。

      如下图所示:

      Springboot如何根据docx填充生成word文件并导出pdf

      3、编写java代码实现数据填充

      import com.deepoove.poi.XWPFTemplate;
      import org.springframework.core.io.ClassPathResource;
      import org.springframework.core.io.Resource;
      import java.io.File;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.OutputStream;
      import java.util.HashMap;
      import java.util.Map;
      
      /**
       * word 填充测试
       */
      public class TestWord {
          public static void main(String[] args) throws IOException {
              Map<String, Object> params = new HashMap<>();
              params.put("username","xiangjiao1");
              params.put("password","******");
              params.put("age",22);
              params.put("email","专注写bug测试中文");
              Resource resource = new ClassPathResource("templates_report/001.docx");
              File file = resource.getFile();
              // 数据填充
              XWPFTemplate template = XWPFTemplate.compile(file).render(params);
      
              String docOutPath = System.getProperty("user.dir")+File.separator+"springboot-poi"+File.separator+"pdf"+File.separator+ "1.doc";
              OutputStream outputStream = new FileOutputStream(docOutPath);
              template.write(outputStream);
      
          }
      }

      运行程序,查看结果。

      测试项目结构如下:

      Springboot如何根据docx填充生成word文件并导出pdf

      docx文件填充数据导出pdf(web)

      1、依赖引入

      docx模板中填充数据,并导出pdf类型的文件,除了上面的pom依赖之外,还需要引入其他的依赖信息,完整依赖如下所示:

      <!-- docx 数据填充生成 doc文件  这个是主要 -->
      <dependency>
          <groupId>com.deepoove</groupId>
          <artifactId>poi-tl</artifactId>
          <version>1.5.0</version>
      </dependency>
      <!-- doc 转 pdf -->
      <dependency>
          <groupId>com.itextpdf</groupId>
          <artifactId>itextpdf</artifactId>
          <version>5.5.13</version>
      </dependency>
      <!-- docx4j docx2pdf -->
      <dependency>
          <groupId>org.docx4j</groupId>
          <artifactId>docx4j</artifactId>
          <version>6.1.2</version>
      </dependency>
      <dependency>
          <groupId>org.docx4j</groupId>
          <artifactId>docx4j-export-fo</artifactId>
          <version>6.0.0</version>
      </dependency>

      2、字体文件

      src\main\resources下创建一个font文件夹,其中放入simsun.ttc字体文件。

      3、编写工具类

      思想很简单

      • 1、先使用上面的docx模板填充数据生成临时doc文件,
      • 2、再将doc文件转换为pdf文件
      • 3、删除临时文件

      【注意:】

      为了避免出现多人同时操作,导致文件误删的问题,需要尽可能地保证临时文件名称的唯一性。

      import com.deepoove.poi.XWPFTemplate;
      import com.itextpdf.text.*;
      import com.itextpdf.text.Image;
      import com.itextpdf.text.pdf.*;
      import lombok.extern.slf4j.Slf4j;
      import org.apache.commons.io.FileUtils;
      import org.apache.commons.lang3.text.WordUtils;
      import org.docx4j.Docx4J;
      import org.docx4j.convert.out.FOSettings;
      import org.docx4j.fonts.IdentityPlusMapper;
      import org.docx4j.fonts.Mapper;
      import org.docx4j.fonts.PhysicalFonts;
      import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
      import org.springframework.stereotype.Component;
      import java.io.*;
      import java.util.Map;
      import java.util.UUID;
      import java.util.zip.ZipOutputStream;
       
      /**
       * pdf 导出工具类
       */
      @Component
      @Slf4j
      public final class FreeMarkUtils {
       
          /**
           * 根据docx模板填充数据  并生成pdf文件
           *
           * @param dataMap      数据源
           * @param docxFile     docx模板的文件名
           * @return 生成的文件路径
           */
          public static byte[] createDocx2Pdf(Map<String, Object> dataMap, String docxFile) {
              //输出word文件路径和名称 (临时文件名,本次为测试,最好使用雪花算法生成,或者用uuid)
              String fileName = UUID.randomUUID().toString() + ".docx";
      
              // word 数据填充
              // 生成docx临时文件
              final File tempPath = new File(fileName);
              final File docxTempFile = getTempFile(docxFile);
              XWPFTemplate template = XWPFTemplate.compile(docxTempFile).render(dataMap);
              try {
                  template.write(new FileOutputStream(tempPath));
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
              // word转pdf
              final String pdfFile = convertDocx2Pdf(fileName);
              return getFileOutputStream(new File(pdfFile)).toByteArray();
          }
       
          /**
           * word(doc)转pdf
           *
           * @param wordPath doc 生成的临时文件路径
           * @return 生成的带水印的pdf路径
           */
          public static String convertDocx2Pdf(String wordPath) {
              OutputStream os = null;
              InputStream is = null;
              //输出pdf文件路径和名称  (临时文件  尽可能保证文件名称的唯一性)
              final String fileName = UUID.randomUUID().toString() + ".pdf";
              try {
                  is = new FileInputStream(wordPath);
                  WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(is);
                  Mapper fontMapper = new IdentityPlusMapper();
                  fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
                  fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
                  fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));
                  fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
                  fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
                  fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
                  fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
                  fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
                  fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
                  fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
                  fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
                  fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
                  fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
                  fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
                  //解决宋体(正文)和宋体(标题)的乱码问题
                  PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));
                  PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));
                  // 字体文件
                  PhysicalFonts.addPhysicalFonts("SimSun", WordUtils.class.getResource("/font/simsun.ttc"));
       
                  mlPackage.setFontMapper(fontMapper);
                  jsos = new FileOutputStream(fileName);
       
                  //docx4j  docx转pdf
                  FOSettings foSettings = Docx4J.createFOSettings();
                  foSettings.setWmlPackage(mlPackage);
                  Docx4J.toFO(foSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL);
                  is.close();//关闭输入流
                  os.close();//关闭输出流
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  // 删除docx 临时文件
                  File file = new File(wordPath);
                  if (file != null && file.isFile() && file.exists()) {
                      file.delete();
                  }
                  try {
                      if (is != null) {
                          is.close();
                      }
                      if (os != null) {
                          os.close();
                      }
                  } catch (Exception ex) {
                      ex.printStackTrace();
                  }
              }
              return fileName;
          }
       
          /**
           * 文件转字节输出流
           *
           * @param outFile 文件
           * @return
           */
          public static ByteArrayOutputStream getFileOutputStream(File outFile) {
              // 获取生成临时文件的输出流
              InputStream input = null;
              ByteArrayOutputStream bytestream = null;
              try {
                  input = new FileInputStream(outFile);
                  bytestream = new ByteArrayOutputStream();
                  int ch;
                  while ((ch = input.read()) != -1) {
                      bytestream.write(ch);
                  }
              } catch (FileNotFoundException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  try {
                      bytestream.close();
                      input.close();
                      log.info("删除临时文件");
                      if (outFile.exists()) {
                          outFile.delete();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              return bytestream;
          }
       
          /**
           * 获取资源文件的临时文件
           * 资源文件打jar包后,不能编程直接获取,需要通过流获取生成临时文件
           *
           * @param fileName 文件路径 templates/xxx.docx
           * @return
           */
          public static File getTempFile(String fileName) {
              final File tempFile = new File(fileName);
              InputStream fontTempStream = null;
              try {
                  fontTempStream = FreeMarkUtils.class.getClassLoader().getResourceAsStream(fileName);
                  FileUtils.copyInputStreamToFile(fontTempStream, tempFile);
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (fontTempStream != null) {
                          fontTempStream.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              return tempFile;
          }
       
       
          /**
           * 插入图片水印
           * @param srcByte 已生成PDF的字节数组(流转字节)
           * @param destFile 生成有水印的临时文件 temp.pdf
           * @return
           */
          public static FileOutputStream addwaterMark(byte[] srcByte, String destFile) {
              // 待加水印的文件
              PdfReader reader = null;
              // 加完水印的文件
              PdfStamper stamper = null;
              FileOutputStream fileOutputStream = null;
              try {
          android        reader = new PdfReader(srcByte);
                  fileOutputStream = new FileOutputStream(destFile);
                  stamper = new PdfStamper(reader, fileOutputStream);
                  int total = reader.getNumberOfPages() + 1;
                  PdfContentByte content;
                  // 设置字体
                  //BaseFont font = BaseFont.createFont();
                  // 循环对每页插入水印
                  for (int i = 1; i < total; i++) {
                      final PdfGState gs = new PdfGState();
                      // 水印的起始
                      content = stamper.getUnderContent(i);
                      // 开始
                      content.beginText();
                      // 设置颜色 默认为蓝色
                      //content.setColorFill(BaseColor.BLUE);
                      // content.setColorFill(Color.GRAY);
                      // 设置字体及字号
                      //content.setFontAndSize(font, 38);
                      // 设置起始位置
                      // content.setTextMatrix(400, 880);
                      //content.setTextMatrix(textWidth, textHeight);
                      // 开始写入水印
                      //content.showTextAligned(Element.ALIGN_LEFT, text, textWidth, textHeight, 编程客栈45);
       
                      // 设置水印透明度
                      // 设置笔触字体不透明度为0.4f
                 编程     gs.setStrokeOpacity(0f);
                      Image image = null;
                      image = Image.getInstance("url");
                      // 设置坐标 绝对位置 X Y 这个位置大约在 A4纸 右上角展示LOGO
                      image.setAbsolutePosition(472, 785);
                      // 设置旋转弧度
                      image.setRotation(0);// 旋转 弧度
                      // 设置旋转角度
                      image.setRotationDegrees(0);// 旋转 角度
                      // 设置等比缩放 图片大小
                      image.scalePercent(4);// 依照比例缩放
                      // image.scaleAbsolute(200,100);//自定义大小
                      // 设置透明度
                      content.setGState(gs);
                      // 添加水印图片
                      content.addImage(image);
                      // 设置透明度
                      content.setGState(gs);
                      //结束设置
                      content.endText();
                      content.stroke();
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (DocumentException e) {
                  e.printStackTrace();
              } finally {
                  try {
                      stamper.close();
                      fileOutputStream.close();
                      reader.close();
                  } catch (DocumentException e) {
                      e.printStackTrace();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              return fileOutputStream;
          }
      }

      4、编写测试接口

      import cn.xj.util.FreeMarkUtils;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      import java.util.HashMap;
      import java.util.Map;
      import java.util.UUID;
      
      @RestController
      @RequestMapping("/report")
      public class ReportController {
      
          @GetMapping("/doc2pdf")
          public void doc2pdf(HttpServletResponse response) {
              Map<String, Object> params = new HashMap<>();
              params.put("username","xiangjiao1");
              params.put("password","******");
              params.put("age",22);
              params.put("email","专注写bug测试中文");
      
              final byte[] data = FreeMarkUtils.createDocx2Pdf(params, "templates_report/001.docx");
              String fileName = UUID.randomUUID().toString() + "_001_test.pdf";
              generateFile(response, data, fileName);
          }
      
          /**
           * 下载文件
           * @param response 相应
           * @param data 数据
           * @param fileName 文件名
           */
          private void generateFile(HttpServletResponse response, byte[] data, String fileName) {
              response.setHeader("content-Type", "application/octet-stream");
              response.setCharacterEncoding("utf-8");
              response.setHeader("Content-disposition", "attachment;filename=" + fileName);
              try {
                  response.getOutputStream().write(data);
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  try {
                      response.getOutputStream().close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      }

      请求测试

      http://localhost/report/doc2pdf

      Springboot如何根据docx填充生成word文件并导出pdf

      docx4j 复杂docx文件转pdf碰见的坑总结

      转pdf出现空格压缩、中文缩减等问题,可以考虑将半角替换成全角,将模板中的空格使用全角空格替换。

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜