Создание PDF-файлов в Java
Введение
В этой статье мы рассмотрим создание PDF-документов с помощью популярных библиотек iText и PdfBox.
Зависимости Maven
Зависимости в Maven, которые необходимо включить в проект:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.10</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.4</version>
</dependency>
Актуальные версии библиотек доступны по следующим ссылкам: iText и PdfBox.
Также необходимо добавить ещё одну зависимость, чтобы зашифровать файл. Пакет Bounty Castle Provider включает в себя криптографические алгоритмы и необходим для работы обеих библиотек:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.56</version>
</dependency>
Актуальная версия пакета доступна здесь: The Bounty Castle Provider.
Обзор
iText и PdfBox, - это Java-библиотеки, которые используются для создания и управления pdf-файлами. В итоге они выдают одинаковый результат. Но работают библиотеки по-разному.
Создание Pdf в IText
Вставка текста в Pdf
Рассмотрим, как новый файл с текстом “Hello World” вставляется в pdf-файл.
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("iTextHelloWorld.pdf"));
document.open();
Font font = FontFactory.getFont(FontFactory.COURIER, 16, BaseColor.BLACK);
Chunk chunk = new Chunk("Hello World", font);
document.add(chunk);
document.close();
Создание pdf-документа с помощью библиотеки iText основано на операциях с объектами, реализующими интерфейс Elements в объекте Document.
Наименьший элемент, который можно добавить в документ, называется Chunk. Он представляет собой строку с применённым стилем шрифта.
Элементы Chunk могут комбинироваться с другими элементами, такими как параграфы (Paragraphs), разделы (Section) и т. п. В результате этого получаются правильно отформатированные документы.
Добавление изображения
Библиотека iText предоставляет простой способ добавить изображение в документ. Достаточно создать экземпляр объекта Image и добавить его в Document.
Path path = Paths.get(ClassLoader.getSystemResource("Java_logo.png").toURI())
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("iTextImageExample.pdf"));
document.open();
Image img = Image.getInstance(path.toAbsolutePath().toString());
document.add(img);
document.close();
Добавление таблицы
Чтобы с помощью iText добавить таблицу в pdf-файл, создаем объект PdfTable, в конструкторе которого указываем количество колонок таблицы. После этого используем метод addCel ,чтобы добавить новые ячейки.
iText будет добавлять строки таблицы по мере заполнения ячеек. Например, если создать таблицу с тремя колонками и добавить в неё 8 ячеек, то первоначально отобразятся только 2 строки по 3 ячейки в каждой.
Рассмотрим приведенный ниже пример:
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("iTextTable.pdf"));
document.open();
PdfPTable table = new PdfPTable(3);
addTableHeader(table);
addRows(table);
addCustomRows(table);
document.add(table);
document.close();
Мы создали новую таблицу с тремя столбцами и тремя строками. Первая строка используется в качестве заголовка для столбцов с измененным фоном и шириной границ:
private void addTableHeader(PdfPTable table) {
Stream.of("column header 1", "column header 2", "column header 3")
.forEach(columnTitle -> {
PdfPCell header = new PdfPCell();
header.setBackgroundColor(BaseColor.LIGHT_GRAY);
header.setBorderWidth(2);
header.setPhrase(new Phrase(columnTitle));
table.addCell(header);
});
}
Вторая строка таблицы будет состоять из трёх ячеек с простым текстом без форматирования.
private void addRows(PdfPTable table) {
table.addCell("row 1, col 1");
table.addCell("row 1, col 2");
table.addCell("row 1, col 3");
}
В таблицу можно добавлять не только текстовые ячейки, но и изображения. Кроме этого каждой ячейке можно задать свой стиль и форматирование. В следующем примере мы применяем к ячейкам горизонтальное и вертикальное выравнивание:
private void addCustomRows(PdfPTable table)
throws URISyntaxException, BadElementException, IOException {
Path path = Paths.get(ClassLoader.getSystemResource("Java_logo.png").toURI());
Image img = Image.getInstance(path.toAbsolutePath().toString());
img.scalePercent(10);
PdfPCell imageCell = new PdfPCell(img);
table.addCell(imageCell);
PdfPCell horizontalAlignCell = new PdfPCell(new Phrase("row 2, col 2"));
horizontalAlignCell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(horizontalAlignCell);
PdfPCell verticalAlignCell = new PdfPCell(new Phrase("row 2, col 3"));
verticalAlignCell.setVerticalAlignment(Element.ALIGN_BOTTOM);
table.addCell(verticalAlignCell);
}
Шифрование файла
Чтобы настроить права доступа к файлу с помощью библиотеки iText, мы будем использовать файл HelloWorld.pdf, созданный ранее.
После загрузки файла с помощью PdfReader создаем объект PdfStamper, Он позволяет добавить в документ дополнительный контент, такой как метаданные, шифрование и т. п.:
PdfReader pdfReader = new PdfReader("HelloWorld.pdf");
PdfStamper pdfStamper
= new PdfStamper(pdfReader, new FileOutputStream("encryptedPdf.pdf"));
pdfStamper.setEncryption(
"userpass".getBytes(),
"ownerpass".getBytes(),
0,
PdfWriter.ENCRYPTION_AES_256
);
pdfStamper.close();
В нашем примере мы зашифровали файл двумя паролями. Пароль пользователя (“userpass”), который имеет только права на чтение. Пароль владельца (“ownerpass”), применяется как мастер-ключ и предоставляет полный доступ к pdf- файлу.
Если нужно разрешить пользователю печать документа, то вместо 0 (третий параметр метода setEncryption) передаем следующее значение:
PdfWriter.ALLOW_PRINTING
Также можно совмещать различные права на файл:
Используя iText для установки разрешений, мы создаём временный pdf-файл. Впоследствии он должен быть удалён, иначе доступ к нему сможет получить любой пользователь.
Создание Pdf в PdfBox
Вставка текста в Pdf
В отличие от iText, библиотека PdfBox предоставляет API, основанный на операциях с потоками. Здесь не существует классов Chunk/Paragraph и т. п. Класс PDDocument является представлением pdf-документа в памяти, куда пользователь добавляет данные с помощью класса PDPageContentStream.
Рассмотрим приведенный ниже пример кода:
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
contentStream.setFont(PDType1Font.COURIER, 12);
contentStream.beginText();
contentStream.showText("Hello World");
contentStream.endText();
contentStream.close();
document.save("pdfBoxHelloWorld.pdf");
document.close();
Добавление изображений
Сначала необходимо загрузить файл и создать объект PDImageXObject. Затем отрисовать его в документе (нужно добавить координаты x и y).
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
Path path = Paths.get(ClassLoader.getSystemResource("Java_logo.png").toURI());
PDPageContentStream contentStream = new PDPageContentStream(document, page);
PDImageXObject image
= PDImageXObject.createFromFile(path.toAbsolutePath().toString(), document);
contentStream.drawImage(image, 0, 0);
contentStream.close();
document.save("pdfBoxImage.pdf");
document.close();
Добавление таблиц
К сожалению, PdfBox не предоставляет методов для создания таблицы. Но можно «нарисовать» её вручную.
Шифрование файла
Библиотека PdfBox предоставляет возможность шифрования создаваемого файла и настройки пользовательских привилегий. При этом ей не требуется уже созданный pdf-документ, поскольку она использует объект PDDocument.
Права доступа для pdf-файла настраиваются с помощью класса AccessPermission. Сначала мы создаём объект StandardProtectionPolicy, который добавляет к файлу защиту паролем для пользователя и владельца.
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
AccessPermission accessPermission = new AccessPermission();
accessPermission.setCanPrint(false);
accessPermission.setCanModify(false);
StandardProtectionPolicy standardProtectionPolicy
= new StandardProtectionPolicy("ownerpass", "userpass", accessPermission);
document.protect(standardProtectionPolicy);
document.save("pdfBoxEncryption.pdf");
document.close();
В приведенном выше примере пользовательский пароль открывает файл без права печати и изменения.
Заключение
Мы рассмотрели создание pdf-файла с помощью двух популярных Java- библиотек. Полный код примеров, приведенных в этой статье, доступен на GitHub.