基本的读取
1. 添加依赖
确保在你的项目中添加PDFBox的依赖。如果你使用Maven,可以在pom.xml
中添加以下依赖:
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>3.0.3</version>
</dependency>
2. 读取PDF文件
使用PDDocument
类加载PDF文件,获取文字和图片。
3. 完整代码示例
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class PDFParser {
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Usage: java PDFParser <path-to-pdf-file>");
System.exit(1);
}
String pdfFilePath = args[0];
try (PDDocument document = PDDocument.load(new File(pdfFilePath))) {
// 提取文本及其坐标
extractTextWithPositions(document);
// 提取图片及其坐标
extractImagesWithPositions(document);
} catch (IOException e) {
System.err.println("Error reading PDF file: " + e.getMessage());
e.printStackTrace();
}
}
private static void extractTextWithPositions(PDDocument document) throws IOException {
PDFTextStripper stripper = new PDFTextStripper() {
@Override
protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
for (TextPosition text : textPositions) {
System.out.println("Text: " + text.getUnicode() +
", X: " + text.getXDirAdj() +
", Y: " + text.getYDirAdj() +
", Font Size: " + text.getFontSizeInPt());
}
}
};
stripper.setSortByPosition(true);
stripper.setStartPage(0);
stripper.setEndPage(document.getNumberOfPages());
stripper.writeText(document, System.out);
}
private static void extractImagesWithPositions(PDDocument document) throws IOException {
for (PDPage page : document.getPages()) {
PDResources resources = page.getResources();
for (COSName name : resources.getXObjectNames()) {
if (resources.isImageXObject(name)) {
PDImageXObject image = (PDImageXObject) resources.getXObject(name);
Rectangle2D position = getImagePosition(page, name, resources);
System.out.println("Image: " + name.getName() +
", Width: " + image.getWidth() +
", Height: " + image.getHeight() +
", X: " + position.getX() +
", Y: " + position.getY());
}
}
}
}
private static Rectangle2D getImagePosition(PDPage page, COSName name, PDResources resources) throws IOException {
PDRectangle mediaBox = page.getMediaBox();
double pageWidth = mediaBox.getWidth();
double pageHeight = mediaBox.getHeight();
List<PDRectangle> positions = page.findImagePositions(name, resources);
if (positions.isEmpty()) {
throw new IOException("Image not found on the page.");
}
PDRectangle position = positions.get(0); // Assuming the first occurrence is the correct one
return new Rectangle2D.Double(position.getLowerLeftX(), pageHeight - position.getUpperRightY(),
position.getWidth(), position.getHeight());
}
}
如果想自定义一些操作,比如读取的结果要解析坐标,根据坐标进行一些判断,最终形成结构化的数据,怎么做呢?
定制化的读取
1. 说明
PDFBox 提供了丰富的API来处理PDF文档中的文本、图像、表格、链接等元素。下面是一个详细的示例,来看看怎么做。
2. 创建PDF解析器
创建一个PDF解析器类,继承自PDFStreamEngine
,重写processOperator
方法以处理不同的PDF操作符。
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.contentstream.operator.state.SetLineWidth;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
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.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.text.PDFTextStripper;
import java.io.IOException;
import java.util.List;
public class CustomPDFParser extends PDFStreamEngine {
public CustomPDFParser() throws IOException {
super();
addOperator(new SetLineWidth());
// 添加其他需要处理的操作符
}
@Override
protected void processOperator(Operator operator, List<COSBase> arguments) throws IOException {
String operation = operator.getName();
switch (operation) {
case "Tj": // 处理文本
handleText(arguments);
break;
case "Do": // 处理图像
handleImage(arguments);
break;
case "re": // 处理矩形
handleRectangle(arguments);
break;
case "BDC": // 处理标记内容
handleMarkedContentBegin(arguments);
break;
case "EMC": // 处理标记内容结束
handleMarkedContentEnd();
break;
case "Tf": // 处理字体设置
handleFontSetting(arguments);
break;
case "rg": // 处理填充颜色
handleFillColor(arguments);
break;
case "RG": // 处理描边颜色
handleStrokeColor(arguments);
break;
default:
super.processOperator(operator, arguments);
break;
}
}
private void handleText(List<COSBase> arguments) throws IOException {
String text = ((COSString) arguments.get(0)).getString();
System.out.println("Text: " + text);
}
private void handleImage(List<COSBase> arguments) throws IOException {
PDImageXObject image = (PDImageXObject) getResources().getXObject((COSName) arguments.get(0));
System.out.println("Image: " + image.getHeight() + "x" + image.getWidth());
}
private void handleRectangle(List<COSBase> arguments) throws IOException {
float x = arguments.get(0).toFloat();
float y = arguments.get(1).toFloat();
float width = arguments.get(2).toFloat();
float height = arguments.get(3).toFloat();
System.out.println("Rectangle: " + x + ", " + y + ", " + width + ", " + height);
}
private void handleMarkedContentBegin(List<COSBase> arguments) throws IOException {
String tag = ((COSName) arguments.get(0)).getName();
System.out.println("Marked Content Begin: " + tag);
}
private void handleMarkedContentEnd() throws IOException {
System.out.println("Marked Content End");
}
private void handleFontSetting(List<COSBase> arguments) throws IOException {
PDFont font = (PDType1Font) getResources().getFont((COSName) arguments.get(0));
float size = arguments.get(1).toFloat();
System.out.println("Font Setting: " + font.getFontName() + ", " + size);
}
private void handleFillColor(List<COSBase> arguments) throws IOException {
float r = arguments.get(0).toFloat();
float g = arguments.get(1).toFloat();
float b = arguments.get(2).toFloat();
System.out.println("Fill Color: " + r + ", " + g + ", " + b);
}
private void handleStrokeColor(List<COSBase> arguments) throws IOException {
float r = arguments.get(0).toFloat();
float g = arguments.get(1).toFloat();
float b = arguments.get(2).toFloat();
System.out.println("Stroke Color: " + r + ", " + g + ", " + b);
}
}
3. 解析PDF文件
创建一个测试类来解析PDF文件并调用自定义的解析器。
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try (PDDocument document = PDDocument.load(new File("path/to/your/pdf/file.pdf"))) {
CustomPDFParser parser = new CustomPDFParser();
for (PDPage page : document.getPages()) {
parser.processPage(page);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.特定元素的定制化读取
比如,我们只需要处理的是文本,上面的方法其实偏底层一点,我们是判断了操作符号来处理各个元素的,其实PDFBox还有更高级一点的方法,提供了更友好的API和更丰富的功能。
PDFTextStripper
类,专门用于提取PDF文档中的文本内容。如果你需要对文本进行更复杂的处理,可以继承 PDFTextStripper
并重写其方法。
以下是一个示例
1. 创建自定义的 PDFTextStripper
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import java.io.IOException;
public class CustomPDFTextStripper extends PDFTextStripper {
public CustomPDFTextStripper() throws IOException {
super();
}
@Override
protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
// 处理每个字符的位置和属性
for (TextPosition text : textPositions) {
System.out.println("Character: " + text.getCharacter());
System.out.println("X: " + text.getXDirAdj());
System.out.println("Y: " + text.getYDirAdj());
System.out.println("Font: " + text.getFont().getFontDescriptor().getFontName());
System.out.println("Size: " + text.getFontSizeInPt());
System.out.println("Color: " + text.getColor());
System.out.println("-------------------");
}
}
@Override
protected void writePageSeparator() throws IOException {
// 处理页面分隔符
System.out.println("\n------------------- Page Separator -------------------\n");
}
@Override
protected void startPage(PDPage page) throws IOException {
// 处理页面开始
System.out.println("Starting page: " + page.getPageNumber());
}
@Override
protected void endPage(PDPage page) throws IOException {
// 处理页面结束
System.out.println("Ending page: " + page.getPageNumber());
}
}
2. 使用自定义的 PDFTextStripper
解析PDF文件
import org.apache.pdfbox.pdmodel.PDDocument;
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try (PDDocument document = PDDocument.load(new File("path/to/your/pdf/file.pdf"))) {
CustomPDFTextStripper stripper = new CustomPDFTextStripper();
stripper.setSortByPosition(true); // 按位置排序文本
stripper.setStartPage(1); // 设置起始页
stripper.setEndPage(document.getNumberOfPages()); // 设置结束页
stripper.getText(document); // 提取文本
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 解释
- writeString: 重写此方法可以处理每个字符的位置和属性。
textPositions
列表包含了当前字符串中每个字符的位置信息。 - writePageSeparator: 重写此方法可以在每页之间插入分隔符。
- startPage: 重写此方法可以在每页开始时执行某些操作。
- endPage: 重写此方法可以在每页结束时执行某些操作。
其他元素的读取也有相应的处理类,可以继承并重写,我们后续一一介绍。有问题欢迎关注公众号交流。