【问题标题】:extract folders from portfolio pdf java从投资组合pdf java中提取文件夹
【发布时间】:2015-10-25 16:59:02
【问题描述】:

我有一个包含文件夹、子文件夹和文件的投资组合 pdf。我需要在 java 中使用 iText 提取与文件夹、子文件夹和文件相同的结构。我只得到带有 EMBEDEDFILES 的文件。获取文件夹的方式也是什么。

请找到我正在使用的代码。这段代码只给了我文件夹中存在的文件。

public static void extractAttachments(String src, String dir) throws         IOException
{
    File folder = new File(dir);
    folder.mkdirs();

    PdfReader reader = new PdfReader(src);

    PdfDictionary root = reader.getCatalog();

    PdfDictionary names = root.getAsDict(PdfName.NAMES);
    System.out.println(""+names.getKeys().toString());
    PdfDictionary embedded = names.getAsDict(PdfName.EMBEDDEDFILES);
    System.out.println(""+embedded.toString());

    PdfArray filespecs = embedded.getAsArray(PdfName.NAMES);

    System.out.println(filespecs.getAsString(root1));
    for (int i = 0; i < filespecs.size();)
    {
        extractAttachment(reader, folder, filespecs.getAsString(i++),
                filespecs.getAsDict(i++));
    }
}

protected static void extractAttachment(PdfReader reader, File dir, PdfString name, PdfDictionary filespec)
        throws IOException
{
    PRStream stream;
    FileOutputStream fos;
    String filename;
    PdfArray parent;
    PdfDictionary refs = filespec.getAsDict(PdfName.EF);
    //System.out.println(""+refs.getKeys().toString());

    for (Object key : refs.getKeys())
    {
        stream = (PRStream)         PdfReader.getPdfObject(refs.getAsIndirectObject((PdfName) key));

        filename = filespec.getAsString((PdfName) key).toString();

        // System.out.println("" + filename);
        fos = new FileOutputStream(new File(dir, filename));
        fos.write(PdfReader.getStreamBytes(stream));
        fos.flush();
        fos.close();
    }
}

【问题讨论】:

  • 您好,欢迎来到 StackOverflow!请花点时间阅读 ho 提出一个好问题:stackoverflow.com/help/how-to-ask
  • 您是否使用iText RUPS查看了PDF?您是否能够发现 PDF 中的文件夹结构是如何定义的?正如@Mauker 解释的那样,您的问题需要做更多的工作。
  • 嗨,我没有查看 iText RUPS,如果有任何方法或类帮助我从 iText Rups 中提取文件夹结构,请分享,即使我在您的推荐后搜索相同但没有得到任何事情,其次,当我搜索 PdfName 变量时,我只了解结构在 PDF 中定义为 COLLECTION 作为 KEY 的东西,没有任何提供 /Folders 的变量,即使我通过 new PdfName("Folders") 尝试过但没有得到任何东西而不是每次都出现空指针异常。
  • @Nitesh 正如 Mauker 和 Bruno 所暗示的那样,您的问题需要更多细节。请分享 A 一个示例 PDF 组合和 B 您迄今为止尝试提取文件夹结构的代码。您之前的评论表明有问题的投资组合不包含文件夹(根据 PDF 规范的 Adob​​e ExtensionLevel 3),而只是看起来像文件夹的东西,或者您的代码是错误的。因此,PDF 和代码都需要。
  • 你能分享给我你的邮件ID,以便我可以分享pdf文件。在这里不可能附加相同的。我将附上相同 pdf 的屏幕截图和文件夹以寻求帮助

标签: pdf itext directory portfolio


【解决方案1】:

Adobe® Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3 中指定了 OP 在提取投资组合文件时尝试复制的文件夹结构。因此,它不是当前 PDF 标准的一部分,因此 PDF 处理软件不需要理解此类信息。不过,它似乎已计划添加到即将推出的 PDF-2 (ISO 32000-2) 标准中。

因此,要将投资组合文件提取到相关的文件夹结构中,我们必须检索 Adob​​e® 补充文件中指定的文件夹信息:

从扩展级别 3 开始,可移植集合可以包含 Folders 对象,用于 将文件组织成层次结构。该结构由具有单个根文件夹的树表示 作为集合中所有其他文件夹和文件的共同祖先。单个根文件夹是 在第 29 页的表 8.6 的 Folders 条目中引用。

表 8.6c 描述了文件夹字典中的条目

  • ID integer (必需;ExtensionLevel 3) 一个非负整数值 表示唯一的文件夹标识号。两个文件夹 不应共享相同的ID 值。

    文件夹ID 值显示为任何文件的名称树键的一部分 与此文件夹关联。的详细说明 文件夹和文件的关联可以在这张表后面找到。

  • Name 文本字符串 (必需;ExtensionLevel 3) 一个文件名,表示 文件夹。两个同级文件夹不得同名 以下案例规范化。

  • Child 字典 (如果文件夹有任何后代,则需要;ExtensionLevel 3) 间接引用此文件夹的第一个子文件夹。

  • Next 字典 (除每个级别的最后一项之外的所有项都需要;扩展级别 3) 间接引用此级别的下一个同级文件夹。

(第 8.2.4 节集合)

例如像这样:

static Map<Integer, File> retrieveFolders(PdfReader reader, File baseDir) throws DocumentException
{
    Map<Integer, File> result = new HashMap<Integer, File>();

    PdfDictionary root = reader.getCatalog();
    PdfDictionary collection = root.getAsDict(PdfName.COLLECTION);
    if (collection == null)
        throw new DocumentException("Document has no Collection dictionary");
    PdfDictionary folders = collection.getAsDict(FOLDERS);
    if (folders == null)
        throw new DocumentException("Document collection has no folders dictionary");

    collectFolders(result, folders, baseDir);

    return result;
}

static void collectFolders(Map<Integer, File> collection, PdfDictionary folder, File baseDir)
{
    PdfString name = folder.getAsString(PdfName.NAME);
    File folderDir = new File(baseDir, name.toString());
    folderDir.mkdirs();
    PdfNumber id = folder.getAsNumber(PdfName.ID);
    collection.put(id.intValue(), folderDir);

    PdfDictionary next = folder.getAsDict(PdfName.NEXT);
    if (next != null)
        collectFolders(collection, next, baseDir);
    PdfDictionary child = folder.getAsDict(CHILD);
    if (child != null)
        collectFolders(collection, child, folderDir);
}

final static PdfName FOLDERS = new PdfName("Folders");
final static PdfName CHILD = new PdfName("Child");

(摘自PortfolioFileExtraction.java

并在写入文件时使用这些检索到的文件夹信息。

文件和文件夹的关联在 Adob​​e® Supplement 中指定如下:

如前所述,EmbeddedFiles 名称树中的文件通过特殊的 应用于名称树键字符串的命名约定。符合以下规则的字符串服务 将相应文件与文件夹关联:

  • 名称树键是 PDF 文本字符串。
  • 第一个字符(不包括任何字节顺序标记)是 U+003C,即小于号 (
  • 以下字符应为一个或多个数字(0 到 9)后跟结束 U+003E, 大于号 (>)
  • 字符串的其余部分是文件名。

由 LESS-THAN SIGN GREATER-THAN SIGN() 包围的字符串部分被解释为 指定与文件关联的文件夹的 ID 值的数值。该值应 对应于文件夹 ID。文件夹 ID 标记后面的字符串部分表示文件名 嵌入文件。

EmbeddedFiles 名称树中不符合这些规则的文件应被视为关联文件 与根文件夹。

(第 8.2.4 节集合)

您的方法可以这样扩展:

public static void extractAttachmentsWithFolders(PdfReader reader, String dir) throws IOException, DocumentException
{
    File folder = new File(dir);
    folder.mkdirs();

    Map<Integer, File> folders = retrieveFolders(reader, folder);

    PdfDictionary root = reader.getCatalog();

    PdfDictionary names = root.getAsDict(PdfName.NAMES);
    System.out.println("" + names.getKeys().toString());
    PdfDictionary embedded = names.getAsDict(PdfName.EMBEDDEDFILES);
    System.out.println("" + embedded.toString());

    PdfArray filespecs = embedded.getAsArray(PdfName.NAMES);

    for (int i = 0; i < filespecs.size();)
    {
        extractAttachment(reader, folders, folder, filespecs.getAsString(i++), filespecs.getAsDict(i++));
    }
}

protected static void extractAttachment(PdfReader reader, Map<Integer, File> dirs, File dir, PdfString name, PdfDictionary filespec) throws IOException
{
    PRStream stream;
    FileOutputStream fos;
    String filename;
    PdfDictionary refs = filespec.getAsDict(PdfName.EF);

    File dirHere = dir;
    String nameString = name.toUnicodeString();
    if (nameString.startsWith("<"))
    {
        int closing = nameString.indexOf('>');
        if (closing > 0)
        {
            int folderId = Integer.parseInt(nameString.substring(1, closing));
            File folderFile = dirs.get(folderId);
            if (folderFile != null)
                dirHere = folderFile;
        }
    }

    for (PdfName key : refs.getKeys())
    {
        stream = (PRStream) PdfReader.getPdfObject(refs.getAsIndirectObject(key));

        filename = filespec.getAsString(key).toString();

        fos = new FileOutputStream(new File(dirHere, filename));
        fos.write(PdfReader.getStreamBytes(stream));
        fos.flush();
        fos.close();
    }
}

(摘自PortfolioFileExtraction.java

将这些方法应用于您的示例 PDF(例如,在 PortfolioFileExtraction.java 中使用测试方法 testSamplePortfolio11Folders)得到

Root
│   ThumbImpression.pdf
│
├───Folder 1
│   │   EStampPdf.pdf
│   │   Presentation.pdf
│   │
│   ├───Folder 11
│   │   │   Test.pdf
│   │   │
│   │   └───Folder 111
│   └───Folder 12
└───Folder 2
        SealDeed.pdf

【讨论】:

  • 嗨,我正在分别获取文件和文件夹。首先我得到所有文件夹,然后我得到文件夹外的文件。我需要文件夹中的文件原样。请推荐?
  • 检查 if(nameString.startsWith("
  • 您是否从我的答案中复制了代码?您是否将它与您的示例文件一起使用?运行上面链接的单元测试我肯定会得到文件夹中的文件。只需使用我在答案中链接到的 java 文件 PortfolioFileExtraction.java
  • 检查 if(nameString.startsWith(" 时为假 - 对于您的示例文档,这是不可能的。
  • 我在我拥有的其他 pdf 上运行相同的文件并将名称字符串作为 þÿ
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-06
  • 2018-01-19
  • 1970-01-01
  • 2020-01-22
  • 2017-03-20
  • 1970-01-01
相关资源
最近更新 更多