Apache POI生成docx转PDF遇SAXParseException等错误求助
解决Apache POI生成Docx转PDF的样式缺失问题
问题场景
使用以下依赖:
org.apache.poi:poi-ooxml:5.2.3fr.opensages.xdocreport:org.apache.poi.xwpf.converter.pdf:1.0.6
通过Apache POI创建空白Docx并写入内容后,调用PDF转换器时触发样式相关错误。原始代码如下:
@GetMapping("pdf") fun getPdf(aResponse: HttpServletResponse) { val myDoc = XWPFDocument () val myParagraph = myDoc.createParagraph () val myRun = myParagraph.createRun () myRun.setText ("Hello, world!") val myPdfOptions = PdfOptions.create () aResponse.contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE aResponse.addHeader ("Content-disposition", "attachment; filename=\"hello-world.pdf\"") aResponse.outputStream.use { PdfConverter.getInstance ().convert (myDoc, it, myPdfOptions) } }
首次错误信息
java.lang.IllegalStateException: Expecting one Styles document part, but found 0 at org.apache.poi.xwpf.usermodel.XWPFDocument.getStyle(XWPFDocument.java:587) at org.apache.poi.xwpf.converter.core.styles.XWPFStylesDocument.<init>(XWPFStylesDocument.java:190) at org.apache.poi.xwpf.converter.core.styles.XWPFStylesDocument.<init>(XWPFStylesDocument.java:184) at org.apache.poi.xwpf.converter.core.XWPFDocumentVisitor.createStylesDocument(XWPFDocumentVisitor.java:166) at org.apache.poi.xwpf.converter.core.XWPFDocumentVisitor.<init>(XWPFDocumentVisitor.java:159) at org.apache.poi.xwpf.converter.pdf.internal.PdfMapper.<init>(PdfMapper.java:149) at org.apache.poi.xwpf.converter.pdf.PdfConverter.doConvert(PdfConverter.java:55) at org.apache.poi.xwpf.converter.pdf.PdfConverter.doConvert(PdfConverter.java:38) at org.apache.poi.xwpf.converter.core.AbstractXWPFConverter.convert(AbstractXWPFConverter.java:45)
尝试调用myDoc.createStyles()添加空样式后,出现新错误:
org.xml.sax.SAXParseException: Premature end of file. at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204) at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:178) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1471) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:1013) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:534) at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888) at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824) at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1216) at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:635) at org.apache.xmlbeans.impl.store.Locale$SaxLoader.load(Locale.java:2532) at org.apache.xmlbeans.impl.store.Locale.lambda$parseToXmlObject$3(Locale.java:717) at org.apache.xmlbeans.impl.store.Locale.syncWrap(Locale.java:490) at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:716) at org.apache.xmlbeans.impl.schema.SchemaTypeLoaderBase.parse(SchemaTypeLoaderBase.java:233) at org.apache.xmlbeans.impl.schema.AbstractDocumentFactory.parse(AbstractDocumentFactory.java:71) at org.apache.poi.xwpf.usermodel.XWPFDocument.getStyle(XWPFDocument.java:591) ... continues same as above error
解决方案
直接创建的XWPFDocument不包含Word默认的样式结构(styles.xml),而xdocreport的PDF转换器依赖完整的样式定义。最简便可靠的解决方式是基于包含默认样式的空白Docx模板来创建文档:
- 准备一个空白的Word文档(通过Microsoft Word或WPS创建,确保包含默认样式),保存为
blank-template.docx,放入项目的resources目录。 - 修改代码,从模板加载文档而非创建全新的空白文档:
@GetMapping("pdf") fun getPdf(aResponse: HttpServletResponse) { // 从资源目录加载带默认样式的空白模板 val templateStream = this::class.java.classLoader.getResourceAsStream("blank-template.docx") ?: throw IllegalArgumentException("空白模板文件不存在") // 使用use自动关闭流和文档资源 XWPFDocument(templateStream).use { myDoc -> val myParagraph = myDoc.createParagraph() val myRun = myParagraph.createRun() myRun.setText("Hello, world!") val myPdfOptions = PdfOptions.create() aResponse.contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE aResponse.addHeader("Content-disposition", "attachment; filename=\"hello-world.pdf\"") aResponse.outputStream.use { PdfConverter.getInstance().convert(myDoc, it, myPdfOptions) } } }
原理说明
手动创建的XWPFDocument仅包含最基础的文档结构,缺少Word默认的样式集合。而通过模板加载的文档自带完整的styles.xml部分,能满足PDF转换器对样式结构的要求,避免触发样式缺失相关的异常。
内容的提问来源于stack exchange,提问作者Jesser
相关产品推荐
相关产品推荐

