Javenue logo

Javenue

Программирование на Java

Информационные технологии

Генерация PDF в Java используя iText

PDF (portable document format) - это популярный формат документов, который не зависит от операционной системы или железа. Очевидно, его можно использовать для генерации отчетов, дипломов, чеков в приложениях, а так же для редактирования документов. Для этих целей лучше всего подойдет java-библиотека iText. Кстати, библиотека доступна так же и для C#.

В статье мы рассмотрим следующие вопросы:

  • Генерация нового PDF документа на примере чека.
  • Заполнение шаблона PDF документа.
  • Краткий обзор некоторых классов и методов библиотеки iText.

Для нетерпеливых сразу же привожу java код, который создает pdf-шаблон чека, а потом заполняет его данными из объекта Receipt. Не забудьте скачать jar-файл библиотеки и положить его на classpath.

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URL;
import java.text.NumberFormat;
import java.util.Date;

public class PdfGeneration {
    static int FONT_SIZE_SMALL = 16;
    static int FONT_SIZE_BIG = 32;
    static int OFFSET = 40;

    public static void main(String[] args) throws Exception {
        createTemplate();

        Receipt receipt = new Receipt(
                "This is a veeeeeeeeeeeeeeeeeeeeee" + 
                "eeeeeeeeeeeeeeeeeeeeery long purpose " +
                "text, so it will overflow with font size = 16",
                123.45,
                "Name Surname");

        fillInReceipt(receipt);
    }

    public static void createTemplate() throws Exception {
        Document document = new Document();

        Font font1 = new Font(Font.FontFamily.HELVETICA, 
                FONT_SIZE_BIG, Font.BOLD);
        Font font2 = new Font(Font.FontFamily.HELVETICA, 
                FONT_SIZE_SMALL, Font.ITALIC | Font.UNDERLINE);

        PdfWriter.getInstance(document, 
                new FileOutputStream("template.pdf"));

        document.open();

        // отцентрированный параграф
        Paragraph title = new Paragraph("Receipt", font1);
        title.setAlignment(Element.ALIGN_CENTER);
        title.setSpacingAfter(FONT_SIZE_BIG);
        document.add(title);

        // параграф с текстом
        Paragraph purpose = new Paragraph("Purpose", font2);
        purpose.setSpacingAfter(FONT_SIZE_BIG);
        document.add(purpose);

        // параграф с добавленным чанком текста
        Paragraph amount = new Paragraph();
        amount.setFont(font2);
        amount.setSpacingAfter(8);
        amount.add(new Chunk("Amount"));
        document.add(amount);

        // параграф с фразой, в которую добавлен чанк
        Paragraph date = new Paragraph();
        date.setFont(font2);
        Phrase phrase = new Phrase();
        phrase.add(new Chunk("Date"));
        date.add(phrase);
        document.add(date);

        document.add(new Paragraph("Name", font2));

        Paragraph footer = new Paragraph(
            "Important - please retain for your records - ");

        // ссылка
        Anchor anchor = new Anchor("Javenue");
        anchor.setReference("http://www.javenue.info");
        footer.add(anchor);

        footer.setAlignment(Element.ALIGN_CENTER);
        footer.setSpacingBefore(FONT_SIZE_BIG);
        document.add(footer);

        // картинка, загруженная по URL
        String imageUrl = "http://www.javenue.info/files/sample.png";
        // Image.getInstance("sample.png")
        Image stamp = Image.getInstance(new URL(imageUrl)); 
        stamp.setAlignment(Element.ALIGN_RIGHT);
        document.add(stamp);

        document.close();
    }

    public static void fillInReceipt(Receipt receipt) 
            throws Exception {
        PdfReader reader = new PdfReader(
            new FileInputStream("template.pdf"));
        PdfStamper stamper = new PdfStamper(reader, 
                new FileOutputStream("receipt.pdf"));

        PdfContentByte stream = stamper.getOverContent(1);
        stream.beginText();
        stream.setColorFill(BaseColor.BLUE);

        BaseFont font = BaseFont.createFont();

        float pageWidth = reader.getPageSize(1).getWidth();
        stream.setFontAndSize(font, FONT_SIZE_SMALL);
        float v = stream.getEffectiveStringWidth(
            receipt.getPurpose(), false);

        float fitSize = (pageWidth-OFFSET*2) * FONT_SIZE_SMALL/v;
        stream.setFontAndSize(font, fitSize);
        stream.setTextMatrix(OFFSET, 680);
        stream.showText(receipt.getPurpose());

        stream.setFontAndSize(font, FONT_SIZE_SMALL);

        String amount = NumberFormat.getCurrencyInstance()
                .format(receipt.getAmount());
        v = stream.getEffectiveStringWidth(amount, false);
        stream.setTextMatrix(pageWidth - v - OFFSET, 655);
        stream.showText(amount);

        v = stream.getEffectiveStringWidth(
                receipt.getDate() + "", false);
        stream.setTextMatrix(pageWidth - v - OFFSET, 630);
        stream.showText(receipt.getDate() + "");

        v = stream.getEffectiveStringWidth(
                receipt.getName(), false);
        stream.setTextMatrix(pageWidth - v - OFFSET, 605);
        stream.showText(receipt.getName());

        stream.endText();
        stamper.setFullCompression();
        stamper.close();
    }

    static class Receipt {
        private String purpose;
        private double amount;
        private Date date = new Date();
        private String name;

        public Receipt(String purpose, double amount, String name) {
            this.purpose = purpose;
            this.amount = amount;
            this.name = name;
        }

        public String getPurpose() { return purpose;}
        public void setPurpose(String purpose) { 
            this.purpose = purpose; 
        }

        public double getAmount() { return amount; }
        public void setAmount(double amount) { 
            this.amount = amount; 
        }

        public Date getDate() { return date; }
        public void setDate(Date date) { this.date = date; }

        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
    }
}

Для начала необходимо создать экземпляр com.itextpdf.text.Document и с помощью PDFWriter связать его с файлом на файловой системе. Создать шрифт можно с помощью класса Font, указывая размер шрифта, его семейство и стиль.

Основные объекты, с которыми вы будете работать, это Phrase, Paragraph, возможно Anchor и самый маленький кусок текста - Chunk. Обратите внимание, что для каждого параграфа вы можете задать свой шрифт и далее добавлять в параграф объекты Chunk, Phrase, Anchor.

Ссылки Anchor могут быть открыты при просмотре PDF документа, если в вашей операционной системе файлы *.pdf связаны с программой для их просмотра.

C помощью следующего кода можно добавить картинку в PDF документ:

  String imageUrl = "http://site.com/image.png";
  Image stamp = Image.getInstance(new URL(imageUrl)); 
  stamp.setAlignment(Element.ALIGN_RIGHT);
  document.add(stamp);

Кроме загрузки по URL можно считать картинку с файловой системы, например: Image stamp = Image.getInstance("sample.png").

Не забудьте вызвать document.close() после добавления всех объектов. Это необходимо для того, чтобы корректно закрыть PDFWriter.

Писать поверх существующего PDF документа можно через PdfContentByte. Немного придется помучиться с расположением текста на чеке, но кто говорил, что будет легко. Думаю, код метода fillInReceipt понятен и не требует дополнительных объяснений.

А вот еще классы из iText API, которые могут вам пригодиться:

  • List и ListItem - это список и элемент списка, который может быть добавлен как list.add(new ListItem("item")). Списки как и в HTML могут быть упорядоченные и неупорядоченные.
  • Chapter и Section - раздел и соответственно его секция. Секции можно добавлять с помощью вызова chapter.addSection
  • Более сложный объект PdfPTable, состоящий из ячеек PdfPCell. Можно делать достаточно красивые таблички, но как по мне API не самый удобный.

Вот и все. Если есть вопросы - обращайтесь: всегда рад помочь.


Комментариев: 1

  Выйти

  * для публикации комментариев нужно  

Denis Panschinski:

спасибо за статью. Но как обстоят дела с созданием второй страницы в ПДФ документе, если слишком много текста, как определить, что место на перво странице закончилось?