Apache FOP使用PDF作为背景时出现COSStream已关闭无法读取异常
问题场景
我使用Apache FOP 2.5.x版本生成PDF文件,需要将现有PDF作为背景图,因此引入fop-pdf-images-2.3依赖实现该能力,该组件依赖PDFBox 2.0.20支撑FOP读取PDF作为背景图的需求。
运行过程中偶发如下报错:
Error on PDF page: https://s2.q4cdn.com/498544986/files/doc_downloads/test.pdf COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed? at org.apache.fop.render.pdf.pdfbox.PDFBoxImageHandler.handleImage(PDFBoxImageHandler.java:84) at org.apache.fop.render.intermediate.AbstractIFPainter.drawImage(AbstractIFPainter.java:249) at org.apache.fop.render.intermediate.AbstractIFPainter.drawImage(AbstractIFPainter.java:205) at org.apache.fop.render.intermediate.AbstractIFPainter.drawImageUsingImageHandler(AbstractIFPainter.java:170) at org.apache.fop.render.pdf.PDFPainter.drawImageUsingURI(PDFPainter.java:218) at org.apache.fop.render.pdf.PDFPainter.drawImage(PDFPainter.java:181) at org.apache.fop.render.intermediate.IFRenderer.drawImage(IFRenderer.java:1301) at org.apache.fop.render.AbstractPathOrientedRenderer.drawImage(AbstractPathOrientedRenderer.java:973) at org.apache.fop.render.AbstractPathOrientedRenderer.drawBackground(AbstractPathOrientedRenderer.java:308) at org.apache.fop.render.intermediate.IFRenderer.drawBackground(IFRenderer.java:1390) at org.apache.fop.render.AbstractPathOrientedRenderer.drawBackground(AbstractPathOrientedRenderer.java:215) at org.apache.fop.render.AbstractPathOrientedRenderer.drawBackAndBorders(AbstractPathOrientedRenderer.java:173) at org.apache.fop.render.AbstractPathOrientedRenderer.handleRegionTraits(AbstractPathOrientedRenderer.java:128) at org.apache.fop.render.AbstractRenderer.renderRegionViewport(AbstractRenderer.java:373) at org.apache.fop.render.intermediate.IFRenderer.renderRegionViewport(IFRenderer.java:738) at org.apache.fop.render.AbstractRenderer.renderPageAreas(AbstractRenderer.java:345) at org.apache.fop.render.AbstractRenderer.renderPage(AbstractRenderer.java:318) at org.apache.fop.render.intermediate.IFRenderer.renderPage(IFRenderer.java:587) at org.apache.fop.area.RenderPagesModel.addPage(RenderPagesModel.java:123) at org.apache.fop.layoutmgr.AbstractPageSequenceLayoutManager.finishPage(AbstractPageSequenceLayoutManager.java:316) at org.apache.fop.layoutmgr.PageSequenceLayoutManager.finishPage(PageSequenceLayoutManager.java:243) at org.apache.fop.layoutmgr.PageSequenceLayoutManager.activateLayout(PageSequenceLayoutManager.java:147) at org.apache.fop.area.AreaTreeHandler.endPageSequence(AreaTreeHandler.java:267) at org.apache.fop.fo.pagination.PageSequence.endOfNode(PageSequence.java:139) at org.apache.fop.fo.FOTreeBuilder$MainFOHandler.endElement(FOTreeBuilder.java:362) at org.apache.fop.fo.FOTreeBuilder.endElement(FOTreeBuilder.java:190) ...省略剩余栈帧 Caused by: java.io.IOException: COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed? at org.apache.pdfbox.cos.COSStream.checkClosed(COSStream.java:154) at org.apache.pdfbox.cos.COSStream.createInputStream(COSStream.java:239) at org.apache.pdfbox.cos.COSStream.createInputStream(COSStream.java:234) at org.apache.pdfbox.pdmodel.PDPage.getContents(PDPage.java:157) at org.apache.fop.render.pdf.pdfbox.PDFBoxAdapter.getContents(PDFBoxAdapter.java:460) at org.apache.fop.render.pdf.pdfbox.PDFBoxAdapter.createStreamFromPDFBoxPage(PDFBoxAdapter.java:352) at org.apache.fop.render.pdf.pdfbox.AbstractPDFBoxHandler.createStreamForPDF(AbstractPDFBoxHandler.java:111) at org.apache.fop.render.pdf.pdfbox.PDFBoxImageHandler.handleImage(PDFBoxImageHandler.java:71) ...省略剩余栈帧
经排查发现该错误与.fo文件中设置的pageHeight、pageWidth参数相关,.fo文件配置如下:
{% set cardWidth = 8.3 %} {% set cardHeight = 8 %} {% set bleedLength = 0.125 %} {% set totalWidth = cardWidth + 2 * bleedLength %} {% set totalHeight = cardHeight + 2 * bleedLength %} <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="MyPick" page-width="{{ totalWidth }}in" page-height="{{ totalHeight }}in"> <fo:region-body background-image="https://s2.q4cdn.com/498544986/files/doc_downloads/test.pdf"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="MyPick"> <fo:flow flow-name="xsl-region-body"> <fo:block-container absolute-position="absolute" top="{{ bleedLength }}in" left="{{ bleedLength }}in" width="{{ cardWidth }}in" height="{{ cardHeight }}in"> <fo:block-container display-align="center" margin-left="1.5in" margin-top="1.2in" margin-right="1.5in" height="6.539in"> <fo:block-container margin="0"> <fo:block font-family="Bookerly Display" text-align="start" color="rgb(255,255,255)" font-size="160pt" line-height="1"> Header </fo:block> <fo:block font-family="Bookerly Display" margin-top="0.3in" text-align="start" color="rgb(255,255,255)" font-size="110pt" line-height="1"> SubHeader </fo:block> </fo:block-container> </fo:block-container> <fo:block margin-top="0.75in" margin-bottom="0.4175in" margin-right="0.39in" text-align="right" font-family="Arial" font-size="6pt" color="rgb(255,255,255)"> Footer </fo:block> </fo:block-container> </fo:flow> </fo:page-sequence> </fo:root>
将配置中的cardWidth从8.3调整为8.2时,同一份PDF可以正常渲染,异常仅在特定页面尺寸下触发。
问题根因
该异常是fop-pdf-images 2.3版本的已知资源管理缺陷:
- 当FO设置的页面尺寸和背景PDF的页面尺寸差异达到阈值时,FOP会触发两次背景图渲染逻辑:第一次读取PDF资源做尺寸适配计算,计算完成后组件错误地提前关闭了关联的PDDocument实例;第二次实际渲染需要读取PDF流时,就会抛出COSStream已关闭的报错。
- 调整cardWidth为8.2后,页面尺寸适配逻辑无需二次读取PDF资源,因此可以正常渲染。
解决方案
方案1:升级依赖(优先推荐)
将fop-pdf-images升级到2.6及以上版本,该版本已修复PDDocument提前关闭的问题,同时将配套依赖的PDFBox版本升级到2.0.24及以上,避免版本兼容冲突,可适配当前使用的Apache FOP 2.5.x版本。
方案2:对齐页面尺寸(无需修改依赖)
提前将背景PDF的页面尺寸调整为和FO配置中totalWidth、totalHeight完全一致的尺寸,避免FOP渲染时触发背景重计算逻辑,即可绕开关流缺陷。
方案3:替换背景格式(应急使用)
如果前两个方案暂时无法落地,可以将背景PDF导出为300DPI以上的PNG图片作为背景使用,绕开PDFBox读取PDF流的逻辑,缺点是会丢失矢量图的无限缩放清晰度,仅适合临时应急。
内容的提问来源于stack exchange,提问作者Suparv Sharma
相关产品推荐
相关产品推荐

