Pdfbox2(2) - 优化生成速度和pdf大小

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);
        }
    }
上一篇:PDF远非主流用途


下一篇:跨平台打印尝试