前言
在代碼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 讀書筆記。