PdfSharpCore Documentation

repository·master·Indexed 22 days ago

https://github.com/ststeiger/pdfsharpcore

A .NET Standard library for generating PDF documents, providing a port of PdfSharp and MigraDoc with modern image and font support via SixLabors.ImageSharp and SixLabors.Fonts. It includes PdfSharpCore for low-level PDF creation and MigraDocCore for high-level document modeling with support for paragraphs, tables, charts, and automated layout.

Tokens
35K
Snippets
55
Records
91
Agent score
78%

What's inside PdfSharpCore

  1. Overview of PdfSharpCore

    master
    PdfSharpCore is a .NET Standard port of PdfSharp.Xamarin. It includes a port of MigraDoc (from version 1.32). It provides PDF generation capabilities with integrated support for images via SixLabors.ImageSharp and fonts via SixLabors.Fonts.
  2. Language and Character Support (Arabic, Hebrew, CJK)

    master

    PdfSharpCore currently has limited support for complex languages:

    • Right-to-left (RTL) languages: Not natively supported. Hebrew can be approximated by reversing strings and left-aligning paragraphs. Arabic requires manual handling of glyph shapes (beginning, end, middle, isolated) by reversing strings and selecting specific Unicode characters.
    • CJK (Chinese, Japanese, Korean): Japanese characters can be displayed if a compatible font is selected, but they will be rendered left-to-right rather than top-to-bottom.
    • Note: Always ensure the selected font contains the required character sets.
  3. Apply transformations and state management with XGraphics

    master

    When drawing complex shapes (like clock hands), use coordinate transformations to simplify math:

    • TranslateTransform: Moves the origin of the coordinate system (e.g., to the center of the page).
    • ScaleTransform: Scales the coordinate system.
    • RotateTransform: Rotates the coordinate system around the current origin.
    • State Management: Always use gfx.Save() before applying transformations and gfx.Restore(gs) after drawing to ensure subsequent drawing operations are not affected by the previous transformations.
    void DrawHourHand(XGraphics gfx, XPen pen, XBrush brush)
    {
        XGraphicsState gs = gfx.Save();
        gfx.RotateTransform(360 * Time.Hour / 12 + 30 * Time.Minute / 60);
        gfx.DrawPolygon(
            pen, brush,
            new XPoint[]{new XPoint(0,  150), new XPoint(100, 0), 
            new XPoint(0, -600), new XPoint(-100, 0)},
            XFillMode.Winding);
        gfx.Restore(gs);
    }
  4. Use XGraphicsPath for complex drawing and clipping

    master

    The XGraphicsPath class allows you to build complex shapes by combining lines, arcs, and curves. Once a path is constructed, you can:

    • Stroke/Fill: Use gfx.DrawPath(pen, path) or gfx.DrawPath(pen, brush, path).
    • Convert Text to Path: Use path.AddString(...) to turn text into a geometric path, which can then be stroked or filled like any other shape.
    • Clipping: Use gfx.IntersectClip(path) to restrict all subsequent drawing operations to the area defined by the path.
  5. Work directly with underlying PDF objects

    master

    When specialized PdfSharpCore classes do not support a specific PDF feature, you can manipulate the underlying PDF structure directly using low-level objects like PdfDictionary, PdfArray, PdfName, and PdfReference.

    This approach requires knowledge of the PDF specification (e.g., Adobe's PDF Reference). By using these objects, you can implement features like custom actions, advanced metadata, or specific document behaviors that are not yet part of the high-level API.

    Key concepts for direct object manipulation:

    • Indirect References: Instead of adding a high-level object (like a PdfPage) directly to a collection, use PdfInternals.GetReference(object) to create a PdfReference. This ensures the object is treated as an indirect object in the PDF structure.
    • Object Table: Use document.Internals.AddObject(object) to add a manually constructed object to the document's object table.
    • Catalog Access: Use document.Internals.Catalog to access the document's root dictionary to add top-level keys like /OpenAction.
    // Example: Adding an OpenAction to a document
    PdfDocument document = PdfReader.Open(filename);
    
    // 1. Create a dictionary for the action
    PdfDictionary dict = new PdfDictionary(document);
    dict.Elements["/S"] = new PdfName("/GoTo");
    
    // 2. Create an array for the destination
    PdfArray array = new PdfArray(document);
    dict.Elements["/D"] = array;
    
    // 3. Add elements to the array (e.g., a reference to page 3, a fit command, and a coordinate)
    PdfReference iref = PdfInternals.GetReference(document.Pages[2]);
    array.Elements.Add(iref);
    array.Elements.Add(new PdfName("/FitV"));
    array.Elements.Add(new PdfInteger(-32768));
    
    // 4. Register the dictionary in the document's object table
    document.Internals.AddObject(dict);
    
    // 5. Link the action to the document catalog
    document.Internals.Catalog.Elements["/OpenAction"] = PdfInternals.GetReference(dict);
  6. Implement a LayoutHelper for automatic pagination

    master

    Because PdfSharpCore is a low-level drawing library, it does not handle automatic page breaks. You can implement a LayoutHelper pattern to manage vertical positioning and trigger AddPage() calls when the current position exceeds a defined bottom margin.

    A LayoutHelper should track:

    • The PdfDocument instance.
    • The _currentPosition (vertical Y coordinate).
    • The _topPosition and _bottomMargin for the current page.
    • The current XGraphics and PdfPage objects.

    When requesting a line position via a method like GetLinePosition(requestedHeight), the helper checks if _currentPosition + requestedHeight exceeds the _bottomMargin. If it does, it calls a CreatePage() method to reset the position and refresh the graphics context.

    public class LayoutHelper
    {
        private readonly PdfDocument _document;
        private readonly XUnit _topPosition;
        private readonly XUnit _bottomMargin;
        private XUnit _currentPosition;
        
        public LayoutHelper(PdfDocument document, XUnit topPosition, XUnit bottomMargin)
        {
            _document = document;
            _topPosition = topPosition;
            _bottomMargin = bottomMargin;
            // Set a value outside the page - a new page will be created on the first request.
            _currentPosition = bottomMargin + 10000;
        }
        
        public XUnit GetLinePosition(XUnit requestedHeight, XUnit requiredHeight = -1f)
        {
            XUnit required = requiredHeight == -1f ? requestedHeight : requiredHeight;
            if (_currentPosition + required > _bottomMargin)
                CreatePage();
            XUnit result = _currentPosition;
            _currentPosition += requestedHeight;
            return result;
        }
        
        public XGraphics Gfx { get; private set; }
        public PdfPage Page { get; private set; }
        
        void CreatePage()
        {
            Page = _document.AddPage();
            Page.Size = PageSize.A4;
            Gfx = XGraphics.FromPdfPage(Page);
            _currentPosition = _topPosition;
        }
    }
  7. Choose between PdfSharpCore and MigraDocCore

    master

    Decide which library to use based on your document creation requirements:

    • Use PdfSharpCore if you need low-level control over PDF creation. It allows you to control every pixel and every line drawn, making it ideal for precise graphics or custom drawing routines.
    • Use MigraDocCore if you need to create complex documents using a high-level object model. It provides a word-processor-like experience with features such as paragraphs, tables, and styles, and uses PdfSharpCore internally to render the final PDF.
  8. Implement a custom IFontResolver for embedded fonts

    master

    When running in environments like web services or servers where specific fonts are not installed on the OS, you must implement the IFontResolver interface. This allows you to bundle font files (e.g., .ttf) directly with your application.

    To implement a custom resolver, you must provide logic for two methods:

    1. ResolveTypeface(string familyName, bool isBold, bool isItalic): Determines which font file corresponds to a requested typeface. It returns a FontResolverInfo object.
    2. GetFont(string faceName): Retrieves the actual font data as a byte[] using the faceName provided by the first method.
  9. Create and use XForm objects as templates

    master

    An XForm object acts as a reusable template that can be drawn multiple times anywhere in a PDF document. When you create an XForm, it is bound to its target document, allowing it to share fonts and other resources with that document.

    To use an XForm:

    1. Instantiate XForm with a target document and dimensions using XUnit.
    2. Create an XGraphics object specifically for the form using XGraphics.FromForm(form).
    3. Draw content (text, graphics, images, or even other forms) onto the XGraphics object.
    4. Dispose of the XGraphics object when finished.
    5. Draw the completed form onto a PDF page using gfx.DrawImage(form, ...).
  10. Configure Page Setup, Headers, and Footers

    master

    MigraDocCore allows fine-grained control over page layouts within a Section.

    • Page Setup: Use section.PageSetup to set StartingNumber and toggle OddAndEvenPagesHeaderFooter.
    • Headers/Footers: Access section.Headers.Primary, section.Headers.EvenPage, or section.Headers.OddPage.
    • Cloning Objects: When adding the same object (like a page number paragraph) to multiple headers or footers, you must use .Clone() to avoid exceptions, as an object cannot belong to more than one parent.
    • Page Fields: Use paragraph.AddPageField() to insert dynamic page numbers.