Como identificar e remover texto oculto do PDF usando PDFBox java

Sep 17 2020

Estou lendo um texto em PDF usando a biblioteca pdfbox e salvando-o em um arquivo de texto. Ele também lê texto oculto, que não fica visível quando o PDF é visualizado no PDF Reader. Meu requisito é obter algumas características desse texto oculto que possam distingui-lo do texto normal.

Respostas

mkl Sep 27 2020 at 22:02

Um possível critério para os textos ignorarem em seus arquivos de exemplo é a cor do texto, branco CMYK puro em um caso, 0,753 em um espaço de cores baseado em Gray Gamma 2.2 XYZ ICC no outro caso.

Portanto, vamos estender o removedor de texto com uma opção de filtro de cores. Em particular, isso significa adicionar processadores de operador para instruções de configuração de cores, já que PDFTextStripperpor padrão os ignora:

public class PDFFilteringTextStripper extends PDFTextStripper {
    public interface TextStripperFilter {
        public boolean accept(TextPosition text, PDGraphicsState graphicsState);
    }

    public PDFFilteringTextStripper(TextStripperFilter filter) throws IOException {
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetStrokingColorSpace());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetNonStrokingColorSpace());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetStrokingColor());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetNonStrokingColor());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetStrokingColorN());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetNonStrokingColorN());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetStrokingDeviceGrayColor());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetNonStrokingDeviceGrayColor());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetStrokingDeviceRGBColor());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetNonStrokingDeviceRGBColor());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetStrokingDeviceCMYKColor());
        addOperator(new org.apache.pdfbox.contentstream.operator.color.SetNonStrokingDeviceCMYKColor());

        this.filter = filter;
    }

    @Override
    protected void processTextPosition(TextPosition text) {
        PDGraphicsState graphicsState = getGraphicsState();
        if (filter.accept(text, graphicsState))
            super.processTextPosition(text);
    }

    final TextStripperFilter filter;
}

( Classe PDFFilteringTextStripper )

Usando essa classe de stripper de texto, podemos filtrar o texto em branco do primeiro exemplo de PDF como este:

float[] colorToFilter = new float[] {0,0,0,0};

PDDocument document = ...;
PDFFilteringTextStripper stripper = new PDFFilteringTextStripper((text, gs) -> {
    PDColor color = gs.getNonStrokingColor();
    return color == null || !((color.getColorSpace() instanceof PDDeviceCMYK) && Arrays.equals(color.getComponents(), colorToFilter));
});
String text = stripper.getText(document);

( Teste ExtractFilteredTexttestExtractNoWhiteText... )

Da mesma forma, podemos filtrar o texto cinza do segundo exemplo de PDF assim:

float[] colorToFilter = new float[] {0.753f};

PDDocument document = ...;
PDFFilteringTextStripper stripper = new PDFFilteringTextStripper((text, gs) -> {
    PDColor color = gs.getNonStrokingColor();
    return color == null || !((color.getColorSpace() instanceof PDICCBased) && Arrays.equals(color.getComponents(), colorToFilter));
});
String text = stripper.getText(document);

( Teste ExtractFilteredTexttestExtractNoGrayText... )


Em um comentário que você perguntou

Uma pergunta rápida - este texto em 0,753 em um espaço de cores baseado em Gray Gamma 2.2 XYZ ICC - texto invisível? Ou é apenas por causa do espaço de cores, o texto não é visível em PDF?

É visível! (Portanto, estritamente falando, você não deve removê-lo do texto extraído.)

É simplesmente muito pequeno. Na página de título, amplie o ano "2016":