import org.apache.pdfbox.io.MemoryUsageSetting; import org.apache.pdfbox.multipdf.PDFMergerUtility; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.util.Matrix; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; import java.util.Base64; import java.util.List; public class PdfUtil { private static final Logger LOG = LoggerFactory.getLogger(PdfUtil.class); private static final String PRE_HTML_CODE = "<html><head><meta charset="UTF-8"></head>" + "<body style="background-color:gray;"><style>" + "img {background-color:#fff; text-align:center; " + "100%; max-100%;margin-top:6px;}</style>"; private static final String SUF_HTML_CODE = "</body></html>"; private static final String MID_HTML_CODE = "<img src="data:image/png;base64,"; private static final String MIDD_HTML_CODE = "">"; /** * pdf转html */ public static String pdfToHtml(InputStream inputStream) { StringBuilder sb = new StringBuilder(); sb.append(PRE_HTML_CODE); PDDocument document = null; ByteArrayOutputStream outputStream = null; try { document = PDDocument.load(inputStream); int pages = document.getNumberOfPages(); PDFRenderer render = new PDFRenderer(document); BufferedImage image; for (int i = 0; i < pages; i++) { sb.append(MID_HTML_CODE); outputStream = new ByteArrayOutputStream(); image = render.renderImage(i, 2.5f); ImageIO.write(image, "png", outputStream); sb.append(Base64.getEncoder().encodeToString(outputStream.toByteArray())); sb.append(MIDD_HTML_CODE); } } catch (IOException e) { LOG.error(e.getMessage()); } finally { if (null != outputStream) { try { outputStream.close(); } catch (IOException ex) { } } if (null != document) { try { document.close(); } catch (IOException ex) { } } } sb.append(SUF_HTML_CODE); return sb.toString(); } /** * pdf合并 * * @param inputStreams 输入流 * @param outputStream 输出流 */ public static void mergePdf(List<InputStream> inputStreams, OutputStream outputStream) { try { PDFMergerUtility pdfMergerUtility = new PDFMergerUtility(); inputStreams.forEach(pdfMergerUtility::addSource); pdfMergerUtility.setDestinationStream(outputStream); pdfMergerUtility.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly()); } catch (Exception e) { throw new RuntimeException("pdf合并失败: " + e.getMessage(), e); } } private static final PDFont DEFAULT_FONT = PDType1Font.TIMES_BOLD; private static final Float FONT_SIZE = 20.0f; private static final int OFFSET_X = 20; private static final int OFFSET_Y = 20; /** * pdf header中增加自定义文字 * * @param inputStream 输入流 * @param outputStream 输出流 */ public static void appendHeaderText(InputStream inputStream, OutputStream outputStream, String appendText) { try { PDDocument doc = PDDocument.load(inputStream); doc.setAllSecurityToBeRemoved(true); for (PDPage page : doc.getPages()) { PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true); PDRectangle pdRectangle = page.getBBox(); cs.beginText(); cs.setFont(DEFAULT_FONT, FONT_SIZE); // 文字位置,指定x轴、y轴坐标偏移量 Matrix matrix = Matrix.getTranslateInstance(pdRectangle.getLowerLeftX() + OFFSET_X, pdRectangle.getUpperRightY() - OFFSET_Y); cs.setTextMatrix(matrix); cs.showText(appendText); cs.endText(); try { cs.close(); } catch (Exception e) { } } doc.save(outputStream); } catch (Exception e) { throw new RuntimeException("新增内容失败", e); } } /** * 根据图片创建pdf对象 * * @param inputStream 输入流 * @param outputStream 输出流 */ public static void createPDFFromImage(InputStream inputStream, OutputStream outputStream) { try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { byte[] cached = new byte[1024]; int read; while ((read = inputStream.read(cached)) > -1) { byteArrayOutputStream.write(cached, 0, read); } createPDFFromImage(byteArrayOutputStream.toByteArray(), outputStream); } catch (Exception e) { throw new RuntimeException("图片生成pdf失败", e); } } /** * 根据文件内容获取对应pdf对象 * * @param imageContent 图片内容 * @param outputStream 输出流 */ private static void createPDFFromImage(byte[] imageContent, OutputStream outputStream) { try (PDDocument doc = new PDDocument()) { PDPage page = new PDPage(); doc.addPage(page); // 获取图片大小 PDImageXObject ximage = PDImageXObject.createFromByteArray(doc, imageContent, ""); // 图片宽度 int imageWidth = ximage.getWidth(); // 图片高度 int imageHeight = ximage.getHeight(); // 获取pdf page大小 PDRectangle pdRectangle = page.getBBox(); float pageWidth = pdRectangle.getUpperRightX(); // pdf高度需要预留增加文本部分 float pageHeight = pdRectangle.getUpperRightY() - OFFSET_Y; // 分别计算宽度和高度的缩放量,并取缩放量中更小值所谓实际缩放量 float widthScale = pageWidth / imageWidth; float heightScale = pageHeight / imageHeight; float realScale = widthScale < heightScale ? widthScale : heightScale; // 计算实际宽度 float realWidth = ximage.getWidth() * realScale; float realHeight = ximage.getHeight() * realScale; try (PDPageContentStream contentStream = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.OVERWRITE, true, true)) { // x轴偏移量为page宽度 - 真实宽度的二分之一 // y轴偏移量为page高度 - 真实高度的二分之一 contentStream.drawImage(ximage, (pageWidth - realWidth) / 2, (pageHeight - realHeight) / 2, realWidth, realHeight); } doc.save(outputStream); } catch (Exception e) { throw new RuntimeException("Image转图片失败", e); } } }