2009-06-02 5 views
22

Я использую PdfBox в Java для извлечения текста из файлов PDF. Некоторые из представленных входных файлов недействительны, и PDFTextStripper останавливается на этих файлах. Есть ли чистый способ проверить, действительно ли предоставленный файл действительно является PDF?Как определить, является ли файл PDF-файлом?

+2

Отличный вопрос! Мое королевство для высококачественного непубличного PDF-валидатора. –

ответ

9

вы можете узнать тип mime файла (или массив байтов), чтобы вы не полагались на расширение. Я делаю это с MimeExtractor апертуры в (http://aperture.sourceforge.net/) или я видел несколько дней назад библиотеку только для этого (http://sourceforge.net/projects/mime-util)

я использую отверстие для извлечения текста из различных файлов, а не только PDF, но нужно настроить думает PDFs например (диафрагма использует pdfbox, но я добавил еще одну библиотеку в качестве запасного варианта, когда pdfbox терпит неудачу)

+0

спасибо, я попробую – 2009-06-07 15:36:44

+1

О, я забыл упомянуть, что теперь есть проект apache для извлечения текста, http://lucene.apache.org/tika/, если вы предпочитаете его апертуру – Persimmonium

+1

Не знаете, как это сделать решает исходный вопрос, используя PDFBox. – cherouvim

4

Pdf файлы начинаются «% PDF» (открыть один в TextPad или аналогичный и взглянуть)

какой-либо причине вы не можете просто прочитать файл с StringReader и проверить это?

+0

Я пробовал это, и кажется, что в файлах PDF могут использоваться различные кодировки, и иногда чтение текста не соответствует% PDF для действительных и читаемых PDF-файлов. – 2009-06-02 21:19:31

+0

Не все файлы, начинающиеся с% PDF, являются действительными файлами PDF. –

16

Вот что я использую в моих тестах NUnit, которые должны подтвердить против нескольких версий PDF, полученных с помощью Crystal Reports:

public static void CheckIsPDF(byte[] data) 
    { 
     Assert.IsNotNull(data); 
     Assert.Greater(data.Length,4); 

     // header 
     Assert.AreEqual(data[0],0x25); // % 
     Assert.AreEqual(data[1],0x50); // P 
     Assert.AreEqual(data[2],0x44); // D 
     Assert.AreEqual(data[3],0x46); // F 
     Assert.AreEqual(data[4],0x2D); // - 

     if(data[5]==0x31 && data[6]==0x2E && data[7]==0x33) // version is 1.3 ? 
     {     
      // file terminator 
      Assert.AreEqual(data[data.Length-7],0x25); // % 
      Assert.AreEqual(data[data.Length-6],0x25); // % 
      Assert.AreEqual(data[data.Length-5],0x45); // E 
      Assert.AreEqual(data[data.Length-4],0x4F); // O 
      Assert.AreEqual(data[data.Length-3],0x46); // F 
      Assert.AreEqual(data[data.Length-2],0x20); // SPACE 
      Assert.AreEqual(data[data.Length-1],0x0A); // EOL 
      return; 
     } 

     if(data[5]==0x31 && data[6]==0x2E && data[7]==0x34) // version is 1.4 ? 
     { 
      // file terminator 
      Assert.AreEqual(data[data.Length-6],0x25); // % 
      Assert.AreEqual(data[data.Length-5],0x25); // % 
      Assert.AreEqual(data[data.Length-4],0x45); // E 
      Assert.AreEqual(data[data.Length-3],0x4F); // O 
      Assert.AreEqual(data[data.Length-2],0x46); // F 
      Assert.AreEqual(data[data.Length-1],0x0A); // EOL 
      return; 
     } 

     Assert.Fail("Unsupported file format"); 
    } 
+2

Спасибо, это только помогло мне разобраться, что случилось с созданным мной PDF-файлом - проблема EOL, проявленная только в Adobe Reader, а не в Foxit/GoogleApps/Sumatra. –

+0

Это на Java? Также он не будет обнаруживать зашифрованные PDF-файлы. Поскольку OP хочет извлечь информацию, вам это тоже нужно. – cherouvim

+2

Спасибо! Я очень признателен, что этот ответ является агностиком библиотеки. Это спасло мне кучу времени =) – Spina

5

Поскольку вы используете PDFBox, вы можете просто:

PDDocument.load(file); 

Это будет терпеть неудачу с Exception, если PDF поврежден и т.д.

Если это удастся вы также можете проверить, если PDF зашифрован с помощью .isEncrypted()

+2

Из того, что я видел, это неправда. Я могу использовать PDDocument.load (поток) для загрузки поврежденного PDF-файла. Я получаю сообщение об ошибке при попытке сохранить PDF после изменения его прав доступа. – MonkeyWrench

+0

Использование Исключения для потока приложений - это плохая практика. –

+1

@BenTurner: Вы правы, и я с вами на этом. Однако API не дает нам возможности проверить действительность файла. – cherouvim

5

Вы должны попробовать это ... .

public boolean isPDF(File file){ 
    file = new File("Demo.pdf"); 
    Scanner input = new Scanner(new FileReader(file)); 
    while (input.hasNextLine()) { 
     final String checkline = input.nextLine(); 
     if(checkline.contains("%PDF-")) { 
      // a match! 
      return true; 
     } 
    } 
    return false; 
} 
+5

Этот ответ беспокоит меня ... Есть ли PDF, который не начинается с «% PDF-», а просто содержит его? Почему проблема чтения всего файла? Что делать, если я проверяю zip-файл на 2 ГБ? – boumbh

+0

для больших файлов, файлов размером 10 + МБ и неправильных расширений (например, mp3File.pdf), потребуется много времени (например, 5 и более секунд). – shanraisshan

-1

Существует очень удобная и простая библиотека для тестирования содержания PDF: https://github.com/codeborne/pdf-test

API очень прост:

import com.codeborne.pdftest.PDF; 
import static com.codeborne.pdftest.PDF.*; 
import static org.junit.Assert.assertThat; 

public class PDFContainsTextTest { 
    @Test 
    public void canAssertThatPdfContainsText() { 
    PDF pdf = new PDF(new File("src/test/resources/50quickideas.pdf")); 
    assertThat(pdf, containsText("50 Quick Ideas to Improve your User Stories")); 
    } 
} 
5

Здесь адаптированный Java версия кода NinjaCross в.

/** 
* Test if the data in the given byte array represents a PDF file. 
*/ 
public static boolean is_pdf(byte[] data) { 
    if (data != null && data.length > 4 && 
      data[0] == 0x25 && // % 
      data[1] == 0x50 && // P 
      data[2] == 0x44 && // D 
      data[3] == 0x46 && // F 
      data[4] == 0x2D) { // - 

     // version 1.3 file terminator 
     if (data[5] == 0x31 && data[6] == 0x2E && data[7] == 0x33 && 
       data[data.length - 7] == 0x25 && // % 
       data[data.length - 6] == 0x25 && // % 
       data[data.length - 5] == 0x45 && // E 
       data[data.length - 4] == 0x4F && // O 
       data[data.length - 3] == 0x46 && // F 
       data[data.length - 2] == 0x20 && // SPACE 
       data[data.length - 1] == 0x0A) { // EOL 
      return true; 
     } 

     // version 1.3 file terminator 
     if (data[5] == 0x31 && data[6] == 0x2E && data[7] == 0x34 && 
       data[data.length - 6] == 0x25 && // % 
       data[data.length - 5] == 0x25 && // % 
       data[data.length - 4] == 0x45 && // E 
       data[data.length - 3] == 0x4F && // O 
       data[data.length - 2] == 0x46 && // F 
       data[data.length - 1] == 0x0A) { // EOL 
      return true; 
     } 
    } 
    return false; 
} 

И несколько простых модульных тестов:

@Test 
public void test_valid_pdf_1_3_data_is_pdf() { 
    assertTrue(is_pdf("%PDF-1.3 CONTENT %%EOF \n".getBytes())); 
} 

@Test 
public void test_valid_pdf_1_4_data_is_pdf() { 
    assertTrue(is_pdf("%PDF-1.4 CONTENT %%EOF\n".getBytes())); 
} 

@Test 
public void test_invalid_data_is_not_pdf() { 
    assertFalse(is_pdf("Hello World".getBytes())); 
} 

Если вы придумали какие-либо неудовлетворительные модульные тесты, пожалуйста, дайте мне знать.

1

Возможно, я опоздал, чтобы ответить. Но вы должны взглянуть на Тику. Он использует PDFBox Parser внутренне для анализа PDF в

Вам просто нужно импортировать ТИКА-приложение-последний * .jar

public String parseToStringExample() throws IOException, SAXException, TikaException 
{ 

     Tika tika = new Tika(); 
     try (InputStream stream = ParsingExample.class.getResourceAsStream("test.pdf")) { 
      return tika.parseToString(stream); // This should return you the pdf's text 
     } 
} 

Было бы гораздо чище решение. Вы можете обратиться сюда для получения более подробной информации о Tika Использование: https://tika.apache.org/1.12/api/

1

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

В конце концов, после того, как мастерить вокруг с различными методами в API, я попытался это:

PDDocument.load(file).getPage(0).getContents().toString(); 

Это не сгенерирует исключение, но он сделал вывод этого:

WARN [COSParser:1154] The end of the stream doesn't point to the correct offset, using workaround to read the stream, stream start position: 171, length: 1145844, expected end position: 1146015 

Лично я хотел, чтобы исключение было выброшено, если файл был поврежден, поэтому я мог справиться с этим сам, но оказалось, что API, который я реализовал, уже обрабатывал их по-своему.

Чтобы обойти это, я решил попробовать разбор файлов, используя класс, который дал теплый оператор (COSParser). Я обнаружил, что существует подкласс под названием PDFParser, который унаследовал метод «setLenient», который был ключом (https://pdfbox.apache.org/docs/2.0.4/javadocs/org/apache/pdfbox/pdfparser/COSParser.html).

Я тогда реализован следующим образом:

 RandomAccessFile accessFile = new RandomAccessFile(file, "r"); 
     PDFParser parser = new PDFParser(accessFile); 
     parser.setLenient(false); 
     parser.parse(); 

Это бросил исключение для моего поврежденного файла, как я хотел. Надеюсь, это поможет кому-то!

1

Ответ Roger Keays неверен! поскольку не все файлы PDF в версии 1.3 и не все завершены EOL. Ответ ниже работ для всех не поврежденных файлов PDF:

public static boolean is_pdf(byte[] data) { 
    if (data != null && data.length > 4 
      && data[0] == 0x25 && // % 
      data[1] == 0x50 && // P 
      data[2] == 0x44 && // D 
      data[3] == 0x46 && // F 
      data[4] == 0x2D) { // - 

     // version 1.3 file terminator 
     if (//data[5] == 0x31 && data[6] == 0x2E && data[7] == 0x33 && 
       data[data.length - 7] == 0x25 && // % 
       data[data.length - 6] == 0x25 && // % 
       data[data.length - 5] == 0x45 && // E 
       data[data.length - 4] == 0x4F && // O 
       data[data.length - 3] == 0x46 && // F 
       data[data.length - 2] == 0x20 // SPACE 
       //&& data[data.length - 1] == 0x0A// EOL 
       ) { 
      return true; 
     } 

     // version 1.3 file terminator 
     if (//data[5] == 0x31 && data[6] == 0x2E && data[7] == 0x34 && 
       data[data.length - 6] == 0x25 && // % 
       data[data.length - 5] == 0x25 && // % 
       data[data.length - 4] == 0x45 && // E 
       data[data.length - 3] == 0x4F && // O 
       data[data.length - 2] == 0x46 // F 
       //&& data[data.length - 1] == 0x0A // EOL 
       ) { 
      return true; 
     } 
    } 
    return false; 
} 
+0

«%% EOF» должно быть единственным содержимым в последней строке PDF. Таким образом, файлы с пробелом после '%% EOF' строго говоря недействительны. После него может быть только разделитель строк, то есть один CR, одна LF или пара CR LF. – mkl

0

В общем, мы можем, как это, любая PDF версия собирается закончить с %% EOF, чтобы мы могли проверить, как сильфон.

public static boolean is_pdf(byte[] data) { 
     String s = new String(data); 
     String d = s.substring(data.length - 7, data.length - 1); 
     if (data != null && data.length > 4 && 
       data[0] == 0x25 && // % 
       data[1] == 0x50 && // P 
       data[2] == 0x44 && // D 
       data[3] == 0x46 && // F 
       data[4] == 0x2D) { // - 

       if(d.contains("%%EOF")){ 
       return true; 
       }   
     } 
     return false; 
    } 
Смежные вопросы