itext
Eventi della pagina (iText 5) contro Gestori di eventi e Renderer (iText 7)
Ricerca…
Osservazioni
In iText 5, abbiamo introdotto il concetto di eventi della pagina per consentire agli sviluppatori di aggiungere comportamenti specifici all'apertura di un documento, all'apertura di una nuova pagina, alla chiusura di una pagina e alla chiusura di un documento.
Nella documentazione, abbiamo reso molto chiaro che era vietato aggiungere contenuto nel metodo onStartPage()
; il contenuto può essere aggiunto solo nel metodo onEndPage()
. Abbiamo anche chiarito che l'oggetto Document
passato ai metodi evento della pagina è stato passato solo a scopo di sola lettura . Era proibito usare document.add()
anche nel metodo onEndPage()
.
Sfortunatamente, molti sviluppatori ignorano completamente la documentazione, il che ha portato a problemi come:
- "'System.StackOverflowException" nel gestore di eventi "OnEndPage"
- Ottenere un overflow dello stack Eccezione generando un PDF
- Il separatore di riga iTextSharp scompare con PdfPageEventHelper
- Il documento di errore iTextSharp non ha una pagina prima di aprirlo
- ...
Non ricordo quante volte sono stato agitato perché un altro sviluppatore ha pubblicato un duplicato di queste domande. Le persone spesso si chiedono perché ottengono una risposta dura, ma non si rendono conto che un minimo sforzo da parte loro avrebbe salvato tutti, compresi se stessi, un sacco di tempo. A tutte queste domande avrebbe potuto rispondere dicendo "Leggi il manuale (tu sai che)".
Un'altra opzione era una revisione completa di iText in modo che questo tipo di problemi possa essere evitato.
A causa della crescita organica di iText, la classe di eventi della pagina è stata estesa anche con funzionalità non correlate agli eventi della pagina. Conteneva funzionalità generiche di blocchi , registrava l'inizio e la fine dei paragrafi e così via.
Cosa abbiamo risolto in iText 7:
Abbiamo rimosso la funzionalità dell'evento della pagina.
Per tutti gli eventi relativi alle pagine, implementiamo ora l'interfaccia IEventHandler
e usiamo addEventHandler
per aggiungere questo gestore come PdfDocumentEvent
a PdfDocument
. Nell'esempio, utilizziamo un evento END_PAGE
, ma potremmo anche aver utilizzato un evento START_PAGE
. Non importa più se aggiungi contenuti all'inizio o alla fine. Puoi leggere ulteriori informazioni su questo in Eventi di gestione; impostare le preferenze del visualizzatore e le proprietà del writer, che è il capitolo 7 del tutorial di iText 7: Building Blocks .
Abbiamo migliorato gli elementi costitutivi nel senso che li abbiamo resi più gerarchici (vedi Prima di iniziare: Panoramica delle classi e delle interfacce che è l'introduzione del tutorial di iText 7: Building Blocks ). Abbiamo anche introdotto una serie di classi Renderer, una per ogni blocco predefinito, e permettiamo agli sviluppatori di adattare questi renderer in modo che un blocco predefinito mostri un comportamento diverso durante il rendering. Si veda ad esempio l'esempio del renderer in Aggiunta di oggetti AbstractElement (parte 1), che è il capitolo 7 dell'esercitazione di iText 7: Building Blocks .
Queste modifiche semplificano la funzionalità per gli sviluppatori che non vogliono sapere molto su PDF e iText, offrendo allo stesso tempo un'abbondanza di flessibilità a quegli sviluppatori che non hanno paura di scavare in profondità nel codice iText per creare un PDF esattamente come lo vogliono.
Voglio sapere di più? Ottieni l'ebook gratuito!
Text2PdfPageEvents.java (iText 5)
Supponiamo di avere il seguente file di testo: jekyll_hyde.txt
Come lo convertiamo in un PDF che assomiglia a questo:
Nota il bordo blu che viene aggiunto ai titoli e il numero di pagina nella parte inferiore di ogni pagina. In iText 5, questi elementi vengono aggiunti utilizzando gli eventi della pagina:
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();
}
}
Possiamo riutilizzare il codice per convertire un file di testo in un PDF dall'esempio Text2Pdf.java (iText 5) e introdurre l'evento della pagina in 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();
}
Fonte: developers.itextpdf.com
Text2PdfPageEvents1.java (iText 7)
Supponiamo di avere il seguente file di testo: jekyll_hyde.txt
Come lo convertiamo in un PDF che assomiglia a questo:
Nota i numeri di pagina nella parte inferiore di ogni pagina. Questi vengono aggiunti utilizzando un'implementazione di 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);
}
}
Possiamo riutilizzare l'esempio di Text2Pdf.java (iText 7) con solo due modifiche minori:
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();
}
Aggiungiamo un gestore di eventi che attiverà il metodo handleEvent()
della classe Footer
ogni volta che termina una pagina. Definiamo anche un bordo per gli oggetti Paragraph
che sono usati per un titolo.
Fonte: developers.itextpdf.com e il tutorial di iText 7: Building Blocks .
Text2PdfPageEvents2.java
Supponiamo di avere il seguente file di testo: jekyll_hyde.txt
Come lo convertiamo in un PDF che assomiglia a questo:
Nota che questo è molto simile a quello che avevamo prima, ma il bordo dei titoli ora ha gli angoli arrotondati. Abbiamo creato un ParagraphRenderer
personalizzato per raggiungere questo obiettivo e abbiamo creato un oggetto TitleParagraph
che utilizza quel renderer:
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();
}
};
}
}
Il nostro codice per convertire il testo in PDF è molto semplice ora. Non è più necessario impostare il carattere in grassetto per i titoli e non è più necessario definire un bordo:
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();
}
Fonte: developers.itextpdf.com e il tutorial di iText 7: Building Blocks .