it-swarm.com.ru

Использование PDFbox для определения координат слов в документе

Я использую PDFbox для извлечения координат слов/строк в документе PDF, и до сих пор успешно определял положение отдельных символов. этот код пока что из документа PDFbox:

package printtextlocations;

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.List;

public class PrintTextLocations extends PDFTextStripper {

    public PrintTextLocations() throws IOException {
        super.setSortByPosition(true);
    }

    public static void main(String[] args) throws Exception {

        PDDocument document = null;
        try {
            File input = new File("C:\\path\\to\\PDF.pdf");
            document = PDDocument.load(input);
            if (document.isEncrypted()) {
                try {
                    document.decrypt("");
                } catch (InvalidPasswordException e) {
                    System.err.println("Error: Document is encrypted with a password.");
                    System.exit(1);
                }
            }
            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();
            }
        }
    }

    /**
     * @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() + " height=" + text.getHeightDir() + " space="
                + text.getWidthOfSpace() + " width="
                + text.getWidthDirAdj() + "]" + text.getCharacter());
    }
}

Это создает ряд строк, содержащих позицию каждого символа, включая пробелы, которые выглядят так:

String[202.5604,41.880127 fs=1.0 xscale=13.98 height=9.68814 space=3.8864403 width=9.324661]P

Где «P» - символ Мне не удалось найти функцию в PDFbox для поиска слов, и я недостаточно знаком с Java, чтобы иметь возможность точно объединять эти символы обратно в слова для поиска, даже при наличии пробелов. Кто-нибудь еще был в подобной ситуации, и если да, то как вы к ней подошли? Мне действительно нужна только координата первого символа в Word, чтобы детали были упрощены, но то, как я собираюсь сопоставить строку с таким выводом, мне не подходит.

16
jbrain

В PDFBox нет функции, позволяющей автоматически извлекать слова. В настоящее время я работаю над извлечением данных, чтобы собрать их в блоки, и вот мой процесс:

  1. Я извлекаю все символы документа (называемые глифами) и сохраняю их в списке.

  2. Я делаю анализ координат каждого глифа, проходя по списку. Если они перекрываются (если верхняя часть текущего глифа находится между верхней и нижней частями предыдущего/или нижняя часть текущего глифа содержится между верхней и нижней частями предыдущего глифа), я добавляю его к той же строке.

  3. На этом этапе я извлек различные строки документа (будьте осторожны, если ваш документ состоит из нескольких столбцов, выражение «линии» означает все глифы, которые перекрываются по вертикали, то есть текст всех столбцов, имеющих одинаковую вертикаль). координаты).

  4. Затем вы можете сравнить левую координату текущего глифа с правой координатой предыдущего, чтобы определить, принадлежат ли они одному и тому же слову или нет (класс PDFTextStripper предоставляет метод getSpacingTolerance (), который дает вам на основе проб и ошибок). , значение "нормального" пробела. Если разница между правой и левой координатами меньше, чем это значение, оба глифа принадлежат одному и тому же слову.

Я применил этот метод к своей работе, и он работает хорошо.

8
Nicolas W.

Исходя из оригинальной идеи, здесь приводится версия текстового поиска для PDFBox 2. Сам код является грубым, но простым. Это должно начать вас довольно быстро.

import Java.io.IOException;
import Java.io.Writer;
import Java.util.List;
import Java.util.Set;
import lu.abac.pdfclient.data.PDFTextLocation;
import org.Apache.pdfbox.pdmodel.PDDocument;
import org.Apache.pdfbox.text.PDFTextStripper;
import org.Apache.pdfbox.text.TextPosition;

public class PrintTextLocator extends PDFTextStripper {

    private final Set<PDFTextLocation> locations;

    public PrintTextLocator(PDDocument document, Set<PDFTextLocation> locations) throws IOException {
        super.setSortByPosition(true);
        this.document = document;
        this.locations = locations;
        this.output = new Writer() {
            @Override
            public void write(char[] cbuf, int off, int len) throws IOException {
            }
            @Override
            public void flush() throws IOException {
            }

            @Override
            public void close() throws IOException {
            }
        };
    }

    public Set<PDFTextLocation> doSearch() throws IOException {

        processPages(document.getDocumentCatalog().getPages());
        return locations;
    }

    @Override
    protected void writeString(String text, List<TextPosition> textPositions) throws IOException {
        super.writeString(text);

        String searchText = text.toLowerCase();
        for (PDFTextLocation textLoc:locations) {
            int start = searchText.indexOf(textLoc.getText().toLowerCase());
            if (start!=-1) {
                // found
                TextPosition pos = textPositions.get(start);
                textLoc.setFound(true);
                textLoc.setPage(getCurrentPageNo());
                textLoc.setX(pos.getXDirAdj());
                textLoc.setY(pos.getYDirAdj());
            }
        }

    }


}
4
Dainesch

посмотрите на это, я думаю, это то, что вам нужно. 

https://jackson-brain.com/using-pdfbox-to-locate-text-coordinates-within-a-pdf-in-Java/

Вот код:

import Java.io.File;
import Java.io.IOException;
import Java.text.DecimalFormat;
import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.List;

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;

public class PrintTextLocations extends PDFTextStripper {

public static StringBuilder tWord = new StringBuilder();
public static String seek;
public static String[] seekA;
public static List wordList = new ArrayList();
public static boolean is1stChar = true;
public static boolean lineMatch;
public static int pageNo = 1;
public static double lastYVal;

public PrintTextLocations()
        throws IOException {
    super.setSortByPosition(true);
}

public static void main(String[] args)
        throws Exception {
    PDDocument document = null;
    seekA = args[1].split(",");
    seek = args[1];
    try {
        File input = new File(args[0]);
        document = PDDocument.load(input);
        if (document.isEncrypted()) {
            try {
                document.decrypt("");
            } catch (InvalidPasswordException e) {
                System.err.println("Error: Document is encrypted with a password.");
                System.exit(1);
            }
        }
        PrintTextLocations printer = new PrintTextLocations();
        List allPages = document.getDocumentCatalog().getAllPages();

        for (int i = 0; i < allPages.size(); i++) {
            PDPage page = (PDPage) allPages.get(i);
            PDStream contents = page.getContents();

            if (contents != null) {
                printer.processStream(page, page.findResources(), page.getContents().getStream());
            }
            pageNo += 1;
        }
    } finally {
        if (document != null) {
            System.out.println(wordList);
            document.close();
        }
    }
}

@Override
protected void processTextPosition(TextPosition text) {
    String tChar = text.getCharacter();
    System.out.println("String[" + text.getXDirAdj() + ","
            + text.getYDirAdj() + " fs=" + text.getFontSize() + " xscale="
            + text.getXScale() + " height=" + text.getHeightDir() + " space="
            + text.getWidthOfSpace() + " width="
            + text.getWidthDirAdj() + "]" + text.getCharacter());
    String REGEX = "[,.\\[\\](:;!?)/]";
    char c = tChar.charAt(0);
    lineMatch = matchCharLine(text);
    if ((!tChar.matches(REGEX)) && (!Character.isWhitespace(c))) {
        if ((!is1stChar) && (lineMatch == true)) {
            appendChar(tChar);
        } else if (is1stChar == true) {
            setWordCoord(text, tChar);
        }
    } else {
        endWord();
    }
}

protected void appendChar(String tChar) {
    tWord.append(tChar);
    is1stChar = false;
}

protected void setWordCoord(TextPosition text, String tChar) {
    tWord.append("(").append(pageNo).append(")[").append(roundVal(Float.valueOf(text.getXDirAdj()))).append(" : ").append(roundVal(Float.valueOf(text.getYDirAdj()))).append("] ").append(tChar);
    is1stChar = false;
}

protected void endWord() {
    String newWord = tWord.toString().replaceAll("[^\\x00-\\x7F]", "");
    String sWord = newWord.substring(newWord.lastIndexOf(' ') + 1);
    if (!"".equals(sWord)) {
        if (Arrays.asList(seekA).contains(sWord)) {
            wordList.add(newWord);
        } else if ("SHOWMETHEMONEY".equals(seek)) {
            wordList.add(newWord);
        }
    }
    tWord.delete(0, tWord.length());
    is1stChar = true;
}

protected boolean matchCharLine(TextPosition text) {
    Double yVal = roundVal(Float.valueOf(text.getYDirAdj()));
    if (yVal.doubleValue() == lastYVal) {
        return true;
    }
    lastYVal = yVal.doubleValue();
    endWord();
    return false;
}

protected Double roundVal(Float yVal) {
    DecimalFormat rounded = new DecimalFormat("0.0'0'");
    Double yValDub = new Double(rounded.format(yVal));
    return yValDub;
}
}

Зависимости:

PDFBox, FontBox, Apache Common Logging Interface.

Вы можете запустить его, набрав в командной строке:

javac PrintTextLocations.Java 
Sudo Java PrintTextLocations file.pdf Word1,Word2,....

вывод похож на:

[(1)[190.3 : 286.8] Word1, (1)[283.3 : 286.8] Word2, ...]
1
Marouita