java - 尝试使用 Apache poi 制作简单的 PDF 文档

标签 java apache apache-poi

我看到互联网上到处都是提示 apache 的 pdf 产品的人,但我在这里找不到我的特定用例。我正在尝试用 apache poi 做一个简单的 Hello World。现在我的代码如下:

public ByteArrayOutputStream export() throws IOException {
    //Blank Document
    XWPFDocument document = new XWPFDocument();

    //Write the Document in file system
    ByteArrayOutputStream out = new ByteArrayOutputStream();;

    //create table
    XWPFTable table = document.createTable();
    XWPFStyles styles = document.createStyles();
    styles.setSpellingLanguage("English");
    //create first row
    XWPFTableRow tableRowOne = table.getRow(0);
    tableRowOne.getCell(0).setText("col one, row one");
    tableRowOne.addNewTableCell().setText("col two, row one");
    tableRowOne.addNewTableCell().setText("col three, row one");

    //create second row
    XWPFTableRow tableRowTwo = table.createRow();
    tableRowTwo.getCell(0).setText("col one, row two");
    tableRowTwo.getCell(1).setText("col two, row two");
    tableRowTwo.getCell(2).setText("col three, row two");

    //create third row
    XWPFTableRow tableRowThree = table.createRow();
    tableRowThree.getCell(0).setText("col one, row three");
    tableRowThree.getCell(1).setText("col two, row three");
    tableRowThree.getCell(2).setText("col three, row three");

    PdfOptions options = PdfOptions.create();
    PdfConverter.getInstance().convert(document, out, options);
    out.close();
    return out;
}

调用它的代码是:

    public ResponseEntity<Resource> convertToPDFPost(@ApiParam(value = "DTOs passed from the FE" ,required=true )  @Valid @RequestBody ExportEnvelopeDTO exportDtos) {

        if (exportDtos.getProdExportDTOs() != null) {
            try {
                FileOutputStream out = new FileOutputStream("/Users/kornhaus/Desktop/test.pdf");
                out.write(exporter.export().toByteArray());
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return new ResponseEntity<Resource>(responseFile, responseHeaders, HttpStatus.OK);
        }

        return new ResponseEntity<Resource>(HttpStatus.INTERNAL_SERVER_ERROR);
    }

}

在此处的这一行:out.write(exporter.export().toByteArray()); 代码抛出异常:

org.apache.poi.xwpf.converter.core.XWPFConverterException: java.io.IOException: Unable to parse xml bean

我不知道是什么原因造成的,甚至不知道去哪里寻找此类文档。我已经编写了十多年的代码,并且从来没有遇到过应该是一个简单的 Java 库的困难。任何帮助都会很棒。

最佳答案

主要问题是那些 PdfOptionsPdfConverter 不是 apache poi 项目的一部分。它们由 opensagres 开发,第一个版本被错误地命名为 org.apache.poi.xwpf.converter.pdf.PdfOptionsorg.apache.poi.xwpf。转换器.pdf.PdfConverter。这些旧类自 2014 年以来未更新,需要 apache poi 版本 3.9 才能使用。

但相同的开发人员提供了 fr.opensagres.poi.xwpf.converter.pdf ,这是最新的并且使用最新的稳定版本 apache poi 3.17。所以我们应该使用它。

但由于即使是较新的 PdfOptionsPdfConverter 也不是 apache poi 项目的一部分,apache poi不会用他们的版本测试那些。因此 apache poi 创建的默认 *.docx 文档缺少 PdfConverter 需要的一些内容。

  1. 必须有一个样式文档,即使它是空的。

  2. 至少设置了页面大小的页面必须有部分属性。

  3. 表格必须有表格网格集。

为了实现这一点,我们必须在程序中额外添加一些代码。不幸的是,这需要所有模式的完整 jar ooxml-schemas-1.3.jar,如 Faq-N10025 中所述。 .

并且因为我们需要更改底层低级对象,所以必须编写文档以便提交底层对象。否则我们交给 PdfConverterXWPFDocument 将是不完整的。

例子:

import java.io.*;
import java.math.BigInteger;

//needed jars: fr.opensagres.poi.xwpf.converter.core-2.0.1.jar, 
//             fr.opensagres.poi.xwpf.converter.pdf-2.0.1.jar,
//             fr.opensagres.xdocreport.itext.extension-2.0.1.jar,
//             itext-2.1.7.jar                                  
import fr.opensagres.poi.xwpf.converter.pdf.PdfOptions;
import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter;

//needed jars: apache poi and it's dependencies
//             and additionally: ooxml-schemas-1.3.jar 
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.util.Units;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

public class XWPFToPDFConverterSampleMin {

 public static void main(String[] args) throws Exception {

  XWPFDocument document = new XWPFDocument();

  // there must be a styles document, even if it is empty
  XWPFStyles styles = document.createStyles();

  // there must be section properties for the page having at least the page size set
  CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
  CTPageSz pageSz = sectPr.addNewPgSz();
  pageSz.setW(BigInteger.valueOf(12240)); //12240 Twips = 12240/20 = 612 pt = 612/72 = 8.5"
  pageSz.setH(BigInteger.valueOf(15840)); //15840 Twips = 15840/20 = 792 pt = 792/72 = 11"

  // filling the body
  XWPFParagraph paragraph = document.createParagraph();

  //create table
  XWPFTable table = document.createTable();

  //create first row
  XWPFTableRow tableRowOne = table.getRow(0);
  tableRowOne.getCell(0).setText("col one, row one");
  tableRowOne.addNewTableCell().setText("col two, row one");
  tableRowOne.addNewTableCell().setText("col three, row one");

  //create CTTblGrid for this table with widths of the 3 columns. 
  //necessary for Libreoffice/Openoffice and PdfConverter to accept the column widths.
  //values are in unit twentieths of a point (1/1440 of an inch)
  //first column = 2 inches width
  table.getCTTbl().addNewTblGrid().addNewGridCol().setW(BigInteger.valueOf(2*1440));
  //other columns (2 in this case) also each 2 inches width
  for (int col = 1 ; col < 3; col++) {
   table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(2*1440));
  }

  //create second row
  XWPFTableRow tableRowTwo = table.createRow();
  tableRowTwo.getCell(0).setText("col one, row two");
  tableRowTwo.getCell(1).setText("col two, row two");
  tableRowTwo.getCell(2).setText("col three, row two");

  //create third row
  XWPFTableRow tableRowThree = table.createRow();
  tableRowThree.getCell(0).setText("col one, row three");
  tableRowThree.getCell(1).setText("col two, row three");
  tableRowThree.getCell(2).setText("col three, row three");

  paragraph = document.createParagraph();

  //trying picture
  XWPFRun run = paragraph.createRun();
  run.setText("The picture in line: ");
  InputStream in = new FileInputStream("samplePict.jpeg");
  run.addPicture(in, Document.PICTURE_TYPE_JPEG, "samplePict.jpeg", Units.toEMU(100), Units.toEMU(30));
  in.close();  
  run.setText(" text after the picture.");

  paragraph = document.createParagraph();

  //document must be written so underlaaying objects will be committed
  ByteArrayOutputStream out = new ByteArrayOutputStream();
  document.write(out);
  document.close();

  document = new XWPFDocument(new ByteArrayInputStream(out.toByteArray()));
  PdfOptions options = PdfOptions.create();
  PdfConverter converter = (PdfConverter)PdfConverter.getInstance();
  converter.convert(document, new FileOutputStream("XWPFToPDFConverterSampleMin.pdf"), options);

  document.close();

 }
}

使用 XDocReport

另一种方法是使用最新版本的 opensagres/xdocreportConverter only with ConverterRegistry 中所述:

import java.io.*;
import java.math.BigInteger;

//needed jars: xdocreport-2.0.1.jar, 
//             odfdom-java-0.8.7.jar,
//             itext-2.1.7.jar  
import fr.opensagres.xdocreport.converter.Options;
import fr.opensagres.xdocreport.converter.IConverter;
import fr.opensagres.xdocreport.converter.ConverterRegistry;
import fr.opensagres.xdocreport.converter.ConverterTypeTo;
import fr.opensagres.xdocreport.core.document.DocumentKind;

//needed jars: apache poi and it's dependencies
//             and additionally: ooxml-schemas-1.3.jar 
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.util.Units;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

public class XWPFToPDFXDocReport {

 public static void main(String[] args) throws Exception {

  XWPFDocument document = new XWPFDocument();

  // there must be a styles document, even if it is empty
  XWPFStyles styles = document.createStyles();

  // there must be section properties for the page having at least the page size set
  CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
  CTPageSz pageSz = sectPr.addNewPgSz();
  pageSz.setW(BigInteger.valueOf(12240)); //12240 Twips = 12240/20 = 612 pt = 612/72 = 8.5"
  pageSz.setH(BigInteger.valueOf(15840)); //15840 Twips = 15840/20 = 792 pt = 792/72 = 11"

  // filling the body
  XWPFParagraph paragraph = document.createParagraph();

  //create table
  XWPFTable table = document.createTable();

  //create first row
  XWPFTableRow tableRowOne = table.getRow(0);
  tableRowOne.getCell(0).setText("col one, row one");
  tableRowOne.addNewTableCell().setText("col two, row one");
  tableRowOne.addNewTableCell().setText("col three, row one");

  //create CTTblGrid for this table with widths of the 3 columns. 
  //necessary for Libreoffice/Openoffice and PdfConverter to accept the column widths.
  //values are in unit twentieths of a point (1/1440 of an inch)
  //first column = 2 inches width
  table.getCTTbl().addNewTblGrid().addNewGridCol().setW(BigInteger.valueOf(2*1440));
  //other columns (2 in this case) also each 2 inches width
  for (int col = 1 ; col < 3; col++) {
   table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(2*1440));
  }

  //create second row
  XWPFTableRow tableRowTwo = table.createRow();
  tableRowTwo.getCell(0).setText("col one, row two");
  tableRowTwo.getCell(1).setText("col two, row two");
  tableRowTwo.getCell(2).setText("col three, row two");

  //create third row
  XWPFTableRow tableRowThree = table.createRow();
  tableRowThree.getCell(0).setText("col one, row three");
  tableRowThree.getCell(1).setText("col two, row three");
  tableRowThree.getCell(2).setText("col three, row three");

  paragraph = document.createParagraph();

  //trying picture
  XWPFRun run = paragraph.createRun();
  run.setText("The picture in line: ");
  InputStream in = new FileInputStream("samplePict.jpeg");
  run.addPicture(in, Document.PICTURE_TYPE_JPEG, "samplePict.jpeg", Units.toEMU(100), Units.toEMU(30));
  in.close();  
  run.setText(" text after the picture.");

  paragraph = document.createParagraph();

  //document must be written so underlaaying objects will be committed
  ByteArrayOutputStream out = new ByteArrayOutputStream();
  document.write(out);
  document.close();

  // 1) Create options DOCX 2 PDF to select well converter form the registry
  Options options = Options.getFrom(DocumentKind.DOCX).to(ConverterTypeTo.PDF);

  // 2) Get the converter from the registry
  IConverter converter = ConverterRegistry.getRegistry().getConverter(options);

  // 3) Convert DOCX 2 PDF
  InputStream docxin= new ByteArrayInputStream(out.toByteArray());
  OutputStream pdfout = new FileOutputStream(new File("XWPFToPDFXDocReport.pdf"));
  converter.convert(docxin, pdfout, options);

  docxin.close();       
  pdfout.close();       
 
 }
}

2018 年 10 月: 此代码使用 apache poi 3.17 运行。它无法使用 apache poi 4.0.0,因为 apache poi 中的更改直到现在才在 fr.opensagres.poi.xwpf.converter 中考虑 以及 fr.opensagres.xdocreport.converter


2019 年 2 月: 现在为我工作,使用最新的 apache poi 版本 4.0.1fr.opensagres.poi.xwpf.converter.pdf 的最新版本 2.0.2和配偶。


2021 年 6 月: 使用 apache poi 版本 4.1.2fr.opensagres.poi.xwpf.converter.pdf 的最新版本 2.0.2 工作和配偶。 无法使用 apache poi 版本 5.0.0 因为 XDocReport 需要 ooxml-schemas which apache poi 5 不再支持。


2022 年 4 月: 使用 apache poi 版本 5.2.2fr.opensagres.poi.xwpf.converter.pdf 的最新版本 2.0.3 工作和配偶。

关于java - 尝试使用 Apache poi 制作简单的 PDF 文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51330192/

相关文章:

php - 如何对多个 mySQL 表中包含的各种关键数据进行汇总和分组?

java - 无法使用 Apache POI 获取/设置 Word 文档 (.doc) 中的复选框值

java - 阻塞队列、单个生产者-多个消费者同步问题

java - 为什么CopyOnWriteArrayList需要自定义序列化

java - 如何使用 Java 生成电子邮件以获取公钥和私钥

java - 在 Hibernate 中使用 MySQL GET_LOCK 函数执行查询

javascript - 如何 SSI 包含变量文件名

具有别名和代理匹配的 Apache

java - XSLFGroupShape 不包含其子形状

java - Apache POI - 从 Excel 中的应用程序中删除行