itext
Zdarzenia strony (iText 5) a procedury obsługi zdarzeń i rendererzy (iText 7)
Szukaj…
Uwagi
W iText 5 wprowadziliśmy koncepcję zdarzeń na stronie, aby umożliwić programistom dodanie określonych zachowań podczas otwierania dokumentu, otwierania nowej strony, kończenia strony i zamykania dokumentu.
W dokumentacji wyraźnie stwierdziliśmy, że zabronione jest dodawanie treści w onStartPage()
; zawartość można dodać tylko w onEndPage()
. Wyjaśniliśmy również bardzo wyraźnie, że obiekt Document
przekazany do metod zdarzeń strony został przekazany wyłącznie do odczytu . Zabronione było używanie document.add()
nawet w onEndPage()
.
Niestety wielu programistów całkowicie ignoruje dokumentację, co doprowadziło do problemów takich jak:
- „” System.StackOverflowException ”w procedurze obsługi zdarzeń„ OnEndPage ”
- Uzyskiwanie wyjątku przepełnienia stosu generującego plik PDF
- Separator linii iTextSharp znika wraz z PdfPageEventHelper
- Dokument błędu iTextSharp nie ma strony przed jej otwarciem
- ...
Nie pamiętam, ile razy byłem wzburzony, ponieważ inny programista opublikował duplikat tych pytań. Ludzie często zastanawiają się, dlaczego otrzymują trudną odpowiedź, ale nie zdają sobie sprawy, że minimum wysiłku z ich strony zaoszczędziłoby wszystkim, w tym sobie, mnóstwo czasu. Na wszystkie te pytania można było odpowiedzieć, mówiąc „Przeczytaj (wiesz, który) podręcznik”.
Inną opcją był całkowity przegląd iText, aby uniknąć tego rodzaju problemów.
Ze względu na organiczny rozwój iText klasa zdarzeń stron została również rozszerzona o funkcjonalność niezwiązaną ze zdarzeniami strony. Zawierał ogólną funkcjonalność fragmentu , rejestrował początek i koniec akapitów i tak dalej.
Co naprawiliśmy w iText 7:
Usunęliśmy funkcję zdarzenia strony.
W przypadku wszystkich zdarzeń dotyczących stron wdrażamy teraz interfejs IEventHandler
i używamy addEventHandler
aby dodać ten PdfDocumentEvent
obsługi jako PdfDocumentEvent
do PdfDocument
. W tym przykładzie używamy zdarzenia END_PAGE
, ale moglibyśmy również użyć zdarzenia START_PAGE
. Nie ma już znaczenia, czy dodasz treść na początku, czy na końcu. Więcej informacji na ten temat można znaleźć w części Obsługa wydarzeń; ustawianie preferencji przeglądarki i właściwości programu piszącego, który znajduje się w rozdziale 7 samouczka iText 7: Building Blocks .
Ulepszyliśmy bloki konstrukcyjne w tym sensie, że uczyniliśmy je bardziej hierarchicznymi (zobacz Zanim zaczniemy: Przegląd klas i interfejsów, który jest wprowadzeniem do samouczka iText 7: Bloki konstrukcyjne ). Wprowadziliśmy również zestaw klas Renderer, po jednej dla każdego bloku konstrukcyjnego, i pozwalamy programistom dostosować te renderery, aby podczas renderowania blok konstrukcyjny wykazywał inne zachowanie. Zobacz na przykład przykład mechanizmu renderującego w części Dodawanie obiektów AbstractElement (część 1), który jest rozdziałem 7 w samouczku iText 7: Building Blocks .
Zmiany te upraszczają funkcjonalność dla programistów, którzy nie chcą (dużo) wiedzą o plikach PDF i iText, a jednocześnie oferują dużą elastyczność dla programistów, którzy nie boją się zagłębić się w kod iText, aby utworzyć PDF dokładnie tak, jak tego chcą.
Chcieć wiedzieć więcej? Zdobądź bezpłatny ebook!
Text2PdfPageEvents.java (iText 5)
Załóżmy, że mamy następujący plik tekstowy: jekyll_hyde.txt
Jak przekonwertować go na plik PDF, który wygląda następująco:
Zwróć uwagę na niebieską ramkę dodaną do tytułów i numer strony na dole każdej strony. W iText 5 elementy te są dodawane przy użyciu zdarzeń strony:
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();
}
}
Możemy ponownie użyć kodu do konwersji pliku tekstowego na plik PDF z przykładu Text2Pdf.java (iText 5) i wprowadzić zdarzenie strony do 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();
}
Źródło: developers.itextpdf.com
Text2PdfPageEvents1.java (iText 7)
Załóżmy, że masz następujący plik tekstowy: jekyll_hyde.txt
Jak przekonwertować go na plik PDF, który wygląda następująco:
Zanotuj numery stron u dołu każdej strony. Są one dodawane przy użyciu implementacji 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);
}
}
Możemy ponownie użyć przykładu Text2Pdf.java (iText 7) z tylko dwiema drobnymi zmianami:
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();
}
Dodajemy handleEvent()
obsługi zdarzeń, który będzie uruchamiał handleEvent()
klasy Footer
każdym razem, gdy strona się kończy. Definiujemy również granicę dla obiektów Paragraph
które są używane dla tytułu.
Źródło: developers.itextpdf.com i samouczek iText 7: Building Blocks .
Text2PdfPageEvents2.java
Załóżmy, że masz następujący plik tekstowy: jekyll_hyde.txt
Jak przekonwertować go na plik PDF, który wygląda następująco:
Zauważ, że jest to bardzo podobne do tego, co mieliśmy wcześniej, ale granica tytułów ma teraz zaokrąglone rogi. Stworzyliśmy zwyczaj ParagraphRenderer
aby to osiągnąć, a my stworzyliśmy TitleParagraph
obiekt, który wykorzystuje mechanizm renderujący, że:
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();
}
};
}
}
Nasz kod do konwersji tekstu na PDF jest teraz bardzo prosty. Nie musimy już ustawiać pogrubionej czcionki dla tytułów i nie musimy już definiować ramki:
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();
}
Źródło: developers.itextpdf.com i samouczek iText 7: Building Blocks .