问题描述
在使用XEasyPdf(XEasyPdf是优良的封装Apache PDFBox的工具)时需要将文本实现如图所示的效果。
我没有找到相关的方法,如果官方支持请看到@我,我的方法开销很大,容易gc
,每次使用都要手动gc
不是一个好方法,但总归可以实现文字效果。
特此记下文字旋转的方法
public static InputStream backY(XEasyPdfPage xEasyPdfPage, Map<String, Object> params) {
// 创建 PDF 文档
XEasyPdfDocument xDocument = XEasyPdfHandler.Document.build();
ByteArrayOutputStream out = new ByteArrayOutputStream();
xDocument.addPage(xEasyPdfPage);
xDocument.save(out);
try (InputStream inputStream = new ByteArrayInputStream(out.toByteArray())) {
try (PDDocument document = PDDocument.load(inputStream)) {
// 加载自定义中文字体文件
try (InputStream inputStreamFont = Thread.currentThread().getContextClassLoader().getResourceAsStream("static/font/fs_GB2312.ttf")) {
PDType0Font customFont = PDType0Font.load(document, inputStreamFont);
float startX = (float) params.get("startX"); // 右侧开始绘制,留边距
float yStart = (float) params.get("startY"); // 顶部开始绘制,留边距
int fontSize = (int) params.getOrDefault("fontSize", 22); // 字体大小,默认为 22
float fontWeight = (int) params.getOrDefault("fontWeight", 16); // 字体宽度,默认为 16
int eWeight = (int) params.getOrDefault("eWeight", 2); // 字体宽度,默认为 2
int specialWeight = (int) params.getOrDefault("specialWeight", 4); // 字体宽度,默认为 16
float charHeight = customFont.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize; // 计算字符高度
// 统计偏移量
int offsetCount = 0;
try (PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(0), true, true, true)) {
contentStream.beginText(); // 开始文本块
contentStream.setFont(customFont, fontSize);
customFont.getFontDescriptor().setForceBold(true);//加粗
String text = params.get("text").toString(); // 包含中英符号混合的文本
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
boolean isChinese = isChinese(ch);
boolean isCLastE = (i > 0) && isChinese && !isChinese(text.charAt(i - 1));
// 回补偏移量
float offsetRe = 0;
float offset = isChinese ? fontSize : customFont.getStringWidth(String.valueOf(ch)) / 1000 * fontWeight;
//恰好此时是数字和文字的分割处 取消回补的偏移量
if (isChinese && isCLastE && yStart != (float) params.get("startY")) {
//英文下面的汉字回补偏移量 除二是回补偏移量,这里取的是偏移量的双倍,为了明显的文字间隔作用
offsetRe = (fontSize - fontWeight);
offset += (fontSize - fontWeight);
}
offsetCount += offset;
if (isChinese) {
//中文保持不变
contentStream.setTextMatrix(1, 0, 0, 1, startX, yStart - offsetRe);
} else {
//英文或者符号旋转
//中置偏移量
float offsetX = (charHeight - fontWeight) / 2;
//计算文本宽度
float offsetDiff = customFont.getStringWidth(String.valueOf(ch)) / 1000 * fontWeight;
//上面的回补操作和这里对于,解决了文字旋转脱离原本的位置的问题 这个 reWeight 是增加英文或者符号的间隔距离
float reWeight = eWeight;
if (ch == '—') {
reWeight = eWeight + specialWeight;
}
if (ch == '、' || ch == '。' || ch == ',' || ch == ':' || ch == ';') {
reWeight = 0;
}
float offsetY = offsetDiff + (fontSize - offsetDiff) / 2 + reWeight;
contentStream.setTextMatrix(0, -1, 1, 0, startX + offsetX, yStart + offsetY);
yStart -= reWeight;
offsetCount += reWeight;
}
yStart -= offset;
contentStream.showText(Character.toString(ch));
//这里发现超过440也就是会超过边框一点点 430-22-1=407 所以是407
if (offsetCount > 407) {
startX -= charHeight + 2;
yStart = (float) params.get("startY");
System.out.println("重置偏移量成功!" + offsetCount + ": " + ch);
//重置
offsetCount = 0;
}
}
contentStream.endText(); // 结束文本块
}
ByteArrayOutputStream outDoc = new ByteArrayOutputStream();
document.save(outDoc);
return new ByteArrayInputStream(outDoc.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// 判断是否为中文字符
private static boolean isChinese(char c) {
return c >= '\u4e00' && c <= '\u9fff';
}