Размещение изображения поверх текста с помощью текстового поститона в PDF с использованием PDFBox. - PullRequest
0 голосов
/ 14 мая 2018

Результат состоит в том, что изображение неправильно размещено над текстом. Я неправильно понимаю текстовые позиции?

Это пример того, как получить координаты x / y и размер каждого символ в PDF

public class MyClass extends PDFTextStripper {

    pdocument = PDDocument.load(new File(fileName));

    stripper = new GetCharLocationAndSize();
    stripper.setSortByPosition(true);
    stripper.setStartPage(0);
    stripper.setEndPage(pdocument.getNumberOfPages());
    Writer dummy = new OutputStreamWriter(new 
    ByteArrayOutputStream());
    stripper.writeText(pdocument, dummy);


 /*
 * Override the default functionality of PDFTextStripper.writeString()
 */
@Override
protected void WriteString(String string, List<TextPosition> 
textPositions) throws IOException {

     String imagePath = "image.jpg";
     PDImageXObject pdImage = 
     PDImageXObject.createFromFile(imagePath,pdocument);

     PDPageContentStream contentStream = new 
     PDPageContentStream(pdocument, stripper.getCurrentPage(), true, 
     true);

     for (TextPosition text : textPositions) {

         if (text.getUnicode().equals("a")) {
         contentStream.drawImage(pdImage, text.getXDirAdj(), 
         text.getYDirAdj(), text.getWidthDirAdj(),text.getHeightDir()); 
       }
       }
    contentStream.close();
    pdocument.save("newdoc.pdf");
    }
    }

1 Ответ

0 голосов
/ 14 мая 2018

Получение разумных координат

Вы используете text.getXDirAdj() и text.getYDirAdj() в качестве x и y координат в потоке контента. Это не сработает, потому что координаты PDFBox, используемые при извлечении текста, преобразуются в систему координат, которую они предпочитают для извлечения текста, ср. JavaDocs:

/**
 * This will get the text direction adjusted x position of the character.
 * This is adjusted based on text direction so that the first character
 * in that direction is in the upper left at 0,0.
 *
 * @return The x coordinate of the text.
 */
public float getXDirAdj()

/**
 * This will get the y position of the text, adjusted so that 0,0 is upper left and it is
 * adjusted based on the text direction.
 *
 * @return The adjusted y coordinate of the character.
 */
public float getYDirAdj()

Для TextPosition text вместо этого следует использовать

text.getTextMatrix().getTranslatex()

и

text.getTextMatrix().getTranslateY()

Но даже эти цифры, возможно, придется исправить, ср. этот ответ , поскольку PDFBox умножил матрицу на перевод, сделав начало координат нижнего левого угла рамки обрезки.

Таким образом, если PDRectangle cropBox - это рамка обрезки текущей страницы, используйте

text.getTextMatrix().getTranslatex() + cropBox.getLowerLeftX()

и

text.getTextMatrix().getTranslateY() + cropBox.getLowerLeftY()

(Эта координата нормализация PDFBox является PITA для всех, кто действительно хочет работать с текстовыми координатами ...)

Другие вопросы

У вашего кода есть некоторые другие проблемы, одна из которых становится понятной в документе, которым вы поделились: вы добавляете в поток содержимого страницы без сброса графического контекста:

PDPageContentStream contentStream = new PDPageContentStream(pdocument,
        stripper.getCurrentPage(), true, true);

Конструктор с этой подписью предполагает, что вы не хотите сбрасывать контекст. Используйте параметр с дополнительным параметром boolean и установите его на true для запроса сброса контекста:

PDPageContentStream contentStream = new PDPageContentStream(pdocument,
        stripper.getCurrentPage(), true, true, true);

Теперь контекст сброшен, и позиция снова в порядке.

Однако оба этих конструктора устарели и не должны использоваться по этой причине. В ветке разработки они уже удалены. Вместо этого используйте

PDPageContentStream contentStream = new PDPageContentStream(pdocument,
        stripper.getCurrentPage(), AppendMode.APPEND, true, true);

Однако возникает еще одна проблема: вы создаете новый PDPageContentStream для каждого writeString вызова. Если это делается с помощью сброса контекста каждый раз, вложение пар saveGraphicsState / restoreGraphicsState может стать довольно глубоким. Таким образом, вы должны создать только один такой поток контента на страницу и использовать его во всех writeString вызовах для этой страницы.

Таким образом, ваш подкласс стриппера может выглядеть так:

class CoverCharByImage extends PDFTextStripper {
    public CoverCharByImage(PDImageXObject pdImage) throws IOException {
        super();
        this.pdImage = pdImage;
    }

    final PDImageXObject pdImage;
    PDPageContentStream contentStream = null;

    @Override
    public void processPage(PDPage page) throws IOException {
        super.processPage(page);
        if (contentStream != null) {
            contentStream.close();
            contentStream = null;
        }
    }

    @Override
    protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
        if (contentStream == null)
            contentStream = new PDPageContentStream(document, getCurrentPage(), AppendMode.APPEND, true, true);

        PDRectangle cropBox = getCurrentPage().getCropBox();

        for (TextPosition text : textPositions) {
            if (text.getUnicode().equals("a")) {
                contentStream.drawImage(pdImage, text.getTextMatrix().getTranslateX() + cropBox.getLowerLeftX(),
                        text.getTextMatrix().getTranslateY() + cropBox.getLowerLeftY(),
                        text.getWidthDirAdj(), text.getHeightDir());
            }
        }
    }
}

( CoverCharacterByImage внутренний класс)

и его можно использовать так:

PDDocument pdocument = PDDocument.load(...);

String imagePath = ...;
PDImageXObject pdImage = PDImageXObject.createFromFile(imagePath, pdocument);

CoverCharByImage stripper = new CoverCharByImage(pdImage);
stripper.setSortByPosition(true);
Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream());
stripper.writeText(pdocument, dummy);
pdocument.save(...);

( CoverCharacterByImage test testCoverLikeLez)

в результате

screenshot

* * Тысяча семьдесят-девять и т.д.
...