iText 7.1.0中RoundDotsBorder属性传递至同页独立行问题咨询
问题分析与解决方案
问题描述
在使用iText 7.1.0搭配Java JDK-9.0.1时,碰到了一个奇怪的问题:当页面里有使用RoundDotsBorder(圆点边框)的表格后,同一页面上用PdfCanvas绘制的独立线条(比如页脚的分隔线)会莫名其妙继承这个边框的线宽和线型——变成粗圆点虚线,而换成其他边框类型(比如普通实线边框)就完全正常。
原因剖析
其实问题出在iText的PdfCanvas是状态持久化的:当RoundDotsBorder渲染表格边框时,它会修改画布的线宽、虚线模式等stroke属性,而且这些修改会一直保留在画布上下文里。之后在处理页脚时,我们直接用同一个PdfCanvas画线条,没去重置这些属性,自然就继承了表格边框的样式。
其他边框类型没这个问题,是因为它们的stroke参数(比如默认线宽1、无虚线)和画布初始状态一致,或者渲染后会自动重置状态,但RoundDotsBorder刚好没做这个重置操作。
解决方案
要解决这个问题,核心就是在绘制自定义线条前,把画布的stroke状态重置回来,或者用状态保存/恢复的方式隔离不同绘制操作的影响。这里给两种靠谱的实现方式:
方式1:手动重置线条属性
在绘制页脚线条前,明确设置我们需要的线宽和线型,直接覆盖之前的状态:
private static class FooterEventHandler implements IEventHandler { @Override public void handleEvent(Event event) { PdfDocumentEvent docEvent = (PdfDocumentEvent)event; PdfPage page = docEvent.getPage(); Rectangle pageSize = page.getPageSize(); PdfCanvas pdfCanvas = new PdfCanvas(page); try(Canvas canvas = new Canvas(pdfCanvas, docEvent.getDocument(), pageSize)) { canvas.setFontColor(DeviceRgb.BLACK); float x = (pageSize.getLeft() + pageSize.getRight()) / 2; float y = pageSize.getBottom() + 10; canvas.showTextAligned("Page " + String.valueOf(docEvent.getDocument().getPageNumber(page)), x, y, TextAlignment.CENTER); // 手动重置线条属性:用默认线宽和实线模式 pdfCanvas.setStrokeColor(new DeviceRgb(java.awt.Color.BLACK)) .setLineWidth(1f) // 恢复默认线宽 .setLineDash(0); // 取消虚线效果 pdfCanvas.moveTo(pageSize.getLeft() + 10, pageSize.getBottom() + 30); pdfCanvas.lineTo(pageSize.getRight() - 10, pageSize.getBottom() + 30); pdfCanvas.closePathStroke(); } } }
方式2:用状态保存/恢复隔离上下文
更严谨的方式是在处理页脚前先保存当前画布状态,绘制完再恢复,这样完全不会受之前的操作影响:
private static class FooterEventHandler implements IEventHandler { @Override public void handleEvent(Event event) { PdfDocumentEvent docEvent = (PdfDocumentEvent)event; PdfPage page = docEvent.getPage(); Rectangle pageSize = page.getPageSize(); PdfCanvas pdfCanvas = new PdfCanvas(page); // 先把当前画布状态存起来 pdfCanvas.saveState(); try(Canvas canvas = new Canvas(pdfCanvas, docEvent.getDocument(), pageSize)) { canvas.setFontColor(DeviceRgb.BLACK); float x = (pageSize.getLeft() + pageSize.getRight()) / 2; float y = pageSize.getBottom() + 10; canvas.showTextAligned("Page " + String.valueOf(docEvent.getDocument().getPageNumber(page)), x, y, TextAlignment.CENTER); pdfCanvas.setStrokeColor(new DeviceRgb(java.awt.Color.BLACK)); pdfCanvas.moveTo(pageSize.getLeft() + 10, pageSize.getBottom() + 30); pdfCanvas.lineTo(pageSize.getRight() - 10, pageSize.getBottom() + 30); pdfCanvas.closePathStroke(); } finally { // 恢复到之前的画布状态,完美隔离 pdfCanvas.restoreState(); } } }
验证效果
修改完代码重新运行,页脚的分隔线就会恢复成正常的实线样式,再也不会被表格的圆点边框带偏了。个人更推荐方式2,因为它的隔离性更强,后续如果页面有其他复杂绘制操作,也能避免类似的状态污染问题。
内容的提问来源于stack exchange,提问作者aGe
相关产品推荐
相关产品推荐

