1. 程式人生 > >用C# itextsharp.dll製作PDF檔案全攻略

用C# itextsharp.dll製作PDF檔案全攻略

--------------------------------------------------------------------------------------

StreamWriter pPDF=new StreamWriter(filePath);

ArrayList xRefs=new ArrayList();

float yPos =0f;

long streamStart=0;

long streamEnd=0;

long streamLen =0;

string strPDFMessage=null;

//PDF文件頭資訊

strPDFMessage="%PDF-1.1/n";

ConvertToByteAndAddtoStream(strPDFMessage);

xRefs.Add(mPDF.Length);

strPDFMessage="1 0 obj/n";

ConvertToByteAndAddtoStream(strPDFMessage);

strPDFMessage="<< /Length 2 0 R >>/n";

ConvertToByteAndAddtoStream(strPDFMessage);

strPDFMessage="stream/n";

ConvertToByteAndAddtoStream(strPDFMessage);

……

看了上面的製作辦法,我眼鏡都摔壞了三幅,如果用上面這樣原始的辦法能製作出滿意的PDF檔案,那一定是天才所為。後來,我從一個網站(網址:http://itextsharp.sourceforge.net/index.HTML)中看到了專門製作PDF檔案的控制元件的介紹,暗喜之餘,立馬下載試驗,果然非常輕鬆地製作出了想要的PDF檔案,因為網站為英文,內容又多,讀起來非常費力,在解決了自己的問題後,看到許多網友還在為PDF檔案製作而鬱悶,遂決定將該內容翻譯為中文,由於本人英語水平一般,許多地方又晦澀難懂,故翻譯質量不是很滿意,敬請斧正,但大部分能看懂。本文的目的一是解決部分網友的燃眉之急,二是拋磚引玉,如果哪位仁兄願意將該網站中的內容準確翻譯出來,則是天下之大幸。

要用本文的方法生成PDF檔案,需要兩個控制元件:itextsharp.dll和ICSharpCode.SharpZipLib.dll,由於示例程式碼實在太多,我將程式碼全部整理出來,放在另外一個檔案“示例程式碼.doc”中,所有這些資源,我均放在了本人的ftp站點(ftp://202.107.251.26)上的“Pdf檔案製作全攻略”資料夾中(資料夾中另外兩個rar壓縮檔案為兩個控制元件的原始碼,供大家學習研究使用),你可以到這裡下載相應的資源,或者直接到原網站下載。

為便於除錯和敘述,所有例子均為DOS控制檯程式,windows程式使用方法完全一樣,按照下面的步驟建立一個可除錯的專案:

1、 開啟VS2003;

2、 單擊選單“檔案”→“新建”→“專案”,在專案型別中選擇“Visual C#專案”,在模板中選擇“控制檯應用程式”,輸入檔名稱如“MakePdf”,指定好存放路徑,然後點確定按鈕;

3、 在“解決方案資源管理器”中右鍵單擊“引用”,從彈出的選單中選擇“新增引用”,在“.NET”選項夾中選擇“瀏覽”,新增前面提到的兩個應用,如下圖:


4、 在程式碼視窗頂部新增兩個引用:

using iTextSharp.text;

using iTextSharp.text.pdf;

至此,準備工作完畢。

第一部分 iText的簡單應用
第一章 建立一個Document
利用iText五步建立一個PDF檔案:helloword。

第一步,建立一個 iTextSharp.text.Document物件的例項:

Document document = new Document();

第二步,為該Document建立一個Writer例項:

PdfWriter.getInstance(document, new FileStream("Chap0101.pdf", FileMode.Create));

第三步,開啟當前Document

document.Open();

第四步,為當前Document新增內容:

document.Add(new Paragraph("Hello World"));

第五步,關閉Document

document.Close();

完整的程式碼見示例程式碼0101。

在例中,不難看出,製作一個PDF檔案是非常簡單的。

注:如果你將例中“document.Add(new Paragraph("Hello World"));”中的字串“Hello Word”換成中文,如“這是我的第一個PDF檔案”,產生的結果一定讓你大失所望,因為生成的PDF檔案中並沒有將中文顯示出來,不要擔心,在第9章中要專門講解字型問題,中文顯示也就迎刃而解了,如果不能正確顯示中文,也就沒有必要翻譯本文了。

下面對這幾步做詳細介紹。

第一步 建立一個Document例項:
iTextSharp.text.Document-object共有三個建構函式:

public Document();

public Document(Rectangle pageSize);

public Document(Rectangle pageSize,

int marginLeft,

int marginRight,

int marginTop,

int marginBottom);

第一個建構函式以A4頁面作為引數呼叫第二個建構函式,第二個建構函式以每邊36磅頁邊距為引數呼叫第三個建構函式

u 頁面尺寸:

你可以通過指定的顏色和大小建立你自己的頁面,示例程式碼0102建立一個細長的淺黃色背景的頁面:

Rectangle pageSize = new Rectangle(144, 720);

pageSize.BackgroundColor = new Color(0xFF, 0xFF, 0xDE);

Document document = new Document(pageSize);

通常,你不必建立這樣的頁面,而可以從下面頁面尺寸中選擇:

A0-A10, LEGAL, LETTER, HALFLETTER, _11x17, LEDGER, NOTE, B0-B5, ARCH_A-ARCH_E, FLSA 和 FLSE

大多數情況下使用縱向頁面,如果希望使用橫向頁面,你只須使用rotate()函式:

Document document = new Document(PageSize.A4.rotate());

詳細程式碼見示例程式碼0103。

u 頁邊距:

當建立一個檔案時,你還可以定義上、下、左、右頁邊距:

Document document = new Document(PageSize.A5, 36, 72, 108, 180);

在示例程式碼0104中你可以看到該文件有一個0.5英寸的左邊距和1英寸的右邊距,上邊距為1.5英寸,下邊距為2.5英寸。

說明:

當建立一個矩形或設定邊距時,你可能希望知道該用什麼度量單位:釐米、英寸或象素,事實上,預設的度量系統以排版單位磅為基礎得出其他單位的近似值,如1英寸=72磅,如果你想在A4頁面的PDF中建立一個矩形,你需要計算以下資料:

21 釐米 / 2.54 = 8.2677 英寸

8.2677英寸* 72 = 595 磅

29.7 釐米 / 2.54 = 11.6929 英寸

11.6929英寸* 72 = 842 磅

預設邊距為36磅即半英寸。

如果你修改了頁面尺寸,僅僅影響到下一頁,如果你修改了頁邊距,則影響到全部,故慎用。

關於頁面的初始值,請參考第三步。

第二步 建立Writer例項
一旦建立了document,我們可以建立該文件的多個Writer的例項,所有這些Writer例項均繼承自抽象類“iTextSharp.text.DocWriter”。

同時還有另外一種情況,你可以用iTextSharp.text.pdf.PdfWriter產生文件PDF檔案,如果你想建立一個TeX文件,你可以使用iTextSharp.text.TeX.TeXWriter包。

Writer類的建構函式是私有的,你只能通過下面的方法建立一個例項:

public static xxxWriter getInstance(Document document, Stream os);(xxx 是 Pdf 或 XML)

你可以通過下面的方法建立一個例項:

PdfWriter writer = PdfWriter.getInstance(document, new FileStream("Chap01xx.pdf"));

但是你幾乎永遠不會用到Writer例項(除非你想建立高階PDF或者希望用一些非常特殊的函式,如ViewerPreferences 或 Encryption)。所以通過下面的辦法得到例項已經足夠了: PdfWriter.getInstance(document, new FileStream("Chap01xx.pdf"));

在第一步中建立一個文件時,第一個引數意義不大,第二個引數可以是任何一種流,到目前為止我們一直使用System.IO.FileStream將Document寫入檔案中,示例程式碼0105用到了System.IO.MemoryStream(這不是一個獨立的例子,你必須在Servlet Engine中測試這些程式碼。

第三步 開啟Document
u 摘要

在你寫入任何實際資料之前,你可能希望通過以下幾種方法寫入一些關於本文件的摘要:

public boolean addTitle(String title)

public boolean addSubject(String subject)

public boolean addKeywords(String keywords)

public boolean addAuthor(String author)

public boolean addCreator(String creator)

public boolean addProducer()

public boolean addCreationDate()

public boolean addHeader(String name, String content)

你可以選擇自己的標題、主題、關鍵字、作者、建立程式,但以下產品資訊將始終被新增:iTextSharp (或者iTextSharp的引用)和建立時間(實際上這兩種方法是自動呼叫的)。

你還可以將自定義的名稱新增為“報頭資訊”,但是這對於PdfWriter沒有任何作用,如果看看例項程式碼0101產生的pdf檔案的“文件屬性”,我們可以看到僅僅有PDF建立程式和產品日期,而示例程式碼0106的“文件屬性”框中有更多的資訊。

開啟document前要做的事:

你只能在Open方法呼叫之前新增摘要,這是iText開發工具提供的一個選擇。

在HTML中,報頭資訊被放在文件前面報頭標識中間,呼叫Open方法將導致報頭資訊寫入流,因而在Document被開啟後無法更改這些資料。

PDF報頭資訊不包括摘要,看起來有類似於:

%PDF-1.2

該行顯示生成的文件是一個版本為1.2的PDF格式的檔案,在PDF中,摘要儲存在PdfInfo物件中,當文件關閉時已經寫入PdfWriter中了,因此,沒有關於為什麼不能修改庫來滿足任何時候新增或更改摘要的技術原因

u 頁面初始化

Open方法在不同的Witer中同時會產生初始化事件,舉例來說,如果你需要一個水印或者頁首頁角物件出現在文件第一頁的開始處,你需要在開啟文件前新增這些,同樣的用於設定該文件其他頁水印、頁首、頁角、頁數和尺寸。

當呼叫下列方法:

public bool setPageSize(Rectangle pageSize)

public bool Add(Watermark watermark)

public void removeWatermark()

setting Header property

public void resetHeader()

setting Footer property

public void resetFooter()

public void resetPageCount()

setting PageCount property

產生的結果只能在下一個新頁中看到(當在本頁呼叫初始化方法時),程式碼見示例程式碼0107,你必須要準備一張名為watermark.jpg的圖片,如下圖:


u 閱讀器引數:

你可以通過下面的辦法為PDF檔案指定一些閱讀器 (如Adobe Reader) 引數:

public void setViewerPreferences(int preferences)

在示例程式碼0108中,指定了下面一些引數:

writerA.setViewerPreferences(PdfWriter.PageLayoutTwoColumnLeft);

writerB.setViewerPreferences(PdfWriter.HideMenubar | PdfWriter.HideToolbar);

writerC.setViewerPreferences(PdfWriter.PageLayoutTwoColumnLeft | PdfWriter.PageModeFullScreen | PdfWriter.NonFullScreenPageModeUseThumbs);

正如你所看到的,引數可以使用以下一些常量:

l 檔案被開啟時,頁面佈局用到下面的其中一個 :

PdfWriter.PageLayoutSinglePage – 同時只顯示一個頁面
PdfWriter.PageLayoutOneColumn –單列顯示
PdfWriter.PageLayoutTwoColumnLeft –雙列顯示,奇數頁在左
PdfWriter.PageLayoutTwoColumnRight -雙列顯示,奇數頁在右
l 檔案開啟時,頁面模式用到下面其中之一:

PdfWriter.PageModeUseNone – 既不顯示大鋼也不顯示縮圖
PdfWriter.PageModeUseOutlines – 顯示大綱
PdfWriter.PageModeUseThumbs – 顯示縮圖
PdfWriter.PageModeFullScreen – 全屏模式,沒有選單、windows控制元件或者其他任何windows可見控制元件
l PdfWriter.HideToolbar – 當文件啟用時,是否隱藏閱讀程式(如Adobe Reader)的工具條

l PdfWriter.HideMenubar -當文件啟用時,是否隱藏閱讀程式的選單.

l PdfWriter.HideWindowUI -當文件啟用時,是否隱藏閱讀程式的介面元素,如滾動條、導航條等,而僅僅保留文件顯示

l PdfWriter.FitWindow – 是否調整文件視窗尺寸以適合顯示第一頁。

l PdfWriter.CenterWindow – 是否將文件視窗放到螢幕中央

l 在全屏模式下,指定如何顯示介面元素(選擇一個)

PdfWriter.NonFullScreenPageModeUseNone -既不顯示大鋼也不顯示縮圖
PdfWriter.NonFullScreenPageModeUseOutlines – 顯示大鋼
PdfWriter.NonFullScreenPageModeUseThumbs – 顯示縮圖
說明:你只能在類PdfWriter中呼叫這些方法。

u 加密

開啟文件之前還要做的一件事情就是加密(如果你希望該文件加密),要達到這個目的,你可以使用下面的方法:

public void setEncryption(boolean strength, String userPassword, String ownerPassword, int permissions);

strength 是下面兩個常量之一:
PdfWriter.STRENGTH40BITS: 40 位
PdfWriter.STRENGTH128BITS: 128位 (Acrobat Reader 5.0及以上版本支援)
UserPassword和ownerPassword 可以為空或零長度, 這種情況下, ownerPassword 將被隨機的字串代替
Permissions 為下列常量之一:
PdfWriter.AllowPrinting
PdfWriter.AllowModifyContents
PdfWriter.AllowCopy
PdfWriter.AllowModifyAnnotations
PdfWriter.AllowFillIn
PdfWriter.AllowScreenReaders
PdfWriter.AllowAssembly
PdfWriter.AllowDegradedPrinting
該功能參見示例程式碼0109和示例程式碼0110。

writer.setEncryption(PdfWriter.STRENGTH40BITS, null, null, PdfWriter.AllowCopy);

示例程式碼0109產生的檔案能夠被開啟而無須密碼,但使用者不能列印、修改本文件。

writer.setEncryption(PdfWriter.STRENGTH128BITS, "userpass", "ownerpass", PdfWriter.AllowCopy | PdfWriter.AllowPrinting);

打你試圖開啟示例程式碼0110產生的檔案時,將要求輸入密碼('userpass'),因為添加了AllowPrinting引數,你可以列印該文件而不會發生任何問題。

第四步 新增內容
在解釋第一步到第三步的不同示例中,你可能已經遇到了一些物件如Phrase, Paragraph等 在接下來的幾章中,所有這些問題都將得到詳細解釋。

有時你可能想一個writer故意忽略document產生的行為,如示例程式碼0111:

當我們建立了兩個writer: writerA 和 writerB:

PdfWriter writerA = PdfWriter.getInstance(document, new FileStream("Chap0111a.pdf", FileMode.Create));

PdfWriter writerB = PdfWriter.getInstance(document, new FileStream("Chap0111b.pdf", FileMode.Create));

我們可以建立兩個有細微差別的文件:

writerA.Pause();

document.add(new Paragraph("This paragraph will only be added to Chap0111b.pdf, not to Chap0111a.pdf"));

writerA.resume();

你可以比較檔案: Chap0111a.pdf和Chap0111b.pdf的區別

第五步,關閉 document
關閉 document 非常重要, 因為它將關閉正在執行的Writer並將內容寫入檔案,該方法在最後被呼叫,你應該總是要關閉文件。

高階話題:閱讀PDF檔案

該部分內容介紹了iText只能產生PDF格式的檔案而不能解析PDF格式檔案,不再翻譯。

第二章 塊、短句和段落

塊(Chunk)是能被新增到文件的文字的最小單位,塊可以用於構建其他基礎元素如短句、段落、錨點等,塊是一個有確定字型的字串,要新增塊到文件中時,其他所有佈局變數均要被定義。下面一行中,我們建立了一個內容為“hello World”、紅色、斜體、COURIER字型、尺寸20的一個塊:

Chunk chunk = new Chunk("Hello world", FontFactory.getFont(FontFactory.COURIER, 20, Font.ITALIC, new Color(255, 0, 0)));

u 典型字型1:

在本指南中,除了第九章外(你可以在這裡學會使用其他字型),我們將始終使用典型字型1,這些是不同的典型字型1:

· Courier (該字型定寬)

· Helvetica

· Times Roman

· Symbol

· ZapfDingbats

u 下劃線/刪除線

如果你希望一些塊有下劃線或刪除線,你可以通過改變字型風格簡單做到:

Chunk chunk1 = new Chunk("This text is underlined", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE));

Chunk chunk2 = new Chunk("This font is of type ITALIC | STRIKETHRU", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.ITALIC | Font.STRIKETHRU));

u 上標/下標

在塊中有幾個方法可以呼叫,其中大部分將在接下來的章節中介紹,本章中只介紹一個方法 setTextRise(float f). 你可以使用該方法在上標或下標中寫塊。

u 塊的背景

如果你想改變塊的背景,你可以使用方法setBackground(Color color). 這將在塊文字的下面新增一個彩色矩形:

ck.setBackground(new Color(0xFF, 0xFF, 0x00));

在示例程式碼0101中,你可以概覽典型字型1和一個使用setTextRise, setBackground等方法的的例子。

短句
短句(Phrases)是一系列以特定間距(兩行之間的距離)作為引數的塊,一個短句有一個主字型,但短句中的一些塊具有不同於主字型的字型,你有更多的選擇去建立短句,一些具體使用參見程式碼0202。

u 古希臘語

因為古希臘語經常使用,在類Phrase的建構函式中有一個特徵:將一個字串作為引數(如果你想避免這種情況,你只能使用塊工作而不能使用字串),正如你在示例程式碼0203中看到的,這個特徵自動地將913至937(除903)和945至969(古希臘的ASCII值)範圍內的所有字型改為希臘符號。

u 非主要性

與其說這是一個特徵,不如說是一個缺陷,但無論如何,這使建立一個非主要性的短句或段落成為可能,這將產生一個由下向上書寫的臨時作用(參見示例程式碼0204)。如果你想在一頁中將一些位置移動到上面時可能有用。

說明,當你穿越上邊屆時無法檢查,也沒有辦法讓你回到前一頁。

段落
段落是一系列塊和(或)短句。同短句一樣,段落有確定的間距。使用者還可以指定縮排;在邊和(或)右邊保留一定空白,段落可以左對齊、右對齊和居中對齊。新增到文件中的每一個段落將自動另起一行。有幾種辦法建立一個段落,如:

Paragraph p1 = new Paragraph(new Chunk("This is my first paragraph.", FontFactory.getFont(FontFactory.HELVETICA, 12)));

Paragraph p2 = new Paragraph(new Phrase("This is my second paragraph.", FontFactory.getFont(FontFactory.HELVETICA, 12)));

Paragraph p3 = new Paragraph("This is my third paragraph.", FontFactory.getFont(FontFactory.HELVETICA, 12));

所有有些物件將被新增到段落中:

p1.add("you can add strings, "); p1.add(new Chunk("you can add chunks ")); p1.add(new Phrase("or you can add phrases."));

說明:一個段落有一個且僅有一個間距,如果你添加了一個不同字型的短句或塊,原來的間距仍然有效,你可以通過SetLeading來改變間距,但是段落中所有內容將使用新的中的間距。見示例程式碼0205。

u 保持段落的整體性

在示例程式碼0206中,我們使用了setKeepTogether(true)方法來試圖將一個段落放在同一頁中,該方法並不是始終有效,舉個例子,第一段不能剛好在一頁中,於是被分成了兩部分。第二段被放置在第二頁,但第三段順沿到了第三頁上。

字型的延續
你應該掌握字型延續的一些規則,這些規則的應用見示例程式碼0207,當我們將一些內容用指定的字型(非預設字型)建立一個短句或者段落後再新增更多內容時,初始物件的字型風格將被延續,請看“Hello 1!”和“Hello 2”:

Phrase myPhrase = new Phrase("Hello 2! ", new Font(Font.TIMES_NEW_ROMAN, 8, Font.BOLD));

myPhrase.Add(new Phrase("some other font ", new Font(Font.HELVETICA, 8, Font.ITALIC)));

myPhrase.Add(new Phrase("This is the end of the sentence./n", new Font(Font.TIMES_NEW_ROMAN, 8, Font.ITALIC)));

document.Add(myPhrase);

我們由Times New Roman 粗體字開始,新增一些文字使用Helvetica字型而不指定風格,我們發現文字被改變成了粗體,當我們再加一些文字使用Times New Roman字型和斜體風格,結果變成了粗斜體。

如果我們使用FontFactory來建立字型,字型風格不會被延續,因為FontFactory使用了另外的技術構建一個字型:

myPhrase = new Phrase("Hello 1bis! ", FontFactory.getFont(FontFactory.TIMES_NEW_ROMAN, 8, Font.BOLD));

myPhrase.Add(new Phrase("some other font ", FontFactory.getFont(FontFactory.HELVETICA, 8, Font.ITALIC)));

myPhrase.Add(new Phrase("This is the end of the sentence./n", FontFactory.getFont(FontFactory.TIMES_NEW_ROMAN, 8, Font.ITALIC)));

document.Add(myPhrase);

在上面的程式碼中,使用Helvetica字型的文字風字型沒有指定(既不是粗體也不是斜體)。採用Times New Roman的額外文字僅僅顯示為斜體。

你也看到我們添加了一個段落,新增該段落就如同一個短句。

Paragraph myParagraph = new Paragraph("Hello 1! ", new Font(Font.TIMES_NEW_ROMAN, 8, Font.BOLD));

myParagraph.Add(new Paragraph("This is the end of the sentence.",FontFactory.getFont(new Font.TIMES_NEW_ROMAN, 8)));

document.Add(myParagraph);

你可以不這樣做,但將失去字型風格的延續,首先不用任何字型建立段落(例中我們僅僅給字型出間距為1.5倍),然後新增內容的不同部分。

myParagraph = new Paragraph(12);

myParagraph.Add(new Paragraph("Hello 3! ", new Font(Font.TIMES_NEW_ROMAN, 8, Font.BOLD)));

myParagraph.Add(new Paragraph("This is the end of the sentence.", new Font(Font.TIMES_NEW_ROMAN, 8, Font.ITALIC)));

document.Add(myParagraph);

如果你使用了Phrase物件,你同樣會失去字型風格的延續:

myPhrase = new Phrase(12);

myPhrase.Add(new Phrase("Hello 4! ", new Font(Font.TIMES_NEW_ROMAN, 8, Font.BOLD)));

myPhrase.Add(new Phrase("This is the end of the sentence.", newFont(Font.TIMES_NEW_ROMAN, 8, Font.ITALIC)));

document.Add(myPhrase);

u 更改分割符

通常,當文字不能放在一行時,文字將被分割成不同的部分,iText首先會查詢分割符,如果沒有找到,文字將在行尾被截斷。有一些預定的分割符如“ ”空格和“-”連字元,但是你可以使用setSplitCharacter方法來覆蓋這些預設值。在示例程式碼0208中,你可以看到當到達行尾時一個塊是如何被分割的。然後分隔符被改成點“.”,該行在該字元處被分割。

第三章 錨點、列表和註釋
錨點
我們都知道HTML中的超文字連結,當我們點選某些語句,你能夠跳轉到網上的其他頁。在PDF中也可以實現這種功能。事實上,在第十一章整個章節中有關於PDF連結的介紹,但這是iText的更高階的應用,本章中我們處理簡單的iText。

如果你想在文件中新增一個外部連結(例如使用URL連結到WEB上的其他文件),你可以簡單地使用Anchor物件,它派生於Phrase物件,使用方法相同。只有兩種額外方法定義兩種額外變數:setName和 setReference。

外部連結示例:

Anchor anchor = new Anchor("website", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE, new Color(0, 0, 255)));

anchor.Name = "website";

如果你想新增內部連結,你需要選擇該連結不同的名稱,就象你相位在HTML中利用名稱作為錨點一樣。為達到該目的,你需要新增一個“#”。

內部連結示例:

Anchor anchor1 = new Anchor("This is an internal link");

anchor1.Name = "link1";

Anchor anchor2 = new Anchor("Click here to jump to the internal link");

anchor.Reference = "#link1";

這兩個連結的例子請見示例程式碼0301。

列表
通過類List 和ListItem,你可以新增列表到PDF檔案中,對於列表你還可以選擇是否排序。

排序列表示例:

List list = new List(true, 20);

list.Add(new ListItem("First line"));

list.Add(new ListItem("The second line is longer to see what happens once the end of the line is reached. Will it start on a new line?"));

list.Add(new ListItem("Third line"));

結果如下:

First line
The second line is longer to see what happens once the end of the line is reached. Will it start on a new line?
Third line
不排序示例如下:

List overview = new List(false, 10);

overview.Add(new ListItem("This is an item"));

overview.Add("This is another item");

結果如下:

This is an item
This is another item
你可以通過setListSymbol方法更改列表符號:

// 用字串作為列表符號

list1.ListSymbol = "*";

// 用Chunk 作為列表符號(包含“•”字元)

list2.ListSymbol = new Chunk("/u2022", FontFactory.getFont(FontFactory.HELVETICA, 20));

//用圖片作為列表符號

list3.ListSymbol = new Chunk(Image.getInstance("myBullet.gif"), 0, 0);

還可以使用setIndentationLeft和setIndentationRight方法設定縮排,列表符號的縮排在建構函式中設定。更多的例子請參見示例程式碼0302。

註釋
iText支援不同風格的註釋。

u 文字註釋:

你可以新增一小段文字到你的文件中,但它並非文件內容的一部分,註釋有標題和內容:

Annotation a = new Annotation(

"authors",

"Maybe it's because I wanted to be an author myself that I wrote iText.");

u 外部連結註釋:

你需要指定一個可點選的矩形和一個字串(URL描述)或URL物件:

Annotation annot = new Annotation(100f, 700f, 200f, 800f, new URL("http://www.lowagie.com"));

Annotation annot = new Annotation(100f, 700f, 200f, 800f, "http://www.lowagie.com");

u 外部PDF檔案連結註釋:

你需要指定一個可點選的矩形和一個字串(檔名稱)和目的檔案或頁碼。

Annotation annot = new Annotation(100f, 700f, 200f, 800f, "other.pdf", "mark");

Annotation annot = new Annotation(100f, 700f, 200f, 800f, "other.pdf", 2);

u 指定行為連結註釋

你需要指定一個可點選的矩形和一個指定的行為:

Annotation annot = new Annotation(100f, 700f, 200f, 800f, PdfAction.FIRSTPAGE);

u 應用程式連結註釋:

你需要指定一個可點選的矩形和一個應用程式:

Annotation annot = new Annotation(300f, 700f, 400f, 800f, "C://winnt/notepad.exe", null, null, null);

我們無須在頁面上指定一個位置,iText會內部處理。你能夠看到iText新增文字註釋在頁面上當前位置下面,第一個在段後第一行下面,第二個在短句結束處的下面。

所有其他註釋需要指定想匹配的矩形區域,在示例程式碼0304中,我們畫了一些正方形(使用的函式將在第十章中介紹),為每個正方形添加了一些連結註釋。

第四章 頁首頁尾、章節、區域和繪圖物件
使用在第三至第五章中描述的大量簡單iText物件可以避免更多的高階話題(第九至十二章),緊記這些簡單物件限制的功能,大量複雜的功能在第三部分。

頁首頁尾
HeaderFooter物件可以於為文件每頁新增頁首和頁尾。這樣一個頁首或頁尾包含一個標準的短句(如果需要)和當前頁碼,如果你需要更多複雜的頁首和頁尾(使用表格或者第幾頁共幾頁),請閱讀第十二章。

在示例程式碼0401中,你可以看到我們首先添加了一個包含頁碼沒有任何邊框的頁尾。

HeaderFooter footer = new HeaderFooter(new Phrase("This is page: "), true);

footer.Border = Rectangle.NO_BORDER;

document.Footer = footer

我們還可以使用下面的建構函式:

HeaderFooter footer = new HeaderFooter(new Phrase("This is page "), new Phrase("."));

建構函式知道你希望新增一個頁碼和將其放置在兩個短句間,如果你只是設定一個HeaderFooter而不改變邊框,頁首或頁尾的文字上下各有一條直線。

HeaderFooter header = new HeaderFooter(new Phrase("This is a header without a page number"), false);

document.Header = header;

章節和區域
在第十一章中將描述如何構建一個樹的外觀,如果你只需要一個簡單的章節和(子)區域,你可以用Chapter物件和Section物件自動構建一個樹:

Paragraph cTitle = new Paragraph("This is chapter 1", chapterFont);

Chapter chapter = new Chapter(cTitle, 1);

Paragraph sTitle = new Paragraph("This is section 1 in chapter 1", sectionFont);

Section section = chapter.addSection(sTitle, 1);

在示例程式碼0402中,我們添加了一系列的章節和子區域,你可以看到完整的樹形,樹形結構預設開啟,如果你希望部分節點關閉,你必須使用用BookmarkOpen屬性其值為false,詳見示例程式碼0403。

圖形
如果你想新增圖形,如直線、圓、幾何窗體,你應該閱讀讀十章,但如果你只需要一些有限的功能,你可以使用Graphic物件

Graphic grx = new Graphic();

//新增一個矩形

grx.rectangle(100, 700, 100, 100);

// 新增一條斜線

grx.moveTo(100, 700);

grx.lineTo(200, 800);

// 將圖形顯示出來

grx.stroke();

document.Add(grx);

完整的程式碼請見示例程式碼0404,如果想看到全部的方法,請參見PdfContentByte物件API。

當你想給頁面加一個邊框或者在文本當前位置畫一條水平線時,圖形物件非常有用。下面的方法用指定的寬度、間距(如果需要)和顏色畫一個邊框。

public void setBorder(float linewidth, float extrASPace);

public void setBorder(float linewidth, float extrASPace, Color color);

下面的方法用指定的寬度(如果需要)和顏色畫一條水平線,線的長度是指定兩邊緣間可用面積的的百分比。

public void setHorizontalLine(float linewidth, float percentage)

public void setHorizontalLine(float linewidth, float percentage, Color color)

示例程式碼5中,有一個離邊界5磅,線寬3磅的邊框,還有兩條水平線,一條為黑色,5磅寬,可用空間的100%,另外一條為紅色,線寬3磅,可用空間的80%。

第五章 表格
重點:如果你僅僅生成PDF檔案(沒有XML、HTML、RTF……),使用類pdfPTable代替類Table更好。

一些簡單的表格
一個表格是包含單元格排列成矩陣的矩形區域。表格的距陣並不要求是m×n的,它可以有空洞或者單元格比單個的要大。

建立一個表格最通用的辦法是預先知道有幾行幾列:

public Table(int columns, int rows);

在示例程式碼0501中,我們構建了一個簡單的表:

Table aTable = new Table(2,2);

aTable.addCell("0.0");

aTable.addCell("0.1");

aTable.addCell("1.0");

aTable.addCell("1.1");

該表格有兩行兩列,單元格被自動新增,從第一行第一列開始,然後是第二列,當一行滿後,下一單元格自動新增到下一行的第一列中。

也可以將單元格新增到表中指定的位置,如示例程式碼0502,別了要新增System.Drawing.dll引用,以獲得Point物件,我們建立了一個4行4列的表格然後新增一些單元格到隨機的位置上:

Table aTable = new Table(4,4);

aTable.AutoFillEmptyCells = true;

aTable.addCell("2.2", new Point(2,2));

aTable.addCell("3.3", new Point(3,3));

aTable.addCell("2.1", new Point(2,1));

aTable.addCell("1.3", new Point(1,3));

你可以看到我們將AutoFillEmptyCells屬性設定為true,這將自動、預設的單元格佈局填充空的單元格,如果我們忘記了這樣做(就象本例中第二個表格),將沒有額外的單元格新增,不包含任何單格的行也將被忽略,在本例中,第一行將不顯示,因為該行是空行。

經常用資料庫查詢結果來填充表格,大多數情況下,你預先並不知道到底需要多少行,這就是為什麼還有第二個建構函式的原因:

public Table(int columns);

iText根據需要自動新增行,在示例程式碼0503中,初始化了4行4列,當我們新增第6行和第7行的單元格時,iText自動增加行數到7。

增加列數也是可能的,但是有點麻煩,它不能自動生成,你必須使用addColumns方法並設定列寬,詳見示例程式碼0504。

一些表格引數
前面例子中的表格並不美觀,我們可以設定大量的引數來改變表格外觀。類Table和類Cell派生於類Rectangle,我們可以用大量典型的Rectangle方法,讓我們來看看示例程式碼0505。

Table table = new Table(3);

table.BorderWidth = 1;

table.BorderColor = new Color(0, 0, 255);

table.Cellpadding = 5;

5. table.Cellspacing = 5;

Cell cell = new Cell("header");

cell.Header = true;

cell.Colspan = 3;

table.addCell(cell);

10. cell = new Cell("example cell with colspan 1 and rowspan 2");

cell.Rowspan = 2;

cell.BorderColor = new Color(255, 0, 0);

table.addCell(cell);

table.addCell("1.1");

15. table.addCell("2.1");

table.addCell("1.2");

table.addCell("2.2");

table.addCell("cell test1");

cell = new Cell("big cell");

20. cell.Rowspan = 2;

cell.Colspan = 2;

cell.BackgroundColor = new Color(0xC0, 0xC0, 0xC0);

table.addCell(cell);

table.addCell("cell test2");

25. document.Add(table);

u 單元格間距和填距

在第4行中,我們設定了表格的填距,就是單元格邊界和內容間一定數量的空間,在前面的示例中,我們看到文字緊貼邊界,通過使用用特定的填距,就可以避免。

在第5行中,我們設定了表格的間距,就是單元格和表格邊界間的一定數量的空間,不同的單元格間使用了半數空間,具體程式碼見示例程式碼0506。

u 對齊方式

在示例程式碼0506中,我們也改變了單元格“big cell”的對齊方式:

cell.HorizontalAlignment = Element.ALIGN_CENTER;

cell.VerticalAlignment = Element.ALIGN_MIDDLE;

注:不能總是相信垂直對齊方式。

u 邊框

如果我們象在第14行中那樣添加了一個單元格,將使用預設的單元格佈局(預設的佈局可以SetDefalut方法改變),如果我們使用了Cell物件,我們可以控制每一個單元格的佈局。

在第2和第三中,我們設定整個表格的邊框寬度和邊框顏色,我們在單元格上可以使用的方法,在12行中,每個單元格用“box”作為邊界繪製(就象在HTML中),但是示例程式碼0507顯示,我們在PDF中有大量更多可能。

u 顏色

在第22行中,你也能定義單元格的背景色,在示例程式碼0507中,我們不使用顏色只是用一定灰度填充。

u 行跨和列跨

最後,你也能設定單元格的行跨(11/20行)和列跨(8/21列)。通過這種方法可以將幾個單元格合併成一個大的單元格。

u 備註

第7行在PDF中沒有意義,用於生成HTML,在HTML中並不是總能產生同樣的佈局,PDF表格有點象:

header

example cell with colspan 1 and rowspan 2
1.1
2.1

1.2
2.2

cell test1
big cell

cell test2

u 表格分割

如果一個表格不能放在一頁中,將自動被分割,示例程式碼0508顯示了當一個表格到達頁邊時發生的情況,這將在下一節中解釋。

大表格
跨越幾頁的表格將自動被分割成不同的部分。示例程式碼0509顯示了一個跨越多頁的報表。該報表有一個表頭,如果你希望這個表頭在每頁都出現,你可以用endHeaders()方法標記表頭區域的結束點,見示例程式碼0510。

為做這樣的報表,建議設定單元格間距為0和僅使用指定的填距。

你可能已經注意到了,當一個表格被分割時,一些邊界好象丟失了。這是因為單元格在前一頁被完整地繪製了而不會傳遞給下一頁。

u 強行將一個表格或單元格佈置到一頁上

有有些情況下,你可能希望避免單元格或者整個表被拆分成兩個部分,示例程式碼0511差不多和示例程式碼0508完全一樣,但我們設定了引數TableHasToFit為true,看看示例程式碼0508和示例程式碼0511結果區別。在示例程式碼0512中我們修改了示例程式碼0510的CellsHaveToFit屬性為true,比較兩個示例產生結果的區別。

記憶體管理
當我們新增一個物件到文件時,該物件一有可能就寫入了輸出流,但當建立一個表格時,該Table物件一直儲存著,對於真正的大表格,這將成為一個問題。

同樣,當你正寫一個HttpServletResponse物件到輸出流時,瀏覽器也可能超時。這就是為什麼你自己用fitsPage()方法控制表分割是有用的,示例程式碼0513告訴你如何做。

巢狀表格
有兩種方法巢狀表格,第一種是利用insertTable方法明確地將一個表格插入到另外一個表格,示例程式碼0514顯示了通過插入到其他表格的辦法建立的5個表格。正如你看到的在前面兩個表中,所有空的單元格自動得到分割,因為改變了原來的表格。如果一個單元格不空,列跨度和(或)行跨度將自動調整到新的位置,頁面上第三個表格顯示所有原表中列的相關寬度都得到了保護,第四個表格顯示我們可以在插入了表格後新增其他單元格:該單元格自由地新增到下一個單元格中。最後是一個深度巢狀的表格。

當你使用insertTable方法時,插入表的寬度百分比不會被考慮,如果你希望插入表僅佔單元格的80%(這是預設的寬度百分比),你不得不在單元格中繞排,見示例程式碼0515,這也是讓一個表結合其他資料存放在同一個單元格中的唯一辦法,見示例程式碼0516。

備註:你只能將一個表格插入到列跨度和行跨度均為1的單元格中。

表格偏移
當一個表格被新增到文件之前,以當前間距為準的新行將被新增(如前一個插入物件的間距)。有時因為前一個插入物件和當前表格間的間距過大或過小你並不希望這樣做,如果你想改變這個空間,你不得不設定表格偏移,如示例程式碼0517。

表格的絕對位置
iTextSharp.text.Table是一個通過標準方法建立表格的相當簡單的類,但有時你希望表格有一些特殊的行為,這種情況下你將使用更復雜的類com.lowagie.text.pdf.PdfPTable,示例程式碼0518是一個非常簡單的例子,在第十章和十二章中將有一些更復雜的例子。

第六章 圖片
Image物件
如果你學習過API,你可能已經注意到可以通過幾種建構函式來建立圖片,本手冊中,我們將僅僅告訴你最簡單的解決方案,如訪問通過檔名或URL確定的圖片生成的Image物件。

public static Image getInstance(Uri url)

public static Image getInstance(string filename)

Image是一個抽象類,故得到例項的方法將判斷給出的圖片的類別(GIF、Jpeg、PNG……)並返回物件的類別Gif、Jpeg、Png……,一些圖片會被忽略,如果你想知道哪些圖片會被忽略,請查閱FAQ(http://www.lowagie.com/iText/faq.HTML#images)。

u 通過URL得到圖片例項

這是新增一個圖片最簡單的辦法,見示例程式碼0601,我們添加了一個WMF、一個Gif、一個Jpeg和一個PNG圖片到文件中,使用4個URL得到:

Image wmf = Image.getInstance(new URL("../examples/harbour.wmf"));

Image gif = Image.getInstance(new URL("../examples/vonnegut.gif"));

Image jpeg = Image.getInstance(new URL("../examples/myKids.jpg"));

Image png = Image.getInstance(new URL("../examples/hitchcock.png"));

備註:許多PDF庫在插入一個圖片前都將其解壓縮並轉換成點陣圖格式,下面是幾個我為什麼不這樣做的原因:

這將導致PDF檔案增大,這樣產生的PDF檔案尺寸是不同圖片檔案尺寸總和的數十倍。
面臨一個法律問題:LZW演算法受專利保護,所以不允許使用這種演算法來解壓縮GIF等檔案。
u 通過檔名得到圖片例項

通過簡單地改變圖片引用路徑將示例程式碼0601改成示例程式碼0602:

Image gif = Image.getInstance("vonnegut.gif");

Image jpeg = Image.getInstance("myKids.jpg");

Image png = Image.getInstance("hitchcock.png");

同示例程式碼0601的區別只是該圖象從本地獲取而已,另外一個例子見示例程式碼0603。

圖片的位置
u 對齊方式

通過下面方法設定圖片的對齊方式:

Alignment = Image.RIGHT

Alignment = Image.MIDDLE

Alignment = Image.LEFT

參見示例程式碼0604。

我們將Vonnegut的圖片放在右邊,小孩的圖片放在中間,hitchcock的圖片放在左邊。

u 圖片和文字

另外,你還可以指定文字相對圖片的環繞方式:

Alignment = Image.RIGHT | Image.TEXTWRAP

Alignment = Image.MIDDLE

Alignment = Image.LEFT | Image.UNDERLYING

見示例程式碼0506,文字在Vonnegut圖片的左側,並不在我小孩的圖處環繞,且排在Hitchcock圖片的上面。

說明:該功能尚有一些BUG。

u 絕對位置

當製作PDF檔案時,你可能用到該方法:

public void setAbsolutePosition(int absoluteX, int absoluteY)

將一個圖片放要頁面上一個絕對位置的程式碼見示例程式碼0606,我們在不同的座標處添加了兩幅圖片,這裡使用給定的座標將圖片放在了左下角,通過將圖片的寬度和高度作為X和Y座標將設定第一個圖片,座標的2倍設定第二個圖片。

縮放和旋轉圖片
u 縮放

有幾種辦法可以縮放圖片:

public void scaleAbsolute(int newWidth, int newHeight)

public void scalePercent(int percent)

public void scalePercent(int percentX, int percentY)

public void scaleToFit(int fitWidth, int fitHeight)

小孩的圖片大小為194×202象素,如果你想讓圖片小一些,你可以通過scaleAbsolute(97, 101)進行縮放,使用scalePercent(50)也能到達同樣的效果。

還可以通過scaleAbsolute(194, 101)來減小,所以這些例子都放在了示例程式碼0607中。

u 對解析度的影響

如果一個圖片不經任何縮放,其解析度(resolution)為72,如果該圖片縮放比例為50%,則解析度為144,如果有更低的縮放比,則解析度將更大,因為象素相同但尺寸變得更小了。使用72/300=24%的比例放置一個300dpi的圖片,例:你用300dpi掃描了一個5×5英寸的圖片,圖片結果為1500×1500象素(5×300),當你用24%(72/300=0.24)的比例將該圖片放置到PDF檔案中時,PDF中的圖片將為5×5英寸1500X1500象素300dpi,該圖片將始終為1500X1500象素而不管尺寸如何。

u 旋轉

可以通過下面的方法旋轉圖片

public void setRotation(double r)

詳見示例程式碼0608。

原始圖片資料
到目前為止,所有例子中使用的圖片均來自地本地磁碟或者某個網站,但也可能使用包含圖片資訊的陣列來得到圖片的例項:

public static Image getInstance(byte[] img)

該方法同前面方法的效果相同,返回一個新的Gif,Jpeg或者Png類別的Image物件。

在示例程式碼0609中,我們新增一個從一個Jpeg檔案中讀入到位元組陣列中的圖片,很明顯,使用其他getInstance方法得到例項更優越,但這僅僅是一個例子,該getInstance方法在動態建立那些根本不存在的圖片時非常有用。

該例子也演示瞭如何建立和使用一個原始圖片。

public static Image getInstance(int width, int height, int components, int bpc, byte data[])

本例中建立了一個100×100象素的圖片,因為每個象素用RGB描述,所以圖片大小為100×100×3位元組。

System.Drawing.Bitmap
示例程式碼0610是一個比較高階的話題,理由如下:

首次使用到了System.Drawing.Bitmap類。該類在.net框架中,可以使用更多型別的圖片,如TIFF、GIF,而這些圖片在iText中均不支援,你可以檢查一下.net框架看看你需要的圖片格式是否得到支援。
前面的例子中,還有一些事情要注意:當新增一個圖片時不會出現什麼問題,文字始終浮於圖片上面,本例中,我們希望圖片浮在文字上面。這也是為什麼我們將使用iTextSharp.text.pdf.PdfContentByte類的原因(見第十章)。
你將發現使用的圖片為透明的gif格式,你可以到http://itextsharp.sourceforge.net/examples/h.gif下載得到。
TIFF和CCITT
示例程式碼0611也是一個比較高階的話題,例中轉換一個TIFF檔案到PDF檔案。

u 圖片遮罩

示例程式碼0613在,我們建立了一個用作遮罩的圖片

3C

7E

E7

C3

C3

E7

7E

3C

該圖片尺寸為8×8象素,每組一個位元組,使用makeMask()方法可以轉換成遮罩。

byte maskr[] = {(byte)0x3c, (byte)0x7e, (byte)0xe7, (byte)0xc3, (byte)0xc3, (byte)0xe7, (byte)0x7e, (byte)0x3c};

Image mask = Image.getInstance(8, 8, 1, 1, maskr);

mask.makeMask();

mask.setInvertMask(true);

我們可以用該遮罩直接遮住其他圖片的一部分。

PdfContentByte cb = writer.DirectContent;

Image image = Image.getInstance("vonnegut.gif");

image.ImageMask = mask;

或者我們將該遮罩用於模板遮罪中。

PdfContentByte cb = writer.DirectContent;

cb.setRGBColorFill(255, 0, 0);

cb.addImage(mask, mask.scaledWidth() * 8, 0, 0, mask.scaledHeight() * 8, 100, 400);

關於ContentByte物件更多資訊請參見第十章。

圖片和其他物件
u 圖片在塊中

有時,可以方便地將圖片置於塊中,通過一定偏移將一個圖片置於塊中:

Chunk ck = new Chunk(img, 0, -5);

u 圖片在表格中

你可以將圖片新增到單元格中,但有兩個副作用:

l 表格的寬度是確定,當圖片超出單元格的寬度時,將自動縮小。

l 你不能進行文字繞排和為圖片新增下劃線。

參見示例程式碼0615。

u 圖片連結註釋

如果你希望得到一個可點選的圖片,或者想新增連結註釋到圖片上,你需要建立一個Annotation物件,並新增到圖片上,你不需要指定位置(你可以使用0,0,0,0),該位置會內部更新以適合該圖片。

gif.Annotation = new Annotation(0, 0, 0, 0, "Chap1102b.pdf", 3);

jpeg.Annotation = new Annotation("picture", "These are my children", 0, 0, 0, 0);

參加示例程式碼0616。

第二部分 其他文件格式
第七章 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,頁首和頁尾均不會起作用。

表格效果見示例程式碼0804。

第三部分 iText的高階應用
第九章 字型
本章原文講了許多字型的使用技巧,但就是沒有講如何使用中文,因此,意義不大,再說,如果不支援中文,前面的也就白翻譯了,因此,根據原文講到的一些知識,我摸索出漢字的使用方法,自己寫了本章內容,應該算是“原創”了吧^_^(哎呀!誰拿雞蛋扔我……)。

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物件進行很大的調整,不要期