使用 PDFBox 解析器从 PDF 中提取嵌入式 flash 文件的最佳方法是什么?

What is the best way to extract embedded flash file from a PDF using the PDFBox parser?

自上周晚些时候以来,我一直在研究 PDF 解析。设法找到 Java 的 Apache PDFBox 库,我已经提取了我正在开发的项目所需的按页面、URL、图像和 PDF 元数据分隔的文本。现在我缺少从 PDF 中提取嵌入式 Flash 视频的方法。

我目前正在分析此解析器如何从 PDF 中提取富媒体,出于测试目的使用可用的 pdf 文件 here。此文件包含我打算获取的 Flash 视频。

我已经尝试使用此 approach 来搜索 PDF 中的嵌入文件,但它目前对我不起作用,因为它在我创建的用于存储此类文件的文件夹中找不到并保存任何内容。

我的代码目前的样子,改编自上述方法。

package myproject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationFileAttachment;

/**
 * This is an example on how to extract all embedded files from a PDF document.
 *
 */
public final class ExtractEmbeddedFiles
{
    private ExtractEmbeddedFiles()
    {
    }

    /**
     * This is the main method.
     *
     * @param args The command line arguments.
     *
     * @throws IOException If there is an error parsing the document.
     */
    public static void main( String[] args ) throws IOException
    {
            PDDocument document = null;
            try
            {
                File pdfFile = new File("/Users/henriqueferreira/Documents/PDFBoxDocuments/inOntario.pdf");
                String filePath = pdfFile.getParent() + System.getProperty("file.separator");
                document = PDDocument.load(new File("/Users/henriqueferreira/Documents/PDFBoxDocuments/inOntario.pdf"));
                PDDocumentNameDictionary namesDictionary = 
                        new PDDocumentNameDictionary( document.getDocumentCatalog() );
                PDEmbeddedFilesNameTreeNode efTree = namesDictionary.getEmbeddedFiles();
                if (efTree != null)
                {
                    Map<String, PDComplexFileSpecification> names = efTree.getNames();
                    if (names != null)
                    {
                        extractFiles(names, filePath);
                    }
                    else
                    {
                        List<PDNameTreeNode<PDComplexFileSpecification>> kids = efTree.getKids();
                        for (PDNameTreeNode<PDComplexFileSpecification> node : kids)
                        {
                            names = node.getNames();
                            extractFiles(names, filePath);
                        }
                    }
                }

                // extract files from annotations
                for (PDPage page : document.getPages())
                {
                    for (PDAnnotation annotation : page.getAnnotations())
                    {
                        if (annotation instanceof PDAnnotationFileAttachment)
                        {
                            PDAnnotationFileAttachment annotationFileAttachment = (PDAnnotationFileAttachment) annotation;
                            PDComplexFileSpecification fileSpec = (PDComplexFileSpecification) annotationFileAttachment.getFile();
                            PDEmbeddedFile embeddedFile = getEmbeddedFile(fileSpec);
                            extractFile(filePath, fileSpec.getFilename(), embeddedFile);
                        }
                    }
                }

            }
            finally
            {
                if( document != null )
                {
                    document.close();
                }
            }
    }

    private static void extractFiles(Map<String, PDComplexFileSpecification> names, String filePath) 
            throws IOException
    {
        for (Entry<String, PDComplexFileSpecification> entry : names.entrySet())
        {
            String filename = entry.getKey();
            PDComplexFileSpecification fileSpec = entry.getValue();
            PDEmbeddedFile embeddedFile = getEmbeddedFile(fileSpec);
            extractFile(filePath, filename, embeddedFile);
        }
    }

    private static void extractFile(String filePath, String filename, PDEmbeddedFile embeddedFile)
            throws IOException
    {
        String embeddedFilename = filePath + filename;
        File file = new File("/Users/henriqueferreira/Documents/PDFBoxFiles/"+filename);
        System.out.println("Writing " + embeddedFilename);
        try (FileOutputStream fos = new FileOutputStream(file))
        {
            fos.write(embeddedFile.toByteArray());
        }
    }

    private static PDEmbeddedFile getEmbeddedFile(PDComplexFileSpecification fileSpec )
    {
        // search for the first available alternative of the embedded file
        PDEmbeddedFile embeddedFile = null;
        if (fileSpec != null)
        {
            embeddedFile = fileSpec.getEmbeddedFileUnicode(); 
            if (embeddedFile == null)
            {
                embeddedFile = fileSpec.getEmbeddedFileDos();
            }
            if (embeddedFile == null)
            {
                embeddedFile = fileSpec.getEmbeddedFileMac();
            }
            if (embeddedFile == null)
            {
                embeddedFile = fileSpec.getEmbeddedFileUnix();
            }
            if (embeddedFile == null)
            {
                embeddedFile = fileSpec.getEmbeddedFile();
            }
        }
        return embeddedFile;
    }
}

所以,我的问题是,从 PDF 文件中获取此类 Flash 视频的最合适方法应该是什么?

下面是一些基于我在 PDFDebugger 中看到的内容的快速代码:

PDDocument doc = PDDocument.load(new File("Mississauga_Advantages.pdf"));
for (int p = 0; p < doc.getNumberOfPages(); ++p)
{
    PDPage page = doc.getPage(p);
    List<PDAnnotation> annotations = page.getAnnotations();
    for (PDAnnotation ann : annotations)
    {
        if ("RichMedia".equals(ann.getSubtype()))
        {
            COSArray array = (COSArray) ann.getCOSObject().getObjectFromPath("RichMediaContent/Assets/Names/");
            String name = array.getString(0);
            COSDictionary filespec = (COSDictionary) array.getObject(1);
            PDComplexFileSpecification cfs = new PDComplexFileSpecification(filespec);
            PDEmbeddedFile embeddedFile = cfs.getEmbeddedFile();
            System.out.println("page: " + (p+1) + ", name: " + name + ", size: " + embeddedFile.createInputStream().available());
        }
    }
}

您的富媒体在注释中。所以我浏览了列表并寻找我看到的模式。我不知道这是不是标准的,我在PDF 32000规范中没有找到它。 (更新:写代码后发现here