ApachePDFBox是一个用于处理PDF文档的Java库。它提供了许多功能和方法来读取、创建、操作和提取PDF文档的内容。

引入maven依赖

org.apache.pdfboxpdfbox2.0.24

pdfbox生成pdf实例

try { // 创建一个空白的PDF文档 PDDocument document = new PDDocument(); // 创建一个页面 PDPage page = new PDPage(PDRectangle.A4); document.addPage(page); // 创建一个内容流 PDPageContentStream contentStream = new PDPageContentStream(document, page); // 设置字体和字号 contentStream.setFont(PDType1Font.HELVETICA_BOLD, 12); // 在页面上绘制文本 contentStream.beginText(); contentStream.newLineAtOffset(100, 700); contentStream.showText("Hello, World!"); contentStream.endText(); // 关闭内容流 contentStream.close(); // 保存PDF文档 document.save("output.pdf"); // 关闭PDF文档 document.close(); System.out.println("PDF生成成功!"); } catch (IOException e) { e.printStackTrace(); }

常用方法

PDDocument类

引用源码中对PDDocument类的描述

Thisisthein-memoryrepresentationofthePDFdocument

这是PDF文档的内存表示,在java程序中,你可以简单理解为他就是pdf文档,后续对他的一系列操作就是对pdf文档的一系列操作。

创建全新的pdf文档:文档中无任何页面

PDDocument document=new PDDocument();

如果你想对原有的pdf模板进行动态数据的填充,可以使用PDDocument.load()方法来加载已经制作好的pdf模板,

PDDocument document = PDDocument.load(new ClassPathResource("/static/reportTemplate.pdf").getInputStream());

你也可以用文件形式来加载pdf模板,不过更推荐文件流的形式

PDDocument document = PDDocument.load(new ClassPathResource("/static/reportTemplate.pdf").getFile());

如果你想对你生成的pdf进行加密操作,你可以使用PDDocumentload(InputStreaminput,Stringpassword)方法,如下设置了解密的密码为123456.

PDDocument document = PDDocument.load(new ClassPathResource("/static/reportTemplate.pdf").getInputStream(),"123456");

PDDocument.load()中有好多重载的方法,这里就不一一列出。感兴趣的可以查看pdfbox的源码,

ByteArrayOutputStream baos = new ByteArrayOutputStream();;document.save(baos); //保存文件到文件流document.save("output.pdf"); //保存文件到文件

保存成文件流之后,有时候我们需要将文件传输到前端进行下载,

// 将PDF文件转换为字节数组byte[] pdfBytes = baos.toByteArray();// 创建InputStreamResource对象ByteArrayInputStream bis = new ByteArrayInputStream(pdfBytes);InputStreamResource resource = new InputStreamResource(bis);// 设置HTTP响应头信息HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=output.pdf");headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PDF_VALUE);// 返回带有PDF内容的响应实体return ResponseEntity.ok() .headers(headers) .body(resource);

在对document操作完成之后,一定要执行document.close()方法关闭pdf文档。

document.close();

PDPage类

PDPage属于pdf文档中的的页面,

int pageNumber=document.getNumberOfPages();

获取指定页面,

PDPage page = document.getPage(0);

如果你是对pdf模板进行操作,你可以通过document.getPage(index)方法来获取pdf文档的指定页面,并对其进行操作(index从0开始)。你也可以通过newPDPage();创建一个全新的page,

PDPage newPage = new PDPage(PDRectangle.A4);

如果我们是通过newPDPage()的方式生成page页面时,我们需要将page页面添加到pdf文档中去(document),

document.addPage(newPage);

不过这种方式会将page添加到pdf文档的末尾,我们有时候需要将page添加到指定的位置,可以使用以下方法。

PDPage page=document.getPage(1); //获取第2页PDPage newPage = new PDPage(PDRectangle.A4);PDPageTree pages = document.getPages();pages.insertAfter(newPage,page); //插入到第2页后面pages.insertBefore(newPage,page); //插入到第2页前面

获取page页面总高度和总宽度,这个在后续的文字坐标定位中很有用,在page中原点坐标位于左下角,如果你想你的元素左边距为10,上边距为10,那么你的坐标将是(10,pageHeight-10)

float pageWidth = page.getMediaBox().getWidth();
float pageHeight = page.getMediaBox().getHeight();

PDPageContentStream

PDPageContentStream类提供写入页面内容流的功能,它需要绑定pdf文档和指定的page页面,这样相当于创建了page当前页面的内容流。

PDPageContentStream contentStream = new PDPageContentStream(document, page);

如果不指定PDPageContentStream.AppendMode,默认会以重写模式执行,后续对page页面添加元素会覆盖现有页面内容流。

PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);

模式代码

模式

注释

PDPageContentStream.AppendMode.OVERWRITE

重写模式

覆盖现有页面内容流

PDPageContentStream.AppendMode.APPEND

追加模式

将内容流附加到所有现有页面内容流之后

PREPENDPDPageContentStream.AppendMode.

准备模式

在所有其他页面内容流之前插入

对contentStream操作完成之后,需要关闭内容流。

contentStream.close();

pdf写入内容

关于字体

在ApachePDFBox中,字体相关的类主要位于org.apache.pdfbox.pdmodel.font包下。下面是一些常用的字体类:

  1. PDType1Font:这个类表示Type1字体,它是一种基于轮廓的字体格式。Type1字体常用于PDF文档中,如Helvetica、TimesRoman和Courier等。

示例:

PDType1Font font = PDType1Font.HELVETICA_BOLD;
public static final PDType1Font TIMES_ROMAN = new PDType1Font("Times-Roman");public static final PDType1Font TIMES_BOLD = new PDType1Font("Times-Bold");public static final PDType1Font TIMES_ITALIC = new PDType1Font("Times-Italic");public static final PDType1Font TIMES_BOLD_ITALIC = new PDType1Font("Times-BoldItalic");public static final PDType1Font HELVETICA = new PDType1Font("Helvetica");public static final PDType1Font HELVETICA_BOLD = new PDType1Font("Helvetica-Bold");public static final PDType1Font HELVETICA_OBLIQUE = new PDType1Font("Helvetica-Oblique");public static final PDType1Font HELVETICA_BOLD_OBLIQUE = new PDType1Font("Helvetica-BoldOblique");public static final PDType1Font COURIER = new PDType1Font("Courier");public static final PDType1Font COURIER_BOLD = new PDType1Font("Courier-Bold");public static final PDType1Font COURIER_BOLD_OBLIQUE = new PDType1Font("Courier-BoldOblique");public static final PDType1Font SYMBOL = new PDType1Font("Symbol");public static final PDType1Font ZAPF_DINGBATS = new PDType1Font("ZapfDingbats");
  1. PDTrueTypeFont:这个类表示TrueType字体,也是一种基于轮廓的字体格式。TrueType字体在PDF中也很常见。

PDTrueTypeFont font = PDType1Font.TIMES_ROMAN;
  1. PDType0Font:这个类表示Type0字体,它是一种复合字体格式,可以包含多个子字体。Type0字体通常用于支持多语言和复杂字形需求,你可以使用它来加载自己自定义的字体文件。

PDType0Font font = PDType0Font.load(document, new ClassPathResource("/static/wryhRegular.ttf").getInputStream());
写入单行文本
contentStream.setFont(PDType1Font.COURIER_BOLD_OBLIQUE, 16);contentStream.beginText();contentStream.newLineAtOffset(50, pageHeight-50);contentStream.showText("测试文本");contentStream.endText();

在写入文本之前需要通过contentStream.setFont(PDFontfont,floatfontSize)方法设置字体和字号,并通过beginText()方法开始一个新的文本段落,通过newLineAtOffset(x,y);方法设置文本的坐标位置,这里设置(50,pageHeight-50)表示文本位置位于左上角,离上面和左边50个单位。然后通过showText(Stringtext)显示你需要展示的文本,最后用endText()方法结束文本段落。

连续写入多行文本
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 12);// 设置文本起始坐标float startX = 50;float startY = page.getMediaBox().getHeight() - 50;// 设置行间距float leading = 15;// 写入多行文本String[] lines = {"第一行文本","第二行文本","第三行文本"};contentStream.beginText();contentStream.newLineAtOffset(startX, startY);for (String line : lines) {contentStream.showText(line);contentStream.newLineAtOffset(0, -leading);}contentStream.endText();

写入多行文本和单行文本流程差不多,都需要先设置字体和字号,确定写入文字的坐标,不同的是,我们在beginText()方法和endText()方法之间,多次执行了showText()和newLineAtOffset(),newLineAtOffset(0,-leading)方法代表着在上一行的位置基础上,X轴不变,Y轴向下移动leading个单位。多次循环之后将多行文本添加到pdf文档中。

插入图片
PDImageXObject image = PDImageXObject.createFromFileByExtension(new File("path/to/image.jpg"), document);float imageWidth = image.getWidth();float imageHeight = image.getHeight();PDPageContentStream contentStream = new PDPageContentStream(document, page);contentStream.drawImage(image, x, y, imageWidth, imageHeight);

这里我们使用PDImageXObject.createFromFileByExtension()方法加载图片文件,创建一个PDImageXObject对象。确保将”path/to/image.jpg”替换为实际图片文件的路径,这里我将图片的宽度和高度设置为真实图片的宽高,在实际情况中你也可以自定义宽高,最后通过drawImage(image,x,y,imageWidth,imageHeight)方法将图片写入到pdf文档中,x,y代表其xy坐标,后面的imageWidth,imageHeight分别代表图片的宽度和高度。

添加矩形框
//设置边框颜色contentStream.setStrokingColor(new Color(213, 213, 213));//设置边框宽度为1contentStream.setLineWidth(1);// 添加矩形框到页面内容流contentStream.addRect(50, pageHeight-50, 100, 100);// 绘制矩形框的边框contentStream.stroke();//恢复原来的颜色,否则会影响文字颜色contentStream.setStrokingColor(Color.BLACK);

文字坐标计算常用方法

/** * 获取字体高度 * */float getFontHeight(PDType0Font customFont,float fontSize){return customFont.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize;}/*** 计算文本宽度* */float getTextWidth(String text,float fontSize){return fontSize * text.length();}

附件

PDFBox官方文档(2.0.24)