<<iText in Action 2nd>>3.2节(Adding text at absolute positions)读书笔记
前言
在代码listing3.1中我们在文档的顶部打印出字符串"SOLD OUT"。我们使用了SetTextRenderingMode和SetTextMatrix等方法,不过使用这些比较低级的方法来创建一个完整的文档并不是一件很容易的事情,我们应该使用iText中提供的便利方法来替我们完成部分工作。这些便利方法需要的代码更少,代码的复杂性也随之减少。
便利的方法: PdfContentByte.ShowTextAligned()
在listing3.1中当我们添加单词"SOLD OUT"时,其中包含了大量的数学计算:我们需要计算旋转角度的sin值和cos值,还需要丈量字符串"SOLD OUT"的长度来决定其坐标值以便于让文本看起来有点居中。字符串的长度并不只取决于字符串中的字符,我们还需要知道字符串使用的字体。单词"SOLD OUT"如果使用相同字体大小的Helvetia和TimesRoman其长度是不同的。
丈量字符串的长度
如果可以获取字符对应字体BaseFont类的实例就可以计算字符的长度。在以下的代码中,我们就通过GetWidthPoint方法计算了字符串"Foobar Film Festival"的长度。就以下例子而言:使用12pt的Helvetica字体,那么丈量的长度就是108.684pt,使用相同字体大小的times.tff字体丈量的长度就是100.572pt,它们之间有0.11英寸的差距。
listing 3.7 FoobarFilmFestival.cs
Chunk c; string foobar = "Foobar Film Festival"; // Measuring a String in Helvetica Font helvetica = new Font(Font.FontFamily.HELVETICA, 12); BaseFont bf_helv = helvetica.GetCalculatedBaseFont(false); float width_helv = bf_helv.GetWidthPoint(foobar, 12); c = new Chunk(foobar + ": " + width_helv, helvetica); document.Add(new Paragraph(c)); document.Add(new Paragraph(string.Format("Chunk width:{0}", c.GetWidthPoint()))); // Measuring a String in Times BaseFont bf_times = BaseFont.CreateFont("c:/windows/fonts/times.ttf", BaseFont.WINANSI, BaseFont.EMBEDDED); Font times = new Font(bf_times, 12); float width_times = bf_times.GetWidthPoint(foobar, 12); c = new Chunk(foobar + ": " + width_times, times); document.Add(new Paragraph(c)); document.Add(new Paragraph(string.Format("Chunk width:{0}", c.GetWidthPoint())));
在以上代码中大家还会发现Chunk类也有GetWidthPoint方法,不过丈量的结果是Chunk对象的长度。
ASCENT AND DESCENT OF THE STRING
ascent是字符位于基准线之上的距离,descent就是字符位于基准线之下的距离。在以下的例子中我们使用BaseFont对象的GetAsecentPoint方法和GetDescentPoint方法来计算ascent和descent值。
listing 3.8 FoobarFilmFestival.cs (continued)
// Ascent and descent of the String document.Add(new Paragraph("Ascent Helvetica: " + bf_helv.GetAscentPoint(foobar, 12))); document.Add(new Paragraph("Ascent Times: " + bf_times.GetAscentPoint(foobar, 12)));
有了对应的ascent和descent值,就可以通过ascent值减去descent值获取字符串的高度。这里要注意的是:字体的大小不是一个特定字符的高度,而且一行文本在垂直方向的指定距离。
下图就是代码生成pdf的部分截图:
在上图中你可能会认为字体为9pt,但实际上不是。Helvetica字体的字符串的ascent值为8.616001,加上0.18总的值小于9。因为在这个例子中,所有的字符的descent的都比较小,它没有包含一些像g,j,p,q或者y等这些descent值超过-0.18的字符。上图的下部分是单词"Foobar"放大后的效果,大家可以看到基本上都在基准线附近。
定位字符串
现在我们已经知道丈量一个字符串的长度,这样我们就可以计算坐标值并使用文本矩阵(text matrix)从而使文本靠右或靠左对齐。幸运的是我们可以使用便利的方法来完成就如以下代码,具体的效果图大家已经从上一部的截图看到。
listing 3.9 FoobarFilmFestival.cs (continued)
// Adding text with PdfContentByte.showTextAligned() canvas.BeginText(); canvas.SetFontAndSize(bf_helv, 12); //文本从座标x=400开始,基准线为788 canvas.ShowTextAligned(Element.ALIGN_LEFT, foobar, 400, 788, 0); //文本在座标x=400结束,基准线为752 canvas.ShowTextAligned(Element.ALIGN_RIGHT, foobar, 400, 752, 0); //文本在座标x=400居中,基准线为716 canvas.ShowTextAligned(Element.ALIGN_CENTER, foobar, 400, 716, 0); //文本在座标x=400居中,基准线为680,但有30度的旋转 canvas.ShowTextAligned(Element.ALIGN_CENTER, foobar, 400, 680, 30); //kerned 模式 canvas.ShowTextAlignedKerned(Element.ALIGN_LEFT, foobar, 400, 644, 0); canvas.EndText();
以上代码的ShowTextAlignedKerned方法是对字符进行了一些字距调整。
KERNING(字距调整)
kerning(字距调整)就是在一些比例对称的字符之间调整距离。使用kerning可以在连续的特定字符之间节省空间。具体例子来说:我们可以将单词"AWAY"中字符之间的距离减少,因为字符A和字符W是对称的,说的通俗一点就是字符A是上凹下凸,字符W是上凹下凸,两个字符是可以"合体"的。不过对于字符串"Foobar Film Festival"而言使用kerning模式减少的空间不是很多。
listing 3.10 FoobarFilmFestival.cs (continued)
// Kerned text width_helv = bf_helv.GetWidthPointKerned(foobar, 12); c = new Chunk(foobar + ": " + width_helv, helvetica); document.Add(new Paragraph(c));
从以上的代码大家可以得知:使用了kerned版本的字符串长度是107.664pt,没有使用kerned的长度是108.684pt。
ADDING TEXT TO THE TIMETABLE
啰啰嗦嗦的将了很多,现在应该到了往上一节的Film festival timeable添加文本的时候了。以下为代码:
listing 3.11 MovieTextInfo.cs
protected void DrawDateInfo(DateTime day, int d, PdfContentByte directcontent) { directcontent.BeginText(); directcontent.SetFontAndSize(bf, 18); float x, y; x = OFFSET_LOCATION; y = OFFSET_BOTTOM + HEIGHT + 24; directcontent.ShowTextAligned(Element.ALIGN_LEFT, "Day " + d, x, y, 0); x = OFFSET_LEFT + WIDTH; directcontent.ShowTextAligned(Element.ALIGN_RIGHT, day.ToString("yyyy-MM-dd"), x, y, 0); directcontent.EndText(); }
以上的代码是在pdf文档中打印类似"Day5"和"2011-10-16"的文本。代码中调用的方法都已经解释过了,大家要注意以下几点:
- BeginText和EndText方法要对称。
- 文本状态的设置要放置于BeginText和EndText方法之间。
- 不要忘记是在字体和字体大小。
目前为止ShowTextAligned方法表述的都比较直接,但如果我们要直接定位不是文本而是一个Phrase对象,那么此方法就不太适用。这个问题我们会在接下来使用ColumText对象的showTextAligned方法解决。
便利的方法: ColumnText.ShowTextAligned()
直接上代码:
listing 3.12 FoobarFilmFestival.cs (continued)
Phrase phrase = new Phrase(foobar, times); ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, phrase, 200, 572, 0); ColumnText.ShowTextAligned(canvas, Element.ALIGN_RIGHT, phrase, 200, 536, 0); ColumnText.ShowTextAligned(canvas, Element.ALIGN_CENTER, phrase, 200, 500, 0); ColumnText.ShowTextAligned(canvas, Element.ALIGN_CENTER, phrase, 200, 464, 30); ColumnText.ShowTextAligned(canvas, Element.ALIGN_CENTER, phrase, 200, 428, -30);
在以上代码中,我们创建了一个包含了文本和字体的Phrase对象,并调用了ColumnText的ShowTextAligned方法,在这里就少了BeginText和EndText等一系列毕竟底层的方法,看起来有一点不同,不过更加简洁明了。
POSITIONING A PHRASE
Phrase对象可以有一系列的Chunk对象组成,通过将Phrase对象绝对定位我们可以很容易的更换字体,字体大小,而对应文本状态的底层工作就被掩盖了。接下来我们会在用来预览的屏幕上答应一个很大的白色P字符,而且此字符位于带颜色矩形的上面。
listing 3.13 MovieTextInfo.cs (continued)
Font f = new Font(bf, HEIGHT_LOCATION / 2); f.Color = BaseColor.WHITE; press = new Phrase("P", f); protected virtual void DrawMovieInfo(Screening screening, PdfContentByte directcontent) { if (screening.IsPress) { Rectangle rect = GetPosition(screening); ColumnText.ShowTextAligned(directcontent, Element.ALIGN_CENTER, press, (rect.Left + rect.Right) / 2, rect.Bottom + rect.Height / 4, 0); } }
虽然以上的代码是处理一下high-level的对象,不过我们还是可以通过Chunk对象的属性来设置文本状态。
CHUNKS: SCALING, SKEWING, RENDERING MODE
图的左边是设置了不同的对其或旋转的效果图。图的右边相同的字符串都是左对齐,但文本被缩放(scaled),倾斜(skewed)或者呈现模式(rendering mode)被修改。
以下为具体的实现代码:
listing 3.14 FoobarFilmFestival.cs (continued)
c = new Chunk(foobar, times); c.SetHorizontalScaling(0.5f); phrase = new Phrase(c); ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 572, 0); c = new Chunk(foobar, times); c.SetSkew(15, 15); phrase = new Phrase(c); ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 536, 0); c = new Chunk(foobar, times); c.SetSkew(0, 25); phrase = new Phrase(c); ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 500, 0); c = new Chunk(foobar, times); c.SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_STROKE, 0.1f, BaseColor.RED); phrase = new Phrase(c); ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 464, 0); c = new Chunk(foobar, times); c.SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, null); phrase = new Phrase(c); ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 428, -0);
我们可以通过设置Chunk对象的SetHorizontalScaling方法来改变Chunk对象的长度,在以上的代码中,我们缩放了50%的宽度,但字符的高度没有变,也就说字符外观的高宽比变了,调用这个方法时注意不要设置的太大或太小。
Chunk对象的SetSkew方法可以将字体倾斜,方法有两个参数,第一个参数修改了基准线的旋转角度,第二个参数是用来定义字符和基准线之间的角度。具体的效果大家可以看生成的pdf文档。这里有一个小技巧:如果一个字体中没有倾斜的样式,我们可以设置SetSkew(0,25)来模拟倾斜。
最后要介绍的是SetTextRenderMode方法,此方法的一个参数可以有以下几种:
- PdfContentByte.TEXT_RENDER_MODE_FILL--这个参数也是默认的选项:字体被填充,但没有边框。
- PdfContentByte.TEXT_RENDER_MODE_STROKE--这个参数会导致字体没有变填充,但又边框存在,看起来就是中空的。
- PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE--这个参数设置字体被填充以及有边框。
- PdfContentByte.TEXT_RENDER_MODE_INVISIBLE--文本在,但是看不见。
两个格外的参数定义的是边框的线框和颜色,如果颜色为null值就从Chunk类的Font对象获取具体的颜色。同样这里也有一个小技巧:如果一个字体中没有粗体样式,那么可以通过SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, null)方法来模拟,就如果代码中一样。
以上介绍Chunk对象的属性在通过Document.Add方法添加到文档时也是适用的。
总结
现在我们的Film festival Timetable基本上快完成了,以下为效果图:
现在在文档上缺少的就只有每部电影的名称,这里我们希望当电影的标题太长的时候矩形中的电影标题文本可以自动的缩放或者换行。但这个不能通过ShowTextAligned方法来实现,因为我们只是传入一个坐标值。不过我们可以通过ColumnText对象的实例来实现此功能,具体的使用在下一篇中会介绍,最后是这一节的代码下载。
同步
此文章已同步到目录索引:iText in Action 2nd 读书笔记。