使用iTextPDF Java API生成大字体PDF时字符水平偏移的问题
iTextPDF Java API生成大字体PDF时字符水平偏移的问题
我看了你遇到的麻烦——用iTextPDF做带字母网格的PDF时,小字号(比如44号)能完美让每个字母在单元格里水平垂直居中,但换成大字号(94号)就出问题了:设置单元格最小宽度的话,字母会偏离单元格中心;不设的话单元格又会被字母撑得太宽,导致一行显示不全。我来帮你拆解下原因,再给你调整代码的具体方案。
问题根源分析
大字体下的偏移主要源于字符实际宽度和你预设的单元格宽度不匹配:
- 你预设的
cellWidth是按表格总宽度平均分出来的,但像A、B这类字符,大字号下的实际渲染宽度可能已经超过了这个预设值; - 当你强制设置
setMinWidth时,iText会把单元格锁死在指定宽度里,但字符本身的宽度超出了这个范围,此时的“居中”计算会基于字符的渲染边界,而非你预期的单元格中心,自然就出现了偏移; - 另外你设置的
setMultipliedLeading(0.1f)行高过小,也可能间接影响垂直居中的效果。
解决方案:动态适配字符宽度+精准居中
我们可以通过计算字符的实际宽度,动态调整单元格参数,既保证字母完全显示,又能实现精准居中。下面是修改后的关键代码片段,我会标注出调整的核心点:
1. 修改generateCell方法(核心调整)
private static Cell generateCell (final String cellText, final float cellWidth, final float cellHeight, final int fontSize, final boolean showGrid, boolean setMinWidth) throws IOException { final Cell nextCell = new Cell(); // 新增:计算当前字符的实际渲染宽度 FontProgram fontProgram = FontProgramFactory.createFont(); // 如果用自定义字体,替换成你的字体路径/名称 GlyphLine glyphLine = new GlyphLine(); glyphLine.add(new Glyph(fontProgram.getGlyphId(cellText.charAt(0)), cellText.charAt(0), fontSize)); float charActualWidth = glyphLine.getWidth(); final Paragraph p = new Paragraph() .setFontColor(DeviceGray.WHITE) .setFontSize(fontSize) .setTextAlignment(TextAlignment.CENTER) .setPadding(0.0f) .setMargins(0.0f,0.0f,0.0f,0.0f) .setCharacterSpacing(0.0f) .setWordSpacing(0.0f) .setFirstLineIndent(0.0f) .setMultipliedLeading(1.0f); // 调整:把极小行高改回正常,保证垂直居中不受挤压 p.setProperty(Property.OVERFLOW_Y, OverflowPropertyValue.VISIBLE); p.setProperty(Property.OVERFLOW_X, OverflowPropertyValue.VISIBLE); p.add(cellText); nextCell.add(p); nextCell.setMargins(0.0f,0.0f,0.0f,0.0f); nextCell.setBorder(showGrid ? new SolidBorder(DeviceRgb.RED, 0.1f) : Border.NO_BORDER); // 调整:动态设置单元格宽度,取预设宽度和字符实际宽度的较大值 float finalCellWidth = Math.max(cellWidth, charActualWidth); nextCell.setWidth(finalCellWidth); if (setMinWidth) { nextCell.setMinWidth(finalCellWidth); } nextCell.setHeight(cellHeight); nextCell.setPadding(0.0f); nextCell.setVerticalAlignment(VerticalAlignment.MIDDLE); nextCell.setHorizontalAlignment(HorizontalAlignment.CENTER); // 新增:如果字符宽度小于单元格宽度,手动设置左右内边距实现绝对居中 if (charActualWidth < cellWidth) { float padding = (cellWidth - charActualWidth) / 2; nextCell.setPaddingLeft(padding); nextCell.setPaddingRight(padding); } return nextCell; }
2. 辅助调整:表格宽度的弹性设置(可选)
如果调整后表格总宽度仍超出页面,可以把generateTable里的lettersTable.setWidth(MATRIX_WIDTH);改成lettersTable.setWidth(UnitValue.createPercentValue(100));,让表格自适应父容器宽度,避免溢出。
调整后的效果说明
- 计算字符实际宽度,彻底避免了强制单元格宽度小于字符宽度导致的偏移问题;
- 恢复正常行高,确保垂直居中不受行高挤压;
- 动态适配单元格宽度,既保证字母能完全显示,又不会让表格无限制扩张;
- 手动设置内边距,让字符在单元格里实现绝对水平居中,彻底解决偏移问题。
备注:内容来源于stack exchange,提问作者Itzik Stauber
相关产品推荐
相关产品推荐

