Извлечь папки из портфолио pdf java

У меня есть портфолио PDF с папками, подпапками и файлами. Мне нужно извлечь ту же структуру, что и с папками, подпапками и файлами, используя iText в Java. Я получаю только файлы с 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();
    }
}

1 ответ

Решение

Структура папок, которую ОП пытается реплицировать при извлечении файлов портфолио, указана в Приложении Adobe® к ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3. Таким образом, он не является частью текущего стандарта PDF, и, следовательно, программное обеспечение для обработки PDF не требуется для понимания такого рода информации. Похоже, что планируется добавить его в новый стандарт PDF-2 (ISO 32000-2).

Таким образом, чтобы извлечь файлы портфолио в связанную структуру папок, нам нужно извлечь информацию о папке, как указано в Приложении Adobe®:

Начиная с уровня расширения 3, переносимая коллекция может содержать объект Folders с целью организации файлов в иерархическую структуру. Структура представлена ​​в виде дерева с единственной корневой папкой, выступающей в качестве общего предка для всех других папок и файлов в коллекции. На одну корневую папку ссылаются в Folders запись таблицы 8.6 на стр. 29.

Таблица 8.6c описывает записи в словаре папок

  • ID целое число (обязательно; ExtensionLevel 3) неотрицательное целочисленное значение, представляющее уникальный идентификационный номер папки. Две папки не должны быть общими ID значение.

    Папка ID значение отображается как часть ключа дерева имен любого файла, связанного с этой папкой. Подробное описание связи между папкой и файлами можно найти после этой таблицы.

  • Name текстовая строка (обязательно; ExtensionLevel 3) Имя файла, представляющее имя папки. Две дочерние папки не должны иметь одно и то же имя после нормализации регистра.

  • Child словарь (необходим, если в папке есть потомки; ExtensionLevel 3) Косвенная ссылка на первую дочернюю папку этой папки.

  • Next словарь (необходим для всех элементов, кроме последнего на каждом уровне; ExtensionLevel 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)

и использовать эти извлеченные данные папки при записи файлов.

Ассоциация файлов и папок указана в Приложении Adobe® следующим образом:

Как уже упоминалось ранее, файлы в EmbeddedFiles дерево имен связано с папками по специальному соглашению об именах, применяемому к строкам ключей дерева имен. Строки, соответствующие следующим правилам, служат для связывания соответствующего файла с папкой:

  • Ключи дерева имен представляют собой текстовые строки PDF.
  • Первый символ, исключая любой маркер порядка байтов, - это U+003C, МЕНЬШЕ ЗНАЧКА (<).
  • Следующие символы должны содержать одну или несколько цифр (от 0 до 9), за которыми следует закрывающий U+003E, ЗНАК БОЛЬШОГО, ЧЕМ (>)
  • Остальная часть строки является именем файла.

Раздел строки, заключенный в знак LESS-THAN SIGN GREATER-THAN SIGN(<>), интерпретируется как числовое значение, которое указывает значение идентификатора папки, с которой связан файл. Значение должно соответствовать идентификатору папки. Раздел строки, следующий за тегом идентификатора папки, представляет имя файла внедренного файла.

Файлы в дереве имен 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 (например, с использованием метода теста testSamplePortfolio11Folders в PortfolioFileExtraction.java) каждый получает

Root
│   ThumbImpression.pdf
│
├───Folder 1
│   │   EStampPdf.pdf
│   │   Presentation.pdf
│   │
│   ├───Folder 11
│   │   │   Test.pdf
│   │   │
│   │   └───Folder 111
│   └───Folder 12
└───Folder 2
        SealDeed.pdf
Другие вопросы по тегам