第七章 XML和 (X)HTML
本章主要介绍了如何利用iText控件生成XLM文档和(X)HTML文档,但我们对这些并不感兴趣,故只介绍本章中提到的将XML转为PDF。
在第一章中,我们通过5步生产一个PDF文件,为了将一个XML文件转换为PDF文件,只需重写第3和第4步,第5步由解析器自动处理。
//第3步:创建一个解析器并设置文档句柄:
iTextHandler h = new iTextHandler(document);
//第4步,转换该文档:
h.Parse("Chap0701.xml");
示例代码见示例代码0702
第八章 RTF文件
RTF包
RTF包是基于iText包扩展出来的,允许iText除生成PDF文件外还可以输出RTF文件,除了一些在RTF包中不支持的特性外,大多数PDF文件特性都可以使用。
创建一个RTF文档
创建一个RTF文档和创建一个PDF文档方法是一样的,都是这基本的5步,唯一的区别是第2步中用RtfWriter代替了PdfWriter,见示例代码0801。
第1步 创建一个the iTextSharp.text.Document对象的实例:
Document document = new Document();
第2步 创建一个document的RtfWriter将document写入你选择的输出流:
RtfWriter.getInstance(document, new FileStream("Chap0801.rtf"), FileMode.Create);
第3步 打开document:
document.Open();
第4步 添加内容到document
document.Add(new Paragraph("Hello World"));
第5步 关闭document
document.Close();
关于如何创建其他对象并添加到document中,请参见其他章节的内容。
不支持的特性
- 水印
- 阅读器参数
- 加密
- 内嵌字体
- 块间距
- 段落右缩排
- 列表右缩排
- 无圆点符号列表
- 嵌套表格
- 除JPEG和PNG的其他图片
RTF中扩展的页眉和页脚
写入RTF时无法在开始新页前通过setHeader方法改变文档的页眉或页脚,这里有两个办法来解决这个问题。
- 利用“Chapters”,添加一个新“chapter”到文档前,使用setHeader或setFooter,你可以在不同的“Chapters”中使用不同的页眉或页脚,见示例代码0802。
- 使用RtfHeaderFooters类。该类允许你设置4个页眉或页脚,并指定在哪页出现。你当然可以结合Chapter创建4个不同的页眉或页脚,见示例代码0803。
u 使用RtfHeaderFooters类
第1步创建一个RtfHeaderFooters类:
RtfHeaderFooters headers = new RtfHeaderFooters();
第2步添加HeaderFooter对象
headers.Add(RtfHeaderFooters.LEFT_PAGES, new HeaderFooter(new Phrase("This header is only on left hand pages")));
headers.Add(RtfHeaderFooters.RIGHT_PAGES, new HeaderFooter(new Phrase("This header is only on right hand pages")));
第3步如同使用页眉页脚一样使用RtfHeaderFooters
document.Header = headers;
使用RtfHeaderFooters.add(...)的常量:
- FIRST_PAGE: 在你文档的第一页使用该页眉或页脚。你将使用rtfWriter.HasTitlePage = true来完成
- LEFT_PAGES: 所有左边页均使用该页眉或页脚
- RIGHT_PAGES: 所有右边页均使用该页眉或页脚
- ALL_PAGES: 所有页均使用该页眉或页脚,只有和FIRST_PAGE 结合使用才有意义。
有一件事非常重要:如果你使用LEFT_PAGES或者RIGHT_PAGES来设置页眉或页脚,再使用ALL_PAGES,页眉和页脚均不会起作用。
第九章 字体
本章原文讲了许多字体的使用技巧,但就是没有讲如何使用中文,因此,意义不大,再说,如果不支持中文,前面的也就白翻译了,因此,根据原文讲到的一些知识,我摸索出汉字的使用方法,自己写了本章内容,应该算是“原创”了吧^_^(哎呀!谁拿鸡蛋扔我……)。
Windows中一般都是使用TrueType字体,每个中文版Windows操作系统均默认安装了宋体、仿宋、黑体和楷体四种字体,你还可以安装其他第三方字体,如安装了Office 2000后,会自动安装华文行楷等字体,比较奇怪的是,在PDF文件中插入了一种本计算机才有的字体,在打开PDF文件的计算机上虽然没有该字体,但仍然能正常显示!这有别于Word文件,Word文件将当前计算机中没有的字体一律用宋体代替,这大概是意外收获吧。
字体文件一般保存在windir\Fonts目录中,扩展名为TTF,还有扩展名为TTC的字体文件,也是TrueType字体,不过是一个集合,也就是里面有多种字体。下面列出windows2000简体中文版四种标准字体的文件名称:
SIMSUN.TTC:宋体和新宋体
SIMKAI.TTF:楷体
SIMHEI.TTF:黑体
SIMFANG.TTF:仿宋体
TrueType字体应用
按下面的方法写入黑体字文字,大小为32磅:
BaseFont bfHei = BaseFont.createFont(@"c:\winnt\fonts\SIMHEI.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
Font font = new Font(bfHei, 32);
String text = "这是黑体字测试!";
document.Add(new Paragraph(text, font));
不要管BaseFont.createFont方法第二、三个参数的意思,依葫芦画瓢就行了,第一个参数显示就是字体文件存放的位置。
后面的代码都非常好理解,不再赘述。
TruType字体集合的应用
字体集合的使用同上面差不多,只是在在createFont方中要指定使用哪种字体。如:
BaseFont bfSun=BaseFont.createFont(@"c:\winnt\fonts\SIMSUN.TTC,1", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
font = new Font(bfSun, 16);
text = "这是字体集合中的新宋体测试!";
document.Add(new Paragraph(text, font));
不难看出,在使用BaseFont.createFont方法时,第一个参数@"c:\winnt\fonts\SIMSUN.TTC,1"中多了一个“,1”,表示使用序号为1字体,序号为0的字体为宋体。
毕竟我们不是做排版软件,有了上面的办法就基本上够用了,真正很复杂的PDF文件制作,不妨做成XML文件(最简单的办法就是用Word排版,然后另存为web页了),然后按第七章的办法转换。
代码见示例代码0901。
pdfContentByte
到目前为止,我们已经使用了简单的iText,我们已经添加了文本、图片、段落、章节、列表、表格等,没有涉及到布局问题。Itext分割文本到每页中,并将每个单词、句子、段落布置到页面上,但有时我们并不需要这种自动格式,有时我们希望将一些图象或者文本放置在某页的指定位置,为实现该功能,我们将使用PdfContentByte类。
为代替第一章,仅用PdfWriter类的getInstance方法是不够的,你必须真实地拥有一个PdfWriter对象,你可以通过在使用Writer对象中使用getDirectContent()方来得到该对象。例:
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("test.pdf"));
PdfContentByte cb = writer.DirectContent;
说明:当你添加高级对象(如表格)时,两个PdfContentByte对象将被内部使用:一个用于文本,一个用用于图象(如边界或单元格背景)。文本绘制浮于图象的上面。
当你通过getDirectContent()方法直接使用PdfContentByte对象时,你所添加的所有对象都将浮于文本和图象。如果你想避免这种情况和希望添加内容在图象或文本的背后,你需要使用用getDirectContentUnder()方。
一句话,当一页完成时,4层的重叠遵照如下顺序:
1、 通过getDirectContentUnder()得到的PdfContentByte
2、 包含图象或高级对象的内部PdfContentByte
3、 病文本或高级对象的内部PdfContentByte
4、 通过getDirectContent()得到的PdfContentByte
简单图形
在示例代码1001中,绘制了一些简单图形,我们使用了诸如moveTo和lineTo方法来在移动到页面上当前位置然后画一条直线到其他位置。我们使用了诸如setLineWidth和setLineDash方法来改变直线的外观,如:
cb.LineWidth = 10f;
cb.moveTo(100, 700);
cb.lineTo(200, 800);
cb.stroke();
说明:当你改变诸如颜色、线宽等属性时,只有你在调用stroke方法时才起作用。在例中绘制三角形时,我们设置颜色为绿色,在使用stroke方法前我们改变颜色为红色,则绘制三角形的结果为为红色而不是绿色,该例中还有矩形、圆等使用方法。
文本
当你想将文本写入ContentByte中时,你必须使用方法beginText()和endText,你也必须设置字体和尺寸。就象图形示例中一样,还有许多方法用于写入和放置文本,但你最需要的是方法showTextAligned和方法showText配合setTextMatrix。
例1:
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); cb.beginText();
cb.setFontAndSize(bf, 12);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, text + "This text is centered", 250, 700, 0);
cb.endText();
例2:
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
cb.beginText();
cb.setFontAndSize(bf, 12);
cb.setTextMatrix(100, 400);
cb.showText("Text at position 100,400.");
cb.endText();
请参见示例代码1002。
模板(Form xObjects)
当我们在第四章讨论页眉和页脚时,我们定义了一小块添加到每一页的信息,实际上,该小块信息写到了文件的每一个新页上。这并不是最经济的解决方案,更好的办法是将该信息作为一个Form Xobject仅在文档中添加一次,在其可见位置重复出现。我达到该目的,我们将使用模板。
u 创建一个PdfTemplate
u 创建PdfTemplate的最好方法是调用PdfContentByte对象中的createTemplate方法:
PdfContentByte-object:
PdfTemplate template = cb.createTemplate(500, 200);
这样,该模板的宽度为500,高度为200。
通过该模板我们可以做象PdfContentByte同样的事情
template.moveTo(0, 200);
template.lineTo(500, 0);
template.stroke();
template.beginText();
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
template.setFontAndSize(bf, 12);
template.setTextMatrix(100, 100);
template.showText("Text at the position 100,100 (relative to the template!)");
template.endText();
u 添加一个模板到文档
通过象下面一样在绝对位置添加一个模板:
cb.addTemplate(template, 0, 400);
你还可以做一些有趣的事情,如缩放或旋转他们:
//将模板旋转90度
cb.addTemplate(template, 0, 1, -1, 0, 500, 200);
// 缩放模板为50%
cb.addTemplate(template, .5f, 0, 0, .5f, 100, 400);
//缩放模板为200%
cb.addTemplate(template, 2, 0, 0, 2, -200, 400);
具体演示见示例代码1003。
u 第几页共几页
在一些情况下,你希望插入一些你在写本页时外壳无法知道的信息到文本中去,如:在一篇文档的第一页,你并不知道该文档共有几页。只能在完成了整个文档时才知道总的页数。当使用模板时,该问题就不存在了。在示例代码0103中,我们在添加模板到ContentByte前添加了一些信息到模板中,这是没有必要的。我们可以在任何时候添加信息到模板,因为iText添加Form Xobject是在PDF结束的地方(当通过close方法关闭该文档时调用)。示例代码1004显示了首先创建4页然后添加总到页数,该例非常简单和有用。
分栏
在本章以前,你已经掌握了如何将文本放在一个绝对位置,这种情况下,我们要确定文本的开始坐标。如果我们想知道文本的结束位置,我们得做一些计算工作。
现在我们要加一些文本到一个矩形框的内部,希望文本到达右边界时自动换行。超出矩形部分将不显示,可以通过ColumnText类实现。
举个例子:
为显示一个指定的短句在坐标(100, 300)和(200, 500)间的矩形内居中,我们使用下面的代码:
PdfContentByte cb = writer.DirectContent;
ColumnText ct = new ColumnText(cb);
ct.setSimpleColumn(phrase, 60, 300, 100, 500, 15, Element.ALIGN_CENTER);
ct.go();
通过查看示例代码1005,你会立即发现通过该方法可以可以画一些复杂的表格而无须Table对象。
另一个例子:
没有必要一次性将文本全部添加进去,你可以先定义一个矩形,然后添加一些文本,最后用go方法显示分栏。
PdfContentByte cb = writer.DirectContent;
ColumnText ct = new ColumnText(cb);
ct.setSim7pleColumn(60, 300, 100, 500, 15, Element.ALIGN_CENTER);
ct.addText(phrase1);
ct.addText(phrase2);
ct.addText(phrase3);
ct.go();
详见示例代码1006。
多栏
当然,如果文本超出了矩形范围,我们并不想丢失这些多出的文本,或许我们想将这些文本显示到其他栏中。这就是为什么我们要查看go方法返回值的原因。如果返回标识为“NO_MORE_COLUMN”,表示该栏中没有足够的空间存放该文本,如果所有的文本均显示出来,标识将为“NO_MORE_TEXT”。
请参见示例代码1007。
不规则栏
定义一个非矩形的区域来显示栏也是可能的,通过使用setColumns方法,我们为文本定义了一个左右边界。
float[] left = {70,790, 70,60};
float[] right = {300,790, 300,700, 240,700, 240,590, 300,590, 300,106, 270,60};
ct.setColumns(left, right);
左边界是一条直线,而右边界是不规则的。该函数的结果可以导致一些非常有意思的布局,见示例代码1008,本例中你将用到一个名为caesar_coin.jpg的图片:
PdfTable
在第5章中,我们简要地讲述了PdfPTable对象,现在我们将讨论该对象更多的的特性。
你可以用3种不同的方法创建PdfTable:
PdfPTable(float[] relativeWidths);
PdfPTable(int numColumns);
PdfPTable(PdfPTable table);
你可以给该表设置更多的参数,如表宽度、列宽度、水平对齐方式等,你可以通过下面的办法添加单元格:
public void addCell(PdfPCell cell);
public void addCell(PdfPTable table);
public void addCell(Phrase phrase);
public void addCell(String text);
除了单元格填距和和间距,这些方法同Table对象非常类似。这些参数对每个单元格个体进行了设置,当然,你可以设置单元格的默认值,为改变单元格的默认值,使用getDefaultCell()和调用一个或更多的类PdfPCell的方法(你可以设置对齐方式、间距、边框、颜色甚至最低高度)。
注:通过PdfPTable,你能改变一个单元格的列跨度,但不能改变行跨度!在PdfPTable内部是一些独立的行,要让它支持行跨度更改需要对PdfPTable对象进行很大的调整,不要期望在近期内实现,你可以用嵌套表来解决这些问题。
你可以象第5章一样将一个PdfPTable添加到当前文档中,但你也可以添加一个表在当前页中的绝对位置:
public float writeSelectedRows(int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte canvas);
参数rowStart是你想开始的行的数目,参数rowEnd是你想显示的最后的行(如果你想显示所有的行,用-1),xPos和yPos是表格的坐标,canvas是一个PdfContentByte对象。在示例代码1009中,我们添加了一个表在(100,600)处:
table.writeSelectedRows(0, -1, 100, 600, writer.DirectContent);
使用PdfPTable,你不能设置行跨度和(或)来跨度(怎么和上面的有点矛盾?)你可以使用嵌套表来解决,见示例代码1010。
最后,示例代码1011和示例代码1012展示了PdfTable可以和templates 和 columns一起使用,在示例代码1012中将用到cover.png图片如下:
颜色(SpotColors)和图案(Patterns)
颜色(spotcolors)的使用见示例代码1013,示例代码1014和示例代码1015演示了图案(patterns)的使用方法。
本地转向
有时你需要一个允许读者从文档的一个地方跳转到另外一个地方的链接,你可以通过类Chunk的setLocalGoto 和setLocalDestination两个方法实现,例:
Chunk localgoto = new Chunk("this word", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL, new Color(0, 0, 255))).setLocalGoto("test");
Chunk destination = new Chunk("local destination", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL, new Color(0, 255, 0))).setLocalDestination("test");
见示例代码1101。
异地转向
在第3章中,我们演示了一个锚点如何转向到其他URL,一个锚点通过不同的字体、风格和颜色,可以包含不同的Chunks,在iText的高级应用中,下面定义链接到URL的其他方法:
Chunk chunk = new Chunk("anchor", FontFactory.getFont(FontFactory.HELVETICA, 12)).setAnchor(new URL("http://www.lowagie.com/iText/"));
u 转到PDF文档中的指定位置
如果你在文档中指定了一个目的地,你可以从另外一个文档跳转到这里,为实现该功能,你可以使用方法:
setRemoteGoto: Chunk chunk = new Chunk("jump", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.ITALIC)).setRemoteGoto("test.pdf", "test"));
test.pdf是另外一个pdf文件,”test”是该文件的一个目的地。
跳转到另一个PDF文件指定页
使用方法setRemoteGoto,用页码参数代替名称参数,可以非常容易地跳转定另外一个文档的指定页:
chunk = new Chunk("jump", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.ITALIC)).setRemoteGoto("test.pdf", 3));
见示例代码1102
u 启动一个应用程序
可以使用下面的方法启动一个应用程序:
public PdfAction(String application, String parameters, String operation, String defaultDir)
如果application为“c:/winnt/notepad.exe”(其余参数可以为null),你可以通过PDF文件中的链接来启动记事本程序。
u 文件和URL
如果你想跳转到其他文档或URL,你需要通过下面的构造函数之一创建一个:
PdfAction(String filename, String name);
PdfAction(String filename, int page);
PdfAction(URL url);
PdfAction(String url);
前面两个构造函数允许你跳转到文件的指定位置或页码,后两个构造函数允许你跳转到其他URL上。