简介:在Java文档中添加竖排文字的需求常见于中文、日文等东亚语言场景,但标准库未直接支持。本文通过解析文本旋转、自定义绘制、第三方库三种技术路径,结合Apache PDFBox与iText的完整代码示例,系统阐述竖排文字的实现方法及性能优化策略。
竖排文字的核心在于文本方向控制,需突破Java标准绘图API的横向排列限制。其技术实现主要依赖以下三种路径:
通过矩阵变换实现文字90度旋转,本质是利用AffineTransform类对文本基线进行几何变换。例如将水平向右的基线旋转为垂直向下,需计算旋转中心点与锚点偏移量。此方法兼容性最佳,但需处理字符间距与换行逻辑。
继承Graphics2D类重写drawString方法,逐个字符定位绘制。需建立字符坐标映射表,处理从右至左的阅读顺序。例如中文竖排需按列优先顺序排列,每个字符的Y坐标递增而X坐标固定。
Apache PDFBox与iText等库提供高级排版接口。PDFBox的PDPageContentStream支持设置文本矩阵,iText7的VerticalLayout模块内置竖排支持。此类方案开发效率高,但需引入额外依赖。
<!-- Maven依赖 --><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.27</version></dependency>
try (PDDocument doc = new PDDocument()) {PDPage page = new PDPage(PDRectangle.A4);doc.addPage(page);try (PDPageContentStream content = new PDPageContentStream(doc, page)) {content.beginText();// 设置字体与基础位置PDFont font = PDType1Font.HELVETICA;content.setFont(font, 12);// 创建旋转矩阵:原点(100,500),旋转90度AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(90), 100, 500);content.setTextMatrix(at);content.newLineAtOffset(0, 0); // 调整基线位置content.showText("垂直文本示例");content.endText();}doc.save("vertical_text.pdf");} catch (IOException e) {e.printStackTrace();}
// 分列绘制逻辑String text = "这是多列竖排文本示例,每列20个字符";int charsPerColumn = 20;int columnCount = (int) Math.ceil((double)text.length()/charsPerColumn);for (int col = 0; col < columnCount; col++) {int start = col * charsPerColumn;int end = Math.min(start + charsPerColumn, text.length());String columnText = text.substring(start, end);// 每列向右偏移100单位AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(90), 100 + col*100, 500);content.setTextMatrix(at);content.showText(columnText);}
<dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.2.5</version></dependency>
PdfDocument pdfDoc = new PdfDocument(new PdfWriter("vertical_itext.pdf"));Document doc = new Document(pdfDoc, PageSize.A4);// 创建竖排段落Paragraph verticalPara = new Paragraph("iText竖排文本示例").setFont(PdfFontFactory.createFont(StandardFontFamilies.HELVETICA)).setFontSize(12);// 设置竖排属性verticalPara.setProperty(Property.VERTICAL_WRITING_MODE, true);verticalPara.setProperty(Property.WRITING_MODE, WritingMode.VERTICAL);// 定位控制float x = 100;float y = 500;doc.showTextAligned(verticalPara, x, y, TextAlignment.LEFT,VerticalAlignment.TOP, 90); // 90度旋转doc.close();
// 多列竖排容器Div container = new Div().setWidth(PageSize.A4.getWidth() - 200).setHeight(PageSize.A4.getHeight() - 100).setPosition(100, 50);// 每列配置for (int i = 0; i < 3; i++) {Paragraph col = new Paragraph("第"+(i+1)+"列内容").setProperty(Property.VERTICAL_WRITING_MODE, true).setMarginLeft(i * 150); // 列间距container.add(col);}doc.add(container);
合并相同字体/颜色的文本绘制操作,减少PDPageContentStream的开关次数。示例中将所有竖排文本预先计算位置后统一绘制。
大文档处理时采用分块渲染,每处理10页执行一次PDDocument.save()的增量保存,避免内存溢出。
建立字体对象缓存池,避免重复加载相同字体:
Map<String, PDFont> fontCache = new ConcurrentHashMap<>();PDFont getCachedFont(PDDocument doc, String fontName) {return fontCache.computeIfAbsent(fontName,k -> PDType1Font.load(doc, new File("fonts/"+k+".pfb")));}
使用PDType0Font加载CID字体文件,确保复杂字符集正确显示:
PDType0Font cjkFont = PDType0Font.load(doc,new File("fonts/simsun.ttf"), false);content.setFont(cjkFont, 12);
建立辅助方法计算旋转后的实际坐标:
Point rotatePoint(Point origin, Point p, double angle) {double rad = Math.toRadians(angle);double x = origin.x + (p.x - origin.x)*Math.cos(rad)- (p.y - origin.y)*Math.sin(rad);double y = origin.y + (p.x - origin.x)*Math.sin(rad)+ (p.y - origin.y)*Math.cos(rad);return new Point((int)x, (int)y);}
采用Maven的dependencyManagement统一管理版本,避免iText与PDFBox混用导致的类加载冲突。
通过上述技术方案,开发者可根据项目需求选择最适合的实现路径。对于简单场景,PDFBox的旋转方案即可满足;复杂排版则推荐iText7的垂直布局模块。实际开发中需特别注意字体加载与坐标计算这两个关键环节,它们直接影响最终文档的显示质量。