Создание 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.