1. 程式人生 > >生成PDF檔案方案--學習中

生成PDF檔案方案--學習中

PDF檔案是目前比較流行的電子文件格式,在辦公自動化(OA)等軟體的開發中,經常要用到該格式,但介紹如何製作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.dllICSharpCode.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.FileStreamDocument寫入檔案中,示例程式碼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.2PDF格式的檔案,在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及以上版本支援)
  • UserPasswordownerPassword 可以為空或零長度, 這種情況下, 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.pdfChap0111b.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中看到的,這個特徵自動地將913937(除903)和945969(古希臘的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."));<