如何计算JasperSoft Studio生成的PDF中表格占用的高度(使用iText 7或其他Java/Kotlin库)
如何计算JasperSoft Studio生成的PDF中表格占用的高度(使用iText 7或其他Java/Kotlin库)
嘿,我完全懂你现在的痛点——用JasperSoft Studio生成的PDF里表格没占满整个A4页,但用iText 7只能拿到整页尺寸,想精准算出表格实际占了多高对吧?我之前做报表处理的时候也踩过类似的坑,给你两个实用的方案,都是用Java/Kotlin实现的:
方案一:用iText 7扫描页面内容,计算表格实际边界高度
这个方案的核心是找出页面上属于表格的所有元素(文本、边框、图片等)的Y轴范围,用最大Y值减去最小Y值,就是表格的实际高度。因为Jasper生成的表格通常是由文本块和边框线条组成的,我们可以通过遍历这些元素来定位边界:
import com.itextpdf.kernel.geom.Rectangle import com.itextpdf.kernel.pdf.PdfDocument import com.itextpdf.kernel.pdf.PdfReader import com.itextpdf.kernel.pdf.canvas.parser.PdfCanvasProcessor import com.itextpdf.kernel.pdf.canvas.parser.PdfTextExtractor import com.itextpdf.kernel.pdf.canvas.parser.listener.LocationTextExtractionStrategy import java.io.ByteArrayInputStream import java.util.Base64 fun getTableActualHeight(base64Pdf: String): Float { // 先把Base64字符串解码成PDF字节数组 val pdfByteArray = Base64.getDecoder().decode(base64Pdf) // 使用try-with-resources自动关闭资源,避免内存泄漏 PdfReader(ByteArrayInputStream(pdfByteArray)).use { reader -> PdfDocument(reader).use { pdfDoc -> if (pdfDoc.numberOfPages == 0) return 0f // 假设表格在第一页,多页的话可以循环处理 val targetPage = pdfDoc.getPage(1) val pageMediaBox = targetPage.getMediaBox() // 初始化边界为页面的上下边缘,后续会根据实际内容更新 var minContentY = pageMediaBox.getTop() var maxContentY = pageMediaBox.getBottom() // 第一步:遍历所有文本块,更新内容边界 val textExtractionStrategy = LocationTextExtractionStrategy() PdfTextExtractor.getTextFromPage(targetPage, textExtractionStrategy) textExtractionStrategy.getResultantTextChunks().forEach { chunk -> val chunkBbox = chunk.getLocation().getBBox() if (chunkBbox.getBottom() < minContentY) minContentY = chunkBbox.getBottom() if (chunkBbox.getTop() > maxContentY) maxContentY = chunkBbox.getTop() } // 第二步:遍历所有图形元素(比如表格的边框线),补充更新边界 val canvasProcessor = object : PdfCanvasProcessor() { override fun modifyPath() { super.modifyPath() val currentPath = getCurrentPath() ?: return val pathBbox = currentPath.getBoundingRectangle() ?: return if (pathBbox.getBottom() < minContentY) minContentY = pathBbox.getBottom() if (pathBbox.getTop() > maxContentY) maxContentY = pathBbox.getTop() } } canvasProcessor.processPageContent(targetPage) // 计算实际内容高度:最大Y值 - 最小Y值 return maxContentY - minContentY } } }
这个方案的小提示:
- 如果你的PDF里有页眉、页脚这类无关内容,可以加个判断过滤掉,比如忽略Y值在
pageMediaBox.getTop() - 50以上(页眉区域)或者pageMediaBox.getBottom() + 50以下(页脚区域)的元素,数值可以根据你的Jasper模板调整。 - 表格里如果有图片,上面的图形处理逻辑也能捕捉到图片的边界,不会漏算。
方案二:从Jasper生成的PDF元数据直接读取(更精准)
如果你的Jasper模板是自己可控的,那这个方案是最靠谱的——在生成PDF的时候,直接把表格的实际高度嵌入到PDF的自定义元数据里,后续读取的时候直接拿就行,不用扫描页面内容:
第一步:修改JasperSoft Studio模板
在你的Jasper报告里添加一个变量,用来计算表格的实际高度:
- 新建一个变量
$V{TABLE_ACTUAL_HEIGHT},变量类型选java.lang.Float - 变量的表达式设置为表格的实际高度计算逻辑,比如
$V{REPORT_HEIGHT} - $V{PAGE_HEADER_HEIGHT} - $V{PAGE_FOOTER_HEIGHT}这类表达式,具体根据你的模板结构调整 - 在报告的“属性”里添加一个自定义属性,比如
TableHeight,值设为$V{TABLE_ACTUAL_HEIGHT}
第二步:用iText 7读取元数据
fun getTableHeightFromMetadata(base64Pdf: String): Float { val pdfByteArray = Base64.getDecoder().decode(base64Pdf) PdfReader(ByteArrayInputStream(pdfByteArray)).use { reader -> PdfDocument(reader).use { pdfDoc -> // 读取自定义的元数据属性 val pdfCatalog = pdfDoc.getCatalog() val customTableHeight = pdfCatalog.getPdfObject() .getAsString(com.itextpdf.kernel.pdf.PdfName("TableHeight")) ?.toString() // 转成Float返回,默认返回0表示没找到 return customTableHeight?.toFloat() ?: 0f } } }
这个方案完全不会受页面其他元素干扰,数值是最准确的,唯一的前提是你能修改Jasper的生成模板。
备注:内容来源于stack exchange,提问作者Rodderh
相关产品推荐
相关产品推荐

