該頁面可以包含自動翻譯的文字。

C# PDF 產生快速入門指南

了解如何在 .NET 專案中產生 PDF 文件,例如報告、發票和收據。 使用 C# 或 VB.NET,透過組合結構元素輕鬆 建立 PDF 文件。 這些元素包括頁首、頁尾、容器、表格、段落、圖像等。 API 會自動將內容分成頁面。

在 C# 和 VB.NET 中建立 PDF 文件 一文中描述了生成 PDF 文件的其他一些方法。

PDF生成庫簡介

我們提供佈局 API 作為 Docotic.Pdf 庫的免費附加元件。 該庫和 Layout 附加元件均可在 NuGet 和我們的網 站上取得。 若要嘗試沒有評估模式限制的庫,請在庫頁面上取得免費的限時 許可證金鑰。

Docotic.Pdf 函式庫 9.3.17105-dev 佈局附加元件 9.3.17105-dev
回歸測試 14,681 已通過 NuGet 總下載量 4,234,061

安裝

從 NuGet 安裝 BitMiracle.Docotic.Pdf.Layout 套件。 這是安裝 Layout 外掛最簡單、最方便的方法。

作為替代方案,您可以在我們的網站上下載包含庫二進位檔案的 ZIP。 若要使用 Layout API, 請新增對 ZIP 套件中的下列 DLL 的參考:

  • BitMiracle.Docotic.Pdf.dll
  • Layout add-on/BitMiracle.Docotic.Pdf.Layout.dll

該方法

入口點是PdfDocumentBuilder類別。 若要開始產生 PDF,請呼叫該類別的Generate方法。 此方法需要 Action<Document>類型的委託作為其參數之一。 庫希望您在委託中佈局文檔內容。

PdfDocumentBuilder.Create().Generate("output.pdf", (Document doc) =>
{
    // 在這裡建立文檔內容
});

給定一個Document對象,委託應該定義佈局。 完整的佈局由較小的構建塊組成。 頁面、頁首和頁尾、容器、 文字區塊都是此類區塊的範例。

許多方法調用鏈接在一起。 鏈中的呼叫順序很重要。 這是一個範例,展示如何在幾個連結呼叫中設定頁面內 容。

PdfDocumentBuilder.Create()
    .Generate("output.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Padding(24).AlignCenter().Text("Some text");
    }));

Intellisense 將幫助您發現 API。 自己嘗試一下 API。 我希望您發現它易於使用。

你好世界!

讓我們開發一個簡單的應用程式來顯示正在運行的常見構建塊。 該應用程式將使用上述方法。

我將 完整的應用程式程式碼 放入我們的 Docotic.Pdf 範例程式碼儲存庫中。 此步驟的程 式碼位於範例應用程式的HelloWorld類別中。

public void CreatePdf()
{
    PdfDocumentBuilder.Create().Generate("hello.pdf", doc => doc.Pages(page =>
    {
        page.Content().Text("Hello, world!");
    }));
}

程式碼不多,但是這裡發生了什麼事?

短代碼的長解釋

程式碼建立文件產生器的實例,要求產生器產生hello.pdf。 建構器將文檔內容佈局的工作委託給我的程式 碼。

在委託的程式碼中,我要求文件建立一些頁面。 現在,文檔委託了我的程式碼佈置頁面內容的工作。

在頁面的委託中,我造訪頁面主要內容的佈局容器。 我透過呼叫Content方法來做到這一點。 對Text的鍊式 呼叫將著名的文字範圍新增至頁面內容。

現在是時候讓建構器根據提供的版面配置產生包含一些頁面的文件了。 建構器會建立包含我新增到主要內容的 佈局容器中的資料所需的確切頁面數。 在這種情況下一頁就夠了嗎? 好的。

為未來的更新做好準備

我將向程式碼添加更多功能。 為了方便進一步開發應用,我將程式碼改成了這樣:

public void CreatePdf()
{
    PdfDocumentBuilder.Create().Generate("hello.pdf", BuildDocument);
}

static void BuildDocument(Document doc)
{
    doc.Pages(BuildPages);
}

static void BuildPages(PageLayout pages)
{
    pages.Content().Text("Hello, world!");
}

它可能看起來沒有必要,但將程式碼拆分為方法至少使其更具可讀性。

此步驟的範例程式碼位於範例應用程式HelloWorld2類別中。

請查看以下文章以取得有關文件、頁面和容器的更多資訊。

字體和顏色

預設字體很好,但我將展示如何使用另一種字體。 主要思想是使用字體創建文字樣式。 然後,如果需要,對字 體應用一些可選屬性,最後在文字範圍上使用樣式。

您可以使用作業系統中安裝的字體集合中的字體,或從檔案或流載入字體。

我建議透過設定文件的版式來覆蓋預先定義的樣式。 但這不是必需的,您可以直接使用文字樣式。

此步驟的範例程式碼位於範例應用程式HelloWorld3類別中。 以下是與上一步相比發 生變化的部分。

static void BuildDocument(Document doc)
{
    doc.Typography(t =>
    {
        t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
    });
    doc.Pages(BuildPages);
}

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildTextContent(pages.Content());
}

static void BuildTextContent(LayoutContainer content)
{
    var colorAccented = new PdfRgbColor(56, 194, 10);
    var styleAccented = TextStyle.Parent.FontColor(colorAccented);

    content.Text(t =>
    {
        t.Span("Hello, ").Style(styleAccented);
        t.Span("world!").Style(styleAccented.Underline());
    });
}

如您所見,我更新了BuildDocumentBuildPages的程式碼。 我還新增了一個名為BuildTextContent的新 方法。

BuildDocument中,我從名為「Calibri」的系統字體建立了一個文字樣式。 然後我將此文字樣式設定為文件 的預設文字樣式。

BuildPages方法現在包含用於設定所有頁面的大小和邊距的程式碼。 此外,該方法呼叫BuildTextContent, 將頁面主要內容的容器作為參數傳遞。

我現在建構主要內容的方式已經不同了。 我使用兩個文字範圍並對每個範圍應用不同的文字樣式。 實際上,兩 個跨度都使用強調色,但第二個跨度也有下劃線。

該程式碼產生具有自訂文字字體和顏色的PDF。 如果您的結果包含試用訊息警告,請在 Docotic.Pdf 圖庫頁面上取得免費的限時授權。

許多現實世界的 PDF 文件(例如報告或發票)都包含頁首和頁尾。 讓我展示如何為 PDF 添加頁首和頁尾。

此步驟的範例程式碼位於範例應用程式HelloWorld4類別中。 以下是與上一步相比發 生變化的部分。

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildPagesHeader(pages.Header());
    BuildPagesFooter(pages.Footer());

    BuildTextContent(pages.Content());
}

static void BuildPagesHeader(LayoutContainer header)
{
    header.TextStyle(TextStyle.Parent.FontSize(8))
        .AlignRight()
        .Text(t =>
        {
            t.Line($"Created by: {Environment.UserName}");
            t.Line($"Date: {DateTime.Now}");
        });
}

static void BuildPagesFooter(LayoutContainer footer)
{
    footer.Height(20)
        .Background(new PdfGrayColor(95))
        .AlignCenter()
        .Text(t => t.CurrentPageNumber());
}

我更改了BuildPages方法以使用標頭容器作為參數來呼叫BuildPagesHeader。 該方法還呼叫 BuildPagesFooter,將頁腳容器傳遞給它。

產生的帶有頁首和頁尾的 PDF 看起來 絕對更像專業的 PDF 文件。

BuildPagesHeader 方法為文字設定較小的字體大小。 我使用兩行作為標題內容:第一行包含目前使用者的名 稱,第二行包含目前日期。 文字右對齊。

請注意,我沒有指定標題的任何明確大小。 它將佔據整個頁面寬度減去左右邊距。 高度將取決於文字行的高 度。

頁腳類似,只是它明確指定了其高度。 並且應用了背景顏色。 有趣的是,要將目前頁碼放入 PDF 頁腳中,我可 以使用 CurrentPageNumber 方法。 所有計算 都在庫內進行。

圖片

正如他們所說,使用一張圖片可以節省很多文字。 在報價或收據中,您可能會使用您公司的徽標或其他一些重要 圖像。 對於這個範例程式碼,我將只使用一個漂亮的程式碼。

該文件位於我們的網站上,您可以查看生成的 帶有美麗圖像的 PDF 的外觀。

要使用圖像,您需要先將其新增至文件。 該庫可以從檔案或流中讀取圖像。 我更改了BuildDocument方法來展 示如何將圖像添加到文件中。

此步驟的範例程式碼位於範例應用程式HelloWorld5類別中。 以下是與上一步相比發 生變化的部分。

static void BuildDocument(Document doc)
{
    doc.Typography(t =>
    {
        t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
    });

    var imageFile = new FileInfo("red-flowers-at-butterfly-world.jpg");
    var image = doc.Image(imageFile);

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
        });
    });
}

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildPagesHeader(pages.Header());
    BuildPagesFooter(pages.Footer());
}

static void BuildImageContent(LayoutContainer content, Image image)
{
    content.AlignCenter()
        .PaddingVertical(20)
        .Image(image);
}

如您所見,BuildDocument方法有更多變化。 之前,程式碼使用Text方法將一些文字設定為頁面的主要內 容。 這仍然發生在 BuildTextContent 方法中。 但現在我還想要內容中的圖像。

為了在頁面的主要內容中同時包含文字和圖像,我需要一個容器。 我使用Column容器依次垂直添加文字和圖 像。 列容器的Item方法提供了一個子容器。 我使用對Item方法的一次呼叫來獲取文字的容器,並使用另一 次呼叫來獲取圖像的容器。

如您所見,我沒有對 BuildTextContent 進行任何更改。 但是,當然,我必須從BuildPages方法中刪除對 BuildTextContent的呼叫。

BuildImageContent 在三行中做了重要的工作。 它在圖像的頂部和底部添加了一些填充。 它還使圖像在頁面 上居中。

清單

什麼是清單? 讓我們將其視為一組寫在另一個下面的編號項目。 這個想法提出了一種實現列表的方法。

此步驟的範例程式碼位於範例應用程式HelloWorld6類別中。 以下是與上一步相比發 生變化的部分。

static void BuildDocument(Document doc)
{
    ....

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
            BuildListContent(c.Item());
        });
    });
}

static void BuildListContent(LayoutContainer content)
{
    var dayNames = DateTimeFormatInfo.InvariantInfo.DayNames;
    var dayNamesSpain = DateTimeFormatInfo.GetInstance(new CultureInfo("es-ES")).DayNames;

    content.Column(column =>
    {
        for (int i = 0; i < dayNames.Length; i++)
        {
            column.Item().Row(row =>
            {
                row.Spacing(5);
                row.AutoItem().Text($"{i + 1}.");
                row.RelativeItem().Text(t => {
                    t.Line(dayNames[i]);
                    t.Line($"In Spain they call it {dayNamesSpain[i]}");
                });
            });
        }
    });
}

BuildDocument 中唯一的變化是新的方法呼叫。 我在BuildImageContent呼叫之後新增了對 BuildListContent的呼叫。

BuildListContent 將清單建立為一列行。 每行有兩個項目。 第一個(左)用於項目編號。 另一個(右)用 於項目文字。 此程式碼明確設定行中項目之間的間距。

為了排列項目,Row容器需要事先知道或計算每個項目的大小。 我在本例中使用AutoItemRelativeItem 方法。 結果,行容器將計算包含第一項所需的寬度。 然後容器將使用第二個項目的剩餘可用寬度。

新增清單後,產生的 PDF 內容不再適合一頁。 Layout API 會自動新增第二頁並將清單放在上面。 您可以在多 頁PDF文件中看到API在新頁面上重複了頁首和頁 尾。

表格

許多 PDF 文件都包含表格。 這並不奇怪,因為表格增強了數據的清晰度和組織性。 讓我展示如何使用 Layout API 在 PDF 中建立表格。

此步驟的範例程式碼位於範例應用程式HelloWorld7類別中。 以下是與上一步相比發 生變化的部分。

static void BuildDocument(Document doc)
{
    ....

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
            BuildListContent(c.Item());
            BuildTableContent(c.Item());
        });
    });
}

static void BuildTableContent(LayoutContainer content)
{
    var color = new PdfGrayColor(75);

    content.PaddingTop(20).Table(t =>
    {
        t.Columns(c =>
        {
            c.RelativeColumn(4);
            c.RelativeColumn(1);
            c.RelativeColumn(4);
        });

        t.Header(h =>
        {
            h.Cell().Background(color).Text("Month in 2024");
            h.Cell().Background(color).Text("Days");
            h.Cell().Background(color).Text("First Day");
        });

        for (int i = 0; i < 12; i++)
        {
            var stats = GetMonthStats(2024, i);

            t.Cell().Text(stats.Item1);
            t.Cell().Text(stats.Item2);
            t.Cell().Text(stats.Item3);
        }
    });
}

static (string, string, string) GetMonthStats(int year, int monthIndex)
{
    return (
        DateTimeFormatInfo.InvariantInfo.MonthNames[monthIndex],
        DateTime.DaysInMonth(year, monthIndex + 1).ToString(),
        new DateTime(year, monthIndex + 1, 1).DayOfWeek.ToString()
    );
}

BuildTableContent的呼叫是BuildDocument中的唯一變更。

BuildTableContent中,我建立了一個簡單的表格。 表格顯示了有關 2024 年月份的簡單資訊。首先,我定 義了具有相對寬度的三列。 最左邊和最右邊的列將比中間的列寬 4 倍。

該程式碼也定義了表的標題。 它透過添加標題單元格並為每個單元格指定文字和背景顏色來實現這一點。 當表 格無法容納在一頁上時,標題會在表格佔用的每一頁上重複。 您可以在產生的 帶有表格的 PDF 文 件 中看到這一點。 在文件中,表格從第二頁開 始,一直到第三頁。

我正在使用簡單的循環來組成行。 循環中的程式碼首先檢索有關每個月的資訊。 然後它添加三個單元格組成一 行資訊。

範例程式碼

上面,我介紹了 C# 中 PDF 產生的一些比較流行的功能。 我建議您閱讀以下文章繼續發現 API。 另請嘗試 GitHub 上的 Docotic.Pdf.Samples 儲存庫中的範例程式碼。Layout API 的範例程式碼位於儲存庫的Layout資 料夾

您可以使用範例程式碼作為程式碼遊樂場。 這樣您就可以嘗試一些想法,而不必每次都從頭開始。

相同的範例程式碼位於 ZIP 套件的Samples資料夾中。 有兩個解決方案文件。 SamplesCSharp適用於使用C# 語言的範例專案。 SamplesVB.NET適用於 VB.NET 版本。