在.NET中使用iTextSharp創建/讀取PDF報告: Part I [翻譯]


原文地址:Create/Read Advance PDF Report using iTextSharp in C# .NET: Part I    By Debopam Pal, 27 Nov 2013

 

到PDF原文介紹了iTextSharp這個類庫,並演示了一些基本的操作,基本屬於入門級別的,可惜作者並沒有在編寫后續的文章。

恰好自己也在學習這個類庫,想實現一個導出ASPX頁面到PDF的功能,如作者所說,網上找到的示例好多都是針對舊版本iTextSharp編寫的,還有些驢唇不對馬嘴,而且,很多照抄的連驗證都省了,一點價值都沒有。這篇文章算是詳實的入門文章,實例也都基本操作了一遍實現沒有問題。

ps.:第一次翻譯老外的文章,英語水平一般,有些地方詞不達意,還望海涵,如果出入希望幫忙指出。

文章內容

簡介

最近我一直在尋找一個高級的工具來創建復雜的PDF報告用在C#.NET中,我找到了iTextSharp.主要的問題是iTextSharp.缺乏文檔。好吧,有少量的C#代碼例子,但是那些對初學者來說是不夠的並且這些示例代碼都是建立在iTextSharp的舊版本上,在最新版本上有很多變化。所以,對初學者來說轉換舊版本到新版本比較困難。此外,我認為假如我寫一篇關於iTextSharp的文章,它能幫助我同時也能作為日后的參考,我將為每一個功能點書寫示例。老實說,在這篇文章,我編寫的所有示例,你都能在《iText in Action, Second Edition》這本書的第一節找到,這本書是針對Java開發者編寫的。我將在我的文章中解釋[從java到c#]這本書余下章節的所有的示例.所以,如果有人對這個庫(iTextSharp)感興趣,這里將是一個好的開始。

想知道關於(iTextSharp)的更多細節,可以通過他們的官方網站了解

要求

  • 編譯這個類庫,你需要一個C#2008(vs2008)編譯器或者更高版本,Visual Studio 2008 or Visual C# 2008 Express Edition
  • 這個庫代碼運行在:
    • .NET 2.0
    • .NET 3.0
    • .NET 3.5
    • .NET 4.0
    • .NET 4.0 Client Profile
    • .NET 4.5
    • .NET 4.5 Client Profile

安裝

或者 你可以從下面這個SourceForge的鏈接下載DLL,然后參照下面步驟:

  • 添加引用BlahBlah(步驟如下,翻譯掠過). Just see the image below:
    Adding iTextSharp 5.4.4 DLL
  • 你需要引入到C#文件中的命名空間:
    • iTextSharp.text
    • iTextSharp.text.pdf

快速入門

6步創建一個PDF文檔:

    • Step 1: 創建一個 System.IO.FileStream 對象:
FileStream fs = new FileStream("Chapter1_Example1.pdf", FileMode.Create, FileAccess.Write, FileShare.None);
    • Step 2: 創建一個 iTextSharp.text.Document 對象:
Document doc = new Document();
    • Step 3: 創建一個 iTextSharp.text.pdf.PdfWriter 對象: 它有助於把Document書寫到特定的FileStream:
PdfWriter writer = PdfWriter.GetInstance(doc, fs);
    • Step 4: 打開 Document:
doc.Open();
    • Step 5: 創建一個 iTextSharp.text.Paragraph 對象並添加到Document里:
doc.Add(new Paragraph("Hello World"));
    • Step 6: 關閉 Document:
doc.Close();

關聯PDF文檔的頁面大小:

創建一個特定大小的頁面,我們需要創建一個iTextSharp.text.Rectangle 對象同時傳遞一個頁面大小的參數到它的構造函數里面,下面是定義頁面大小的方法:

  • 第一種定義一個版面大小的方式:
    通過定義像素或者英寸定義一個頁面尺寸。注意:在iTextSharp里面頁面大小的單位是‘point。72point=1英寸。假設我們需要一個寬度=2英寸&高度=10英寸的PDF文件,那么我們需要144pt&72pt,讓我們看下該怎么做:
Rectangle rec = new Rectangle(144, 720);
  • 第二種定義版面大小的方式:
    使用內建的 iTextSharp.text.PageSize 類定義:
    Rectangle rec2 = new Rectangle(PageSize.A4);
    下面是內建的版面大小。. 完整的頁面大小說明鏈接 Documentation of Page Size:
    • _11X17
    • A0
    • A1
    • A10
    • A2
    • A3
    • A4
    • A4_LANDSCAPE
    • A5
    • A6
    • A7
    • A8
    • A9
    • ARCH_A
    • ARCH_B
    • ARCH_C
    • ARCH_D
    • ARCH_E
    • B0
    • B1
    • B10
    • B2
    • B3
    • B4
    • B5
    • B6
    • B7
    • B8
    • B9
    • CROWN_OCTAVO
    • CROWN_QUARTO
    • DEMY_OCTAVO
    • DEMY_QUARTO
    • EXECUTIVE
    • FLSA
    • FLSE
    • HALFLETTER
    • ID_1
    • ID_2
    • ID_3
    • LARGE_CROWN_OCTAVO
    • LARGE_CROWN_QUARTO
    • LEDGER
    • LEGAL
    • LEGAL_LANDSCAPE
    • LETTER
    • LETTER_LANDSCAPE
    • NOTE
    • PENGUIN_LARGE_PAPERBACK
    • PENGUIN_SMALL_PAPERBACK
    • POSTCARD
    • ROYAL_OCTAVO
    • ROYAL_QUARTO
    • SMALL_PAPERBACK
    • TABLOID
  • 第三種定義版面大小的方式:
    反轉文檔的高度變成寬度&反之亦然:
    Rectangle rec3 = new Rectangle(PageSize.A4.Rotate());

現在,把剛剛的這個 iTextSharp.text.Rectangle 對象 (任意一個)也就是上面的 'rec',或者 'rec2'或者 'rec3'加入iTextSharp.text.Document's 的構造函數中:

Document doc = new Document(rec);

設置PDF文檔背景色:

有幾種方式來設置背景色:

  • 第一種方法:
    需要使用對象 iTextSharp.text.BaseColor. 實例化BaseColor 采用System.Drawing.Color (.NET)對象或者你也可以用傳遞RGB值的形式來定義:
    rec.BackgroundColor = new BaseColor(System.Drawing.Color.WhiteSmoke);
  • 第二種方法:
    需要使用對象 iTextSharp.text.pdf.CMYKColor. CMYKColor 通過傳遞 CMYK 值的方式來構造:
    rec2.BackgroundColor = new CMYKColor(25, 90, 25, 0);

設置PDF文檔邊距:

頁邊距可以像設置版面大小一樣來定義
加入我們設置如下的頁邊距:

  • Left : 0.5 inch
  • Right : 1 inch
  • Top : 1.5 inch
  • Bottom : 2.5 inch

所以我們需要分別設置頁面的 Left, Right, Top, Bottom 頁邊距使用point單位,因為我們知道 iTextSharp 中是使用point作為單位的,並且 72 points = 1 inch.

  • Left : 36pt => 0.5 inch
  • Right : 72pt => 1 inch
  • Top : 108pt => 1.5 inch
  • Bottom : 180pt => 2.5 inch

實現如下:

Document doc = new Document(PageSize.A4, 36, 72, 108, 180);

設置PDF文檔文字對齊方式:

AlignmentiTextSharp.text.Paragraph對象的屬性. iTextSharp 提供了各種對齊方式. 可以通過iTextSharp.text.Element 類設置對其.以下是iTextSharp提供的對齊方式:

我們已經知道在 iTextSharp.text.Document 的構造函數中需要iTextSharp.text.Paragraph 對象,所以在創建Paragraph對象以后我們可以設置它的對齊方式,我們可以在Document創建過程把Prargraph傳遞進去.

實現如下:

Paragraph para = new Paragraph("Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World Hello World");
// Setting paragraph's text alignment using iTextSharp.text.Element class
para.Alignment = Element.ALIGN_JUSTIFIED;
// Adding this 'para' to the Document object
doc.Add(para);

設置PDF文檔的元信息或屬性:

下面這些PDF文檔的元信息 你可以通過iTextSharp.text.Document創建的對象doc(上文的doc)里面的方法來設置:

  • Author Name[^]
  • Creation Date[^]
  • Creator Name[^]
  • Header Name & Content[^]
  • Keywords[^]
  • Langugae[^]
  • Producer[^]
  • Subject[^]
  • Title[^]

下面是他們的一些實現:

// Setting Document properties e.g.
// 1. Title
// 2. Subject
// 3. Keywords
// 4. Creator
// 5. Author
// 6. Header
doc.AddTitle("Hello World example");
doc.AddSubject("This is an Example 4 of Chapter 1 of Book 'iText in Action'");
doc.AddKeywords("Metadata, iTextSharp 5.4.4, Chapter 1, Tutorial");
doc.AddCreator("iTextSharp 5.4.4");
doc.AddAuthor("Debopam Pal");
doc.AddHeader("Nothing", "No Header");

現在,打開一個PDF文檔后,右鍵->屬性,你會看到剛才設置的信息:
PDF Document Properties

創建多頁文檔:

我們可以通過iTextSharp.text.DocumentNewPage()方法創建新頁面,我們來創建5個PDF文檔(頁面) :

for (int i = 1; i <= 5; i++)
{
    doc.NewPage();
    doc.Add(new Paragraph(string.Format("This is a page {0}", i)));
}

從已有文檔創建新的PDF文檔:

我們可以使用iTextSharp.text.pdf.PdfReader對象讀取一個PDF文檔,然后使用 iTextSharp.text.pdf.PdfStamper對象來把它寫到另一個PDF文檔。實現如下:

string originalFile = "Original.pdf";
string copyOfOriginal = "Copy.pdf";
using (FileStream fs = new FileStream(originalFile, FileMode.Create, FileAccess.Write, FileShare.None))
using (Document doc = new Document(PageSize.LETTER))
using (PdfWriter writer = PdfWriter.GetInstance(doc, fs))
{
    doc.Open();
    doc.Add(new Paragraph("Hi! I'm Original"));
    doc.Close();
}
PdfReader reader = new PdfReader(originalFile);
using (FileStream fs = new FileStream(copyOfOriginal, FileMode.Create, FileAccess.Write, FileShare.None))
// Creating iTextSharp.text.pdf.PdfStamper object to write
// Data from iTextSharp.text.pdf.PdfReader object to FileStream object
using (PdfStamper stamper = new PdfStamper(reader, fs)) { }

使用Layer為PDF文檔添加水印:

iTextSharp中,PDF文檔創建后可以添加水印,在這里我將使用iTextSharp.text.pdf.PdfLayer為已有的PDF文檔(Original.pdf)添加水印。實現如下:

string watermarkedFile = "Watermarked.pdf";
// Creating watermark on a separate layer
// Creating iTextSharp.text.pdf.PdfReader object to read the Existing PDF Document
PdfReader reader1 = new PdfReader(originalFile);
using (FileStream fs = new FileStream(watermarkedFile, FileMode.Create, FileAccess.Write, FileShare.None))
// Creating iTextSharp.text.pdf.PdfStamper object to write Data from iTextSharp.text.pdf.PdfReader object to FileStream object
using (PdfStamper stamper = new PdfStamper(reader1, fs))
{
    // Getting total number of pages of the Existing Document
    int pageCount = reader1.NumberOfPages;

    // Create New Layer for Watermark
    PdfLayer layer = new PdfLayer("WatermarkLayer", stamper.Writer);
    // Loop through each Page
    for (int i = 1; i <= pageCount; i++)
    {
        // Getting the Page Size
        Rectangle rect = reader1.GetPageSize(i);

        // Get the ContentByte object
        PdfContentByte cb = stamper.GetUnderContent(i);

        // Tell the cb that the next commands should be "bound" to this new layer
        cb.BeginLayer(layer);
        cb.SetFontAndSize(BaseFont.CreateFont(
          BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 50);

        PdfGState gState = new PdfGState();
        gState.FillOpacity = 0.25f;
        cb.SetGState(gState);

        cb.SetColorFill(BaseColor.BLACK);
        cb.BeginText();
        cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, watermarkText, rect.Width / 2, rect.Height / 2, 45f);
        cb.EndText();

        // Close the layer
        cb.EndLayer();
    }
}

實現結果如下:
Watermarked.pdf

使用Removing Layer移除剛剛創建Layer水印的PDF文檔:

Whenever we add Layer in PDF Document, then the content of the Layer resides under OCG Group. So if I remove this Layer we can remove the content of the Layer also e.g. here it is Watermark Text. To remove all the Layers from PDF Document, you have to remove OCG Group completely from the Document usingreader.Catalog.Remove(PdfName.OCPROPERTIES). Now follow the Steps below to remove the Watermark Text from Layer:

  • Read the existing watermarked document using iTextSharp.text.pdf.PdfReader's object
  • Taking each Page in the iTextSharp.text.pdf.PdfDictionary's object using GetPageN(int pageNumber) method of iTextSharp.text.pdf.PdfReader's object.
  • Taking the Content of the Page in the iTextSharp.text.pdf.PdfArray's object usingGetAsArray(PdfName.CONTENTS) method of iTextSharp.text.pdf.PdfDictionary's object
  • Loop through this array and Get each element as iTextSharp.text.pdf.PRStream's object usingGetAsStream(int arrayIndex) method of iTextSharp.text.pdf.PdfArray's object
  • Convert each stream into Bytes using Static method GetStreamBytes(PRStream stream) ofiTextSharp.text.pdf.PdfReader class
  • Convert these Bytes into String using System.Text.Encoding.ASCII.GetString(byte[] bytes)method
  • Search for the String "/OC" and also the Watermarked Text. If found then remove it by giving it zero length and zero data using two methods: Put() & SetData() of iTextSharp.text.pdf.PRStream class
  • Write this modified document exists in the reader to a new document usingiTextSharp.text.pdf.PdfStamper's object

Lets Implement it:

// Removing the layer created above
// 1. First we bind a reader to the watermarked file
// 2. Then strip out a branch of things
// 3. Finally use a simple stamper to write out the edited reader
PdfReader reader2 = new PdfReader(watermarkedFile);

// NOTE: This will destroy all layers in the Document, only use if you don't have any addtional layers
// Remove the OCG group completely from the Document: reader2.Catalog.Remove(PdfName.OCPROPERTIES);

// Clean up the reader, optional
reader2.RemoveUnusedObjects();

// Placeholder variables
PRStream stream;
string content;
PdfDictionary page;
PdfArray contentArray;

// Get the number of pages
int pageCount2 = reader2.NumberOfPages;

// Loop through each page
for (int i = 1; i <= pageCount2; i++)
{
    // Get the page
    page = reader2.GetPageN(i);

    // Get the raw content
    contentArray = page.GetAsArray(PdfName.CONTENTS);

    if (contentArray != null)
    {
        // Loop through content
        for (int j = 0; j < contentArray.Size; j++)
        {
            stream = (PRStream)contentArray.GetAsStream(j);

            // Convert to a String, NOTE: you might need a different encoding here
            content = System.Text.Encoding.ASCII.GetString(PdfReader.GetStreamBytes(stream));

            //Look for the OCG token in the stream as well as our watermarked text
            if (content.IndexOf("/OC") >= 0 && content.IndexOf(watermarkText) >= 0)
            {
                //Remove it by giving it zero length and zero data
                stream.Put(PdfName.LENGTH, new PdfNumber(0));
                stream.SetData(new byte[0]);
            }
        }
    }
}

// Write the content out
using (FileStream fs = new FileStream(unwatermarkedFile, 
          FileMode.Create, FileAccess.Write, FileShare.None))
using (PdfStamper stamper = new PdfStamper(reader2, fs)) { }

在創建過程為每一頁添加水印:

Now, we already know that, watermark cannot be add during Page creation, it have to add after document creation. So, I've created a class PDFWriterEvents which implements the interface iTextSharp.text.pdf.IPdfPageEventand modify the event OnStartPage. This interface contains a set of events from the Openning & to Closing the PDF Document. The events are following:

  • public void OnOpenDocument(PdfWriter writer, Document document)
  • public void OnCloseDocument(PdfWriter writer, Document document)
  • public void OnStartPage(PdfWriter writer, Document document)
  • public void OnEndPage(PdfWriter writer, Document document)
  • public void OnParagraph(PdfWriter writer, Document document, float paragraphPosition)
  • public void OnParagraphEnd(PdfWriter writer, Document document, float paragraphPosition)
  • public void OnChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title)
  • public void OnChapterEnd(PdfWriter writer, Document document, float paragraphPosition)
  • public void OnSection(PdfWriter writer, Document document, float paragraphPosition, int depth, Paragraph title)
  • public void OnSectionEnd(PdfWriter writer, Document document, float paragraphPosition)
  • public void OnGenericTag(PdfWriter writer, Document document, Rectangle rect, String text)

You may modify other events accroding to your needs which occured against a particular action. Let see the which I've created:

// Creating Watermark inside OnStartPage Event by implementing IPdfPageEvent interface
// So that, dusring Page Creation, Watermark will be create
class PDFWriterEvents : IPdfPageEvent
{
    string watermarkText;
    float fontSize = 80f;
    float xPosition = 300f;
    float yPosition = 800f;
    float angle = 45f;

    public PDFWriterEvents(string watermarkText, float fontSize = 80f, 
       float xPosition = 300f, float yPosition = 400f, float angle = 45f)
    {
        this.watermarkText = watermarkText;
        this.xPosition = xPosition;
        this.yPosition = yPosition;
        this.angle = angle;
    }

    public void OnOpenDocument(PdfWriter writer, Document document) { }
    public void OnCloseDocument(PdfWriter writer, Document document) { }
    public void OnStartPage(PdfWriter writer, Document document)
    {
        try
        {
            PdfContentByte cb = writer.DirectContentUnder;
            BaseFont baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
            cb.BeginText();
            cb.SetColorFill(BaseColor.LIGHT_GRAY);
            cb.SetFontAndSize(baseFont, fontSize);
            cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, watermarkText, xPosition, yPosition, angle);
            cb.EndText();
        }
        catch (DocumentException docEx)
        {
            throw docEx;
        }
    }
    public void OnEndPage(PdfWriter writer, Document document) { }
    public void OnParagraph(PdfWriter writer, Document document, float paragraphPosition) { }
    public void OnParagraphEnd(PdfWriter writer, Document document, float paragraphPosition) { }
    public void OnChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title) { }
    public void OnChapterEnd(PdfWriter writer, Document document, float paragraphPosition) { }
    public void OnSection(PdfWriter writer, Document document, float paragraphPosition, int depth, Paragraph title) { }
    public void OnSectionEnd(PdfWriter writer, Document document, float paragraphPosition) { }
    public void OnGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) { }
}

Lets see how & when you use the object of this class:

using (FileStream fs = new FileStream(
      "Watermark_During_Page_Creation.pdf", FileMode.Create, FileAccess.Write, FileShare.None))
using (Document doc = new Document(PageSize.LETTER))
using (PdfWriter writer = PdfWriter.GetInstance(doc, fs))
{
    writer.PageEvent = new PDFWriterEvents("This is a Test");
    doc.Open();
    doc.Add(new Paragraph("This is a page 1"));
    doc.Close();
}

See, OnStartPage event called during adding a new paragraph. So I don't need to add watermark laterSmile | :)

在不存盤的情況下,導出/打印/輸出PDF文件到客戶端:

We can create PDF File in memory by creatig System.IO.MemorySystem's object. Lets see:

using (MemoryStream ms = new MemoryStream())
using(Document document = new Document(PageSize.A4, 25, 25, 30, 30))
using(PdfWriter writer = PdfWriter.GetInstance(document, ms))
{
    document.Open();
    document.Add(new Paragraph("Hello World"));
    document.Close();
    writer.Close();
    ms.Close();
    Response.ContentType = "pdf/application";
    Response.AddHeader("content-disposition", "attachment;filename=First_PDF_document.pdf");
    Response.OutputStream.Write(ms.GetBuffer(), 0, ms.GetBuffer().Length);
}

設置PDF瀏覽參數:

The values of the different ViewerPreferences were originally stored in iTextSharp.text.pdf.PdfWriter class as an integer constant. You can set the ViewerPreferences by following two ways:

  • By setting property ViewerPreferences of iTextSharp.text.pdf.PdfWriter class. To know all theViewerPreferences and its purpose, please read this first. E.g.-
    writer.ViewerPreferences = PdfWriter.HideMenubar;
  • By calling method AddViewerPreference(PdfName key, PdfObject value) ofiTextSharp.text.pdf.PdfWriter's object. To know which value is appropiate for which key, read thisfirst. E.g.-
    writer.AddViewerPreference(PdfName.HIDEMENUBAR, new PdfBoolean(true));

加密PDF文檔:

By SetEncryption() method of iTextSharp.text.pdf.PdfWriter's object, we can encrypt a PDF document. Read full documentation of this method here. To know all the encryption types, click here. E.g.-

writer.SetEncryption(PdfWriter.STRENGTH40BITS, null, null, PdfWriter.ALLOW_COPY);

聲明

Please download the source code for detail. I hope you'll understand as the source code is documented. If any doubt, just post your comment below. Thank you.

參考文獻

歷史

25th Nov, 2013: PART-I Release. PART-II will release soon Smile | :)

許可

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM