Custom Page Elements

Custom Page Elements are used to output PDF code to a page or can utilize other already existing page element objects.

Creating custom page elements require understanding of PDF operators and using them to create a PDF. Core Suite provides methods encapsulating these operators. For example, the CS operator, which sets the color space for a stroking operation, has the corresponding write_CS method.

PageElement API

All page elements inherit from the PageElement base class and override its Draw method.

Custom page elements MUST extend the PageElement class and MUST override the Draw method.

Draw

The PageElement.Draw method accepts a PageWriter object.

public abstract void Draw(PageWriter writer)
MustOverride Sub Draw(writer As PageWriter)

When a PageElement instance is added to the page this method is called. In the method's body you add PDF operators using the methods of the PageWriter object.

  1. Use the SetXXXX (i.e. SetLeading, SetColor, SetGraphicsMode, etc.) methods to set the state on page.
  2. Then use the Write_XX (i.e. Write_l_, Write_m_, Write_re) methods to add the PDF graphic or text operators to the page. Most of the PDF operators have a Write_XXXX method associated with them. If the operator only differs by case, the lower case operator has a trailing "_" character. This is done to comply with the Common Language Specification (CLS).

A RotatingPageElement base class is also included. You can inherit from this to automatically provide support for rotating your page element.

When working with page elements, you do not have to be concerned with compression, encryption or PDF object numbering. This is all handled internally by Core Suite.

Custom PageElement Guidelines

  1. Always use the SetXXXX method to set graphics and text states. This allows the minimum amount of PDF code to be output to the page and allows the next page element that is called to properly set the state that it needs. If you are not going to use these methods you will need to use the q and Q operators to store the current state and then restore it before leaving the Draw method.
  2. Always use a top margin to bottom margin and left margin to right margin coordinate system for the properties of your page element. Remember that PDF uses a bottom edge to top edge and left edge to right edge coordinate system that is complex and confusing to most users. This complexity should be hidden from the user of your page element. The GetPDFX, GetPDFY methods are provided on the PageWriter Dimensions property to help with this translation.
  3. Use the X, Y, Width, and Height properties where applicable.
  4. Use the built-in Color, LineStyle, and Font objects wherever possible. This makes your Page Element function in the same fashion as the built-in page elements and makes it easier for users to use them.

Development Tips

When developing custom page elements, set the document's compression level to zero and don't use security or encryption. Create a simple application that adds one page to the PDF document and adds your custom page element to that page. This makes it possible for you to output the PDF document to a file and view the PDF output from your page element in a text editor.

PDF Resources

An excellent resource for PDF development is "PDF Reference" published by Adobe. This can be downloaded free of charge from the Adobe site. This resource includes documentation of all of the PDF graphic and text operators.

Examples

The following two examples illustrate creating custom page elements. The first customizes a TextArea page element and the second customizes a TaggablePageElement and creates a polygon.

Text Area Example

The following code illustrates customizing a TextArea page element.

using ceTe.DynamicPDF;
using ceTe.DynamicPDF.IO;

namespace DynamicPDFCoreSuite.Examples
{
    public class CustomElement : ceTe.DynamicPDF.PageElements.TextArea
    {
    
        public CustomElement(string text, float x, float y, float width, float height) : base(text, x, y, width, height)
        { }

        public override void Draw(PageWriter writer)
        {
            Font font = Font.CourierBold;
            writer.SetTextMode();
            writer.SetFont(font, 25f);
            writer.SetTextRenderingMode(TextRenderingMode.FillAndStroke);
            writer.SetStrokeColor(RgbColor.Red);
            writer.SetFillColor(RgbColor.LightBlue);
            writer.SetLeading(font.GetDefaultLeading(12));
            writer.Write_Tm(base.X, base.Y);
            writer.Write_SQuote(base.Text.ToCharArray(), 0, base.Text.Length, false);
        }

    }
}
Imports ceTe.DynamicPDF
Imports ceTe.DynamicPDF.IO

Namespace DynamicPDFCoreSuite.Examples
    Public Class CustomElement
        Inherits ceTe.DynamicPDF.PageElements.TextArea

        Public Sub New(ByVal text As String, ByVal x As Single, ByVal y As Single, ByVal width As Single, ByVal height As Single)
            MyBase.New(text, x, y, width, height)
        End Sub

        Public Overrides Sub Draw(ByVal writer As PageWriter)
            Dim font As Font = Font.CourierBold
            writer.SetTextMode()
            writer.SetFont(font, 25.0F)
            writer.SetTextRenderingMode(TextRenderingMode.FillAndStroke)
            writer.SetStrokeColor(RgbColor.Red)
            writer.SetFillColor(RgbColor.LightBlue)
            writer.SetLeading(font.GetDefaultLeading(12))
            writer.Write_Tm(MyBase.X, MyBase.Y)
            writer.Write_SQuote(MyBase.Text.ToCharArray(), 0, MyBase.Text.Length, False)
        End Sub

    End Class
End Namespace

Polygon Example

The following code illustrates creating a Polygon by extending the TaggablePageElement class.

using ceTe.DynamicPDF;
using ceTe.DynamicPDF.IO;
using ceTe.DynamicPDF.PageElements;

namespace DynamicPDFCoreSuite.Examples
{
    class Polygon : TaggablePageElement
    {
        private float[] xCoordinates;
        private float[] yCoordinates;
        private float borderWidth;
        private Color fillColor;
        private Color borderColor;
        private LineStyle borderStyle;
        private const float defaultForBorderWidth = 1.0f;
        private static Color defaultForFillColor = null;
        private static Color defaultForBorderColor = Grayscale.Black;
        private static LineStyle defaultForStyle = LineStyle.Solid;


        public Polygon(float[] xCoordinates, float[] yCoordinates)
        {
            this.xCoordinates = xCoordinates;
            this.yCoordinates = yCoordinates;
            this.fillColor = fillColor;
            this.borderColor = borderColor;

            if (borderWidth <= 0)
                this.borderWidth = 0;
            this.borderWidth = borderWidth;
            this.borderStyle = borderStyle;
        }

        public LineStyle BorderStyle
        {
            get { return borderStyle; }
            set { borderStyle = value; }
        }

        public float[] XCoordinates
        {
            get { return xCoordinates; }
            set { xCoordinates = value; }
        }

        public float[] YCoordinates
        {
            get { return yCoordinates; }
            set { yCoordinates = value; }
        }

        public float BorderWidth
        {
            get { return borderWidth; }
            set
            {
                if (value <= 0)
                    borderWidth = 0;
                else
                    borderWidth = value;
            }
        }

        public Color BorderColor
        {
            get { return borderColor; }
            set { borderColor = value; }
        }

        public Color FillColor
        {
            get { return fillColor; }
            set { fillColor = value; }
        }

        public override void Draw(PageWriter writer)
        {
            writer.SetRelativeToState(base.RelativeTo, base.IgnoreMargins);
            bool draw = true;
            bool fill, stroke;
            if (borderWidth > 0 && fillColor != null)
            {
                stroke = true;
                fill = true;
            }
            else if (borderWidth > 0)
            {
                stroke = true;
                fill = false;
            }
            else if (fillColor != null)
            {
                fill = true;
                stroke = false;
            }
            else
            {
                stroke = false;
                fill = false;
                draw = false;
            }

            if (borderStyle == LineStyle.None)
                stroke = false;

            if (draw)
            {
                if (xCoordinates.Length == yCoordinates.Length && xCoordinates.Length > 2)
                {
                    writer.SetGraphicsMode();
                    if (stroke && fill)
                    {
                        writer.SetLineWidth(borderWidth);
                        writer.SetStrokeColor(borderColor);
                        writer.SetLineStyle(borderStyle);
                        writer.SetLineCap(LineCap.Butt);
                        writer.SetFillColor(fillColor);
                    }
                    else if (stroke)
                    {
                        writer.SetLineWidth(borderWidth);
                        writer.SetStrokeColor(borderColor);
                        writer.SetLineStyle(borderStyle);
                        writer.SetLineCap(LineCap.Butt);
                    }
                    else if (fill)
                    {
                        writer.SetFillColor(fillColor);
                    }

                    writer.Write_m_(xCoordinates[0], yCoordinates[0]);
                    for (int i = 1; i < xCoordinates.Length; i++)
                    {
                        writer.Write_l_(xCoordinates[i], yCoordinates[i]);
                    }
                    writer.Write_l_(xCoordinates[0], yCoordinates[0]);
                    if (fill)
                    {
                        if (stroke) writer.Write_b_();
                        else writer.Write_f();
                    }
                    else writer.Write_s_();
                }
                else
                {
                    throw new GeneratorException("Coordinates are wrong");
                }
            }
        }

    }
}

Imports ceTe.DynamicPDF
Imports ceTe.DynamicPDF.IO
Imports ceTe.DynamicPDF.PageElements

Namespace DynamicPDFCoreSuite.Examples
    Public Class Polygon
        Inherits TaggablePageElement

        Private _xCoordinates As Single()
        Private _yCoordinates As Single()
        Private _borderWidth As Single
        Private _fillColor As Color
        Private _borderColor As Color
        Private _borderStyle As LineStyle

        Private Const defaultForBorderWidth As Single = 1.0F
        Private Shared ReadOnly defaultForFillColor As Color = Nothing
        Private Shared ReadOnly defaultForBorderColor As Color = Grayscale.Black
        Private Shared ReadOnly defaultForStyle As LineStyle = LineStyle.Solid

        Public Sub New(xCoordinates As Single(), yCoordinates As Single())
            Me.xCoordinates = xCoordinates
            Me.YCoordinates = yCoordinates
            Me.fillColor = fillColor
            Me.borderColor = borderColor

            If borderWidth <= 0 Then
                Me.borderWidth = 0
            End If
            Me.borderWidth = borderWidth
            Me.borderStyle = borderStyle
        End Sub

        Public Property BorderStyle As LineStyle
            Get
                Return _borderStyle
            End Get
            Set(value As LineStyle)
                _borderStyle = value
            End Set
        End Property

        Public Property XCoordinates As Single()
            Get
                Return _xCoordinates
            End Get
            Set(value As Single())
                _xCoordinates = value
            End Set
        End Property

        Public Property YCoordinates As Single()
            Get
                Return _yCoordinates
            End Get
            Set(value As Single())
                _yCoordinates = value
            End Set
        End Property

        Public Property BorderWidth As Single
            Get
                Return _borderWidth
            End Get
            Set(value As Single)
                If value <= 0 Then
                    _borderWidth = 0
                Else
                    _borderWidth = value
                End If
            End Set
        End Property

        Public Property BorderColor As Color
            Get
                Return _borderColor
            End Get
            Set(value As Color)
                _borderColor = value
            End Set
        End Property

        Public Property FillColor As Color
            Get
                Return _fillColor
            End Get
            Set(value As Color)
                _fillColor = value
            End Set
        End Property

        Public Overrides Sub Draw(writer As PageWriter)
            writer.SetRelativeToState(MyBase.RelativeTo, MyBase.IgnoreMargins)
            Dim draw As Boolean = True
            Dim fill As Boolean, stroke As Boolean

            If borderWidth > 0 AndAlso fillColor IsNot Nothing Then
                stroke = True
                fill = True
            ElseIf borderWidth > 0 Then
                stroke = True
                fill = False
            ElseIf fillColor IsNot Nothing Then
                fill = True
                stroke = False
            Else
                stroke = False
                fill = False
                draw = False
            End If

            If BorderStyle.Equals(LineStyle.None) Then
                stroke = False
            End If

            If draw Then
                If xCoordinates.Length = yCoordinates.Length AndAlso xCoordinates.Length > 2 Then
                    writer.SetGraphicsMode()
                    If stroke AndAlso fill Then
                        writer.SetLineWidth(borderWidth)
                        writer.SetStrokeColor(borderColor)
                        writer.SetLineStyle(borderStyle)
                        writer.SetLineCap(LineCap.Butt)
                        writer.SetFillColor(fillColor)
                    ElseIf stroke Then
                        writer.SetLineWidth(borderWidth)
                        writer.SetStrokeColor(borderColor)
                        writer.SetLineStyle(borderStyle)
                        writer.SetLineCap(LineCap.Butt)
                    ElseIf fill Then
                        writer.SetFillColor(fillColor)
                    End If

                    writer.Write_m_(xCoordinates(0), yCoordinates(0))
                    For i As Integer = 1 To xCoordinates.Length - 1
                        writer.Write_l_(xCoordinates(i), yCoordinates(i))
                    Next
                    writer.Write_l_(xCoordinates(0), yCoordinates(0))
                    If fill Then
                        If stroke Then
                            writer.Write_b_()
                        Else
                            writer.Write_f()
                        End If
                    Else
                        writer.Write_s_()
                    End If
                Else
                    Throw New GeneratorException("Coordinates are wrong")
                End If
            End If
        End Sub
    End Class
End Namespace


Refer to the following code if you wish to see an example of both custom classes in action.

In this topic