/*
* $Id$
*
* This code is part of the 'iText Tutorial'.
* You can find the complete tutorial at the following address:
* http://itextdocs.lowagie.com/tutorial/
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* itext-questions@lists.sourceforge.net
*/
package com.lowagie.examples.directcontent.pageevents;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeSet;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.ElementTags;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Rectangle;
import com.lowagie.text.TextElementArray;
import com.lowagie.text.html.Markup;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPageEventHelper;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.xml.SAXmyHandler;
import com.lowagie.text.xml.TagMap;
import com.lowagie.text.xml.XmlPeer;
/**
* Example that takes an XML file, converts it to PDF and adds all kinds of
* extra's, such as an alternating header, a footer with page x of y, a page
* with metadata,...
*/
public class Events {
/**
* This is an example of a PageEvents class you should write.
* This is an inner class to keep all the code of the example in one file.
* If you want to use a PageEvent, you may want to put the code in a separate class.
*/
class MyPageEvents extends PdfPageEventHelper {
/** we will keep a list of speakers */
TreeSet speakers = new TreeSet();
/** This is the contentbyte object of the writer */
PdfContentByte cb;
/** we will put the final number of pages in a template */
PdfTemplate template;
/** this is the BaseFont we are going to use for the header / footer */
BaseFont bf = null;
/** this is the current act of the play */
String act = "";
/**
* Every speaker will be tagged, so that he can be added to the list of speakers.
* @see com.lowagie.text.pdf.PdfPageEventHelper#onGenericTag(com.lowagie.text.pdf.PdfWriter, com.lowagie.text.Document, com.lowagie.text.Rectangle, java.lang.String)
*/
public void onGenericTag(PdfWriter writer, Document document,
Rectangle rect, String text) {
speakers.add(new Speaker(text));
}
/**
* The first thing to do when the document is opened, is to define the BaseFont,
* get the Direct Content object and create the template that will hold the final
* number of pages.
* @see com.lowagie.text.pdf.PdfPageEventHelper#onOpenDocument(com.lowagie.text.pdf.PdfWriter, com.lowagie.text.Document)
*/
public void onOpenDocument(PdfWriter writer, Document document) {
try {
bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252,
BaseFont.NOT_EMBEDDED);
cb = writer.getDirectContent();
template = cb.createTemplate(50, 50);
} catch (DocumentException de) {
} catch (IOException ioe) {
}
}
/**
* Every ACT is seen as a Chapter. We get the title of the act, so that
* we can display it in the header.
* @see com.lowagie.text.pdf.PdfPageEventHelper#onChapter(com.lowagie.text.pdf.PdfWriter, com.lowagie.text.Document, float, com.lowagie.text.Paragraph)
*/
public void onChapter(PdfWriter writer, Document document,
float paragraphPosition, Paragraph title) {
StringBuffer buf = new StringBuffer();
for (Iterator i = title.getChunks().iterator(); i.hasNext();) {
Chunk chunk = (Chunk) i.next();
buf.append(chunk.getContent());
}
act = buf.toString();
}
/**
* After the content of the page is written, we put page X of Y
* at the bottom of the page and we add either "Romeo and Juliet"
* of the title of the current act as a header.
* @see com.lowagie.text.pdf.PdfPageEventHelper#onEndPage(com.lowagie.text.pdf.PdfWriter, com.lowagie.text.Document)
*/
public void onEndPage(PdfWriter writer, Document document) {
int pageN = writer.getPageNumber();
String text = "Page " + pageN + " of ";
float len = bf.getWidthPoint(text, 8);
cb.beginText();
cb.setFontAndSize(bf, 8);
cb.setTextMatrix(280, 30);
cb.showText(text);
cb.endText();
cb.addTemplate(template, 280 + len, 30);
cb.beginText();
cb.setFontAndSize(bf, 8);
cb.setTextMatrix(280, 820);
if (pageN % 2 == 1) {
cb.showText("Romeo and Juliet");
} else {
cb.showText(act);
}
cb.endText();
}
/**
* Just before the document is closed, we add the final number of pages to
* the template.
* @see com.lowagie.text.pdf.PdfPageEventHelper#onCloseDocument(com.lowagie.text.pdf.PdfWriter, com.lowagie.text.Document)
*/
public void onCloseDocument(PdfWriter writer, Document document) {
template.beginText();
template.setFontAndSize(bf, 8);
template.showText(String.valueOf(writer.getPageNumber() - 1));
template.endText();
}
/**
* Getting the list of speakers.
* @return a list of speakers and the number of occurrences.
*/
public TreeSet getSpeakers() {
return speakers;
}
}
/**
* Gets a PageEvents object.
* @return a new PageEvents object
*/
public MyPageEvents getPageEvents() {
return new MyPageEvents();
}
/**
* Gets a Handler object.
* @param document the document on which the handler operates
* @return a Handler object
*/
public MyHandler getXmlHandler(Document document) {
try {
return new MyHandler(document, new RomeoJulietMap());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Converts a play in XML into PDF.
* @param args no arguments needed
*/
public static void main(String[] args) {
System.out.println("Romeo and Juliet");
// step 1: creation of a document-object
Document document = new Document(PageSize.A4, 80, 50, 30, 65);
try {
// step 2:
// we create a writer that listens to the document
// and directs a XML-stream to a file
PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream("RomeoJuliet.pdf"));
// create add the event handler
MyPageEvents events = new Events().getPageEvents();
writer.setPageEvent(events);
// step 3: we create a parser and set the document handler
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
// step 4: we parse the document
parser.parse("playRomeoJuliet.xml", new Events().getXmlHandler(document));
document.newPage();
Speaker speaker;
for (Iterator i = events.getSpeakers().iterator(); i.hasNext();) {
speaker = (Speaker) i.next();
document.add(new Paragraph(speaker.getName() + ": "
+ speaker.getOccurrence() + " speech blocks"));
}
document.close();
} catch (Exception e) {
e.printStackTrace();
System.err.println(e.getMessage());
}
}
/**
* Special implementation of the XML handler.
* It adds a paragraph after each SPEAKER block and
* avoids closing the document after the final closing tag.
*/
class MyHandler extends SAXmyHandler {
/**
* We have to override the constructor
* @param document the Document object
* @param tagmap the tagmap
*/
public MyHandler(Document document, HashMap tagmap) {
super(document, tagmap);
}
/**
* We only alter the handling of some endtags.
* @param uri the uri of the namespace
* @param lname the local name of the tag
* @param name the name of the tag
*/
public void endElement(String uri, String lname, String name) {
if (myTags.containsKey(name)) {
XmlPeer peer = (XmlPeer) myTags.get(name);
// we don't want the document to be close
// because we are going to add a page after the xml is parsed
if (isDocumentRoot(peer.getTag())) {
return;
}
handleEndingTags(peer.getTag());
// we want to add a paragraph after the speaker chunk
if ("SPEAKER".equals(name)) {
try {
TextElementArray previous = (TextElementArray) stack
.pop();
previous.add(new Paragraph(16));
stack.push(previous);
} catch (EmptyStackException ese) {
}
}
} else {
handleEndingTags(name);
}
}
}
/**
* Normally you either choose to use a HashMap with XmlPeer objects,
* or a TagMap object that reads a TagMap in XML.
* Here we used a hybrid solution (for educational purposes only!)
* with on one side the tags in the XML tagmap, on the other side
* an XmlPeer object that overrides the properties of one of the tags.
*/
class RomeoJulietMap extends TagMap {
private static final long serialVersionUID = 1024517625414654121L;
/**
* Constructs a TagMap based on an XML file
* and/or on XmlPeer objects that are added.
* @throws IOException
*/
public RomeoJulietMap() throws IOException {
super(new FileInputStream("tagmapRomeoJuliet.xml"));
XmlPeer peer = new XmlPeer(ElementTags.CHUNK, "SPEAKER");
peer.addValue(Markup.CSS_KEY_FONTSIZE, "10");
peer.addValue(Markup.CSS_KEY_FONTWEIGHT, Markup.CSS_VALUE_BOLD);
peer.addValue(ElementTags.GENERICTAG, "");
put(peer.getAlias(), peer);
}
}
/**
* This object contains a speaker and a number of occurrences in the play
*/
class Speaker implements Comparable {
// name of the speaker
private String name;
// number of occurrences
private int occurrence = 1;
/**
* One of the speakers in the play.
* @param name
*/
public Speaker(String name) {
this.name = name;
}
/**
* Gets the name of the speaker.
* @return a name
*/
public String getName() {
return name;
}
/**
* Gets the number of occurrences of the speaker.
* @return a number of textblocks
*/
public int getOccurrence() {
return occurrence;
}
/**
* There is something odd going on in this compareTo.
* Do you see it?
* @param o an other speaker object
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Object o) {
Speaker otherSpeaker = (Speaker) o;
if (otherSpeaker.getName().equals(name)) {
this.occurrence += otherSpeaker.getOccurrence();
otherSpeaker.occurrence = this.occurrence;
return 0;
}
return name.compareTo(otherSpeaker.getName());
}
}
}