2009-06-02 8 views
22

J'utilise PdfBox en Java pour extraire du texte à partir de fichiers PDF. Certains des fichiers d'entrée fournis ne sont pas valides et PDFTextStripper s'arrête sur ces fichiers. Existe-t-il un moyen propre de vérifier si le fichier fourni est bien un PDF valide?Comment puis-je déterminer si un fichier est un fichier PDF?

+2

Bonne question! Mon royaume pour un validateur PDF non commercial de haute qualité. –

Répondre

9

Vous pouvez trouver le type mime d'un fichier (ou d'un tableau d'octets), donc vous ne pouvez pas vous fier à l'extension. Je le fais avec MimeExtractor d'ouverture (http://aperture.sourceforge.net/) ou j'ai vu il y a quelques jours une bibliothèque juste pour que (http://sourceforge.net/projects/mime-util)

J'utilise l'ouverture pour extraire le texte à partir d'une variété de fichiers, non seulement pdf, mais je dois modifier pense pour pdfs par exemple (ouverture utilise PDFBox, mais j'ajouté une autre bibliothèque que fallback lorsque PDFBox échoue)

+0

merci, je vais l'essayer –

+1

Oh, j'ai oublié de mentionner qu'il ya maintenant un projet apache pour l'extraction de texte, http://lucene.apache.org/tika/, au cas où vous préférez l'ouverture – Persimmonium

+1

Pas sûr comment cela résout la question originale en utilisant PDFBox. – cherouvim

4

fichiers PDF commencent « % PDF » (ouvrir un à TextPad ou similaire et jetez un coup d'oeil)

une raison quelconque vous ne pouvez pas lire le fichier avec un StringReader et vérifier cela?

+0

J'ai essayé ceci, et il semble que les fichiers PDF peuvent utiliser une variété de codages et le texte lu parfois ne correspond pas à% PDF pour les fichiers PDF valides et lisibles. –

+0

Tous les fichiers commençant par% PDF ne sont pas des fichiers PDF valides. –

16

Voici ce que je l'utilise dans mes tests NUnit, qui doit valider contre plusieurs versions de PDF à l'aide de 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

Merci, cela m'a juste aidé à comprendre ce qui n'allait pas avec le PDF que je produisais - un problème EOL seulement montré dans Adobe Reader, pas Foxit/GoogleApps/Sumatra. –

+0

Est-ce que c'est Java? En outre, il ne détectera pas les fichiers PDF cryptés. Puisque le PO veut extraire des informations, vous en avez également besoin. – cherouvim

+2

Merci! J'apprécie vraiment que cette réponse soit agnostique. Il m'a sauvé un tas de temps =) – Spina

5

Depuis que vous utilisez PDFBox vous pouvez simplement faire:

PDDocument.load(file); 

Il échouera avec une exception si le PDF est corrompu, etc.

Si elle réussit, vous pouvez également vérifier si le PDF est crypté à l'aide .isEncrypted()

+2

D'après ce que j'ai vu, ce n'est pas vrai. Je peux utiliser PDDocument.load (stream) pour charger un PDF corrompu. Je reçois seulement une erreur en essayant d'enregistrer le PDF après avoir modifié ses permissions. – MonkeyWrench

+0

L'utilisation d'exceptions pour le flux d'applications est une mauvaise pratique. –

+1

@BenTurner: Vous avez raison et je suis avec vous là-dessus. L'API ne nous permet pas de vérifier la validité des fichiers. – cherouvim

5

Vous devez essayer ... .

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

Cette réponse me gêne ... Y at-il PDF qui ne commence pas avec "% PDF-" mais le contient? Pourquoi la peine de lire tout le dossier? Que faire si je vérifie un fichier zip de 2 Go? – boumbh

+0

pour les fichiers plus volumineux, les fichiers de taille 10 + MB et les mauvaises extensions (par exemple mp3File.pdf), cela prendra beaucoup de temps (comme 5 secondes ou plus) – shanraisshan

-1

Il y a une bibliothèque très pratique et simple pour le contenu PDF test: https://github.com/codeborne/pdf-test

API

est très simple:

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

Voici une version Java adaptée du code de 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; 
} 

Et quelques tests unitaires simples:

@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())); 
} 

Si vous venez avec des tests unitaires à défaut, s'il vous plaît laissez-moi savoir.

1

Peut-être que je suis trop tard pour répondre. Mais vous devriez jeter un oeil à Tika. Il utilise PDFBox Parser en interne pour analyser

de PDF

Vous avez juste besoin d'importer tika-app-dernière * .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 
     } 
} 

Ce serait une solution beaucoup plus propre. Vous pouvez consulter ici pour plus de détails de Tika Utilisation: https://tika.apache.org/1.12/api/

1

J'utilisais certaines des suggestions que j'ai trouvées ici et sur d'autres sites/publications pour déterminer si un pdf était valide ou pas. J'ai délibérément corrompu un fichier pdf, et malheureusement, beaucoup de solutions n'ont pas détecté que le fichier était corrompu.

Finalement, après bricoler avec des méthodes différentes de l'API, j'ai essayé ceci:

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

Cela ne génère pas d'exception, mais il fait sortie ceci:

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 

Personnellement, je Je voulais qu'une exception soit levée si le fichier était corrompu afin que je puisse le gérer moi-même, mais il est apparu que l'API que j'appliquais déjà les gérait à sa manière. Pour contourner cela, j'ai décidé d'essayer d'analyser les fichiers en utilisant la classe qui a donné l'instruction chaleureuse (COSParser). J'ai trouvé qu'il y avait une sous-classe, appelée PDFParser, qui héritait d'une méthode appelée "setLenient", qui était la clé (https://pdfbox.apache.org/docs/2.0.4/javadocs/org/apache/pdfbox/pdfparser/COSParser.html).

Je puis mis en œuvre les éléments suivants:

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

Cette lancé une exception pour mon fichier corrompu, comme je le voulais. J'espère que cela aide quelqu'un!

1

La réponse de Roger Keays est fausse! depuis tous les fichiers PDF dans la version 1.3 et pas tous terminés par EOL. La réponse ci-dessous pour tous les travaux non corrompus fichiers 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

Le '%% EOF' doit être le seul contenu de la dernière ligne du PDF. Ainsi, les fichiers avec un espace après le '%% EOF' proprement dit ne sont pas valides. Il peut seulement y avoir un délimiteur de ligne après lui, c'est-à-dire un seul CR, un seul LF, ou une paire CR LF. – mkl

0

En général, nous pouvons comme cela, toute version pdf va finir avec %% EOF afin que nous puissions vérifier comme ci-dessous.

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; 
    } 
Questions connexes