Pdfbox2(1) - 坐标判断和文本换行_CodingCafe-CSDN博客
上篇文章简单描述了如何生成和编排图文版的pdf,这次主要记录下如何优化提升pdf的生成速度和pdf大小
主方法,很简单
public ErrorCode jpgToPdf(Param param, String content, String fileName, HttpServletResponse response) {
COSClient originCosClient = cosClientWrapper.getOriginCosClient();
try (PDDocument pdDocument = new PDDocument()) {
//page=0
addText(pdDocument, content);
//page=1,2,3...
final boolean result = addImg(originCosClient, pdDocument, param.getUrls());
if (result) {
try (ServletOutputStream servletOutputStream = response.getOutputStream()) {
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
pdDocument.save(servletOutputStream);
}
return ErrorCode.SUCCESS;
}
} catch (Exception e) {
log.error("", e);
}
return ErrorCode.SYSTEM_ERROR;
}
生成文本,主要是页面排版和换行的调试
private void addText(PDDocument pdDocument, String content) throws IOException {
if (StringUtils.isBlank(content)) {
return;
}
PDPage pdPage = new PDPage();
pdDocument.addPage(pdPage);
PDRectangle mediaBox = pdPage.getMediaBox();
final PDType0Font pdfFont = initFont(pdDocument);
float fontSize = 14;
float leading = 1.5f * fontSize;
float marginX = 50;
float marginY = 50;
float width = mediaBox.getWidth() - 2 * marginX;
float startX = mediaBox.getLowerLeftX() + marginX;
float startY = mediaBox.getUpperRightY() - marginY;
try (PDPageContentStream contentStream = new PDPageContentStream(pdDocument, pdPage)) {
contentStream.beginText();
contentStream.setFont(pdfFont, fontSize);
contentStream.newLineAtOffset(startX, startY);
//首先根据换行符截取
//其次在跟进页面的大小进行排版
final List<String> contentList = Splitter.on("\n").splitToList(content);
for (String text : contentList) {
final List<String> textList = splitLines(text, width, pdfFont, fontSize);
for (String s1 : textList) {
contentStream.showText(s1);
contentStream.newLineAtOffset(0, -leading);
}
}
contentStream.endText();
} catch (IllegalArgumentException e) {
log.warn("error:{}", e.getMessage());
throw new RuntimeException(e.getMessage());
}
}
public static List<String> splitLines(String text, float lineWidth, PDFont font, float fontSize) throws IOException {
// 如果待输入文本为空,或文本长度为0,则直接返回空列表
if (text==null||text.trim().length()==0) {
// 返回空列表
return new ArrayList<>(0);
}
// 定义文本列表
List<String> lineList = new ArrayList<>(200);
// 定义临时文本
String tempText;
// 计算文本真实宽度
float realWidth = fontSize * font.getStringWidth(text) / 1000;
// 计算总行数(估计)
int count = (int) (lineWidth / realWidth);
// 计算的总行数与文本长度取最小值
count = Math.min(count, text.length());
// 定义开始索引
int beginIndex = 0;
// 遍历文本
for (int i = count, len = text.length(); i <= len; i++) {
// 截取临时文本
tempText = text.substring(beginIndex, i);
// 计算当前文本真实宽度
realWidth = fontSize * font.getStringWidth(tempText) / 1000;
// 如果真实宽度大于行宽度,则减少一个字符
if (realWidth>lineWidth) {
// 加入文本列表
lineList.add(text.substring(beginIndex, i - 1));
// 重置开始索引
beginIndex = i - 1;
}
// 如果当前索引等于文本长度,则直接加入文本列表
if (i==len) {
// 加入文本列表
lineList.add(text.substring(beginIndex, i));
}
}
return lineList;
}
private PDType0Font initFont(PDDocument pdDocument) throws IOException {
getFontFile();
return PDType0Font.load(pdDocument, fontFile);
}
生成图片,主要是异步生成+排版
private boolean addImg(COSClient originCosClient, PDDocument pdDocument, List<Url> urls) throws InterruptedException {
final List<Url> urlList = urls.stream().sorted(Comparator.comparing(Url::getSort)).collect(Collectors.toList());
//预先生成页面
for (int i = 0; i < urlList.size(); i++) {
PDPage pdPage = new PDPage();
pdDocument.addPage(pdPage);
}
//异步处理图片
CountDownLatch countDownLatch = new CountDownLatch(urlList.size());
for (int i = 0; i < urlList.size(); i++) {
final Url url = urlList.get(i);
String key = url.getUrl().substring("https://www.xxx.com/".length() - 1);
//图片输入源
GetObjectRequest getObjectRequest = new GetObjectRequest(BaseConst.BUCKET_NAME, key);
COSObject cosObject = originCosClient.getObject(getObjectRequest);
int pageIndex = i + 1;
pdfBoxThreadPool.submit(() -> {
try (COSObjectInputStream cosObjectInput = cosObject.getObjectContent()) {
addImg(pdDocument, cosObjectInput, pageIndex);
} catch (IOException e) {
log.error("", e);
} finally {
countDownLatch.countDown();
}
});
}
return countDownLatch.await(60L, TimeUnit.SECONDS);
}
private void addImg(PDDocument pdDocument, InputStream input, int pageIndex) throws IOException {
BufferedImage bufferedImage = ImageIO.read(input);
final PDPage pdPage = pdDocument.getPage(pageIndex);
final PDRectangle mediaBox = pdPage.getMediaBox();
float marginX = 50;
float marginY = 50;
float width = mediaBox.getWidth() - 2 * marginX;
float imgWidth = width;
float height = mediaBox.getHeight() - 2 * marginY;
float imgHeight = bufferedImage.getHeight() * (imgWidth / bufferedImage.getWidth());
// 图片过长的话,以高度为准
if (imgHeight > height) {
imgHeight = height;
imgWidth = bufferedImage.getWidth() * (imgHeight / bufferedImage.getHeight());
}
float startX = mediaBox.getLowerLeftX() + marginX + ((width - imgWidth) / 2);
float startY = mediaBox.getUpperRightY() - marginY - imgHeight;
PDImageXObject pdImageXObject = JPEGFactory.createFromImage(pdDocument, bufferedImage);
try (PDPageContentStream contentStream = new PDPageContentStream(pdDocument, pdPage)) {
contentStream.drawImage(pdImageXObject, startX, startY, imgWidth, imgHeight);
} catch (Exception e) {
log.error("", e);
}
}