在开发系统时,需要在PDF上写入总页数。于是在网上搜索到
iText加入页码
这篇文章。但是仍然不知道PdfTemplate是什么使用的。
在Itext in action 2006版 第14章刚好有个这个例子(14.2.3 PageXofY)
/* chapter14/PageXofY.java */
import java.io.FileOutputStream;
import java.io.IOException;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.ExceptionConverter;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
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;
/**
* This example was written by Bruno Lowagie. It is part of the book 'iText in
* Action' by Manning Publications.
* ISBN: 1932394796
* http://itext.ugent.be/itext-in-action/
* http://www.manning.com/lowagie/
*/
public class PageXofY extends PdfPageEventHelper {
/** The PdfTemplate that contains the total number of pages. */
protected PdfTemplate total;
/** The font that will be used. */
protected BaseFont helv;
/**
* @see com.lowagie.text.pdf.PdfPageEvent#onOpenDocument(com.lowagie.text.pdf.PdfWriter,
* com.lowagie.text.Document)
*/
public void onOpenDocument(PdfWriter writer, Document document) {
total = writer.getDirectContent().createTemplate(100, 100);
total.setBoundingBox(new Rectangle(-20, -20, 100, 100));
try {
helv = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI,
BaseFont.NOT_EMBEDDED);
} catch (Exception e) {
throw new ExceptionConverter(e);
}
}
/**
* @see com.lowagie.text.pdf.PdfPageEvent#onEndPage(com.lowagie.text.pdf.PdfWriter,
* com.lowagie.text.Document)
*/
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte cb = writer.getDirectContent();
cb.saveState();
String text = "Page " + writer.getPageNumber() + " of ";
float textBase = document.bottom() - 20;
float textSize = helv.getWidthPoint(text, 12);
cb.beginText();
cb.setFontAndSize(helv, 12);
if ((writer.getPageNumber() % 2) == 1) {
cb.setTextMatrix(document.left(), textBase);
cb.showText(text);
cb.endText();
cb.addTemplate(total, document.left() + textSize, textBase);
}
// for even numbers, show the footer at the right
else {
float adjust = helv.getWidthPoint("0", 12);
cb.setTextMatrix(document.right() - textSize - adjust, textBase);
cb.showText(text);
cb.endText();
cb.addTemplate(total, document.right() - adjust, textBase);
}
cb.restoreState();
}
/**
* @see com.lowagie.text.pdf.PdfPageEvent#onCloseDocument(com.lowagie.text.pdf.PdfWriter,
* com.lowagie.text.Document)
*/
public void onCloseDocument(PdfWriter writer, Document document) {
total.beginText();
total.setFontAndSize(helv, 12);
total.setTextMatrix(0, 0);
total.showText(String.valueOf(writer.getPageNumber() - 1));
total.endText();
}
/**
* Generates a file with a header and a footer.
*
* @param args
* no arguments needed here
*/
public static void main(String[] args) {
System.out.println("Chapter 14: Page X of Y Example");
System.out.println("-> Creates a PDF file with page numbers.");
System.out.println("-> jars needed: iText.jar");
System.out.println("-> files generated in /results subdirectory:");
System.out.println(" page_numbers.pdf");
// step 1: creation of a document-object
Document document = new Document();
try {
// step 2:
PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream("resource/page_numbers.pdf"));
writer.setViewerPreferences(PdfWriter.PageLayoutTwoColumnLeft);
writer.setPageEvent(new PageXofY());
document.setMargins(36, 36, 36, 54);
// step 3:
document.open();
Paragraph p = new Paragraph();
// step 4: we grab the ContentByte and do some stuff with it
for (int k = 1; k <= 30; ++k) {
p.add(new Phrase("Quick brown fox jumps over the lazy dog. "));
}
p.setAlignment(Element.ALIGN_JUSTIFIED);
for (int k = 1; k <= 12; ++k) {
document.add(p);
}
} catch (DocumentException de) {
System.err.println(de.getMessage());
} catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
document.close();
}
}
那么我们一起看看这个PdfTemplate到底是怎么用的。
PdfTemplate
查看一下API,PdfTemplate继承自PdfContentByte,PdfContentByte大家如果使用itext应该比较熟悉。一般来说,PdfContentByte仅仅作为内部使用,通过它你可以使用原生的pdf 语法来生产pdf。而PdfTemplate只不过是基于PdfContentByte,实现了XObject。
PdfContentByte
is an object containing the user positioned text and graphic contents of a page. It knows how to apply the proper font encoding.
那么XObject是什么呢?
PS:下载链接
http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference13.pdf
根据pdf reference v1.3 4.7小节描述:
An external object (commonly called an XObject) is a graphics object whose con-
tents are defined by a self-contained content stream, separate from the content
stream in which it is used. There are three types of external object:
• An image XObject (Section 4.8.4, “Image Dictionaries”) represents a sampled
visual image such as a photograph.
• A form XObject (Section 4.9, “Form XObjects”) is a self-contained description
of an arbitrary sequence of graphics objects.
• A PostScript XObject (Section 4.10, “PostScript XObjects”) contains a fragment
of code expressed in the PostScript page description language.
一共就有三种XObject。而PdfTemplate就是Form XObject的实现。
Form XObject
Form XObject在pdf reference v1.3 4.9小节。
A form XObject is a self-contained description of any sequence of graphics objects
(including path objects, text objects, and sampled images), defined as a PDF
content stream. It may be painted multiple times—either on several pages or at
several locations on the same page—and will produce the same output each time,
subject only to the graphics state at the time it is invoked. Not only is this shared
definition economical to represent in the PDF file, but under suitable circum-
stances, the PDF viewer can optimize execution by caching the results of render-
ing the form XObject for repeated reuse.
PdfTemplate
现在我们的重头戏来了。让我们回到Itext in action 2006 10.4.2小节 。其实我们可以把PdfTemplate当成一个截图工具。如下:创建了一个宽100pt,高100pt的PdfTemplate
protected PdfTemplate total;
total = writer.getDirectContent().createTemplate(100, 100);
PdfPageEventHelper
PageXofY是继承自PdfPageEventHelper的,它覆盖了helper的三个方法onCloseDocument,onEndPage,onOpenDocument
public class PageXofY extends PdfPageEventHelper
方法 | 功能 |
onCloseDocument | Called when the document is closed. |
onEndPage | Called when a page is finished, just before being written to the document. |
onOpenDocument | Called when the document is opened. |
PageXofY
在PageXofY中使用了onOpenDocument创建了PdfTemplate,初始化了BaseFont。
在PageXofY中使用了onEndPage将onOpenDocument创建的PdfTemplate固定在某个位置。
cb.addTemplate(total, document.left() + textSize, textBase);
cb.addTemplate(total, document.right() - adjust, textBase);
在PageXofY中使用了onCloseDocument将最终的页数写入到PdfTemplate。在关闭document前那一页就是总页数。通过writer.getPageNumber-1获得。
total.showText(String.valueOf(writer.getPageNumber() - 1));
在main函数中,通过writer设置event。
writer.setPageEvent(new PageXofY());
那么,writer每写一页就会调用onEndPage一次。那么如果你只需要在第一页写总页数,那么onEndPage就可以这样改写:
public void onEndPage(PdfWriter writer, Document document) {
try {
if(writer.getPageNumber()==1){
tpl = writer.getDirectContent().createTemplate(100, 100);
tpl.setBoundingBox(new Rectangle(-20, -20, 100, 100));
PdfContentByte cb = writer.getDirectContent();
cb.addTemplate(tpl, document.right()-100, document.top()-15);
}
logger.info("Success add SanitationContainer image title");
} catch (DocumentException e) {
logger.error("Fail to add SanitationContainer image title");
e.printStackTrace();
}
}