itext
События страницы (iText 5) против обработчиков событий и Renderers (iText 7)
Поиск…
замечания
В iText 5 мы ввели концепцию событий страницы, чтобы разработчики могли добавлять определенное поведение при открытии документа, когда открывается новая страница, когда страница заканчивается и когда документ закрывается.
В документации мы ясно дали понять, что было запрещено добавлять контент в метод onStartPage()
; содержимое может быть добавлено только в метод onEndPage()
. Мы также ясно дали понять, что объект Document
переданный методам событий страницы, передавался только для чтения . Запрещено использовать document.add()
даже в onEndPage()
.
К сожалению, многие разработчики полностью игнорируют документацию, которая привела к таким проблемам, как:
- «System.StackOverflowException» в обработчике событий «OnEndPage»
- Получение исключения переполнения стека, генерирующего PDF-файл
- Разделитель строк iTextSharp исчезает с PdfPageEventHelper
- Документ об ошибке iTextSharp не имеет страницы, прежде чем открыть его
- ...
Я не помню, сколько раз я волновался, потому что еще один разработчик разместил дубликат этих вопросов. Люди часто задаются вопросом, почему они получают суровый ответ, но они не понимают, что минимальные усилия с их стороны спасли бы всех, включая самих себя, много времени. На все эти вопросы можно было ответить, сказав «Прочитайте руководство (вы знаете, какое)».
Другим вариантом был полный пересмотр iText, чтобы избежать подобных проблем.
Из-за органического роста iText класс событий страницы также был расширен функциональностью, не связанной с событиями страницы. Он содержал общую функциональность блока , он регистрировал начало и конец абзацев и т. Д.
Что мы зафиксировали в iText 7:
Мы удалили функциональность события страницы.
Для всех событий в отношении страниц мы теперь реализуем интерфейс IEventHandler
, и мы используем addEventHandler
для добавления этого обработчика в виде PdfDocumentEvent
к PdfDocument
. В этом примере мы используем событие END_PAGE
, но мы также могли использовать событие START_PAGE
. Не имеет значения, добавляете ли вы контент в начале или в конце. Подробнее об этом можно прочитать в разделе Обработка событий; настройки параметров просмотра и свойств записи, которые являются главой 7 в учебнике iText 7: Building Blocks .
Мы улучшили строительные блоки в том смысле, что сделали их более иерархичными (см. Перед тем, как мы начнем: Обзор классов и интерфейсов, которые представляют собой введение в учебник iText 7: Building Blocks ). Мы также представили набор классов Renderer, по одному для каждого строительного блока, и мы разрешаем разработчикам адаптировать эти средства визуализации так, чтобы строительный блок показывал другое поведение при визуализации. См., Например, пример рендеринга в разделе Добавление объектов AbstractElement (часть 1), который является главой 7 в учебнике iText 7: Building Blocks .
Эти изменения упрощают функциональность для разработчиков, которые (не хотят) много знать о PDF и iText, и в то же время предлагают изобилие возможностей для тех разработчиков, которые не боятся углубляться в код iText, чтобы создать PDF точно так, как они этого хотят.
Хотите узнать больше? Получите бесплатную электронную книгу!
Text2PdfPageEvents.java (iText 5)
Предположим, что у нас есть следующий текстовый файл: jekyll_hyde.txt
Как мы преобразуем его в PDF, который выглядит так:
Обратите внимание на синюю рамку, которая добавляется к заголовкам, и номер страницы внизу каждой страницы. В iText 5 эти элементы добавляются с помощью событий страницы:
class MyPageEvents extends PdfPageEventHelper {
protected float startpos = -1;
protected boolean title = true;
public void setTitle(boolean title) {
this.title = title;
}
@Override
public void onEndPage(PdfWriter writer, Document document) {
Rectangle pagesize = document.getPageSize();
ColumnText.showTextAligned(
writer.getDirectContent(),
Element.ALIGN_CENTER,
new Phrase(String.valueOf(writer.getPageNumber())),
(pagesize.getLeft() + pagesize.getRight()) / 2,
pagesize.getBottom() + 15,
0);
if (startpos != -1)
onParagraphEnd(writer, document,
pagesize.getBottom(document.bottomMargin()));
startpos = pagesize.getTop(document.topMargin());
}
@Override
public void onParagraph(PdfWriter writer, Document document,
float paragraphPosition) {
startpos = paragraphPosition;
}
@Override
public void onParagraphEnd(PdfWriter writer, Document document,
float paragraphPosition) {
if (!title) return;
PdfContentByte canvas = writer.getDirectContentUnder();
Rectangle pagesize = document.getPageSize();
canvas.saveState();
canvas.setColorStroke(BaseColor.BLUE);
canvas.rectangle(
pagesize.getLeft(document.leftMargin()),
paragraphPosition - 3,
pagesize.getWidth() - document.leftMargin() - document.rightMargin(),
startpos - paragraphPosition);
canvas.stroke();
canvas.restoreState();
}
}
Мы можем повторно использовать код для преобразования текстового файла в PDF из примера Text2Pdf.java (iText 5) и ввести событие страницы в PdfWriter
:
public void createPdf(String dest)
throws DocumentException, IOException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
MyPageEvents events = new MyPageEvents();
writer.setPageEvent(events);
document.open();
BufferedReader br = new BufferedReader(new FileReader(TEXT));
String line;
Paragraph p;
Font normal = new Font(FontFamily.TIMES_ROMAN, 12);
Font bold = new Font(FontFamily.TIMES_ROMAN, 12, Font.BOLD);
boolean title = true;
while ((line = br.readLine()) != null) {
p = new Paragraph(line, title ? bold : normal);
p.setAlignment(Element.ALIGN_JUSTIFIED);
events.setTitle(title);
document.add(p);
title = line.isEmpty();
}
document.close();
}
Источник: developers.itxtpdf.com
Text2PdfPageEvents1.java (iText 7)
Предположим, что у вас есть следующий текстовый файл: jekyll_hyde.txt
Как мы преобразуем его в PDF, который выглядит так:
Обратите внимание на номера страниц внизу каждой страницы. Они добавляются с помощью реализации IEventHandler
:
protected class Footer implements IEventHandler {
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(
page.getLastContentStream(), page.getResources(), pdf);
Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
float y = pageSize.getBottom() + 15;
canvas.showTextAligned(
String.valueOf(pdf.getPageNumber(page)),
x, y, TextAlignment.CENTER);
}
}
Мы можем повторно использовать пример Text2Pdf.java (iText 7) только с двумя незначительными изменениями:
public void createPdf(String dest) throws IOException {
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
pdf.addEventHandler(PdfDocumentEvent.END_PAGE, new Footer());
Document document = new Document(pdf)
.setTextAlignment(TextAlignment.JUSTIFIED);
BufferedReader br = new BufferedReader(new FileReader(TEXT));
String line;
PdfFont normal = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);
PdfFont bold = PdfFontFactory.createFont(FontConstants.TIMES_BOLD);
boolean title = true;
Border border = new SolidBorder(Color.BLUE, 1);
while ((line = br.readLine()) != null) {
document.add(new Paragraph(line)
.setFont(title ? bold : normal)
.setBorder(title ? border : Border.NO_BORDER));
title = line.isEmpty();
}
document.close();
}
Мы добавляем обработчик событий, который будет запускать метод handleEvent()
класса Footer
каждый раз, когда страница заканчивается. Мы также определяем границу для объектов Paragraph
, которые используются для названия.
Источник: developers.itxtpdf.com и учебник iText 7: Building Blocks .
Text2PdfPageEvents2.java
Предположим, что у вас есть следующий текстовый файл: jekyll_hyde.txt
Как мы преобразуем его в PDF, который выглядит так:
Обратите внимание, что это очень похоже на то, что мы имели раньше, но граница заголовков теперь имеет закругленные углы. Для этого мы создали пользовательский ParagraphRenderer
, и мы создали объект TitleParagraph
который использует этот рендерер:
public class TitleParagraph extends Paragraph {
public TitleParagraph(String line) {
super(line);
try {
setFont(PdfFontFactory.createFont(FontConstants.TIMES_BOLD));
}
catch (IOException ioe) {
}
}
@Override
protected IRenderer makeNewRenderer() {
return new ParagraphRenderer(this) {
@Override
public void drawBorder(DrawContext drawContext) {
Rectangle occupiedAreaBBox = getOccupiedAreaBBox();
float[] margins = getMargins();
Rectangle rectangle = applyMargins(occupiedAreaBBox, margins, false);
PdfCanvas canvas = drawContext.getCanvas();
canvas.roundRectangle(rectangle.getX() - 1, rectangle.getY() - 1,
rectangle.getWidth() + 2, rectangle.getHeight() + 2, 5).stroke();
}
};
}
}
Наш код для преобразования текста в PDF очень прост. Нам больше не нужно выделять шрифт для заголовков, и нам больше не нужно определять границу:
public void createPdf(String dest) throws IOException {
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
pdf.addEventHandler(PdfDocumentEvent.END_PAGE, new Footer());
Document document = new Document(pdf)
.setTextAlignment(TextAlignment.JUSTIFIED);
BufferedReader br = new BufferedReader(new FileReader(TEXT));
String line;
PdfFont normal = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);
boolean title = true;
Border border = new SolidBorder(Color.BLUE, 1);
while ((line = br.readLine()) != null) {
if (title)
document.add(new TitleParagraph(line));
else
document.add(new Paragraph(line).setFont(normal));
title = line.isEmpty();
}
document.close();
}
Источник: developers.itxtpdf.com и учебник iText 7: Building Blocks .