java提取PDF文字座標
阿新 • • 發佈:2019-02-06
常用java操作PDF的庫有PDFbox和itext,下面我會介紹如何使用PDFbox和itext來提取PDF的文字座標。
一、itext提取文字座標
itext版本:5.5.6,低版本的可能沒有提供這種方法。1、通過定義一個類實現RenderListener,可以通過裡面的幾個方法來操作PDF中的文字和圖片
2、使用自定義的類來實現獲取PDF的文字座標import java.awt.Color; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; import com.itextpdf.awt.geom.Rectangle2D; import com.itextpdf.awt.geom.RectangularShape; import com.itextpdf.text.BaseColor; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.parser.ImageRenderInfo; import com.itextpdf.text.pdf.parser.RenderListener; import com.itextpdf.text.pdf.parser.TextRenderInfo; public class TestRenderListener implements RenderListener { //用來存放文字的矩形 List<Rectangle2D.Float> rectText = new ArrayList<Rectangle2D.Float>(); //用來存放文字 List<String> textList = new ArrayList<String>(); //用來存放文字的y座標 List<Float> listY = new ArrayList<Float>(); //用來存放每一行文字的座標位置 List<Map<String,Rectangle2D.Float>> rows_text_rect = new ArrayList<>(); //PDF檔案的路徑 protected String filepath = null; public TestRenderListener() { } //step 2,遇到"BT"執行 @Override public void beginTextBlock() { // TODO Auto-generated method stub } //step 3 /** * 文字主要處理方法 */ @Override public void renderText(TextRenderInfo renderInfo) { //獲取文字的下面的矩形 //Rectangle2D.Float rectBase = renderInfo.getBaseline().getBoundingRectange(); String text = renderInfo.getText(); if(text.length() > 0){ RectangularShape rectBase = renderInfo.getBaseline().getBoundingRectange(); //獲取文字下面的矩形 Rectangle2D.Float rectAscen = renderInfo.getAscentLine().getBoundingRectange(); //計算出文字的邊框矩形 float leftX = (float) rectBase.getMinX(); float leftY = (float) rectBase.getMinY()-1; float rightX = (float) rectAscen.getMaxX(); float rightY = (float) rectAscen.getMaxY()+1; Rectangle2D.Float rect = new Rectangle2D.Float(leftX, leftY, rightX - leftX, rightY - leftY); System.out.println("text:"+text+"--x:"+rect.x + "--y:"+rect.y + "--width:"+rect.width + "--height:"+rect.height); if(listY.contains(rect.y)){ int index = listY.indexOf(rect.y); float tempx = rect.x > rectText.get(index).x ? rectText.get(index).x : rect.x; rectText.set(index,new Rectangle2D.Float(tempx,rect.y,rect.width + rectText.get(index).width,rect.height)); textList.set(index,textList.get(index) + text); }else{ rectText.add(rect); textList.add(text); listY.add(rect.y); } Map<String,Rectangle2D.Float> map = new HashMap<>(); map.put(text,rect); rows_text_rect.add(map); } } //step 4(最後執行的,只執行一次),遇到“ET”執行 @Override public void endTextBlock() { // TODO Auto-generated method stub } //step 1(圖片處理方法) @Override public void renderImage(ImageRenderInfo renderInfo) { } }
PdfReader reader = new PdfReader(pdfPath); //新建一個PDF解析物件 PdfReaderContentParser parser = new PdfReaderContentParser(reader); //包含了PDF頁面的資訊,作為處理的物件 PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("d:/test.pdf")); for(int i = 1;i <= reader.getNumberOfPages();i++){ //新建一個ImageRenderListener物件,該物件實現了RenderListener介面,作為處理PDF的主要類 TestRenderListener listener = new TestRenderListener(); //解析PDF,並處理裡面的文字 parser.processContent(i, listener); //獲取文字的矩形邊框 List<Rectangle2D.Float> rectText = listener.rectText; List<String> textList = listener.textList; List<Float> listY = listener.listY; List<Map<String,Rectangle2D.Float>> list_text = listener.rows_text_rect; for(int k = 0;k < list_text.size();k++){ Map<String,Rectangle2D.Float> map = list_text.get(k); for(Map.Entry<String, Rectangle2D.Float>entry:map.entrySet()){ System.out.println(entry.getKey()+"---"+entry.getValue()); }
}
}
二、PDFbox獲取文字座標
PDFbox與itext不同的是,PDFbox只能一個一個字的提取PDF的文字座標,而itext是一段一段提取的。
PDFbox版本:1.8.13,不同版本可能部分程式碼寫法不同。
import java.io.*; import org.apache.pdfbox.exceptions.InvalidPasswordException; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.common.PDStream; import org.apache.pdfbox.util.PDFTextStripper; import org.apache.pdfbox.util.TextPosition; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class PrintTextLocations extends PDFTextStripper { static List<Float> list_postion = new ArrayList<Float>(); static List<String> list_text = new ArrayList<String>(); public PrintTextLocations() throws IOException { super.setSortByPosition(true); } public static void main(String[] args) throws Exception { PDDocument document = null; try { File input = new File("D://result.pdf"); document = PDDocument.load(input); if (document.isEncrypted()) { document.decrypt(""); } PrintTextLocations printer = new PrintTextLocations(); List allPages = document.getDocumentCatalog().getAllPages(); for (int i = 0; i < allPages.size(); i++) { PDPage page = (PDPage) allPages.get(i); System.out.println("Processing page: " + i); PDStream contents = page.getContents(); if (contents != null) { printer.processStream(page, page.findResources(), page.getContents().getStream()); } } } finally { if (document != null) { document.close(); } } System.out.println(list_text.size()); for(int i = 0;i < list_text.size();i++){ System.out.println(list_text.get(i) ); } } /** * @param text The text to be processed */ @Override /* this is questionable, not sure if needed... */ protected void processTextPosition(TextPosition text) { System.out.println("String[" + text.getXDirAdj() + "," + text.getYDirAdj() + " fs=" + text.getFontSize() + " xscale=" + text.getXScale()+ " yscale=" + text.getYScale() + " height=" + text.getHeightDir() + " space=" + text.getWidthOfSpace() + " width=" + text.getWidthDirAdj() + " x=" + text.getX() + " y=" + text.getY() + " y1=" + text.getTextPos().getYPosition() + " x1=" + text.getTextPos().getXPosition() + " x1=" + text.getTextPos().getXScale() + " x1=" + text.getTextPos().getYScale() + "]" + text.getCharacter()); } }