Извлечь папки из портфолио 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