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

.NET PDF 產生器

在 Docotic.Pdf 的協助下,你可以透過將頁面、容器、文字區段、影像、連結、頁首、頁尾、表格、清單等結構元素組合起來,產生 PDF 文件。這些元素可透過高階的 Layout API 使用,而該 API 由 Docotic.Pdf 的免費 Layout 外掛提供。此 API 也支援可重用的自訂元件。

說明 Docotic.Pdf 如何產生 PDF:你排列影像、表格和文字等元素,而程式庫會根據該版面配置產生 PDF

Layout API 可讓你完全以 C# 或 VB.NET 程式碼,使用 fluent 方式定義文件。根據這個描述,外掛提供的 PDF 產生器可以建立任意複雜版面配置的文件,從簡單頁面到高度結構化的 PDF 報表皆可。

PDF 產生基礎

若要使用 Layout API 產生 PDF 文件,你需要免費的 Layout 外掛。使用核心程式庫和外掛也需要授權金鑰。你可以使用免費試用金鑰或已購買的金鑰。

安裝外掛

建議的方式是從 NuGet 安裝外掛。

Install-Package BitMiracle.Docotic.Pdf.Layout

套件管理員會自動處理相依性。

如果你偏好手動安裝外掛,請先下載包含 Docotic.Pdf 二進位檔的 ZIP 封存檔。解壓縮後,加入下列 DLL 的參照:

  • BitMiracle.Docotic.Pdf.dll
  • BitMiracle.Docotic.Pdf.Layout.dll,位於 Layout 外掛 子資料夾中。

取得授權金鑰

若要試用程式庫,請填寫 Docotic.Pdf 下載頁面上的表單,以索取免費且有時限的授權金鑰。如果你已經購買授權,請使用購買後提供給你的代碼。

Layout 外掛是免費的,不需要額外授權。你可以使用現有的 Docotic.Pdf 授權搭配 Layout API。

使用 Layout API 的 Hello, world!

以下是使用該 API 產生包含經典 "Hello, world!" 片語的 PDF 的範例程式碼:

BitMiracle.Docotic.LicenseManager.AddLicenseData("PUT-LICENSE-HERE");

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

這段程式碼會產生單頁 PDF,文字位於左上角。

了解程式碼範例

範例先加入授權金鑰。沒有授權,Docotic.Pdf 程式庫不會產生任何內容。產生 PDF 的程式碼從下一行開始。

程式碼透過呼叫靜態 PdfDocumentBuilder.Create() 方法建立文件建構器的執行個體。呼叫 Generate 方法會啟動 PDF 產生程序。此方法接受兩個參數:要產生的檔案名稱,以及 Action<Document> 型別的委派。外掛會將 hello.pdf 作為 Generate 呼叫的結果產生出來。

為了知道 PDF 應該採用什麼版面配置,文件建構器會以 Document 執行個體呼叫該委派。委派的程式碼負責組成文件內容。在這個範例中,委派使用 Pages 方法提供另一個委派給建構器,以定義文件頁面的版面配置。

建構器會以 PageLayout 執行個體呼叫 Pages 方法提供的委派。這個執行個體代表文件中的一頁或多頁。實際頁數取決於加入其中的內容。

頁面委派會呼叫 Content 方法,以存取頁面主要內容的版面配置容器。對容器的連鎖呼叫 Text 方法會將範例文字區段加入頁面。請參閱 版面配置容器 指南,以了解其運作方式的詳細說明。

文件建構器會自動在頁面之間分割內容,建立剛好足以容納加入到主要內容版面配置容器中的所有資料所需的頁數。在這個範例中,一頁就足夠,因此輸出只包含一頁。

超出基本範例的常見工作

範例程式碼先建立 PdfDocumentBuilder 執行個體,然後呼叫 Generate 方法產生 PDF。你可以在建構器開始產生 PDF 之前進行設定。

例如,你可以透過提供加密處理常式給建構器來產生加密 PDF。你也可以指定中繼資料,讓建構器將其包含在產生的文件中。關於如何 自訂建構器 的更多細節,請參閱獨立文章。

範例程式碼只使用主要內容區塊的版面配置容器,但其他內容區塊也可使用。以下是存取內容區塊的 PageLayout 方法完整清單:

  • Background() - 傳回背景圖層,會被其他內容覆蓋。
  • Header() - 傳回所有頁面共用的頁首。
  • Content() - 傳回主要頁面內容區塊。
  • Footer() - 傳回所有頁面共用的頁尾。
  • Foreground() - 傳回前景圖層,會顯示在其他內容之上。

只有主要內容區塊中的內容會影響產生的 PDF 頁數。文件建構器會在每一頁重複除 Content() 提供的區塊之外的所有區塊。關於內容區塊的更多資訊,請參閱 頁面版面配置 文章。

許多文件會在不同頁面使用不同的版面配置。例如,第一頁可能使用封面式設計,而後續頁面則使用較簡單的版面。有些文件包含用於表格的特殊頁面,或依章節套用不同背景。若要使用 Docotic.Pdf 和 Layout 外掛產生這類文件,請多次呼叫 Document.Pages 方法。你可以在我們的範例存放庫中找到 可運作的範例

整理程式碼

對於像範例這樣簡短的程式碼,串接呼叫並使用巢狀 lambda 是可以接受的。當你建立較大的內容時,將程式碼拆分為獨立方法可能更方便。這會讓程式碼更容易閱讀與維護。

以下是將 Hello, world! 範例的程式碼拆成方法後的樣子。

public void GeneratePdf()
{
    BitMiracle.Docotic.LicenseManager.AddLicenseData("PUT-LICENSE-HERE");

    PdfDocumentBuilder.Create().Generate("hello.pdf", BuildDocument);
}

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

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

為何使用 Docotic.Pdf Layout API 產生 PDF

Layout API 是一種對開發者友善的現代方式,可從可組合的建構區塊產生 PDF,而不需要了解 PDF 格式內部細節。它以高效能且可決定、可預測的方式排版 PDF。你可以在大量文件產生情境中使用此 API。

當你將 Docotic.Pdf 與免費的 Layout 外掛一起使用時,就能取得此 API。Docotic.Pdf 和 Layout 外掛都是 100% 托管程式碼 DLL,不含 unsafe 程式碼區塊。Layout API 的實作不依賴任何第三方外部元件,例如瀏覽器或 Skia 二進位檔,因此是一個輕量、低額外負擔且易於部署與維護的解決方案。

API 概觀

Layout API 是一個 fluent、易於使用的 API。你完全以程式碼描述文件的版面配置,使用彈性的版面元素,而產生器會流動你的內容、自動分頁,並將結果渲染為 PDF。

Layout 外掛使用宣告式、基於流動的版面配置系統。其版面元素包括清單、欄、列、表格、影像、文字區段、頁首與頁尾、容器等。你不需要手動將元素放在精確座標上,而是描述元素應該如何行為。然後文件建構器會計算最終版面並建立 PDF。

基於流動的版面配置系統可確保版面隨頁面大小與內容自動適應。得益於此系統,外掛非常適合複雜、結構化、基於規則的版面配置。你可以巢狀使用不同類型的容器,建立複雜結構,同時不犧牲可讀性。

在使用 Layout API 時,你可以將大多數呼叫串接在一起。這比傳統 API 產生更緊湊且更具表達力的程式碼。串接中呼叫的順序很重要。一切都具有強型別,提供編譯期安全性,並讓程式碼更利於重構。你也可以為使用 Layout API 的程式碼撰寫單元測試。

若要讓你的版面配置實作更簡潔,你可以使用自己的方法擴充 API,並在其上建立乾淨、具表達力的 DSL。

關於 定位與 DSL 建立 的更多資訊,請參閱說明如何控制容器大小、位置、對齊與渲染行為的文章。

.NET 版本與平台支援

你可以在目標為 .NET Standard 2.1 及更新架構的專案中使用 Layout API。換句話說,Layout 外掛相容於 .NET 5 到 .NET 10。此外,也支援 .NET Core 3.0+。

你可以在 ASP.NET Core、MAUI 應用程式、Unity、Xamarin 和主控台應用程式中使用 Layout API 產生 PDF。搭配 Layout 外掛的 Docotic.Pdf 可在 Windows、macOS 與 Linux 上產生 PDF。

雲端平台與 Docker 映像

搭配 Layout 外掛的 Docotic.Pdf 可在 Azure 與 AWS 雲端環境中執行,包括無伺服器設定。此程式庫與外掛完整支援動態硬體變更、自動擴縮,以及其他雲端原生執行階段功能。

在大多數雲端情境中,需要使用不限綁定授權。License FAQ 說明如何選擇適合雲端應用程式的 授權

Layout API 在 Docker 容器中可直接運作。當你在容器內執行此程式庫與 Layout 外掛時,不需要任何特殊設定即可產生 PDF。

向 PDF 檔案加入文字

文字是任何 PDF 文件的基本部分。你可以使用 LayoutContainer 類別的 Text 方法,將文字加入內容區塊。

以標題、標註、純文字、超連結、粗體與註腳等文字樣式範例包圍的文件頁面說明

文字區段

在 Hello, world 範例中,我使用 LayoutContainer.Text(string) 多載將文字加入頁面的主要內容。現在讓我們看看 Text 方法的另一個多載。

public static void GenerateTextPdf()
{
    PdfDocumentBuilder.Create().Generate("text-spans.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Text(AddTextSpans);
    }));
}

private static void AddTextSpans(TextContainer text)
{
    text.Line("About VB.NET")
        .Style(t => t.Strong);

    text.Span("VB.NET is a multi-paradigm, object-oriented language ");
    text.Span("that runs on .NET, Mono, and the ");
    text.Hyperlink(
        ".NET Framework",
        new Uri("https://dotnet.microsoft.com/download/dotnet-framework"));
    text.Line(".");

    text.Span("Released by Microsoft in 2002, ");
    text.Line("it continues the lineage of the original Visual Basic language.");
}

這段程式碼使用 TextContainer 類別的 SpanLine 方法,將文字加入目前行。Line 方法另外會完成目前行。範例程式碼也使用 Hyperlink 方法,將外部資源連結附加到特定文字區段。

請注意,範例程式碼透過 Style 方法對第一行套用粗體格式。讓我們更深入探討文字樣式的概念。

文字樣式

文字樣式可讓你自訂文字外觀。TextStyle 類別提供變更字型大小、字元間距、色彩及其他文字屬性的方法。TextStyle 物件是不可變的,因此每次方法呼叫都會產生新的樣式執行個體。你可以在不同的版面配置層級套用文字樣式。

PdfDocumentBuilder.Create().Generate("text-styles.pdf", doc =>
{
    doc.Pages(pages =>
    {
        pages.TextStyle(TextStyle.Parent.FontSize(30));

        pages.Content()
            .TextStyle(TextStyle.Parent.FontColor(new PdfRgbColor(0, 0, 255)))
            .Text(text =>
            {
                text.Span("Hello,");

                text.Span("World!")
                    .Style(TextStyle.Parent.Underline());
            });
    });
});

TextStyle.Parent 屬性會傳回一個特殊樣式,其中所有文字屬性皆未定義。在上面的範例中,程式碼以 30 點字型大小將 “Hello, World!” 以藍色繪製,並將第二個單字加底線。

這會發生是因為程式碼:

  • 在頁面層級將字型大小設為 30 點,
  • 然後將主要內容區塊的文字色彩設為藍色,
  • 再將底線樣式套用到最後一個文字區段。

每個後續樣式都會透過 TextStyle.Parent 屬性繼承前一個樣式。

使用 TextStyle 類別的方法,你可以將文字方向改為從右到左。請參閱我們範例存放庫中另一個使用 文字樣式繼承 的範例。

排版

TextStyle 類別支援自訂所有文字屬性,但不包含關聯字型。若要變更預設字型,請使用 Document.TextStyleWithFont 方法,建立以特定字型為基礎的文字樣式。你可以使用作業系統中安裝的字型,或從檔案或串流載入字型。

建立以字型為基礎的樣式後,你可以套用其他可選屬性,然後將結果樣式用於文字區段。這個 C# 範例示範如何使用系統字型。

PdfDocumentBuilder.Create().Generate("text-style-with-font.pdf", doc =>
{
    var font = SystemFont.Family("Calibri");
    var style = doc.TextStyleWithFont(font).FontSize(30);

    doc.Pages(pages =>
    {
        pages.Content().Text(text =>
        {
            text.Span("Hello,");

            text.Span("World!")
                .Style(style);
        });
    });
});

TextStyleWithFont 方法包含一個可選參數,用來指定字型在產生文件中應如何內嵌。預設情況下,程式庫會:

  • 內嵌 TrueType/OpenType 字型中使用到的字形,
  • 內嵌 Type1 與 CFF 字型的所有字形,
  • 不內嵌內建 PDF 字型(Base14 字型)的字形。

由於這些預設設定,即使使用大型字型,採用 TrueType 與 OpenType 字型的文件仍可能維持較小的大小。你也可以指定自訂字型載入器、備援字型,以及缺少字形時的處理常式。請參閱我們範例存放庫中的範例程式碼,以了解在 PDF 文件中如何 管理字型 的細節。

Layout API 提供一組預先定義的樣式,你可以透過 Typography 類別存取並修改它們。

PdfDocumentBuilder.Create().Generate("typography.pdf", doc =>
{
    doc.Typography(t =>
    {
        var fontsPath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
        var arialFont = new FileInfo(Path.Combine(fontsPath, "arial.ttf"));

        t.Document = doc.TextStyleWithFont(arialFont);
        t.Header = t.Parent.FontSize(20).FontColor(new PdfGrayColor(20));
        t.Footer = t.Footnote;
    });

    doc.Pages(pages =>
    {
        pages.Header().AlignCenter().Text("Header");

        pages.Content().Text(t =>
        {
            t.Line("Title").Style(t => t.Title);
            t.Line("Heading 1").Style(t => t.Heading1);
            t.Line("Regular");
        });

        pages.Footer()
            .Height(20)
            .AlignCenter()
            .Text(t => t.CurrentPageNumber());
    });
});

我建議透過設定整份文件的排版來覆寫預先定義的樣式,雖然這不是必要的,而且你仍然可以在文字區段層級套用文字樣式。

使用 Typography 類別時,你不需要將文字樣式參照儲存在變數中。相反地,你可以使用 Document.Typography 方法註冊所需樣式,之後透過 Typography 物件的屬性存取它們。請參閱 Typography 範例,了解在產生 PDF 文件時如何使用 預先定義與自訂的文字樣式

許多實際的 PDF 文件,例如報表或發票,都包含頁首與頁尾。本節示範如何在 PDF 中加入這兩者。

public static void GeneratePdfWithHeaderAndFooter()
{
    PdfDocumentBuilder.Create()
        .Generate("header-footer.pdf", doc => doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6).Margin(10);

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

        pages.Content().Text("Hello, world!");
    }));
}

public 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}");
        });
}

public static void BuildPagesFooter(LayoutContainer footer)
{
    footer.AlignRight().Text(text =>
    {
        text.Style(t => t.Parent.FontColor(new PdfRgbColor(255, 0, 0)));

        text.CurrentPageNumber();
        text.Span(" / ");
        text.PageCount();
    });
}

BuildPagesHeader 方法會將頁首文字的字型大小設為較小。它使用兩行作為頁首內容:一行顯示目前使用者名稱,另一行顯示目前日期。文字靠右對齊。

請注意,程式碼沒有明確指定頁首大小。頁首會佔用整個頁面寬度減去左右邊界的空間,而其高度取決於文字行的高度。

BuildPagesFooter 方法示範如何在 PDF 頁尾放置目前頁碼。程式庫會自動計算目前頁碼與總頁數。你可以使用 TextContainer 物件的 CurrentPageNumberPageCount 方法存取這些值。

Layout API 也提供格式化頁碼的方法。例如,你可以像這樣繪製十六進位頁碼:

text.CurrentPageNumber().Format(p => "0x" + p?.ToString("x2"));

我們的範例存放庫中還有另一個 向 PDF 加入頁首與頁尾 的範例。該範例會將頁碼格式化為羅馬數字。

插入影像

俗話說,一張圖勝過千言萬語。在報價單或收據中,你通常會加入公司標誌或其他重要影像。這個範例中,我會使用一個簡單且外觀不錯的影像。

若要使用影像,必須先將其加入文件。程式庫可以從檔案或串流載入影像。只支援點陣格式:PNG、JPEG、JPEG 2000、BMP、GIF 與 TIFF。

一旦你有了 Image 物件,就可以呼叫容器的 Image 方法,將它設定為一個或多個版面配置容器的內容。你可以像處理其他內容一樣,縮放、旋轉、加入內距,以及 安排影像

PdfDocumentBuilder.Create().Generate("image-with-text.pdf", doc =>
{
    var imageFile = new FileInfo("red-flowers-at-butterfly-world.jpg");
    var image = doc.Image(imageFile);

    doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6);
        pages.Content().Column(c =>
        {
            c.Spacing(20);

            c.Item()
                .AlignCenter()
                .Text("Hello, world!")
                .FontSize(20);

            c.Item()
                .AlignCenter()
                .MaxWidth(200)
                .Image(image);
        });
    });
});

範例程式碼在主要內容區塊中同時使用文字與影像。然而,版面配置容器只能包含文字或只包含影像。若要在頁面主要內容區塊中同時包含兩者,你需要使用 複合容器

我使用 Column 容器將文字與影像垂直排列,一個接一個。欄容器的 Item 方法會提供子容器。每次呼叫 Item 會建立一個文字容器,另一個呼叫則會建立一個影像容器。包含所有子容器的複合容器會成為主要內容。

若要執行範例程式碼,請從我們的範例存放庫下載 花朵影像,並將其放在應用程式的工作目錄中。

線上影像

如果你只有影像 URL 而不是檔案,請將影像下載到記憶體串流,然後從該串流建立 Image。以下範例示範如何使用 Layout API 搭配線上影像。

public static async Task AddImageWithTextFromUrl()
{
    using var http = new HttpClient();
    using var stream = await http.GetStreamAsync("url/to/image");

    var memoryStream = new MemoryStream();
    await stream.CopyToAsync(memoryStream);
    memoryStream.Position = 0;

    PdfDocumentBuilder.Create().Generate("online-image-with-text.pdf", doc =>
    {
        var image = doc.Image(memoryStream);

        doc.Pages(pages =>
        {
            pages.Size(PdfPaperSize.A6);
            pages.Content().Column(c =>
            {
                c.Spacing(20);

                c.Item()
                    .AlignCenter()
                    .Text("Hello, world!")
                    .FontSize(20);

                c.Item()
                    .AlignCenter()
                    .MaxWidth(200)
                    .Image(image);
            });
        });
    });
}

由於此方法會執行非同步工作(從 URL 下載影像),因此它宣告為 async Task 而非 void。呼叫端必須 await 它,以確保 PDF 產生正確完成。在你自己的程式碼中,類似的方法也可能回傳值,這種情況下它會宣告為 async Task<TResult>

建立清單

什麼是清單?你可以把它想成一組由上而下排列的編號項目。Layout API 沒有為清單提供特殊的容器型別,但可以很容易地使用其他 複合容器 實作。

var dayNames = DateTimeFormatInfo.InvariantInfo.DayNames;
var dayNamesSpain = DateTimeFormatInfo.GetInstance(new CultureInfo("es-ES")).DayNames;

PdfDocumentBuilder.Create().Generate("list.pdf", doc => doc.Pages(pages =>
{
    pages.Size(PdfPaperSize.A6);
    pages.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]}");
                });
            });
        }
    });
}));

範例程式碼將清單建立為一系列列的欄。每一列包含兩個項目:第一個(左側)放置項目編號,第二個(右側)放置項目文字。程式碼明確設定每一列中項目之間的間距。

若要安排項目,Row 容器必須明確指定每個項目的大小,或自行計算。在這個範例中,使用了 AutoItemRelativeItem 方法。結果,列容器會先計算第一個項目所需的寬度,然後使用剩餘可用寬度作為第二個項目的寬度。

透過這種方式並依需要調整列的外觀,你可以建立最符合文件版面與風格的清單。

建立表格

許多 PDF 文件都包含表格,這並不令人意外,因為表格能提升資料的清晰度與組織性。本節示範如何使用 Layout API 在 PDF 中建立表格。

public static void AddTable()
{
    PdfDocumentBuilder.Create().Generate("table.pdf", doc => doc.Pages(pages =>
    {
        pages.Size(PdfPaperSize.A6);

        var color = new PdfGrayColor(75);
        pages.Content().Padding(20).Table(t =>
        {
            t.Columns(c =>
            {
                c.RelativeColumn(4);
                c.RelativeColumn(1);
                c.RelativeColumn(4);
            });

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

            var year = DateTime.Now.Year;
            for (int yearDiff = 0; yearDiff < 4; yearDiff++)
            {
                for (int i = 11; i >= 0; i--)
                {
                    var stats = GetMonthStats(year - yearDiff, i);

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

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

範例程式碼建立了一個簡單表格,顯示本年度與前 3 年的月份基本資訊。程式碼定義了三個相對寬度的欄。最左邊與最右邊的欄寬是中間欄的四倍。

程式碼也透過加入標頭儲存格並為每個儲存格指定文字與背景色彩來定義表格標頭。當表格無法放在單一頁面時,標頭會重複出現在表格所占用的每一頁上。你可以在範例程式碼產生的 PDF 中看到這種行為。

建立列時使用了兩個簡單迴圈。外層迴圈遍歷年份,內層迴圈以反向順序遍歷月份。內層迴圈取得每個月份的資訊,然後加入三個儲存格形成一列。

若要進一步了解,你可以閱讀詳細說明 Table 容器 功能的文章。

PDF 文件支援內部連結,讓讀者可跳到同一檔案中的其他位置。在 PDF 檢視器中,這些連結會顯示為可點選的文字或影像元素。它們的功能類似超連結,但不是指向外部網站,而是在文件內部導覽。

說明 PDF 中的內部連結,顯示以連結與目標圖示連接的文件區段

許多 PDF 文件會使用內部連結作為書籤或目錄,幫助讀者快速在章節間移動。以下是如何使用 Layout API 為文件區段加入連結:

PdfDocumentBuilder.Create().Generate("link.pdf", doc =>
{
    doc.Pages(pages =>
    {
        pages.Content().Column(c =>
        {
            const string SectionName = "Chapter 1";
            c.Item().SectionLink(SectionName).Text("Link");

            c.Item().PageBreak();

            c.Item().Section(SectionName).Text("Target");
        });
    });
});

範例程式碼會將第一頁上的文字變成指向指定名稱區段的連結。它透過在包含文字的版面配置容器上呼叫 SectionLink 方法來達成。該區段此時可以尚未存在。文字會在 PDF 檢視器中變成可點選。

接著,程式碼會將第二頁上的文字標示為同名區段的起點。其外觀與行為不會改變,但它會成為第一頁連結的目標。這是透過在對應的版面配置容器上呼叫 Section 方法完成的。

在這個範例中,連結與其目標都是文字區段,但你可以在任何版面配置容器上建立區段與連結。它可以是包含影像的容器、表格容器,或你自行建立的容器。

請參閱我們範例存放庫中如何 建立 PDF 目錄 的範例。

設計複雜的 PDF 版面配置

Layout API 提供多種可組合的版面配置容器,讓你建立任意複雜的 PDF 文件。你也可以使用自己的自訂元件擴充 API。

本頁範例刻意保持簡單,設計成建立直接明瞭的文件,讓你可以聚焦於核心概念而不被細節淹沒。如果你想了解如何將不同容器組合起來以 產生更複雜的 PDF,請查看我們 GitHub 範例存放庫中的對應範例。

除了使用內建容器之外,你也可以定義並使用自訂版面配置元件。當你希望單一類別同時封裝資料與版面配置邏輯時,這些元件特別有用。把所有內容放在同一處能讓元件更容易理解、修改與重用,同時也提供你彈性處理複雜版面配置的方式。

若要建立自訂版面配置元件,請在你的類別中實作 ILayoutComponent 介面。產生 PDF 時,程式庫會呼叫該介面的 Compose 方法,並提供 LayoutContext 物件。你的程式碼可以使用這個物件建立版面元素,並存取正在產生的文件資訊。

若要將自訂版面配置元件加入你的版面,請呼叫 LayoutContainer 類別的 Component 方法。在大小與定位方面,自訂元件的行為與文字或影像相同。

若要查看使用 ILayoutComponent 介面實作自訂元件的範例,請參閱 Layout 元件 範例。

產生 PDF/A 文件

您可以將 Layout API 與免費的 Conformance 附加元件一起使用,以產生 PDF/A 文件:

using var ms = new MemoryStream();
PdfDocumentBuilder.Create().Generate(ms, doc => doc.Pages(_ => { }));

using var pdf = new PdfDocument(ms);
pdf.SaveAsPdfa("pdfa-4.pdf", PdfaConformanceLevel.Pdfa4);

支援所有 PDF/A 符合性等級。您也可以建立 Factur-X/ZUGFeRD 發票。請參閱 將 PDF 轉換為 PDF/A 一文以了解更多資訊。

建立 PDF 的其他方式

Docotic.Pdf 提供多種建立 PDF 的方式,各自適合不同情境。本節說明何時應依賴 Layout API,以及何時其他方法可能更適合你。

何時優先使用 Layout API

Docotic.Pdf Layout API 是從結構化版面產生 PDF 的強大方式。它讓你透過將文字、影像、容器與表格組合成任意複雜的巢狀結構來建立文件。產生過程快速、記憶體使用合理,且行為可預期。

Docotic.Pdf 與 Layout 外掛組成一套輕量、完全自包含的組合。此 API 不需要外部程式庫或瀏覽器即可產生 PDF。這使它成為 .NET 微服務、雲端應用程式(尤其是無伺服器應用程式)以及其他對體積大小敏感環境的穩固選擇。

由於文件版面配置是以程式碼定義,你可以對其任何部分進行單元測試。版面邏輯可以像其他程式碼一樣重用,而且你可以在 自訂元件 中封裝資料與版面行為。

Layout API 的替代方案

有一篇專門文章詳細比較了使用 Docotic.Pdf 建立 PDF 的各種方式。以下是一些最常用的替代方案。

  • HTML 轉 PDF 轉換
    重用既有的 HTML/CSS 範本。當你的團隊已經以 HTML/CSS 產生文件,並需要這些文件的 PDF 版本時,請選擇 HTML 轉 PDF 方法

  • 低階 PDF 產生
    提供程式庫對 PDF 結構與內容的最高控制權。當你需要 像素級定位或複雜向量圖形 時,請選擇此方法。此方法建議用於效能關鍵情境,或需要盡可能小的體積時。

  • 以範本為基礎的 PDF 產生
    提供快速且可預測的方式來填入文件,而不需要設計或排列其元素。當你已有預先定義的 PDF 結構,例如已核准或受合規控制的範本,且只需要 變更文字欄位、替換預留位置、附加相關文件以及執行類似工作時,請選擇此方法。

  • PDF 合併與組合
    讓你能 從既有片段建立 PDF,而不是從零開始建立。當你有影像、掃描頁面或其他需要合併為單一檔案的 PDF 時,請選擇此方法。

與其他 PDF 產生方案比較

本節包含兩個比較表:一個是重點摘要,另一個是關於搭配 Layout 外掛的 Docotic.Pdf 與其他熱門 PDF 產生方案比較的詳細結構化資訊。

比較重點

有多種強大的 PDF 產生方案,包括免費方案。然而,簽章、加密或合併文件等能力各不相同,這可能限制真正符合你需求的工具。

解決方案 何時使用 最適合
搭配 Layout 外掛的 Docotic.Pdf 當你想要高品質、高效能的版面配置引擎,能產生最佳化 PDF,並且跨平台提供對簽章、加密與 PDF 編輯的進階支援 高品質產生發票、報表、對帳單與類似文件,並提供優異的開發者體驗。當你需要企業級 PDF 處理與專業支援時,是理想選擇
PDFsharp + MigraDoc 當你想要一個免費、採 MIT 授權的程式庫,用於基本 PDF 產生,且不需要數位簽章或現代加密演算法 開放原始碼或預算受限專案中的簡單文件建立
QuestPDF 當你想要一個專門用於 PDF 產生的現代版面配置引擎,且不需要編輯、簽章或加密 使用 MIT 或低成本商業授權進行高品質 PDF 產生,前提是所有非產生功能由其他地方處理
iText 當你需要一套成熟、功能豐富的 PDF 工具組,用於產生與處理 PDF,並且你準備將方案以 AGPL/GPLv3 授權開源,或購買昂貴的商業授權 已熟悉 iText API,因此不受其陡峭學習曲線影響的團隊

詳細比較

請檢視表格,以了解更廣泛的背景並形成你自己的結論。

  搭配 Layout 的 Docotic.Pdf PDFsharp + MigraDoc QuestPDF iText
PDF 功能 功能完整的 PDF 程式庫 PDF 產生與有限編輯 僅限 PDF 產生 廣泛的 PDF 功能
PDF/A 支援 所有符合性等級 建置中 僅支援 PDF/A-2 和 PDF/A-3 所有符合性等級
渲染模型 現代、宣告式、保留模式版面配置引擎 以方塊為基礎的版面配置引擎 現代、宣告式、保留模式版面配置引擎 以方塊為基礎的版面配置引擎 + 渲染樹
API 類型 Fluent API 命令式 API Fluent API 命令式 API
開發者體驗 非常優秀。API 乾淨、現代且直覺 良好。API 設計是傳統式的 非常優秀。API 乾淨、現代且直覺 尚可。API 冗長且過於複雜
字型子集化 支援;預設只內嵌使用到的字形 不支援;可能導致 PDF 不必要地過大 支援;預設只內嵌使用到的字形 支援;預設只內嵌使用到的字形
從右到左(RTL)內容方向 支援 不支援 支援 支援
數位簽章 支援,包括 LTV 與外部簽章 不支援 不支援 支援,包括 LTV 與外部簽章
加密 / 權限 完全支援 僅 RC4 加密,不支援 AES 或憑證 不支援 完全支援
外部相依性 SkiaSharp / 以 Skia 為基礎的元件
支援 對潛在客戶與現有客戶提供專業支援;頂級授權提供優先支援 社群支援;可另行購買專業支援 透過 GitHub 提供社群支援 AGPL 版本提供社群支援;商業授權持有者提供專業支援
授權 商業授權,符合資格的使用情境可取得 免費授權 MIT 個人與小型公司可使用 MIT;較大型企業需要商業授權 開放原始碼用途採 AGPL;專有專案需昂貴的商業授權
開發者授權 所有授權皆不限開發者數量 所有授權皆不限開發者數量 MIT 版不限開發者數量;Professional 版限 10 位開發者;Enterprise 版不限開發者數量 AGPL 版本不限開發者數量;商業授權採每位開發者授權

結論

搭配 Layout 外掛的 Docotic.Pdf 提供了一種現代、高效能且高品質的方式,可在 C# 與 VB.NET 中產生 PDF。此程式庫可產生報表、對帳單、發票與類似文件。其設計良好、fluent 的 API 提供優異的開發者體驗。你可以依賴 Bit Miracle 為 Docotic.Pdf 及其外掛提供的專業支援。

與某些其他 PDF 產生方案不同,Docotic.Pdf 是功能完整的 PDF API。此程式庫可使用數位簽章 簽署產生的 PDF,包括支援 LTV 的簽章。Docotic.Pdf 能使用儲存在安全硬體上的 憑證,例如 USB token 與智慧卡。也支援雲端式硬體安全模組(HSM),例如 Microsoft Azure Key Vault 與 AWS Key Management Service(KMS)。

使用 Docotic.Pdf,你可以將 支援文件(例如試算表或語音筆記)附加到產生的 PDF。若要在網頁或類似介面中顯示文件,你可以從其一個或多個頁面 建立縮圖影像

後續步驟: