Chapter 5: Table, cell, and page events

This is a code example of iText PDF, discover more.

10th October 2015
admin-marketing

Switch code for this example

AlternatingBackground.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Date;
import java.sql.SQLException;
import java.util.List;

import com.lowagie.database.DatabaseConnection;
import com.lowagie.database.HsqldbConnection;
import com.lowagie.filmfestival.Movie;
import com.lowagie.filmfestival.PojoFactory;
import com.lowagie.filmfestival.Screening;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPTableEvent;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.BaseColor;

public class AlternatingBackground implements PdfPTableEvent {

	/** The resulting PDF file. */
    public static final String RESULT
        = "results/part1/chapter05/alternating.pdf";

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     * @throws    SQLException
     */
    public void createPdf(String filename) throws SQLException, DocumentException, IOException {
        // Create a database connection 
        DatabaseConnection connection = new HsqldbConnection("filmfestival");
        // step 1
        Document document = new Document(PageSize.A4.rotate());
        // step 2
        PdfWriter.getInstance(document, new FileOutputStream(filename));
        // step 3
        document.open();
        // step 4
        List days = PojoFactory.getDays(connection);
        PdfPTableEvent event = new AlternatingBackground();
        for (Date day : days) {
            PdfPTable table = getTable(connection, day);
            table.setTableEvent(event);
            document.add(table);
            document.newPage();
        }
        // step 5
        document.close();
        // Close the database connection
        connection.close();
    }
    
    /**
     * Creates a table with film festival screenings.
     * @param connection a database connection
     * @param day a film festival day
     * @return a table with screenings.
     * @throws SQLException
     * @throws DocumentException
     * @throws IOException
     */
    public PdfPTable getTable(DatabaseConnection connection, Date day)
        throws SQLException, DocumentException, IOException {
        PdfPTable table = new PdfPTable(new float[] { 2, 1, 2, 5, 1 });
        table.setWidthPercentage(100f);
        table.getDefaultCell().setPadding(3);
        table.getDefaultCell().setUseAscender(true);
        table.getDefaultCell().setUseDescender(true);
        table.getDefaultCell().setColspan(5);
        table.getDefaultCell().setBackgroundColor(BaseColor.RED);
        table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(day.toString());
        table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
        table.getDefaultCell().setColspan(1);
        table.getDefaultCell().setBackgroundColor(BaseColor.ORANGE);
        for (int i = 0; i < 2; i++) {
            table.addCell("Location");
            table.addCell("Time");
            table.addCell("Run Length");
            table.addCell("Title");
            table.addCell("Year");
        }
        table.getDefaultCell().setBackgroundColor(null);
        table.setHeaderRows(3);
        table.setFooterRows(1);
        List screenings = PojoFactory.getScreenings(connection, day);
        Movie movie;
        for (Screening screening : screenings) {
            movie = screening.getMovie();
            table.addCell(screening.getLocation());
            table.addCell(String.format("%1$tH:%1$tM", screening.getTime()));
            table.addCell(String.format("%d '", movie.getDuration()));
            table.addCell(movie.getMovieTitle());
            table.addCell(String.valueOf(movie.getYear()));
        }
        return table;
    }
    
    /**
     * Draws a background for every other row.
     * @see com.itextpdf.text.pdf.PdfPTableEvent#tableLayout(
     *      com.itextpdf.text.pdf.PdfPTable, float[][], float[], int, int,
     *      com.itextpdf.text.pdf.PdfContentByte[])
     */
    public void tableLayout(PdfPTable table, float[][] widths, float[] heights,
        int headerRows, int rowStart, PdfContentByte[] canvases) {
        int columns;
        Rectangle rect;
        int footer = widths.length - table.getFooterRows();
        int header = table.getHeaderRows() - table.getFooterRows() + 1;
        for (int row = header; row < footer; row += 2) {
            columns = widths[row].length - 1;
            rect = new Rectangle(widths[row][0], heights[row],
                        widths[row][columns], heights[row + 1]);
            rect.setBackgroundColor(BaseColor.YELLOW);
            rect.setBorder(Rectangle.NO_BORDER);
            canvases[PdfPTable.BASECANVAS].rectangle(rect);
        }
    }

    /**
     * Main method.
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException 
     * @throws SQLException
     */
    public static void main(String[] args) throws SQLException, DocumentException, IOException {
        new AlternatingBackground().createPdf(RESULT);
    }
}
RunLengthEvent.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Date;
import java.sql.SQLException;
import java.util.List;

import com.lowagie.database.DatabaseConnection;
import com.lowagie.database.HsqldbConnection;
import com.lowagie.filmfestival.Movie;
import com.lowagie.filmfestival.PojoFactory;
import com.lowagie.filmfestival.Screening;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPCellEvent;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.BaseColor;

public class RunLengthEvent {

    /** The resulting PDF. */
    public static final String RESULT
        = "results/part1/chapter05/run_length.pdf";

    /** Inner class to draw a bar inside a cell. */
    class RunLength implements PdfPCellEvent {
        
        public int duration;
        
        public RunLength(int duration) {
            this.duration = duration;
        }
        
        /**
         * @see com.lowagie.text.pdf.PdfPCellEvent#cellLayout(
         *      com.lowagie.text.pdf.PdfPCell, com.lowagie.text.Rectangle,
         *      com.lowagie.text.pdf.PdfContentByte[])
         */
        public void cellLayout(PdfPCell cell, Rectangle rect,
                PdfContentByte[] canvas) {
            PdfContentByte cb = canvas[PdfPTable.BACKGROUNDCANVAS];
            cb.saveState();
            if (duration < 90) {
                cb.setRGBColorFill(0x7C, 0xFC, 0x00);
            }
            else if (duration > 120) {
                cb.setRGBColorFill(0x8B, 0x00, 0x00);
            } 
            else {
                cb.setRGBColorFill(0xFF, 0xA5, 0x00);
            }
            cb.rectangle(rect.getLeft(), rect.getBottom(),
                rect.getWidth() * duration / 240, rect.getHeight());
            cb.fill();
            cb.restoreState();
        }
    }

    /** Inner class to add the words PRESS PREVIEW to a cell. */
    class PressPreview implements PdfPCellEvent {
        
        public BaseFont bf;
        
        public PressPreview() throws DocumentException, IOException {
            bf = BaseFont.createFont();
        }
        
        /**
         * @see com.lowagie.text.pdf.PdfPCellEvent#cellLayout(com.lowagie.text.pdf.PdfPCell,
         *      com.lowagie.text.Rectangle,
         *      com.lowagie.text.pdf.PdfContentByte[])
         */
        public void cellLayout(PdfPCell cell, Rectangle rect,
                PdfContentByte[] canvas) {
            PdfContentByte cb = canvas[PdfPTable.TEXTCANVAS];
            cb.beginText();
            cb.setFontAndSize(bf, 12);
            cb.showTextAligned(Element.ALIGN_RIGHT, "PRESS PREVIEW",
                rect.getRight() - 3, rect.getBottom() + 4.5f, 0);
            cb.endText();
        }
    }
    
    /** The press cell event. */
    public PdfPCellEvent press;

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     * @throws    SQLException
     */
    public void createPdf(String filename)
        throws SQLException, DocumentException, IOException {
        press = new PressPreview();
        // Create a database connection
        DatabaseConnection connection = new HsqldbConnection("filmfestival");
        // step 1
        Document document = new Document(PageSize.A4.rotate());
        // step 2
        PdfWriter.getInstance(document, new FileOutputStream(filename));
        // step 3
        document.open();
        // step 4
        List days = PojoFactory.getDays(connection);
        for (Date day : days) {
            document.add(getTable(connection, day));
            document.newPage();
        }
        // step 5
        document.close();
        // Close the database connection
        connection.close();
    }
    
    /**
     * @param connection
     * @param day
     * @return
     * @throws SQLException
     * @throws DocumentException
     * @throws IOException
     */
    public PdfPTable getTable(DatabaseConnection connection, Date day)
        throws SQLException, DocumentException, IOException {
        PdfPTable table = new PdfPTable(new float[] { 2, 1, 2, 5, 1 });
        table.setWidthPercentage(100f);
        table.getDefaultCell().setPadding(3);
        table.getDefaultCell().setUseAscender(true);
        table.getDefaultCell().setUseDescender(true);
        table.getDefaultCell().setColspan(5);
        table.getDefaultCell().setBackgroundColor(BaseColor.RED);
        table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(day.toString());
        table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
        table.getDefaultCell().setColspan(1);
        table.getDefaultCell().setBackgroundColor(BaseColor.YELLOW);
        for (int i = 0; i < 2; i++) {
            table.addCell("Location");
            table.addCell("Time");
            table.addCell("Run Length");
            table.addCell("Title");
            table.addCell("Year");
        }
        table.getDefaultCell().setBackgroundColor(null);
        table.setHeaderRows(3);
        table.setFooterRows(1);
        List screenings = PojoFactory.getScreenings(connection, day);
        Movie movie;
        PdfPCell runLength;
        for (Screening screening : screenings) {
            movie = screening.getMovie();
            table.addCell(screening.getLocation());
            table.addCell(String.format("%1$tH:%1$tM", screening.getTime()));
            runLength = new PdfPCell(table.getDefaultCell());
            runLength.setPhrase(new Phrase(String.format("%d '", movie.getDuration())));
            runLength.setCellEvent(new RunLength(movie.getDuration()));
            if (screening.isPress()) {
                runLength.setCellEvent(press);
            }
            table.addCell(runLength);
            table.addCell(movie.getMovieTitle());
            table.addCell(String.valueOf(movie.getYear()));
        }
        return table;
    }

    /**
     * Main method.
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException 
     * @throws SQLException
     */
    public static void main(String[] args) throws SQLException, DocumentException, IOException {
        new RunLengthEvent().createPdf(RESULT);
    }
}
PressPreviews.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;

import com.lowagie.database.DatabaseConnection;
import com.lowagie.database.HsqldbConnection;
import com.lowagie.filmfestival.Movie;
import com.lowagie.filmfestival.PojoFactory;
import com.lowagie.filmfestival.Screening;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPCellEvent;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPTableEvent;
import com.itextpdf.text.pdf.PdfWriter;

public class PressPreviews implements PdfPCellEvent, PdfPTableEvent {

    /** The resulting PDF. */
    public static final String RESULT = "results/part1/chapter05/press_previews.pdf";

    /**
     * @see com.itextpdf.text.pdf.PdfPTableEvent#tableLayout(com.itextpdf.text.pdf.PdfPTable,
     *      float[][], float[], int, int, com.itextpdf.text.pdf.PdfContentByte[])
     */
    public void tableLayout(PdfPTable table, float[][] width, float[] height,
            int headerRows, int rowStart, PdfContentByte[] canvas) {
        float widths[] = width[0];
        float x1 = widths[0];
        float x2 = widths[widths.length - 1];
        float y1 = height[0];
        float y2 = height[height.length - 1];
        PdfContentByte cb = canvas[PdfPTable.LINECANVAS];
        cb.rectangle(x1, y1, x2 - x1, y2 - y1);
        cb.stroke();
        cb.resetRGBColorStroke();
    }

    /**
     * @see com.lowagie.text.pdf.PdfPCellEvent#cellLayout(com.lowagie.text.pdf.PdfPCell,
     *      com.lowagie.text.Rectangle, com.lowagie.text.pdf.PdfContentByte[])
     */
    public void cellLayout(PdfPCell cell, Rectangle position,
            PdfContentByte[] canvases) {
        float x1 = position.getLeft() + 2;
        float x2 = position.getRight() - 2;
        float y1 = position.getTop() - 2;
        float y2 = position.getBottom() + 2;
        PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
        canvas.rectangle(x1, y1, x2 - x1, y2 - y1);
        canvas.stroke();
    }

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     * @throws    SQLException
     */
    public void createPdf(String filename)
        throws SQLException, DocumentException, IOException {
    	// Create a database connection
        DatabaseConnection connection = new HsqldbConnection("filmfestival");
        // step 1
        Document document = new Document(PageSize.A4.rotate());
        // step 2
        PdfWriter.getInstance(document, new FileOutputStream(filename));
        // step 3
        document.open();
        // step 4
        document.add(getTable(connection));
        // step 5
        document.close();
        // close the database connection
        connection.close();
    }
    
    /**
     * Creates a table that mimics cellspacing using table and cell events.
     * @param connection
     * @return a table
     * @throws SQLException
     * @throws DocumentException
     * @throws IOException
     */
    public PdfPTable getTable(DatabaseConnection connection)
        throws SQLException, DocumentException, IOException {
        PdfPTable table = new PdfPTable(new float[] { 1, 2, 2, 5, 1 });
        table.setTableEvent(new PressPreviews());
        table.setWidthPercentage(100f);
        table.getDefaultCell().setPadding(5);
        table.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
        table.getDefaultCell().setCellEvent(new PressPreviews());
        for (int i = 0; i < 2; i++) {
            table.addCell("Location");
            table.addCell("Date/Time");
            table.addCell("Run Length");
            table.addCell("Title");
            table.addCell("Year");
        }
        table.getDefaultCell().setBackgroundColor(null);
        table.setHeaderRows(2);
        table.setFooterRows(1);
        List screenings = PojoFactory.getPressPreviews(connection);
        Movie movie;
        for (Screening screening : screenings) {
            movie = screening.getMovie();
            table.addCell(screening.getLocation());
            table.addCell(String.format("%s   %2$tH:%2$tM",
                screening.getDate().toString(), screening.getTime()));
            table.addCell(String.format("%d '", movie.getDuration()));
            table.addCell(movie.getMovieTitle());
            table.addCell(String.valueOf(movie.getYear()));
        }
        return table;
    }
    
    /**
     * Main method.
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException 
     * @throws SQLException
     */
    public static void main(String[] args) throws SQLException, DocumentException, IOException {
        new PressPreviews().createPdf(RESULT);
    }
}
PdfCalendar.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;

import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPCellEvent;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPTableEvent;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.draw.VerticalPositionMark;

public class PdfCalendar extends part1.chapter04.PdfCalendar {

    /** The resulting PDF. */
    public static final String RESULT = "results/part1/chapter05/calendar.pdf";

    /** A table event. */
    public PdfPTableEvent tableBackground;
    /** A cell event. */
    public PdfPCellEvent cellBackground;
    /** A cell event. */
    public PdfPCellEvent roundRectangle;
    /** A cell event. */
    public PdfPCellEvent whiteRectangle;
    
    /**
     * Inner class with a table event that draws a background with rounded corners.
     */
    class TableBackground implements PdfPTableEvent {
        
        public void tableLayout(PdfPTable table, float[][] width, float[] height,
                int headerRows, int rowStart, PdfContentByte[] canvas) {
            PdfContentByte background = canvas[PdfPTable.BASECANVAS];
            background.saveState();
            background.setCMYKColorFill(0x00, 0x00, 0xFF, 0x0F);
            background.roundRectangle(
                width[0][0], height[height.length - 1] - 2,
                width[0][1] - width[0][0] + 6, height[0] - height[height.length - 1] - 4, 4);
            background.fill();
            background.restoreState();
        }

    }
    
    /**
     * Inner class with a cell event that draws a background with rounded corners.
     */
    class CellBackground implements PdfPCellEvent {
        
        public void cellLayout(PdfPCell cell, Rectangle rect,
                PdfContentByte[] canvas) {
            PdfContentByte cb = canvas[PdfPTable.BACKGROUNDCANVAS];
            cb.roundRectangle(
                rect.getLeft() + 1.5f, rect.getBottom() + 1.5f, rect.getWidth() - 3,
                rect.getHeight() - 3, 4);
            cb.setCMYKColorFill(0x00, 0x00, 0x00, 0x00);
            cb.fill();
        }
    }

    /**
     * Inner class with a cell event that draws a border with rounded corners.
     */
    class RoundRectangle implements PdfPCellEvent {
        /** the border color described as CMYK values. */
        protected int[] color;
        /** Constructs the event using a certain color. */
        public RoundRectangle(int[] color) {
            this.color = color;
        }
        
        public void cellLayout(PdfPCell cell, Rectangle rect,
                PdfContentByte[] canvas) {
            PdfContentByte cb = canvas[PdfPTable.LINECANVAS];
            cb.roundRectangle(
                rect.getLeft() + 1.5f, rect.getBottom() + 1.5f, rect.getWidth() - 3,
                rect.getHeight() - 3, 4);
            cb.setLineWidth(1.5f);
            cb.setCMYKColorStrokeF(color[0], color[1], color[2], color[3]);
            cb.stroke();
        }
    }
    
    /**
     * Main method creating the PDF.
     * @param    args    no arguments needed
     * @throws IOException 
     * @throws DocumentException 
     */
    public static void main(String[] args) throws IOException, DocumentException {
        Locale locale = new Locale(LANGUAGE);
        new PdfCalendar().createPdf(RESULT, locale, YEAR);
    }

    /**
     * Initializes the fonts and collections.
     * @throws DocumentException
     * @throws IOException
     */
    public PdfCalendar() throws DocumentException, IOException {
        super();
        tableBackground = new TableBackground();
        cellBackground = new CellBackground();
        roundRectangle = new RoundRectangle(new int[]{ 0xFF, 0x00, 0xFF, 0x00 });
        whiteRectangle = new RoundRectangle(new int[]{ 0x00, 0x00, 0x00, 0x00 });
    }

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @param locale Locale in case you want to create
     * a Calendar in another language
     * @param year the year for which you want to make a calendar
     * @throws    DocumentException 
     * @throws    IOException
     */
    public void createPdf(String filename, Locale locale, int year) throws IOException, DocumentException {
        // step 1
        Document document = new Document(PageSize.A4.rotate());
        // step 2
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
        // step 3
        document.open();
        // step 4
        PdfPTable table;
        Calendar calendar;
        PdfContentByte canvas = writer.getDirectContent();
        // Loop over the months
        for (int month = 0; month < 12; month++) {
            calendar = new GregorianCalendar(year, month, 1);
            // draw the background
            drawImageAndText(canvas, calendar);
            // create a table with 7 columns
            table = new PdfPTable(7);
            table.setTableEvent(tableBackground);
            table.setTotalWidth(504);
            // add the name of the month
            table.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
            table.getDefaultCell().setCellEvent(whiteRectangle);
            table.addCell(getMonthCell(calendar, locale));
            int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
            int day = 1;
            int position = 2;
            // add empty cells
            while (position != calendar.get(Calendar.DAY_OF_WEEK)) {
                position = (position % 7) + 1;
                table.addCell("");
            }
            // add cells for each day
            while (day <= daysInMonth) {
                calendar = new GregorianCalendar(year, month, day++);
                table.addCell(getDayCell(calendar, locale));
            }
            // complete the table
            table.completeRow();
            // write the table to an absolute position
            table.writeSelectedRows(0, -1, 169, table.getTotalHeight() + 20, canvas);
            document.newPage();
        }
        // step 5
        document.close();
    }
    
    /**
     * Creates a PdfPCell with the name of the month
     * @param calendar a date
     * @param locale a locale
     * @return a PdfPCell with rowspan 7, containing the name of the month
     */
    public PdfPCell getMonthCell(Calendar calendar, Locale locale) {
        PdfPCell cell = new PdfPCell();
        cell.setColspan(7);
        cell.setBorder(PdfPCell.NO_BORDER);
        cell.setUseDescender(true);
        Paragraph p = new Paragraph(String.format(locale, "%1$tB %1$tY", calendar), bold);
        p.setAlignment(Element.ALIGN_CENTER);
        cell.addElement(p);
        return cell;
    }

    /**
     * Creates a PdfPCell for a specific day
     * @param calendar a date
     * @param locale a locale
     * @return a PdfPCell
     */
    public PdfPCell getDayCell(Calendar calendar, Locale locale) {
        PdfPCell cell = new PdfPCell();
        // set the event to draw the background
        cell.setCellEvent(cellBackground);
        // set the event to draw a special border
        if (isSunday(calendar) || isSpecialDay(calendar))
            cell.setCellEvent(roundRectangle);
        cell.setPadding(3);
        cell.setBorder(PdfPCell.NO_BORDER);
        // set the content in the language of the locale
        Chunk chunk = new Chunk(String.format(locale, "%1$ta", calendar), small);
        chunk.setTextRise(8);
        // a paragraph with the day
        Paragraph p = new Paragraph(chunk);
        // a separator
        p.add(new Chunk(new VerticalPositionMark()));
        // and the number of the day
        p.add(new Chunk(String.format(locale, "%1$te", calendar), normal));
        cell.addElement(p);
        return cell;
    }
    
    /**
     * Returns true if the date was found in a list with special days (holidays).
     * @param calendar a date
     * @return true for holidays
     */
    public boolean isSpecialDay(Calendar calendar) {
        if (specialDays.containsKey(String.format("%1$tm%1$td", calendar)))
            return true;
        return false;
    }
}
MovieYears.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import com.lowagie.database.DatabaseConnection;
import com.lowagie.database.HsqldbConnection;
import com.lowagie.filmfestival.Movie;
import com.lowagie.filmfestival.MovieComparator;
import com.lowagie.filmfestival.PojoFactory;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.Font.FontFamily;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.BaseColor;

public class MovieYears {
    
    /** The resulting PDF file. */
    public static final String RESULT = "results/part1/chapter05/movie_years.pdf";

    /**
     * Inner class to add functionality to a Chunk in a generic way.
     */
    class GenericTags extends PdfPageEventHelper {
         
        public void onGenericTag(PdfWriter writer, Document pdfDocument,
                Rectangle rect, String text) {
            if ("strip".equals(text))
                strip(writer.getDirectContent(), rect);
            else if ("ellipse".equals(text))
                ellipse(writer.getDirectContentUnder(), rect);
            else
                countYear(text);
        }

        public void strip(PdfContentByte content, Rectangle rect) {
            content.rectangle(rect.getLeft() - 1, rect.getBottom() - 5f,
                    rect.getWidth(), rect.getHeight() + 8);
            content.rectangle(rect.getLeft(), rect.getBottom() - 2,
                    rect.getWidth() - 2, rect.getHeight() + 2);
            float y1 = rect.getTop() + 0.5f;
            float y2 = rect.getBottom() - 4;
            for (float f = rect.getLeft(); f < rect.getRight() - 4; f += 5) {
                content.rectangle(f, y1, 4f, 1.5f);
                content.rectangle(f, y2, 4f, 1.5f);
            }
            content.eoFill();
        }
        
        public void ellipse(PdfContentByte content, Rectangle rect) {
            content.saveState();
            content.setRGBColorFill(0x00, 0x00, 0xFF);
            content.ellipse(rect.getLeft() - 3f, rect.getBottom() - 5f,
                    rect.getRight() + 3f, rect.getTop() + 3f);
            content.fill();
            content.restoreState();
        }

        TreeMap years = new TreeMap();
        
        public void countYear(String text) {
            Integer count = years.get(text);
            if (count == null) {
                years.put(text, 1);
            }
            else {
                years.put(text, count + 1);
            }
        }
    }
    
    /**
     * Inner class to add lines when a paragraph begins and ends.
     */
    class ParagraphPositions extends PdfPageEventHelper {
        public void onParagraph(PdfWriter writer, Document pdfDocument,
                float paragraphPosition) {
            drawLine(writer.getDirectContent(),
                    pdfDocument.left(), pdfDocument.right(), paragraphPosition - 8);
        }

        public void onParagraphEnd(PdfWriter writer, Document pdfDocument,
                float paragraphPosition) {
            drawLine(writer.getDirectContent(),
                    pdfDocument.left(), pdfDocument.right(), paragraphPosition - 5);
        }
        
        public void drawLine(PdfContentByte cb, float x1, float x2, float y) {
            cb.moveTo(x1, y);
            cb.lineTo(x2, y);
            cb.stroke();
        }
    }

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     */
    public void createPdf(String filename) throws IOException, DocumentException, SQLException {
    	// Create a database connection
        DatabaseConnection connection = new HsqldbConnection("filmfestival");
        // step 1
        Document document = new Document();
        // step 2
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
        GenericTags event = new GenericTags();
        writer.setPageEvent(event);
        writer.setPageEvent(new ParagraphPositions());
        // step 3
        document.open();
        // step 4
        Font bold = new Font(FontFamily.HELVETICA, 11, Font.BOLD);
        Font italic = new Font(FontFamily.HELVETICA, 11, Font.ITALIC);
        Font white = new Font(FontFamily.HELVETICA, 12, Font.BOLD | Font.ITALIC, BaseColor.WHITE);
        Paragraph p;
        Chunk c;
        
        Set movies = 
            new TreeSet(new MovieComparator(MovieComparator.BY_YEAR));
        movies.addAll(PojoFactory.getMovies(connection));
        for (Movie movie : movies) {
            p = new Paragraph(22);
            c = new Chunk(String.format("%d ", movie.getYear()), bold);
            c.setGenericTag("strip");
            p.add(c);
            c = new Chunk(movie.getMovieTitle());
            c.setGenericTag(String.valueOf(movie.getYear()));
            p.add(c);
            c = new Chunk(String.format(" (%d minutes)  ", movie.getDuration()), italic);
            p.add(c);
            c = new Chunk("IMDB", white);
            c.setAnchor("http://www.imdb.com/title/tt" + movie.getImdb());
            c.setGenericTag("ellipse");
            p.add(c);
            document.add(p);
        }
        document.newPage();
        writer.setPageEvent(null);
        for (Map.Entry entry : event.years.entrySet()) {
            p = new Paragraph(String.format("%s: %d movie(s)", entry.getKey(), entry.getValue()));
            document.add(p);
        }
        // step 5
        document.close();
        // Close the database connection
        connection.close();
    }
    
    /**
     * Main method.
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException 
     * @throws SQLException
     */
    public static void main(String[] args)
        throws IOException, DocumentException, SQLException {
        new MovieYears().createPdf(RESULT);
    }
}
MovieHistory1.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import com.lowagie.database.DatabaseConnection;
import com.lowagie.database.HsqldbConnection;
import com.lowagie.filmfestival.Movie;
import com.lowagie.filmfestival.MovieComparator;
import com.lowagie.filmfestival.PojoFactory;
import com.lowagie.filmfestival.PojoToElementFactory;
import com.itextpdf.text.Chapter;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Section;
import com.itextpdf.text.Font.FontFamily;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;

public class MovieHistory1 {
    
    /** The resulting PDF file. */
    public static final String RESULT
        = "results/part1/chapter05/movie_history1.pdf";

    /**
     * Inner class to keep track of the TOC
     * and to draw lines under ever chapter and section.
     */
    class ChapterSectionTOC extends PdfPageEventHelper {
        /** List with the titles. */
        List titles = new ArrayList();
        
        public void onChapter(PdfWriter writer, Document document,
                float position, Paragraph title) {
            titles.add(new Paragraph(title.getContent(), FONT[4]));
        }

        public void onChapterEnd(PdfWriter writer, Document document,
                float position) {
            drawLine(writer.getDirectContent(),
                    document.left(), document.right(), position - 5);
        }

        public void onSection(PdfWriter writer, Document document,
                float position, int depth, Paragraph title) {
            title = new Paragraph(title.getContent(), FONT[4]);
            title.setIndentationLeft(18 * depth);
            titles.add(title);
        }

        public void onSectionEnd(PdfWriter writer, Document document,
                float position) {
            drawLine(writer.getDirectContent(),
                    document.left(), document.right(), position - 3);
        }
        
        public void drawLine(PdfContentByte cb, float x1, float x2, float y) {
            cb.moveTo(x1, y);
            cb.lineTo(x2, y);
            cb.stroke();
        }
    }
    
    /** The different epochs. */
    public static final String[] EPOCH =
        { "Forties", "Fifties", "Sixties", "Seventies", "Eighties",
    	  "Nineties", "Twenty-first Century" };
    /** The fonts for the title. */
    public static final Font[] FONT = new Font[5];
    static {
        FONT[0] = new Font(FontFamily.HELVETICA, 24);
        FONT[1] = new Font(FontFamily.HELVETICA, 18);
        FONT[2] = new Font(FontFamily.HELVETICA, 14);
        FONT[3] = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
        FONT[4] = new Font(FontFamily.HELVETICA, 10);
    }

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     * @throws    SQLException
     */
    public void createPdf(String filename)
        throws IOException, DocumentException, SQLException {
    	// Create a database connection
        DatabaseConnection connection = new HsqldbConnection("filmfestival");
        // step 1
        Document document = new Document();
        // step 2
        PdfWriter writer
            = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
        // IMPORTANT: set linear page mode!
        writer.setLinearPageMode();
        ChapterSectionTOC event = new ChapterSectionTOC();
        writer.setPageEvent(event);
        // step 3
        document.open();
        // step 4
        // add the chapters
        Set movies = 
            new TreeSet(new MovieComparator(MovieComparator.BY_YEAR));
        movies.addAll(PojoFactory.getMovies(connection));
        int epoch = -1;
        int currentYear = 0;
        Paragraph title = null;
        Chapter chapter = null;
        Section section = null;
        Section subsection = null;
        for (Movie movie : movies) {
            if (epoch < (movie.getYear() - 1940) / 10) {
                epoch = (movie.getYear() - 1940) / 10;
                if (chapter != null) {
                    document.add(chapter);
                }
                title = new Paragraph(EPOCH[epoch], FONT[0]);
                chapter = new Chapter(title, epoch + 1);
            }
            if (currentYear < movie.getYear()) {
                currentYear = movie.getYear();
                title = new Paragraph(
                    String.format("The year %d", movie.getYear()), FONT[1]);
                section = chapter.addSection(title);
                section.setBookmarkTitle(String.valueOf(movie.getYear()));
                section.setIndentation(30);
                section.setBookmarkOpen(false);
                section.setNumberStyle(Section.NUMBERSTYLE_DOTTED_WITHOUT_FINAL_DOT);
                section.add(new Paragraph(
                    String.format("Movies from the year %d:", movie.getYear())));
            }
            title = new Paragraph(movie.getMovieTitle(), FONT[2]);
            subsection = section.addSection(title);
            subsection.setIndentationLeft(20);
            subsection.setNumberDepth(1);
            subsection.add(new Paragraph("Duration: " + movie.getDuration(), FONT[3]));
            subsection.add(new Paragraph("Director(s):", FONT[3]));
            subsection.add(PojoToElementFactory.getDirectorList(movie));
            subsection.add(new Paragraph("Countries:", FONT[3]));
            subsection.add(PojoToElementFactory.getCountryList(movie));
        }
        document.add(chapter);
        // add the TOC starting on the next page
        document.newPage();
        int toc = writer.getPageNumber();
        for (Paragraph p : event.titles) {
            document.add(p);
        }
        // always go to a new page before reordering pages.
        document.newPage();
        // get the total number of pages that needs to be reordered
        int total = writer.reorderPages(null);
        // change the order
        int[] order = new int[total];
        for (int i = 0; i < total; i++) {
            order[i] = i + toc;
            if (order[i] > total)
                order[i] -= total;
        }
        // apply the new order
        writer.reorderPages(order);
        // step 5
        document.close();
        // Close the database connection
        connection.close();
    }
    
    /**
     * Main method.
     *
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException 
     * @throws SQLException
     */
    public static void main(String[] args)
        throws IOException, DocumentException, SQLException {
        new MovieHistory1().createPdf(RESULT);
    }
}
NewPage.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfWriter;

public class NewPage {
    
    /** Path to the resulting PDF file. */
    public static final String RESULT
        = "results/part1/chapter05/new_page.pdf";
    
    /**
     * Main method creating the PDF.
     * @param    args    no arguments needed
     * @throws IOException 
     * @throws DocumentException 
     */
    public static void main(String[] args) throws IOException, DocumentException {
    	// step 1
        Document document = new Document();
        // step 2
        PdfWriter writer
            = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
        // step 3
        document.open();
        // step 4
        document.add(new Paragraph("This page will NOT be followed by a blank page!"));
        document.newPage();
        // we don't add anything to this page: newPage() will be ignored
        document.newPage();
        document.add(new Paragraph("This page will be followed by a blank page!"));
        document.newPage();
        writer.setPageEmpty(false);
        document.newPage();
        document.add(new Paragraph("The previous page was a blank page!"));
        // step 5
        document.close();
        
    }
}
Hero1.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;

public class Hero1 {

    /** The resulting PDF file. */
    public static final String RESULT = "results/part1/chapter05/hero1.pdf";
    /** Path to the resources. */
    public static final String RESOURCE = "resources/txt/hero.txt";

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     */
    public void createPdf(String filename)
        throws IOException, DocumentException {
    	// step 1
        Rectangle rect = new Rectangle(-1192, -1685, 1192, 1685);
        Document document = new Document(rect);
        // step 2
        PdfWriter writer =
            PdfWriter.getInstance(document, new FileOutputStream(filename));
        // step 3
        document.open();
        // step 4
        PdfContentByte content = writer.getDirectContent();
        PdfTemplate template = createTemplate(content, rect, 4);
        content.addTemplate(template, -1192, -1685);
        content.moveTo(-595, 0);
        content.lineTo(595, 0);
        content.moveTo(0, -842);
        content.lineTo(0, 842);
        content.stroke();
        // step 5
        document.close();
    }
    
    /**
     * Creates a template based on a stream of PDF syntax.
     * @param content The direct content
     * @param rect The dimension of the templare
     * @param factor A magnification factor
     * @return A PdfTemplate
     * @throws IOException
     */
    public PdfTemplate createTemplate(PdfContentByte content, Rectangle rect, int factor)
        throws IOException {
        PdfTemplate template = content.createTemplate(rect.getWidth(), rect.getHeight());
        template.concatCTM(factor, 0, 0, factor, 0, 0);
        FileReader reader = new FileReader(RESOURCE);
        int c;
        while ((c = reader.read()) > -1) {
            template.setLiteral((char)c);
        }
        return template;
    }

    /**
     * Main method.
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException
     */
    public static void main(String[] args)
        throws IOException, DocumentException {
        new Hero1().createPdf(RESULT);
    }
}
Hero2.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;

public class Hero2 extends Hero1 {

    /** The resulting PDF file. */
    public static final String RESULT = "results/part1/chapter05/hero2.pdf";

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     */
    public void createPdf(String filename) throws IOException, DocumentException {
        float w = PageSize.A4.getWidth();
        float h = PageSize.A4.getHeight();
        Rectangle rect = new Rectangle(-2*w, -2*h, 2*w, 2*h);
        Rectangle crop = new Rectangle(-2 * w, h, -w, 2 * h);
        // step 1
        Document document = new Document(rect);
        // step 2
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
        writer.setCropBoxSize(crop);
        // step 3
        document.open();
        // step 4
        PdfContentByte content = writer.getDirectContent();
        PdfTemplate template = createTemplate(content, rect, 4);
        float adjust;
        while(true) {
            content.addTemplate(template, -2*w, -2*h);
            adjust = crop.getRight() + w;
            if (adjust > 2 * w) {
                adjust = crop.getBottom() - h;
                if (adjust < - 2 * h)
                    break;
                crop = new Rectangle(-2*w, adjust, -w, crop.getBottom());
            }
            else {
                crop = new Rectangle(crop.getRight(), crop.getBottom(), adjust, crop.getTop());
            }
            writer.setCropBoxSize(crop);
            document.newPage();
        }
        // step 5
        document.close();
    }

    /**
     * Main method.
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException
     */
    public static void main(String[] args)
        throws IOException, DocumentException {
        new Hero2().createPdf(RESULT);
    }
}
Hero3.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;

public class Hero3 extends Hero1 {

    /** The resulting PDF file. */
    public static final String RESULT = "results/part1/chapter05/hero3.pdf";

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     */
    public void createPdf(String filename) throws IOException, DocumentException {
    	// step 1
        Document document = new Document(PageSize.A4);
        // step 2
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
        Rectangle art = new Rectangle(50, 50, 545, 792);
        writer.setBoxSize("art", art);
        // step 3
        document.open();
        // step 4
        PdfContentByte content = writer.getDirectContent();
        PdfTemplate template = createTemplate(content, PageSize.A4, 1);
        content.addTemplate(template, 0, 0);
        // step 5
        document.close();
    }

    /**
     * Main method.
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException
     */
    public static void main(String[] args)
        throws IOException, DocumentException {
        new Hero3().createPdf(RESULT);
    }
}
MovieHistory2.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Set;
import java.util.TreeSet;

import com.lowagie.database.DatabaseConnection;
import com.lowagie.database.HsqldbConnection;
import com.lowagie.filmfestival.Movie;
import com.lowagie.filmfestival.MovieComparator;
import com.lowagie.filmfestival.PojoFactory;
import com.lowagie.filmfestival.PojoToElementFactory;
import com.itextpdf.text.Chapter;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.Section;
import com.itextpdf.text.Font.FontFamily;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;

public class MovieHistory2 {
    
    /** The resulting PDF file. */
    public static final String RESULT
        = "results/part1/chapter05/movie_history2.pdf";
    
    /** Inner class to add a header and a footer. */
    class HeaderFooter extends PdfPageEventHelper {
        /** Alternating phrase for the header. */
        Phrase[] header = new Phrase[2];
        /** Current page number (will be reset for every chapter). */
        int pagenumber;
        
        /**
         * Initialize one of the headers.
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onOpenDocument(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
         */
        public void onOpenDocument(PdfWriter writer, Document document) {
            header[0] = new Phrase("Movie history");
        }
        
        /**
         * Initialize one of the headers, based on the chapter title;
         * reset the page number.
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onChapter(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document, float,
         *      com.itextpdf.text.Paragraph)
         */
        public void onChapter(PdfWriter writer, Document document,
                float paragraphPosition, Paragraph title) {
            header[1] = new Phrase(title.getContent());
            pagenumber = 1;
        }

        /**
         * Increase the page number.
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onStartPage(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
         */
        public void onStartPage(PdfWriter writer, Document document) {
            pagenumber++;
        }
        
        /**
         * Adds the header and the footer.
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
         */
        public void onEndPage(PdfWriter writer, Document document) {
            Rectangle rect = writer.getBoxSize("art");
            switch(writer.getPageNumber() % 2) {
            case 0:
                ColumnText.showTextAligned(writer.getDirectContent(),
                        Element.ALIGN_RIGHT, header[0],
                        rect.getRight(), rect.getTop(), 0);
                break;
            case 1:
                ColumnText.showTextAligned(writer.getDirectContent(),
                        Element.ALIGN_LEFT, header[1],
                        rect.getLeft(), rect.getTop(), 0);
                break;
            }
            ColumnText.showTextAligned(writer.getDirectContent(),
                    Element.ALIGN_CENTER, new Phrase(String.format("page %d", pagenumber)),
                    (rect.getLeft() + rect.getRight()) / 2, rect.getBottom() - 18, 0);
        }
    }
    
    /** The different epochs. */
    public static final String[] EPOCH =
        { "Forties", "Fifties", "Sixties", "Seventies", "Eighties",
    	  "Nineties", "Twenty-first Century" };
    /** The fonts for the title. */
    public static final Font[] FONT = new Font[4];
    static {
        FONT[0] = new Font(FontFamily.HELVETICA, 24);
        FONT[1] = new Font(FontFamily.HELVETICA, 18);
        FONT[2] = new Font(FontFamily.HELVETICA, 14);
        FONT[3] = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
    }

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     * @throws    SQLException
     */
    public void createPdf(String filename)
        throws IOException, DocumentException, SQLException {
    	// Create a database connection
        DatabaseConnection connection = new HsqldbConnection("filmfestival");
        // step 1
        Document document = new Document(PageSize.A4, 36, 36, 54, 54);
        // step 2
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
        HeaderFooter event = new HeaderFooter();
        writer.setBoxSize("art", new Rectangle(36, 54, 559, 788));
        writer.setPageEvent(event);
        // step 3
        document.open();
        // step 4
        Set movies = 
            new TreeSet(new MovieComparator(MovieComparator.BY_YEAR));
        movies.addAll(PojoFactory.getMovies(connection));
        int epoch = -1;
        int currentYear = 0;
        Paragraph title = null;
        Chapter chapter = null;
        Section section = null;
        Section subsection = null;
        for (Movie movie : movies) {
            if (epoch < (movie.getYear() - 1940) / 10) {
                epoch = (movie.getYear() - 1940) / 10;
                if (chapter != null) {
                    document.add(chapter);
                }
                title = new Paragraph(EPOCH[epoch], FONT[0]);
                chapter = new Chapter(title, epoch + 1);
            }
            if (currentYear < movie.getYear()) {
                currentYear = movie.getYear();
                title = new Paragraph(String.format("The year %d", movie.getYear()), FONT[1]);
                section = chapter.addSection(title);
                section.setBookmarkTitle(String.valueOf(movie.getYear()));
                section.setIndentation(30);
                section.setBookmarkOpen(false);
                section.setNumberStyle(Section.NUMBERSTYLE_DOTTED_WITHOUT_FINAL_DOT);
                section.add(new Paragraph(String.format("Movies from the year %d:", movie.getYear())));
            }
            title = new Paragraph(movie.getMovieTitle(), FONT[2]);
            subsection = section.addSection(title);
            subsection.setIndentationLeft(20);
            subsection.setNumberDepth(1);
            subsection.add(new Paragraph("Duration: " + movie.getDuration(), FONT[3]));
            subsection.add(new Paragraph("Director(s):", FONT[3]));
            subsection.add(PojoToElementFactory.getDirectorList(movie));
            subsection.add(new Paragraph("Countries:", FONT[3]));
            subsection.add(PojoToElementFactory.getCountryList(movie));
        }
        document.add(chapter);
        // step 5
        document.close();
        // close the database connection
        connection.close();
    }
    
    /**
     * Main method.
     *
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException 
     * @throws SQLException
     */
    public static void main(String[] args)
        throws IOException, DocumentException, SQLException {
        new MovieHistory2().createPdf(RESULT);
    }
}
MovieCountries1.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Set;
import java.util.TreeSet;

import com.lowagie.database.DatabaseConnection;
import com.lowagie.database.HsqldbConnection;
import com.lowagie.filmfestival.FilmFonts;
import com.lowagie.filmfestival.Movie;
import com.lowagie.filmfestival.MovieComparator;
import com.lowagie.filmfestival.PojoFactory;
import com.lowagie.filmfestival.PojoToElementFactory;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.ExceptionConverter;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;

public class MovieCountries1 {
    
    /** The resulting PDF file. */
    public static final String RESULT
        = "results/part1/chapter05/movie_countries1.pdf";

    /**
     * Inner class to add a table as header.
     */
    class TableHeader extends PdfPageEventHelper {
        /** The header text. */
        String header;
        /** The template with the total number of pages. */
        PdfTemplate total;
        
        /**
         * Allows us to change the content of the header.
         * @param header The new header String
         */
        public void setHeader(String header) {
            this.header = header;
        }

        /**
         * Creates the PdfTemplate that will hold the total number of pages.
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onOpenDocument(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
         */
        public void onOpenDocument(PdfWriter writer, Document document) {
            total = writer.getDirectContent().createTemplate(30, 16);
        }
        
        /**
         * Adds a header to every page
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
         */
        public void onEndPage(PdfWriter writer, Document document) {
            PdfPTable table = new PdfPTable(3);
            try {
                table.setWidths(new int[]{24, 24, 2});
                table.setTotalWidth(527);
                table.setLockedWidth(true);
                table.getDefaultCell().setFixedHeight(20);
                table.getDefaultCell().setBorder(Rectangle.BOTTOM);
                table.addCell(header);
                table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);
                table.addCell(String.format("Page %d of", writer.getPageNumber()));
                PdfPCell cell = new PdfPCell(Image.getInstance(total));
                cell.setBorder(Rectangle.BOTTOM);
                table.addCell(cell);
                table.writeSelectedRows(0, -1, 34, 803, writer.getDirectContent());
            }
            catch(DocumentException de) {
                throw new ExceptionConverter(de);
            }
        }
        
        /**
         * Fills out the total number of pages before the document is closed.
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onCloseDocument(
         *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
         */
        public void onCloseDocument(PdfWriter writer, Document document) {
            ColumnText.showTextAligned(total, Element.ALIGN_LEFT,
                    new Phrase(String.valueOf(writer.getPageNumber() - 1)),
                    2, 2, 0);
        }
    }

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     * @throws    SQLException
     */
    public void createPdf(String filename)
        throws IOException, DocumentException, SQLException {
        // Create a database connection
    	DatabaseConnection connection = new HsqldbConnection("filmfestival");
        // step 1
        Document document = new Document(PageSize.A4, 36, 36, 54, 36);
        // step 2
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
        TableHeader event = new TableHeader();
        writer.setPageEvent(event);
        // step 3
        document.open();
        // step 4
        Statement stm = connection.createStatement();
        ResultSet rs = stm.executeQuery(
            "SELECT country, id FROM film_country ORDER BY country");
        while (rs.next()) {
            event.setHeader(rs.getString("country"));
            Set movies = 
                new TreeSet(new MovieComparator(MovieComparator.BY_YEAR));
            movies.addAll(PojoFactory.getMovies(connection, rs.getString("id")));
            for(Movie movie : movies) {
                document.add(new Paragraph(movie.getMovieTitle(), FilmFonts.BOLD));
                if (movie.getOriginalTitle() != null)
                    document.add(new Paragraph(movie.getOriginalTitle(), FilmFonts.ITALIC));
                document.add(new Paragraph(
                    String.format("Year: %d; run length: %d minutes",
                        movie.getYear(), movie.getDuration()), FilmFonts.NORMAL));
                document.add(PojoToElementFactory.getDirectorList(movie));
            }
            document.newPage();
        }
        // step 4
        document.close();
        // close the database connection
        connection.close();
    }
    
    /**
     * Main method.
     *
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException 
     * @throws SQLException
     */
    public static void main(String[] args)
        throws IOException, DocumentException, SQLException {
        new MovieCountries1().createPdf(RESULT);
    }
}
MovieCountries2.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Set;
import java.util.TreeSet;

import com.lowagie.database.DatabaseConnection;
import com.lowagie.database.HsqldbConnection;
import com.lowagie.filmfestival.FilmFonts;
import com.lowagie.filmfestival.Movie;
import com.lowagie.filmfestival.MovieComparator;
import com.lowagie.filmfestival.PojoFactory;
import com.lowagie.filmfestival.PojoToElementFactory;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Font.FontFamily;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.GrayColor;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;

public class MovieCountries2 extends MovieCountries1 {
    
    /** The resulting PDF file. */
    public static final String RESULT
        = "results/part1/chapter05/movie_countries2.pdf";

    /**
     * Inner class to add a watermark to every page.
     */
    class Watermark extends PdfPageEventHelper {
        
        Font FONT = new Font(FontFamily.HELVETICA, 52, Font.BOLD, new GrayColor(0.75f));
        
        public void onEndPage(PdfWriter writer, Document document) {
            ColumnText.showTextAligned(writer.getDirectContentUnder(),
                    Element.ALIGN_CENTER, new Phrase("FOOBAR FILM FESTIVAL", FONT),
                    297.5f, 421, writer.getPageNumber() % 2 == 1 ? 45 : -45);
        }
    }

    /**
     * Creates a PDF document.
     * @param filename the path to the new PDF document
     * @throws    DocumentException 
     * @throws    IOException
     * @throws    SQLException
     */
    public void createPdf(String filename)
        throws IOException, DocumentException, SQLException {
        // Create a database connection
    	DatabaseConnection connection = new HsqldbConnection("filmfestival");
        // step 1
        Document document = new Document(PageSize.A4, 36, 36, 54, 36);
        // step 2
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
        TableHeader event = new TableHeader();
        writer.setPageEvent(event);
        writer.setPageEvent(new Watermark());
        // step 3
        document.open();
        // step 4
        Statement stm = connection.createStatement();
        ResultSet rs = stm.executeQuery(
            "SELECT country, id FROM film_country ORDER BY country");
        while (rs.next()) {
            event.setHeader(rs.getString("country"));
            Set movies = 
                new TreeSet(new MovieComparator(MovieComparator.BY_YEAR));
            movies.addAll(PojoFactory.getMovies(connection, rs.getString("id")));
            for(Movie movie : movies) {
                document.add(new Paragraph(movie.getMovieTitle(), FilmFonts.BOLD));
                if (movie.getOriginalTitle() != null)
                    document.add(new Paragraph(movie.getOriginalTitle(), FilmFonts.ITALIC));
                document.add(new Paragraph(String.format("Year: %d; run length: %d minutes",
                    movie.getYear(), movie.getDuration()), FilmFonts.NORMAL));
                document.add(PojoToElementFactory.getDirectorList(movie));
            }
            document.newPage();
        }
        // step 5
        document.close();
        // close the database connection
        connection.close();
    }
    
    /**
     * Main method.
     *
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException 
     * @throws SQLException
     */
    public static void main(String[] args)
        throws IOException, DocumentException, SQLException {
        new MovieCountries2().createPdf(RESULT);
    }
}
MovieSlideShow.java
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;

import com.lowagie.database.DatabaseConnection;
import com.lowagie.database.HsqldbConnection;
import com.lowagie.filmfestival.Movie;
import com.lowagie.filmfestival.PojoFactory;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfTransition;
import com.itextpdf.text.pdf.PdfWriter;

public class MovieSlideShow {
    /** The resulting PDF file. */
    public static final String RESULT = "results/part1/chapter05/movie_slides.pdf";
    /** Path to the resources. */
    public static final String RESOURCE = "resources/posters/%s.jpg";
    
    
    /**
     * Page event to set the transition and duration for every page.
     */
    class TransitionDuration extends PdfPageEventHelper {

        public void onStartPage(PdfWriter writer, Document document) {
            writer.setTransition(new PdfTransition(PdfTransition.DISSOLVE, 3));
            writer.setDuration(5);
        }
        
    }
        
    /**
     * Creates a PDF with information about the movies
     * @param    filename the name of the PDF file that will be created.
     * @throws    DocumentException 
     * @throws    IOException 
     * @throws    SQLException
     */
    public void createPdf(String filename)
        throws IOException, DocumentException, SQLException {
    	// Create a database connection
        DatabaseConnection connection = new HsqldbConnection("filmfestival");    
        // step 1
        Document document = new Document(PageSize.A5.rotate());
        // step 2
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
        writer.setPdfVersion(PdfWriter.VERSION_1_5);
        writer.setViewerPreferences(PdfWriter.PageModeFullScreen);
        writer.setPageEvent(new TransitionDuration());
        // step 3
        document.open();
        // step 4
        List movies = PojoFactory.getMovies(connection);
        Image img;
        PdfPCell cell;
        PdfPTable table = new PdfPTable(6);
        for (Movie movie : movies) {
            img = Image.getInstance(String.format(RESOURCE, movie.getImdb()));
            cell = new PdfPCell(img, true);
            cell.setBorder(PdfPCell.NO_BORDER);
            table.addCell(cell);
        }
        document.add(table);
        // step 5
        document.close();
        // Close the database connection
        connection.close();
    }
    
    /**
     * Main method creating the PDF.
     * @param    args    no arguments needed
     * @throws IOException 
     * @throws DocumentException 
     * @throws SQLException
     */
    public static void main(String[] args)
        throws IOException, SQLException, DocumentException {
        new MovieSlideShow().createPdf(RESULT);
    }
}
AlternatingBackground.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using System.Collections.Generic;
using iTextSharp.text;
using iTextSharp.text.pdf;
using kuujinbo.iTextInAction2Ed.Intro_1_2;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class AlternatingBackground : IWriter, IPdfPTableEvent {
// ===========================================================================
    public void Write(Stream stream) {
      // step 1
      using (Document document = new Document(PageSize.A4.Rotate())) {
        // step 2
        PdfWriter.GetInstance(document, stream);
        // step 3
        document.Open();
        // step 4
        List days = PojoFactory.GetDays();
        IPdfPTableEvent Pevent = new AlternatingBackground();
        foreach (string day in days) {
          PdfPTable table = GetTable(day);
          table.TableEvent = Pevent;
          document.Add(table);
          document.NewPage();
        }
      }
    }
// ---------------------------------------------------------------------------
    /**
     * Creates a table with film festival screenings.
     * @param day a film festival day
     * @return a table with screenings.
     */
    public PdfPTable GetTable(string day) {
      PdfPTable table = new PdfPTable(new float[] { 2, 1, 2, 5, 1 });
      table.WidthPercentage = 100f;
      table.DefaultCell.Padding = 3;
      table.DefaultCell.UseAscender = true;
      table.DefaultCell.UseDescender = true;
      table.DefaultCell.Colspan = 5;
      table.DefaultCell.BackgroundColor = BaseColor.RED;
      table.DefaultCell.HorizontalAlignment = Element.ALIGN_CENTER;
      table.AddCell(day);
      table.DefaultCell.HorizontalAlignment = Element.ALIGN_LEFT;
      table.DefaultCell.Colspan = 1;
      table.DefaultCell.BackgroundColor = BaseColor.ORANGE;
      for (int i = 0; i < 2; i++) {
        table.AddCell("Location");
        table.AddCell("Time");
        table.AddCell("Run Length");
        table.AddCell("Title");
        table.AddCell("Year");
      }
      table.DefaultCell.BackgroundColor = null;
      table.HeaderRows = 3;
      table.FooterRows = 1;
      List screenings = PojoFactory.GetScreenings(day);
      Movie movie;
      foreach (Screening screening in screenings) {
        movie = screening.movie;
        table.AddCell(screening.Location);
        table.AddCell(screening.Time.Substring(0, 5));
        table.AddCell(movie.Duration.ToString() + " '");
        table.AddCell(movie.MovieTitle);
        table.AddCell(movie.Year.ToString());
      }
      return table;
    }
// ---------------------------------------------------------------------------
    /**
     * Draws a background for every other row.
     * @see com.itextpdf.text.pdf.PdfPTableEvent#tableLayout(
     *      com.itextpdf.text.pdf.PdfPTable, float[][], float[], int, int,
     *      com.itextpdf.text.pdf.PdfContentByte[])
     */
    public void TableLayout(
      PdfPTable table, float[][] widths, float[] heights,
      int headerRows, int rowStart, PdfContentByte[] canvases
    ) {
      int columns;
      Rectangle rect;
      int footer = widths.Length - table.FooterRows;
      int header = table.HeaderRows - table.FooterRows + 1;
      for (int row = header; row < footer; row += 2) {
        columns = widths[row].Length - 1;
        rect = new Rectangle(
          widths[row][0], heights[row],
          widths[row][columns], heights[row + 1]
        );
        rect.BackgroundColor = BaseColor.YELLOW;
        rect.Border = Rectangle.NO_BORDER;
        canvases[PdfPTable.BASECANVAS].Rectangle(rect);
      }
    }    
// ===========================================================================
  }
}
RunLengthEvent.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using iTextSharp.text;
using iTextSharp.text.pdf;
using kuujinbo.iTextInAction2Ed.Intro_1_2;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class RunLengthEvent : IWriter {
// ===========================================================================
    /** Inner class to draw a bar inside a cell. */
    class RunLength : IPdfPCellEvent {
      public int duration;
      
      public RunLength(int duration) {
        this.duration = duration;
      }
      
      /**
       * @see com.lowagie.text.pdf.PdfPCellEvent#cellLayout(
       *      com.lowagie.text.pdf.PdfPCell, com.lowagie.text.Rectangle,
       *      com.lowagie.text.pdf.PdfContentByte[])
       */
      public void CellLayout(
        PdfPCell cell, Rectangle rect, PdfContentByte[] canvas
      ) {
        PdfContentByte cb = canvas[PdfPTable.BACKGROUNDCANVAS];
        cb.SaveState();
        if (duration < 90) {
          cb.SetRGBColorFill(0x7C, 0xFC, 0x00);
        }
        else if (duration > 120) {
          cb.SetRGBColorFill(0x8B, 0x00, 0x00);
        } 
        else {
          cb.SetRGBColorFill(0xFF, 0xA5, 0x00);
        }
        cb.Rectangle(
          rect.Left, rect.Bottom, 
          rect.Width * duration / 240, rect.Height
        );
        cb.Fill();
        cb.RestoreState();
      }
    }
/*
 * end inner class
*/ 
    /** Inner class to add the words PRESS PREVIEW to a cell. */
    class PressPreview : IPdfPCellEvent {
      public BaseFont bf;
      public PressPreview() {
        bf = BaseFont.CreateFont();
      }
      
      /**
       * @see com.lowagie.text.pdf.PdfPCellEvent#cellLayout(com.lowagie.text.pdf.PdfPCell,
       *      com.lowagie.text.Rectangle,
       *      com.lowagie.text.pdf.PdfContentByte[])
       */
      public void CellLayout(
        PdfPCell cell, Rectangle rect, PdfContentByte[] canvas
      ) {
        PdfContentByte cb = canvas[PdfPTable.TEXTCANVAS];
        cb.BeginText();
        cb.SetFontAndSize(bf, 12);
        cb.ShowTextAligned(
          Element.ALIGN_RIGHT, "PRESS PREVIEW",
          rect.Right - 3,
          rect.Bottom + 4.5f, 0
        );
        cb.EndText();
      }
    }
/*
 * end inner class
*/  
// ---------------------------------------------------------------------------
    /** The press cell event. */
    public IPdfPCellEvent press;
    public void Write(Stream stream) {
      press = new PressPreview();
      // step 1
      using (Document document = new Document(PageSize.A4.Rotate())) {
        // step 2
        PdfWriter.GetInstance(document, stream);      
        // step 3
        document.Open();
        // step 4
        List days = PojoFactory.GetDays();
        foreach (string day in days) {
          document.Add(GetTable(day));
          document.NewPage();
        }        
      }
    }
// ---------------------------------------------------------------------------
    /**
     * @param connection
     * @param day
     * @return
     * @throws SQLException
     * @throws DocumentException
     * @throws IOException
     */
    public PdfPTable GetTable(string day) {
      PdfPTable table = new PdfPTable(new float[] { 2, 1, 2, 5, 1 });
      table.WidthPercentage = 100f;
      table.DefaultCell.Padding = 3;
      table.DefaultCell.UseAscender = true;
      table.DefaultCell.UseDescender = true;
      table.DefaultCell.Colspan = 5;
      table.DefaultCell.BackgroundColor = BaseColor.RED;
      table.DefaultCell.HorizontalAlignment = Element.ALIGN_CENTER;
      table.AddCell(day);
      table.DefaultCell.HorizontalAlignment = Element.ALIGN_LEFT;
      table.DefaultCell.Colspan = 1;
      table.DefaultCell.BackgroundColor = BaseColor.YELLOW;
      for (int i = 0; i < 2; i++) {
        table.AddCell("Location");
        table.AddCell("Time");
        table.AddCell("Run Length");
        table.AddCell("Title");
        table.AddCell("Year");
      }
      table.DefaultCell.BackgroundColor = null;
      table.HeaderRows = 3;
      table.FooterRows = 1;
      List screenings = PojoFactory.GetScreenings(day);
      Movie movie;
      PdfPCell runLength;
      foreach (Screening screening in screenings) {
        movie = screening.movie;
        table.AddCell(screening.Location);
        table.AddCell(screening.Time.Substring(0, 5));
        runLength = new PdfPCell(table.DefaultCell);
        runLength.Phrase = new Phrase(String.Format(
          "{0} '", movie.Duration
        ));
        runLength.CellEvent = new RunLength(movie.Duration);
        if (screening.Press) {
          runLength.CellEvent = press;
        }
        table.AddCell(runLength);
        table.AddCell(movie.MovieTitle);
        table.AddCell(movie.Year.ToString());
      }
      return table;
    }
// ===========================================================================
  }
}
PressPreviews.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using System.Collections.Generic;
using iTextSharp.text;
using iTextSharp.text.pdf;
using kuujinbo.iTextInAction2Ed.Intro_1_2;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class PressPreviews : IWriter, IPdfPCellEvent, IPdfPTableEvent {
// ===========================================================================
    /**
     * @see com.itextpdf.text.pdf.PdfPTableEvent#tableLayout(com.itextpdf.text.pdf.PdfPTable,
     *      float[][], float[], int, int, com.itextpdf.text.pdf.PdfContentByte[])
     */
    public void TableLayout(
      PdfPTable table, float[][] width, float[] height,
      int headerRows, int rowStart, PdfContentByte[] canvas
    ) {
      float[] widths = width[0];
      float x1 = widths[0];
      float x2 = widths[widths.Length - 1];
      float y1 = height[0];
      float y2 = height[height.Length - 1];
      PdfContentByte cb = canvas[PdfPTable.LINECANVAS];
      cb.Rectangle(x1, y1, x2 - x1, y2 - y1);
      cb.Stroke();
      cb.ResetRGBColorStroke();
    }

    /**
     * @see com.lowagie.text.pdf.PdfPCellEvent#cellLayout(com.lowagie.text.pdf.PdfPCell,
     *      com.lowagie.text.Rectangle, com.lowagie.text.pdf.PdfContentByte[])
     */
    public void CellLayout(
      PdfPCell cell, Rectangle position, PdfContentByte[] canvases
    ) {
      float x1 = position.Left + 2;
      float x2 = position.Right - 2;
      float y1 = position.Top - 2;
      float y2 = position.Bottom + 2;
      PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
      canvas.Rectangle(x1, y1, x2 - x1, y2 - y1);
      canvas.Stroke();
      canvas.ResetRGBColorStroke();
    }
// ---------------------------------------------------------------------------
    public void Write(Stream stream) {
      // step 1
      using (Document document = new Document(PageSize.A4.Rotate())) {
        // step 2
        PdfWriter.GetInstance(document, stream);
        // step 3
        document.Open();
        // step 4
        document.Add(GetTable()); 
      }
    }
// ---------------------------------------------------------------------------
    /**
     * Creates a table that mimics cellspacing using table and cell events.
     * @param connection
     * @return a table
     * @throws SQLException
     * @throws DocumentException
     * @throws IOException
     */
    public PdfPTable GetTable() {
      PdfPTable table = new PdfPTable(new float[] { 1, 2, 2, 5, 1 });
      table.TableEvent = new PressPreviews();
      table.WidthPercentage = 100f;
      table.DefaultCell.Padding = 5;
      table.DefaultCell.Border = PdfPCell.NO_BORDER;
      table.DefaultCell.CellEvent = new PressPreviews();
      for (int i = 0; i < 2; i++) {
        table.AddCell("Location");
        table.AddCell("Date/Time");
        table.AddCell("Run Length");
        table.AddCell("Title");
        table.AddCell("Year");
      }
      table.DefaultCell.BackgroundColor = null;
      table.HeaderRows = 2;
      table.FooterRows = 1;
      List screenings = PojoFactory.GetPressPreviews();
      foreach (Screening screening in screenings) {
        Movie movie = screening.movie;
        table.AddCell(screening.Location);
        table.AddCell(String.Format(
          "{0}   {1}", screening.Date, 
          screening.Time.Substring(0, 5)
        ));
        table.AddCell(String.Format("{0} '", movie.Duration));
        table.AddCell(movie.MovieTitle);
        table.AddCell(movie.Year.ToString());
      }
      return table;
    }    
// ===========================================================================
  }
}
PdfCalendar.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO; 
using System.Globalization; 
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.draw;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class PdfCalendar : kuujinbo.iTextInAction2Ed.Chapter04.PdfCalendar {
// ===========================================================================
    /** A table event. */
    public IPdfPTableEvent tableBackground;
    /** A cell event. */
    public IPdfPCellEvent cellBackground;
    /** A cell event. */
    public IPdfPCellEvent roundRectangle;
    /** A cell event. */
    public IPdfPCellEvent whiteRectangle;
    /**
     * Inner class with a table event that draws a background with rounded corners.
     */
    class TableBackground : IPdfPTableEvent {
      public void TableLayout(
        PdfPTable table, float[][] width, float[] height,
        int headerRows, int rowStart, PdfContentByte[] canvas
      ) {
        PdfContentByte background = canvas[PdfPTable.BASECANVAS];
        background.SaveState();
        background.SetCMYKColorFill(0x00, 0x00, 0xFF, 0x0F);
        background.RoundRectangle(
          width[0][0], height[height.Length - 1] - 2,
          width[0][1] - width[0][0] + 6, 
          height[0] - height[height.Length - 1] - 4, 4
        );
        background.Fill();
        background.RestoreState();
      }
    }
/*
 * end inner class
*/     
    /**
     * Inner class with a cell event that draws a background with rounded corners.
     */
    class CellBackground : IPdfPCellEvent {
      public void CellLayout(
        PdfPCell cell, Rectangle rect, PdfContentByte[] canvas
    ) {
        PdfContentByte cb = canvas[PdfPTable.BACKGROUNDCANVAS];
        cb.RoundRectangle(
          rect.Left + 1.5f, 
          rect.Bottom + 1.5f, 
          rect.Width - 3,
          rect.Height - 3, 4
        );
        cb.SetCMYKColorFill(0x00, 0x00, 0x00, 0x00);
        cb.Fill();
      }
    }
/*
 * end inner class
*/ 
    /**
     * Inner class with a cell event that draws a border with rounded corners.
     */
    class RoundRectangle : IPdfPCellEvent {
      /** the border color described as CMYK values. */
      protected int[] color;
      /** Constructs the event using a certain color. */
      public RoundRectangle(int[] color) {
        this.color = color;
      }
      
      public void CellLayout(
        PdfPCell cell, Rectangle rect, PdfContentByte[] canvas
      ) {
        PdfContentByte cb = canvas[PdfPTable.LINECANVAS];
        cb.RoundRectangle(
          rect.Left + 1.5f, 
          rect.Bottom + 1.5f, 
          rect.Width - 3,
          rect.Height - 3, 4
        );
        cb.SetLineWidth(1.5f);
        cb.SetCMYKColorStrokeF(color[0], color[1], color[2], color[3]);
        cb.Stroke();
      }
    }
/*
 * end inner class
*/ 
// ---------------------------------------------------------------------------
    /**
     * Initializes the fonts and collections.
     */
    public PdfCalendar() : base() {
      tableBackground = new TableBackground();
      cellBackground = new CellBackground();
      roundRectangle = new RoundRectangle(new int[]{ 0xFF, 0x00, 0xFF, 0x00 });
      whiteRectangle = new RoundRectangle(new int[]{ 0x00, 0x00, 0x00, 0x00 });
    }

    public override void Write(Stream stream) {
      // step 1
      using (Document document = new Document(PageSize.A4.Rotate())) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        // step 3
        document.Open();
        // step 4
        // "en" => System.Globalization.GregorianCalendar 
        Calendar calendar = new CultureInfo(LANGUAGE, false).Calendar;
        PdfPTable table;
        PdfContentByte canvas = writer.DirectContent;
        // Loop over the months
        for (int month = 0; month < 12; month++) {
          int current_month = month + 1;
          DateTime dt = new DateTime(YEAR, current_month, 1, calendar);
          // draw the background
          DrawImageAndText(canvas, dt);
          // create a table with 7 columns
          table = new PdfPTable(7);
          table.TableEvent = tableBackground;
          table.TotalWidth = 504;
          // add the name of the month
          table.DefaultCell.Border  = PdfPCell.NO_BORDER;
          table.DefaultCell.CellEvent = whiteRectangle;
          table.AddCell(GetMonthCell(dt));
          int daysInMonth = DateTime.DaysInMonth(YEAR, dt.Month);
          int day = 1;
          // add empty cells
          // SUNDAY; Java => 1, .NET => 0
          int position = 0;
          while (position++ != (int) dt.DayOfWeek) {
            table.AddCell("");
          }
          // add cells for each day
          while (day <= daysInMonth) {
            dt = new DateTime(YEAR, current_month, day++, calendar);
            table.AddCell(GetDayCell(dt));
          }          
          // complete the table
          table.CompleteRow();
          // write the table to an absolute position
          table.WriteSelectedRows(0, -1, 169, table.TotalHeight + 20, canvas);
          document.NewPage();
        }
      }
    }
// ---------------------------------------------------------------------------
    /**
     * Creates a PdfPCell with the name of the month
     * @param dt a DateTime
     * @return a PdfPCell with rowspan 7, containing the name of the month
     */
    public new PdfPCell GetMonthCell(DateTime dt) {
      PdfPCell cell = new PdfPCell();
      cell.Colspan = 7;
      cell.Border = PdfPCell.NO_BORDER;
      cell.UseDescender = true;
      Paragraph p = new Paragraph(dt.ToString("MMMM yyyy"), bold);
      p.Alignment = Element.ALIGN_CENTER;
      cell.AddElement(p);
      return cell;
    }

    /**
     * Creates a PdfPCell for a specific day
     * @param dt a DateTime
     * @return a PdfPCell
     */
    public new PdfPCell GetDayCell(DateTime dt) {
      PdfPCell cell = new PdfPCell();
      // set the event to draw the background
      cell.CellEvent = cellBackground;
      // set the event to draw a special border
      if (IsSunday(dt) || IsSpecialDay(dt))
          cell.CellEvent = roundRectangle;
      cell.Padding = 3;
      cell.Border = PdfPCell.NO_BORDER;
      // set the content in the language of the locale
      Chunk chunk = new Chunk(dt.ToString("ddd"), small);
      chunk.SetTextRise(8);
      // a paragraph with the day
      Paragraph p = new Paragraph(chunk);
      // a separator
      p.Add(new Chunk(new VerticalPositionMark()));
      // and the number of the day
      p.Add(new Chunk(dt.ToString("%d"), normal));
      cell.AddElement(p);
      return cell;
    }
    
    /**
     * Returns true if the date was found in a list with special days (holidays).
     * @param dt a DateTime
     * @return true for holidays
     */
    public new bool IsSpecialDay(DateTime dt) {
      return specialDays.ContainsKey(dt.ToString("MMdd"))
          ? true: false;
    }    
// ===========================================================================
  }
}
MovieYears.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using iTextSharp.text;
using iTextSharp.text.pdf;
using kuujinbo.iTextInAction2Ed.Intro_1_2;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class MovieYears : IWriter {
// ===========================================================================
    /**
     * Inner class to add functionality to a Chunk in a generic way.
     */
    class GenericTags : PdfPageEventHelper {
      public override void OnGenericTag(
        PdfWriter writer, Document pdfDocument, Rectangle rect, String text
    ) {
        if ("strip".Equals(text)) {
          Strip(writer.DirectContent, rect);
        }
        else if ("ellipse".Equals(text)) {
          Ellipse(writer.DirectContentUnder, rect);
        }
        else {
          CountYear(text);
        }
      }

      public void Strip(PdfContentByte content, Rectangle rect) {
        content.Rectangle(
          rect.Left - 1, rect.Bottom - 5f,
          rect.Width, rect.Height + 8
        );
        content.Rectangle(
          rect.Left, rect.Bottom - 2,
          rect.Width - 2, rect.Height + 2
        );
        float y1 = rect.Top + 0.5f;
        float y2 = rect.Bottom - 4;
        for (float f = rect.Left; f < rect.Right - 4; f += 5) {
          content.Rectangle(f, y1, 4f, 1.5f);
          content.Rectangle(f, y2, 4f, 1.5f);
        }
        content.EoFill();
      }
      
      public void Ellipse(PdfContentByte content, Rectangle rect) {
        content.SaveState();
        content.SetRGBColorFill(0x00, 0x00, 0xFF);
        content.Ellipse(
          rect.Left - 3f, rect.Bottom - 5f,
          rect.Right + 3f, rect.Top + 3f
        );
        content.Fill();
        content.RestoreState();
      }

      internal SortedDictionary years = new SortedDictionary();
      
      public void CountYear(String text) {
        if ( years.ContainsKey(text) ) {
          ++years[text];
        }
        else {
          years.Add(text, 1);
        }
      }
    }
/*
 * end inner class
*/    
    /**
     * Inner class to add lines when a paragraph begins and ends.
     */
    class ParagraphPositions : PdfPageEventHelper {
      public override void OnParagraph(
          PdfWriter writer, Document pdfDocument, float paragraphPosition
      ) {
        DrawLine(
          writer.DirectContent, pdfDocument.Left, 
          pdfDocument.Right, paragraphPosition - 8
        );
      }

      public override void OnParagraphEnd(
          PdfWriter writer, Document pdfDocument, float paragraphPosition
      ) {
          DrawLine(
            writer.DirectContent, pdfDocument.Left, 
            pdfDocument.Right, paragraphPosition - 5
          );
      }
      
      public void DrawLine(PdfContentByte cb, float x1, float x2, float y) {
        cb.MoveTo(x1, y);
        cb.LineTo(x2, y);
        cb.Stroke();
      }
    }
/*
 * end inner class
*/
// ---------------------------------------------------------------------------
    public void Write(Stream stream) {
      // step 1
      using (Document document = new Document()) {
      // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        GenericTags gevent = new GenericTags();
        writer.PageEvent = gevent;
        writer.PageEvent = new ParagraphPositions();       
        // step 3
        document.Open();
        // step 4
        Font bold = new Font(Font.FontFamily.HELVETICA, 11, Font.BOLD);
        Font italic = new Font(Font.FontFamily.HELVETICA, 11, Font.ITALIC);
        Font white = new Font(
          Font.FontFamily.HELVETICA, 12, 
          Font.BOLD | Font.ITALIC, 
          BaseColor.WHITE
        );
        Paragraph p;
        Chunk c;        
        foreach (Movie movie in PojoFactory.GetMovies(true)) {
            p = new Paragraph(22);
            c = new Chunk(String.Format("{0} ", movie.Year), bold);
            c.SetGenericTag("strip");
            p.Add(c);
            c = new Chunk(movie.MovieTitle);
            c.SetGenericTag(movie.Year.ToString());
            p.Add(c);
            c = new Chunk(
              String.Format(" ({0} minutes)  ", movie.Duration), 
              italic
            );
            p.Add(c);
            c = new Chunk("IMDB", white);
            c.SetAnchor("http://www.imdb.com/title/tt" + movie.Imdb);
            c.SetGenericTag("ellipse");
            p.Add(c);
            document.Add(p);
        }
        document.NewPage();
        writer.PageEvent = null;
        foreach (string entry in gevent.years.Keys) {
          p = new Paragraph(String.Format(
            "{0}: {1} movie(s)", entry, gevent.years[entry]
          ));
          document.Add(p);
        }
      }
    }
// ===========================================================================
  }
}
MovieHistory1.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq; 
using iTextSharp.text;
using iTextSharp.text.pdf;
using kuujinbo.iTextInAction2Ed.Intro_1_2;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class MovieHistory1 : IWriter {
// ===========================================================================
    /**
     * Inner class to keep track of the TOC
     * and to draw lines under ever chapter and section.
     */
    class ChapterSectionTOC : PdfPageEventHelper {
      private MovieHistory1 mh1;
      public ChapterSectionTOC(MovieHistory1 mh1) {
        this.mh1 = mh1;
      }
    
      /** List with the titles. */
      public List titles = new List();
      
      public override void OnChapter(PdfWriter writer, Document document,
              float position, Paragraph title) 
      {
        titles.Add(new Paragraph(title.Content, mh1.FONT[4]));
      }

      public override void OnChapterEnd(
        PdfWriter writer, Document document,
        float position) 
      {
        DrawLine(
          writer.DirectContent, document.Left, document.Right, position - 5
        );
      }

      public override void OnSection(
        PdfWriter writer, Document document,
        float position, int depth, Paragraph title) 
      {
          title = new Paragraph(title.Content, mh1.FONT[4]);
          title.IndentationLeft = 18 * depth;
          titles.Add(title);
      }

      public override void OnSectionEnd(
        PdfWriter writer, Document document, float position
        ) 
      {
        DrawLine(
          writer.DirectContent, document.Left, document.Right, position - 3
        );
      }
      
      public void DrawLine(
        PdfContentByte cb, float x1, float x2, float y
        ) 
      {
        cb.MoveTo(x1, y);
        cb.LineTo(x2, y);
        cb.Stroke();
      }
    }
    // end inner class
// ---------------------------------------------------------------------------
    
    /** The different epochs. */
    public readonly string[] EPOCH = { 
      "Forties", "Fifties", "Sixties", "Seventies", "Eighties",
    "Nineties", "Twenty-first Century" 
    };
    /** The fonts for the title. */
    public Font[] FONT = new Font[5];
    
    public MovieHistory1() {
      FONT[0] = new Font(Font.FontFamily.HELVETICA, 24);
      FONT[1] = new Font(Font.FontFamily.HELVETICA, 18);
      FONT[2] = new Font(Font.FontFamily.HELVETICA, 14);
      FONT[3] = new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD);
      FONT[4] = new Font(Font.FontFamily.HELVETICA, 10);
    }
// ---------------------------------------------------------------------------
    public void Write(Stream stream) {
      // step 1
      using (Document document = new Document()) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        // IMPORTANT: set linear page mode!
        writer.SetLinearPageMode();
        ChapterSectionTOC tevent = new ChapterSectionTOC(new MovieHistory1());
        writer.PageEvent = tevent;        
        // step 3
        document.Open();
        // step 4
        int epoch = -1;
        int currentYear = 0;
        Paragraph title = null;
        Chapter chapter = null;
        Section section = null;
        Section subsection = null;
        // add the chapters, sort by year
        foreach (Movie movie in PojoFactory.GetMovies(true)) {
          int year = movie.Year;
          if (epoch < (year - 1940) / 10) {
            epoch = (year - 1940) / 10;
            if (chapter != null) {
              document.Add(chapter);
            }
            title = new Paragraph(EPOCH[epoch], FONT[0]);
            chapter = new Chapter(title, epoch + 1);
          }
          if (currentYear < year) {
            currentYear = year;
            title = new Paragraph(
              String.Format("The year {0}", year), FONT[1]
            );
            section = chapter.AddSection(title);
            section.BookmarkTitle = year.ToString();
            section.Indentation = 30;
            section.BookmarkOpen = false;
            section.NumberStyle = Section.NUMBERSTYLE_DOTTED_WITHOUT_FINAL_DOT;
            section.Add(new Paragraph(
              String.Format("Movies from the year {0}:", year))
            );
          }
          title = new Paragraph(movie.MovieTitle, FONT[2]);
          subsection = section.AddSection(title);
          subsection.IndentationLeft = 20;
          subsection.NumberDepth = 1;
          subsection.Add(new Paragraph(
            "Duration: " + movie.Duration.ToString(), FONT[3]
          ));
          subsection.Add(new Paragraph("Director(s):", FONT[3]));
          subsection.Add(PojoToElementFactory.GetDirectorList(movie));
          subsection.Add(new Paragraph("Countries:", FONT[3]));
          subsection.Add(PojoToElementFactory.GetCountryList(movie));
        }      
        document.Add(chapter);
        // add the TOC starting on the next page
        document.NewPage();
        int toc = writer.PageNumber;
        foreach (Paragraph p in tevent.titles) {
          document.Add(p);
        }
        // always go to a new page before reordering pages.
        document.NewPage();
        // get the total number of pages that needs to be reordered
        int total = writer.ReorderPages(null);
        // change the order
        int[] order = new int[total];
        for (int i = 0; i < total; i++) {
          order[i] = i + toc;
          if (order[i] > total) {
            order[i] -= total;
          }
        }
        // apply the new order
        writer.ReorderPages(order);        
      }
    }
// ===========================================================================
  }
}
NewPage.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class NewPage : IWriter {
// ===========================================================================
    public void Write(Stream stream) {
      // step 1
      using (Document document = new Document()) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        // step 3
        document.Open();
        // step 4
        document.Add(new Paragraph("This page will NOT be followed by a blank page!"));
        document.NewPage();
        // we don't add anything to this page: newPage() will be ignored
        document.NewPage();
        document.Add(new Paragraph("This page will be followed by a blank page!"));
        document.NewPage();
        writer.PageEmpty = false;
        document.NewPage();
        document.Add(new Paragraph("The previous page was a blank page!"));
      }
    }
// ===========================================================================
  }
}
Hero1.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class Hero1 : IWriter {
// ===========================================================================
    public virtual void Write(Stream stream) {
      // step 1
      Rectangle rect = new Rectangle(-1192, -1685, 1192, 1685);
      using (Document document = new Document(rect)) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        // step 3
        document.Open();
        // step 4
        PdfContentByte content = writer.DirectContent;
        PdfTemplate template = CreateTemplate(content, rect, 4);
        content.AddTemplate(template, -1192, -1685);
        content.MoveTo(-595, 0);
        content.LineTo(595, 0);
        content.MoveTo(0, -842);
        content.LineTo(0, 842);
        content.Stroke();
      }
    }
// ---------------------------------------------------------------------------    
    /**
     * Creates a template based on a stream of PDF syntax.
     * @param content The direct content
     * @param rect The dimension of the templare
     * @param factor A magnification factor
     * @return A PdfTemplate
     */
    public virtual PdfTemplate CreateTemplate(
      PdfContentByte content, Rectangle rect, int factor)
    {
      PdfTemplate template = content.CreateTemplate(rect.Width, rect.Height);
      template.ConcatCTM(factor, 0, 0, factor, 0, 0);
      string hero = Path.Combine(Utility.ResourceText, "hero.txt");
      if (!File.Exists(hero)) {
        throw new ArgumentException(hero + " NOT FOUND!");  
      }    
      var fi = new FileInfo(hero);
      using ( var sr = fi.OpenText() ) {
        while (sr.Peek() >= 0) {
          template.SetLiteral((char) sr.Read());
        }
      }
      return template;
    }    
// ===========================================================================
  }
}
Hero2.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class Hero2 : Hero1 {
// ===========================================================================
    public override void Write(Stream stream) {
      float w = PageSize.A4.Width;
      float h = PageSize.A4.Height;
      Rectangle rect = new Rectangle(-2*w, -2*h, 2*w, 2*h);
      Rectangle crop = new Rectangle(-2 * w, h, -w, 2 * h);      
      // step 1
      using (Document document = new Document(rect)) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        writer.CropBoxSize = crop;
        // step 3
        document.Open();
        // step 4
        PdfContentByte content = writer.DirectContent;
        PdfTemplate template = CreateTemplate(content, rect, 4);
        float adjust;
        while(true) {
          content.AddTemplate(template, -2*w, -2*h);
          adjust = crop.Right + w;
          if (adjust > 2 * w) {
            adjust = crop.Bottom - h;
            if (adjust < - 2 * h)
                break;
            crop = new Rectangle(-2*w, adjust, -w, crop.Bottom);
          }
          else {
            crop = new Rectangle(crop.Right, crop.Bottom, adjust, crop.Top);
          }
          writer.CropBoxSize = crop;
          document.NewPage();
        }
      }
    }
// ---------------------------------------------------------------------------    
    /**
     * Creates a template based on a stream of PDF syntax.
     * @param content The direct content
     * @param rect The dimension of the templare
     * @param factor A magnification factor
     * @return A PdfTemplate
     */
    public override  PdfTemplate CreateTemplate(
      PdfContentByte content, Rectangle rect, int factor
    )
    {
      PdfTemplate template = content.CreateTemplate(rect.Width, rect.Height);
      template.ConcatCTM(factor, 0, 0, factor, 0, 0);
      string hero = Path.Combine(Utility.ResourceText, "hero.txt");
      if (!File.Exists(hero)) {
        throw new ArgumentException(hero + " NOT FOUND!");
      }   
      var fi = new FileInfo(hero);
      using ( var sr = fi.OpenText() ) {
        while (sr.Peek() >= 0) {
          template.SetLiteral((char) sr.Read());
        }
      }
      return template;
    }    
// ===========================================================================
  }
}
Hero3.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class Hero3 : Hero1 {
// ===========================================================================
    public override void Write(Stream stream) {
      // step 1
      using (Document document = new Document(PageSize.A4)) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        Rectangle art = new Rectangle(50, 50, 545, 792);
        writer.SetBoxSize("art", art);        
        // step 3
        document.Open();
        // step 4
        PdfContentByte content = writer.DirectContent;
        PdfTemplate template = CreateTemplate(content, PageSize.A4, 1);
        content.AddTemplate(template, 0, 0);
      }
    }
// ---------------------------------------------------------------------------    
    /**
     * Creates a template based on a stream of PDF syntax.
     * @param content The direct content
     * @param rect The dimension of the templare
     * @param factor A magnification factor
     * @return A PdfTemplate
     */
    public override PdfTemplate CreateTemplate(
      PdfContentByte content, Rectangle rect, int factor
    )
    {
      PdfTemplate template = content.CreateTemplate(rect.Width, rect.Height);
      template.ConcatCTM(factor, 0, 0, factor, 0, 0);
      string hero = Path.Combine(Utility.ResourceText, "hero.txt");
      if (!File.Exists(hero)) {
        throw new ArgumentException(hero + " NOT FOUND!");      
      }
      var fi = new FileInfo(hero);
      using ( StreamReader sr = fi.OpenText() ) {
        while (sr.Peek() >= 0) {
          template.SetLiteral((char) sr.Read());
        }
      }
      return template;
    }    
// ===========================================================================
  }
}
MovieHistory2.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq; 
using iTextSharp.text;
using iTextSharp.text.pdf;
using kuujinbo.iTextInAction2Ed.Intro_1_2;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class MovieHistory2 : IWriter {
// ===========================================================================
    /** Inner class to add a header and a footer. */
    class HeaderFooter : PdfPageEventHelper {
      /** Alternating phrase for the header. */
      Phrase[] header = new Phrase[2];
      /** Current page number (will be reset for every chapter). */
      int pagenumber;
      
      /**
       * Initialize one of the headers.
       * @see com.itextpdf.text.pdf.PdfPageEventHelper#onOpenDocument(
       *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
       */
      public override void OnOpenDocument(PdfWriter writer, Document document) {
        header[0] = new Phrase("Movie history");
      }
      
      /**
       * Initialize one of the headers, based on the chapter title;
       * reset the page number.
       * @see com.itextpdf.text.pdf.PdfPageEventHelper#onChapter(
       *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document, float,
       *      com.itextpdf.text.Paragraph)
       */
      public override void OnChapter(
        PdfWriter writer, Document document,
        float paragraphPosition, Paragraph title) 
      {
        header[1] = new Phrase(title.Content);
        pagenumber = 1;
      }

      /**
       * Increase the page number.
       * @see com.itextpdf.text.pdf.PdfPageEventHelper#onStartPage(
       *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
       */
      public override void OnStartPage(PdfWriter writer, Document document) {
        pagenumber++;
      }
        
      /**
       * Adds the header and the footer.
       * @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(
       *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
       */
      public override void OnEndPage(PdfWriter writer, Document document) {
        Rectangle rect = writer.GetBoxSize("art");
        switch(writer.PageNumber % 2) {
        case 0:
          ColumnText.ShowTextAligned(writer.DirectContent,
            Element.ALIGN_RIGHT, 
            header[0],
            rect.Right, rect.Top, 0
          );
          break;
        case 1:
          ColumnText.ShowTextAligned(
            writer.DirectContent,
            Element.ALIGN_LEFT,
            header[1],
            rect.Left, rect.Top, 0
          );
          break;
        }
        ColumnText.ShowTextAligned(
          writer.DirectContent,
          Element.ALIGN_CENTER, 
          new Phrase(String.Format("page {0}", pagenumber)),
          (rect.Left + rect.Right) / 2, 
          rect.Bottom - 18, 0
        );
      }
    }
    // end inner class
// ---------------------------------------------------------------------------
    /** The different epochs. */
    public readonly string[] EPOCH = { 
      "Forties", "Fifties", "Sixties", "Seventies", "Eighties",
    "Nineties", "Twenty-first Century" 
    };
    /** The fonts for the title. */
    public Font[] FONT = new Font[5];
    
    public MovieHistory2() {
      FONT[0] = new Font(Font.FontFamily.HELVETICA, 24);
      FONT[1] = new Font(Font.FontFamily.HELVETICA, 18);
      FONT[2] = new Font(Font.FontFamily.HELVETICA, 14);
      FONT[3] = new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD);
      FONT[4] = new Font(Font.FontFamily.HELVETICA, 10);
    }
// ---------------------------------------------------------------------------
    public void Write(Stream stream) {
      // step 1
      using (Document document = new Document(PageSize.A4, 36, 36, 54, 54)) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        HeaderFooter tevent = new HeaderFooter();
        writer.SetBoxSize("art", new Rectangle(36, 54, 559, 788));
        writer.PageEvent = tevent;
        // step 3
        document.Open();
        // step 4
        int epoch = -1;
        int currentYear = 0;
        Paragraph title = null;
        Chapter chapter = null;
        Section section = null;
        Section subsection = null;
        // add the chapters, sort by year
        foreach (Movie movie in PojoFactory.GetMovies(true)) {
          int year = movie.Year;
          if (epoch < (year - 1940) / 10) {
            epoch = (year - 1940) / 10;
            if (chapter != null) {
              document.Add(chapter);
            }
            title = new Paragraph(EPOCH[epoch], FONT[0]);
            chapter = new Chapter(title, epoch + 1);
          }
          if (currentYear < year) {
            currentYear = year;
            title = new Paragraph(
              String.Format("The year {0}", year), FONT[1]
            );
            section = chapter.AddSection(title);
            section.BookmarkTitle = year.ToString();
            section.Indentation = 30;
            section.BookmarkOpen = false;
            section.NumberStyle = Section.NUMBERSTYLE_DOTTED_WITHOUT_FINAL_DOT;
            section.Add(new Paragraph(
              String.Format("Movies from the year {0}:", year))
            );
          }
          title = new Paragraph(movie.MovieTitle, FONT[2]);
          subsection = section.AddSection(title);
          subsection.IndentationLeft = 20;
          subsection.NumberDepth = 1;
          subsection.Add(new Paragraph(
            "Duration: " + movie.Duration.ToString(), FONT[3]
          ));
          subsection.Add(new Paragraph("Director(s):", FONT[3]));
          subsection.Add(PojoToElementFactory.GetDirectorList(movie));
          subsection.Add(new Paragraph("Countries:", FONT[3]));
          subsection.Add(PojoToElementFactory.GetCountryList(movie));
        }      
        document.Add(chapter);
      }
    }
// ===========================================================================
  }
}
MovieCountries1.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using iTextSharp.text;
using iTextSharp.text.pdf;
using kuujinbo.iTextInAction2Ed.Intro_1_2;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class MovieCountries1 : IWriter {
// ===========================================================================
    /**
     * Inner class to add a table as header.
     */
    protected class TableHeader : PdfPageEventHelper {
      /** The template with the total number of pages. */
      PdfTemplate total;
      
      /** The header text. */
      public string Header { get; set; }

      /**
       * Creates the PdfTemplate that will hold the total number of pages.
       * @see com.itextpdf.text.pdf.PdfPageEventHelper#onOpenDocument(
       *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
       */
      public override void OnOpenDocument(PdfWriter writer, Document document) {
        total = writer.DirectContent.CreateTemplate(30, 16);
      }
      
      /**
       * Adds a header to every page
       * @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(
       *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
       */
      public override void OnEndPage(PdfWriter writer, Document document) {
        PdfPTable table = new PdfPTable(3);
        try {
          table.SetWidths(new int[]{24, 24, 2});
          table.TotalWidth = 527;
          table.LockedWidth = true;
          table.DefaultCell.FixedHeight = 20;
          table.DefaultCell.Border = Rectangle.BOTTOM_BORDER;
          table.AddCell(Header);
          table.DefaultCell.HorizontalAlignment = Element.ALIGN_RIGHT;
          table.AddCell(string.Format("Page {0} of", writer.PageNumber));
          PdfPCell cell = new PdfPCell(Image.GetInstance(total));
          cell.Border = Rectangle.BOTTOM_BORDER;
          table.AddCell(cell);
          table.WriteSelectedRows(0, -1, 34, 803, writer.DirectContent);
        }
        catch(DocumentException de) {
          throw de;
        }
      }
      
      /**
       * Fills out the total number of pages before the document is closed.
       * @see com.itextpdf.text.pdf.PdfPageEventHelper#onCloseDocument(
       *      com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
       */
      public override void OnCloseDocument(PdfWriter writer, Document document) {
        ColumnText.ShowTextAligned(
          total, Element.ALIGN_LEFT,
/*
 * NewPage() already called when closing the document; subtract 1
*/
          new Phrase((writer.PageNumber - 1).ToString()),
          2, 2, 0
        );
      }
    }
// ---------------------------------------------------------------------------
    protected readonly string SQL = 
@"SELECT country, id FROM film_country 
ORDER BY country
"; 
    public virtual void Write(Stream stream) {
      // step 1
      using (Document document = new Document(PageSize.A4, 36, 36, 54, 36)) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        TableHeader tevent = new TableHeader();
        writer.PageEvent = tevent;
        // step 3
        document.Open();
        // step 4       
        using (var c =  AdoDB.Provider.CreateConnection()) {
          c.ConnectionString = AdoDB.CS;
          using (DbCommand cmd = c.CreateCommand()) {
            cmd.CommandText = SQL;        
            c.Open();
            using (var r = cmd.ExecuteReader()) {
              while (r.Read()) {
                tevent.Header = r["country"].ToString();
                // LINQ allows us to sort on any movie object property inline;
                // let's sort by Movie.Year, Movie.Title
                var by_year = from m in PojoFactory.GetMovies(
                  r["id"].ToString()
                )
                  orderby m.Year, m.Title
                  select m
                ;                
                foreach (Movie movie in by_year) {
                  document.Add(new Paragraph(
                    movie.MovieTitle, FilmFonts.BOLD
                  ));
                  if (!string.IsNullOrEmpty(movie.OriginalTitle)) document.Add(
                    new Paragraph(movie.OriginalTitle, FilmFonts.ITALIC)
                  );
                  document.Add(new Paragraph(
                    String.Format("Year: {0}; run length: {1} minutes",
                      movie.Year, movie.Duration
                    ), FilmFonts.NORMAL
                  ));
                  document.Add(PojoToElementFactory.GetDirectorList(movie));
                }
                document.NewPage();              
              }      
            }
          }
        }
      }
    }    
// ===========================================================================
  }
}
MovieCountries2.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using iTextSharp.text;
using iTextSharp.text.pdf;
using kuujinbo.iTextInAction2Ed.Intro_1_2;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class MovieCountries2 : MovieCountries1 {
// ===========================================================================
    /**
     * Inner class to add a watermark to every page.
     */
    class Watermark : PdfPageEventHelper {
      Font FONT = new Font(Font.FontFamily.HELVETICA, 52, Font.BOLD, new GrayColor(0.75f));
      
      public override void OnEndPage(PdfWriter writer, Document document) {
        ColumnText.ShowTextAligned(
          writer.DirectContentUnder,
          Element.ALIGN_CENTER, new Phrase("FOOBAR FILM FESTIVAL", FONT),
          297.5f, 421, writer.PageNumber % 2 == 1 ? 45 : -45
        );
      }
    }
// ---------------------------------------------------------------------------
    public override void Write(Stream stream) {
      // step 1
      using (Document document = new Document(PageSize.A4, 36, 36, 54, 36)) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        TableHeader tevent = new TableHeader();
        writer.PageEvent = tevent;
        writer.PageEvent = new Watermark();
        // step 3
        document.Open();
        // step 4 
        using (var c =  AdoDB.Provider.CreateConnection()) {
          c.ConnectionString = AdoDB.CS;
          using (DbCommand cmd = c.CreateCommand()) {
            cmd.CommandText = SQL;        
            c.Open();
            using (var r = cmd.ExecuteReader()) {
              while (r.Read()) {
                tevent.Header = r["country"].ToString();
                foreach (Movie movie 
                  in PojoFactory.GetMovies(r["id"].ToString(), true)) 
                {
                  document.Add(new Paragraph(
                    movie.MovieTitle, FilmFonts.BOLD
                  ));
                  if (!string.IsNullOrEmpty(movie.OriginalTitle)) document.Add(
                    new Paragraph(movie.OriginalTitle, FilmFonts.ITALIC)
                  );
                  document.Add(new Paragraph(
                    String.Format("Year: {0}; run length: {1} minutes",
                      movie.Year, movie.Duration
                    ), FilmFonts.NORMAL
                  ));
                  document.Add(PojoToElementFactory.GetDirectorList(movie));
                }
                document.NewPage();              
              }      
            }
          }
        }
      }
    }    
// ===========================================================================
  }
}
MovieSlideShow.cs
/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
 * For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using iTextSharp.text;
using iTextSharp.text.pdf;
using kuujinbo.iTextInAction2Ed.Intro_1_2;

namespace kuujinbo.iTextInAction2Ed.Chapter05 {
  public class MovieSlideShow : IWriter {
// ===========================================================================
    /**
     * Page event to set the transition and duration for every page.
     */
    class TransitionDuration : PdfPageEventHelper {
      public override void OnStartPage(PdfWriter writer, Document document) {
        writer.Transition = new PdfTransition(PdfTransition.DISSOLVE, 3);
        writer.Duration = 5;
      }
    }
// ---------------------------------------------------------------------------
    public void Write(Stream stream) {
      // step 1
      using (Document document = new Document(PageSize.A5.Rotate())) {
        // step 2
        PdfWriter writer = PdfWriter.GetInstance(document, stream);
        writer.PdfVersion = PdfWriter.VERSION_1_5;
        writer.ViewerPreferences = PdfWriter.PageModeFullScreen;
        writer.PageEvent = new TransitionDuration();        
        // step 3
        document.Open();
        // step 4
        IEnumerable movies = PojoFactory.GetMovies();
        Image img;
        PdfPCell cell;
        PdfPTable table = new PdfPTable(6);
        string RESOURCE = Utility.ResourcePosters;
        foreach (Movie movie in movies) {
          img = Image.GetInstance(Path.Combine(RESOURCE, movie.Imdb + ".jpg"));
          cell = new PdfPCell(img, true);
          cell.Border = PdfPCell.NO_BORDER;
          table.AddCell(cell);
        }
        document.Add(table);
      }
    }
// ===========================================================================
  }
}
Contact

Still have questions? 

We're happy to answer your questions. Reach out to us and we'll get back to you shortly.

Contact us
Stay updated

Join 11,000+ subscribers and become an iText PDF expert by staying up to date with our new products, updates, tips, technical solutions and happenings.

Subscribe Now