web-dev-qa-db-ja.com

ファイルがPDFファイルであるかどうかを判断するにはどうすればよいですか?

JavaからPdfBoxを使用してPDFファイルからテキストを抽出します。提供された入力ファイルの一部が無効であり、PDFTextStripperがこれらのファイルで停止します。提供されたファイルが実際に有効なPDFであるかどうかを確認するクリーンな方法?

31
user116219

ファイル(またはバイト配列)のMIMEタイプを確認できるため、拡張子に頼る必要はありません。アパーチャのMimeExtractor( http://aperture.sourceforge.net/ )を使ってそれを行うか、数日前にそのためのライブラリを見ました( http://sourceforge.net/projects/mime-util

PDFだけでなく、さまざまなファイルからテキストを抽出するためにアパーチャを使用しますが、たとえばTweakはpdfを考慮しなければなりません(アパーチャはpdfboxを使用しますが、pdfboxが失敗したときの代替として別のライブラリを追加しました)。

8
Persimmonium

これは、NUnitテストで使用するもので、Crystal Reportsを使用して生成されたPDFの複数のバージョンに対して検証する必要があります。

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");
    }
22
NinjaCross

PDFBoxを使用するので、次のことが簡単に行えます。

_PDDocument.load(file);
_

PDFが破損している場合などは、例外で失敗します。

成功した場合は、PDFが.isEncrypted()を使用して暗号化されているかどうかも確認できます

6
cherouvim

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

単体テストが失敗した場合は、お知らせください。

6
Roger Keays

あなたはこれを試さなければなりません...

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
Sheel

PDFファイルは "%PDF"で始まります(TextPadなどで開いて確認してください)

StringReaderでファイルを読み取ってこれを確認できない理由は何ですか?

4
cagcowboy

ここと他のサイト/投稿で見つけた提案の一部を使用して、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)を提供するクラスを使用してファイルの解析を試みることにしました。キーである "setLenient"と呼ばれるメソッドを継承するPDFParserと呼ばれるサブクラスがあることがわかりました( 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();

これにより、破損したファイルの例外がスローされました。これが誰かを助けることを願っています!

4
arjun kumar

多分私は答えるのが遅すぎる。しかし、あなたはティカを見ておくべきです。 PDFBoxパーサーを内部的に使用してPDFを解析します

Tika-app-latest * .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/

2
skashyap

ロジャー・キーズの答えは間違っています!すべてPDFファイルではなく、すべて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;
}
1
Mohsen Abasi

一般に、これは好きです。%% EOFで終わるpdfバージョンは次のようにチェックできます。

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;
    }
1
Binayak

以下は、空白文字のオプションのチェックを使用して%%EOFの存在をチェックするメソッドです。 Fileまたはbyte[]オブジェクトを渡すことができます。一部のPDFバージョンでは、空白文字の制限が少なくなっています。

public boolean isPdf(byte[] data) {
    if (data == null || data.length < 5) return false;
    // %PDF-
    if (data[0] == 0x25 && data[1] == 0x50 && data[2] == 0x44 && data[3] == 0x46 && data[4] == 0x2D) {
        int offset = data.length - 8, count = 0; // check last 8 bytes for %%EOF with optional white-space
        boolean hasSpace = false, hasCr = false, hasLf = false;
        while (offset < data.length) {
            if (count == 0 && data[offset] == 0x25) count++; // %
            if (count == 1 && data[offset] == 0x25) count++; // %
            if (count == 2 && data[offset] == 0x45) count++; // E
            if (count == 3 && data[offset] == 0x4F) count++; // O
            if (count == 4 && data[offset] == 0x46) count++; // F
            // Optional flags for meta info
            if (count == 5 && data[offset] == 0x20) hasSpace = true; // SPACE
            if (count == 5 && data[offset] == 0x0D) hasCr    = true; // CR
            if (count == 5 && data[offset] == 0x0A) hasLf    = true; // LF / EOL
            offset++;
        }

        if (count == 5) {
            String version = data.length > 13 ? String.format("%s%s%s", (char) data[5], (char) data[6], (char) data[7]) : "?";
            System.out.printf("Version : %s | Space : %b | CR : %b | LF : %b%n", version, hasSpace, hasCr, hasLf);
            return true;
        }
    }

    return false;
}
public boolean isPdf(File file) throws IOException {
    return isPdf(file, false);
}
// With version: 16 bytes, without version: 13 bytes.
public boolean isPdf(File file, boolean includeVersion) throws IOException {
    if (file == null) return false;
    int offsetStart = includeVersion ? 8 : 5, offsetEnd = 8;
    byte[] bytes = new byte[offsetStart + offsetEnd];
    InputStream is = new FileInputStream(file);
    try {
        is.read(bytes, 0, offsetStart); // %PDF-
        is.skip(file.length() - bytes.length); // Skip bytes
        is.read(bytes, offsetStart, offsetEnd); // %%EOF,SP?,CR?,LF?
    } finally {
        is.close();
    }
    return isPdf(bytes);
}
0
Mr. Polywhirl