Flow layout API

From
Jump to: navigation, search

Overview

This API provides a new way to generate PDF files programmatically. In opposite to Fixed layout API, designed to be as close as possible to PDF specification, Flow layout API provides you with a number of high-level content building blocks e.g. TextBlock, Grid, Image which can be placed on a page and automatically positioned using so-called layout rules. The layout engine analyzes various properties of these content elements and then logically processes them according to the layout rules generating the resulting PDF document. Properties of these content elements can be assigned directly or defined by styles, thus it becomes possible to easily manage groups of objects with similar properties by changing corresponding style objects.

A process called style matching assigns a style to a particular object(content building block) based on style selector – a rule that defines which elements should be affected by this style. It works similar to HTML+CSS and one familiar with the latter can easily use one's experience and build PDF documents within a minute or even quicker. Actually, styles and content element are so native and easy to work with that we would recommend using it for generating of quickly changing and repetitive content, e.g. invoices, bills, reports, forms etc.

A list of supported features is rather big and we’d recommend checking code samples included along with the download package and showing the full power of Apitron PDF Kit and its Flow layout API in action. To name a few, it supports floating text, automatic pagination of grids and sections, ordered and unordered lists with various types of markers (including nested lists), text justification, dynamic content generation, automatic links and bookmarks creation and much more. It can even be mixed with elements from Fixed layout API.

This API is built around FlowDocument class and it’s possible to save created layout as XML template and load from it. It separates document design from actual processing and can be used to alter document appearance and introduce future changes without(!) recompiling the application.

Creating simple PDF document

Let's see how to create a simple PDF document with “Hello world” message on the first page using Flow layout API.

Consider the following code:

// create output file
using (Stream outputStream = File.Create("hello_world.pdf"))
{
    // create new document
    FlowDocument document = new FlowDocument()
    
    // add textual content element
    document.Add(new TextBlock("Hello world using flow layout API"));

    // save to output stream with page size A4
    document.Write(outputStream,new ResourceManager(), new PageBoundary(Boundaries.A4));    
}
 

Here you can see the basics of flow layout workflow, we create FlowDocument instance first, then we fill it with some content (simple TextBlock is used in this sample) and at the end we write it as PDF document to the output stream.

For writing we need an output stream, a ResourceManager instance that holds all resource objects used in the document (empty in our case) and PageBoundary object describing page dimensions. All pages in the target document will have the same dimensions unless they’re explicitly adjusted using techniques described in the article change page size and styles on the fly. As you can see, there is no notion of any style object or explicit property usage in this code sample. It was made intentionally to give you a very brief overview and a valid PDF document before moving on to more advanced topics.

And the results of the execution can be seen on the image below:

Create simple PDF file using flow layout API

Box model and layout basics

Flow Layout API uses a slightly modified traditional box model to define its content elements. Vertically infinite canvas limited by page width in horizontal dimension is used to layout them and to form the content of the resulting PDF document. The box model used by the Flow layout API assumes that margin, border and padding are inside the box defined by the element's width and height. See the image below:

Box model used by the Flow layout API


A content element gets rendered within its box and later placed on a page according to the layout rules and corresponding properties. It’s important to note that every element will always have implicit box even if you don’t specify its dimensions. One of the important properties, affecting the layout of the element, is Display. It specifies whether the element should be processed as:

  • Block – creating a line break before and after
  • Inline – added to the current line
  • InlineBlock – a special kind of Block element that doesn’t create line breaks
  • None - shouldn’t be processed at all

The main difference between Block, InlineBlock and Inline elements is that pure inline elements can’t be sized explicitly by setting their dimensions. It will be ignored the same way as HTML processors do. In addition several style properties can be inherited only by Block elements (actually a very few of them) and it will be discussed later in section Available style properties.

Main type used for specifying various elements' dimensions in Flow layout API is Length. Its constructor accepts values given in points, pixels, centimeters, inches, %'s and a special value Auto that depends on the context where it's used.

Colors

Colors in Flow layout API are represented by the Color class. It can be created using an array of double values, where empty array means transparent color, 1 element creates the color in DeviceGray colorspace, 3 in DeviceRgb colorspace and 4 in DeviceCmyk colorspace. You can also specify the name of the colorspace explicitly and provide an array of double values to create a color instance. When you use a named or custom colorspace e.g. Pattern colorspace you have to specify it's name (e.g. PATTERN) and also the name of the color, which in case of pattern colorspace is the id of the pattern resource. There is a helper class PredefinedColorSpaces which contains standard colorspace names defined by the PDF specification. Another helper class RgbColors provides access to precreated named colors such as Coral, Blue, Azure etc.

Style system

Style definition

Every content element or control element in Flow layout API has the same set of properties affecting its appearance and behavior at the time of processing. This set is called a style and can be of two kinds:

  • Internal style, holding the properties you can set directly using the object's instance. (inline styles are the closest alternative from the HTML/CSS world)
  • Matched style, provided by the style manager owned by the FlowDocument object instance and assigned using the specific selector

Style and StyleManager relationship

A Style class works as properties container and StyleManager class as document's style manager. FlowDocument class has a special property called StyleManager and all necessary styles should be added by means of this property.

A style however doesn’t come alone. It has a linked selector, an object that describes which elements should be affected by this particular style. In our case, a selector is a string written using the special syntax that defines which elements to match. If you ever worked with HTML and CSS then the syntax of these selectors will look familiar to you.

In short, the workflow is as follows:

  • Define styles and content elements that should be matched
  • Add styles to the flow document's StyleManager
  • Add content elements to the FlowDocument object
  • Save the result as PDF document or convert to FixedDocument instance if needed for further processing

It saves lots of time and code, and is a straightforward way to generate complex PDF content very quickly.

Selectors and style matching

Selector – is a rule that defines which elements should be matched and styled using the given style, it looks very similar to the notation used for CSS and is linked to a property set defined by the accompanying Style object.

Supported selectors:

  • Universal selector (“*”), selects all elements in a document or all children of a given element if used with child selector
  • Type selector (“_typename_”), selects all elements of the given type. E.g. “textbox”. It's possible to list several types using a comma, e.g. "textbox, section, image"
  • Class selector (“.classname”), selects all elements that belong to a specific class. E.g. “*.myclass” selects all elements with class “myclass”. Because element can have multiple classes set, it is possible to specify several classes in one selector, for example “*.myclass1.myclass2”
  • Id selector (“#elementID”). An element can have an id set - a special property usually used as a unique tag, and this selector matches the element with specific id.
    Sample:
    “texbox#help”, selects the textbox element with Id=“help”
  • Child selector (“[ancestor]>child”), selects all elements which are immediate children of specied ancestor element, square brackets indicate optionality of the ancestor, in case of absence it will behave as if a universal selector(“*”) was used instead
    Samples:
    “> textbox” – same as “textbox”, selects all textboxes which are children of any element,
    “section > textbox ” – select all textboxes which are placed in sections,
    “flowdocument > section> textbox.subheader” – selects all textboxes with the class ”subheader” set, nested in any section that is an immediate child of the document element(root)
  • Descendant selector (“element_1 element_2”) selects all elements which are descendants of a given element
    Sample:
    “section#header image” – selects all images in section with ID=”header” regardless of the inclusion level
  • Adjacent element selector (“predessor + element”), selects the element that precedes some element
    Sample:
    “image + textbox” – selects all images that precede a textbox.

How the styles are being resolved (rules of style matching):

  1. Directly assigned value is requested first. It's the value you set using any arbitrary property of the content element.
  2. If no value is set using p.1, the system tries to find the value using the registered selectors and styles
    1. look for all selectors that match given element
    2. select value according to matching rules (these rules are: specificity of the selector and order of addition, if specificity is the same, order is used)
  3. Request parent's value if property is inheritable using the same scheme

In general, this style system is close to CSS used with HTML, so one familiar with the latter can easily find similarities.

Available style properties

There are 36 different properties defined in Style class, plus some of the content elements may have their own specific properties which are not necessary related to style system but can be used to control their layout. It provides great flexibility and provides endless ways for the generation of PDF documents.

Each property defined in the Style class is explained below to give you an overview of the style system and how this properties interact with it, while custom content elements' properties are desctibed in the section Content elements and controls.

Layout-specific properties

Name Description Can be inherited from container
Align Gets or sets the horizontal alignment of the element content, it can be Left, Right, Center or Justified. Only acceptable for block elements, this property will be ignored if the element is not a block-level element. Left, Right and Center values are supported by all elements. In case if Justify is used – only text elements will be affected. All elements inherit value from their block containers; only block elements can override parent's Align setting.
Clear Gets or sets the clear flag for the element indicating whether it should ignore floating elements (if any) and start a new line. No
Float Gets or sets the value indicating that element can float (to be attached to the left or right side of the page and make other content flow around it) No
Display Gets or sets the display setting for the element. No
Height Gets or sets the height of the element. No
Width Gets or sets the width of the element. No
LineHeight Gets or sets the height of the for the container line. For a block container element with content composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. On a non-inline element, 'line-height' specifies the height that is used in the calculation of the line box height. Yes
Margin Gets or sets the margin around the element. No
Padding Gets or sets the padding. No
VerticalAlign Gets or sets the vertical align. No

Text-specific properties

Name Description Can be inherited from container
Color Gets or sets the foreground color for the element. Yes
CharacterSpacing Gets or sets the character spacing. Yes
Font Gets or sets the font for the element. Yes
ScriptLevel Gets or sets the value used to create subscripting or superscripting effect. It defines the level of effect, zero can be used for normal scripting, positive values are for superscripting, negative for subscripting. Affects textual elements. No
TextDecoration Gets or sets the text decoration. Yes
TextIndent Gets or sets the text indent. Yes
TextRenderingMode Gets or sets the text rendering mode used to draw textual elements. By default all text is being drawn using Fill setting. Yes
WordSpacing Gets or sets the word spacing. Yes

Background-specific properties

Name Description Can be inherited from container
Background Gets or sets the background color. No
BackgroundImage Gets or sets the background image for the element. The background area of an element takes the total space occupied by the element content, including padding (but not the margin and border). By default, a background-image is placed at the top-left corner of the element, and repeated both vertically and horizontally. No
BackgroundPosition Gets or sets the background position value for the element. The background position property sets the starting position of a background image. No
BackgroundRepeat Gets or sets the background repeat value for the element. The background repeat property sets if/how a background image will be repeated. By default, background image is repeated both vertically and horizontally. No

Background color property is quite powerful and can be used to creat repeating pattern filled backgrounds usign colors defined in Pattern colorspace, see the example showing how to use a tiling pattern as page background.

Grid-specific properties

Name Description Can be inherited from container
CellPadding Gets or sets the cell padding, affects only Grid elements. No
InnerBorder Gets or sets the inner border, affects only Grid elements. No
InnerBorderColor Gets or sets the color of the inner border, affects only Grid elements. No

List-specific properties

Name Description Can be inherited from container
ListCounter Gets or sets the list counter. No
ListMarker Gets or sets the list marker appearance. Yes
ListMarkerPadding Gets or sets the list marker padding. Yes
ListStyle Gets or sets the list style. No

Use type selector to style a text block

This code sample demonstrates basic usage of style system and assigns the same style to all textblocks in a flow document using the type selector:

// create output file
using (Stream outputStream = File.Create("selectors.pdf"))
{
    // create new document
    FlowDocument document = new FlowDocument();
    
    // register style that matches all textblocks and sets their text color
    document.StyleManager.RegisterStyle("textblock", new Style(){Color = RgbColors.Red});
    
    // add textual content element
    document.Add(new TextBlock("A textblock with red text"));
    
    // add textual content element and directly set its property overriding the matched style
    document.Add(new TextBlock("A textblock with black text"){Color = RgbColors.Black});
    // save to output stream with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

You see from the code that we defined a type selector that matches all textblocks in the document. But according to the Rule #1 of style matching (see selectors and style matching) the second text box will have its color assigned to black, because any direct property assignment overrides the value provided by a matching style.

The results are shown on the image below:

Text block styled using type selector

Content elements and controls

Content elements, as the name suggests, are the building blocks for the flow document's content represented by the FlowDocument class. They all have the same set of properties, but may handle them differently depending on their nature. All content elements are inherited from a single base class ContentElement (even FlowDocument itself).

The following content elements are supported at the moment:

  • Textblock – for adding textual content
  • Image – for adding images
  • Br – indicates that line break should be created between the elements
  • Hr – creates a horizontal line much like in HTML
  • Section – container, for grouping elements together and creating lists
  • Grid - for a creation of grids, support column and row spans
  • PageCount – control that displays a number of pages in the document
  • PageBreak – indicates that a page break needs to be generated
  • ContentReference – used to place reusable pieces of content created with FlowContent, FixedContent or Image resource objects

Control elements, or simply controls are special content elements designed to build forms. From the PDF point of view, they produce widget annotation objects (see this article describing interactive forms for the details) and should have backing fields created for them. But you don’t have to worry about how content elements or controls are being wrapped to PDF entities, because almost all PDF specific operations are implemented under the hood and managed by the Apitron PDF Kit library automatically.

The following control elements are supported at the moment:

  • TextBox – used to create editable text fields that user can use for entering text
  • PushButton – a button that can have various Actions assigned (see Actions article to know more)
  • RadioButton – a control providing a way to choose a single item from a group
  • Choice – works much like a combo box or a list box depending on its properties
  • CheckBox – provides a way to create various check boxes in PDF form
  • SignatureControl – can be used to provide a visual representation for a signature field in Flow layout API context, see also this code sample explaining how signature mechanics are working implemented using Fixed layout API)

TextBlock

This element is designed to add text on page and has only one specific property Text for setting the text value. It’s also possible to create "dynamic" text objects using a delegate, which will be able to use some information present at the time of invocation for advanced text generation, e.g. one may use current time or page number.

Right to left and bidirectional text is supported automatically. Special characters supported are as follows: ‘&nbsp;’ (adds non-breaking space), ’&lt;’( adds ‘<’), ’&gt;’( adds ‘>’). By default this element is considered as Inline element unless its Display property has been changed.

See the code sample:

using (Stream outputStream = File.Create(“textblock.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument(){Margin = new Thickness(5)};
    
    // create simple textblock
    TextBlock textBlock = new TextBlock(“Hello world!”);
    
    //create dynamic textblock and use PageGenerationContent for printing current page
    TextBlock textBlockDynamic = new TextBlock((ctx) => string.Format(“Hello world on page {0}!”,
    ctx.CurrentPage+1));
    
    // add created text blocks
    document.Add(textBlock);
    document.Add(new Br());
    document.Add(textBlockDynamic);
    
    // save to output stream with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

Produced results are as follows:

Regular and dynamic TextBlocks


TextBlock content element uses one of the standard PDF fonts called Helvetica as default font. Default font size is set to 10pt. Font property can be used to change this behavior via inline or matched style.

Image

This element represents an image on PDF page. You have to create an image XObject first and register it using a ResourceManager that the FlowDocument instance uses for storing referenced resources. After these necessary preparation are done, you’ll be able to use it using the Image class from Flow layout API. Its ResourceId property can be used to check which resource this instance represents. By default this element is considered as Inline.

Note: Despite the fact that Image content element is Inline by default, its dimensions can be set explicitly and these settings will affect the final image size. It’s an exclusion from the common rule which however seems logical for such objects like image.

Consider the code below:

// create output file
using (Stream outputStream = File.Create(“image.pdf”))
{
    // create resource manager that will hold our resources
    ResourceManager resourceManager = new ResourceManager();
    // add image XObject to resource manager
    resourceManager.RegisterResource(new Apitron.PDF.Kit.FixedLayout.Resources.Xobjects.Image(“logo”,”apitron.png”));
    
    // create new document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    
    // append image to the document
    document.Add(new Image(“logo”));
    
    // save to output stream with page size set to A4
    document.Write(outputStream, resourceManager, new PageBoundary(Boundaries.A4));
}
 

Result looks as follows:

Image content element added using Flow layout API

Br and Hr

Content element Br represents a line break and default height of the element is zero, it can be changed using its Height property. Content element Hr represents a horizontal line which is useful for visual formatting. Its default thickness is 1pt., but it can be changed using its Height property. Both elements are always considered as Block elements, thus producing a line break.

See the code sample below:

// create output file
using (Stream outputStream = File.Create(“br_and_hr.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    
    // add content
    document.Add(new TextBlock(“Text on line 1”));
    document.Add(new Hr());
    document.Add(new TextBlock(“Text on line 2”));
    document.Add(new Br());
    document.Add(new TextBlock(“Text on line 3”));
    
    // save to output stream with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

Resulting image looks as follows:

Br and Hr elements usage


The Br element could be simulated by setting Display property for target element to Block in some cases. But if you’d like to create vertically spaced content lines with custom spacing between them, it just might be a better choice to use Br with a custom height set.

Section

Section is a container for logical grouping of content elements. It can be styled and added into other containers and also can be used to create ordered and unordered lists by setting its ListStyle property via a matching style or directly. By default this element is considered as Block. The closest alternative in the HTML world for the section is div merged with ol and ul elements.

Consider the following example, creating a basic section with two TextBlocks inside:

// create output file
using (Stream outputStream = File.Create(“section.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument(){Margin = new Thickness(5)};
    
    // create a section and set its properties directly
    Section section = new Section()
    {
        Border = new Border(1),
        BorderColor = RgbColors.Red,
        Padding = new Thickness(Length.FromPoints(5)),
        Width = 150
    };
    
    // add some content to section
    section.Add(new TextBlock(“Inside the section (1)”));
    section.Add(new Br());
    section.Add(new TextBlock(“Inside the section (2)”));
    
    // add to the document
    document.Add(section);
    
    // save as a PDF document with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

Resulting image is shown below:

Section content element

Grid

Grid content element can be used to group other elements in rows and columns. It’s a container for GridRow elements which you will use to create actual grid rows. This element has special properties which can be set via a matching style or direcly:

  • InnerBorder for inner border properties such as thickness, dash pattern etc. (in addition to Border property which all controls have)
  • InnerBorderColor which is essentially the inner border color
  • CellPadding can be used to define the inner cell padding

By default this element is considered to be a Block-level element, unless other setting is used.

Let’s try to create a basic grid showing imaginary shipping info for some company:

// create output file
using (Stream outputStream = File.Create(“grid.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    // create grid with 4 columns each taking 25% of page width
    // set cellpadding and content alignment for each cell
    Grid grid = new Grid(
        Length.FromPercentage(25),
        Length.FromPercentage(25),
        Length.FromPercentage(25),
        Length.FromPercentage(25))
        {CellPadding = new Thickness(5), Align = Align.Center};
        
    // fill the grid, notice the first row it uses colspan for making first cell wider
    grid.Add(new GridRow(new TextBlock(“Shipping info, Dec 2014”){ColSpan = 4}));
    grid.Add(new GridRow(new TextBlock(“Destination”), new TextBlock(“Weight”), new TextBlock(“Date shipped”), new TextBlock(“Tracking ID”)));
    grid.Add(new GridRow(new TextBlock(“New York”),new TextBlock(“1258 Kg”), new TextBlock(“12/04/2014”), new TextBlock(“US381904”)));
    grid.Add(new GridRow(new TextBlock(“Seattle”),new TextBlock(“554 Kg”), new TextBlock(“12/12/2014”), new TextBlock(“US43321”)));
    grid.Add(new GridRow(new TextBlock(“Chicago”),new TextBlock(“7346 Kg”), new TextBlock(“23/12/2014”), new TextBlock(“US67911”)));
    
    // add to the document
    document.Add(grid);
    
    // save to output stream with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

The resulting file looks as follows:

Grid content element usage, page size A4


Notice that we used ColSpan property to get the top row spanned across the grid. We also used percentage-based column widths, so our grid will look similarly even if we change the page size. Image below shows the same grid fit on a page with size A6.


Grid content element usage, page size A6


Each row of the grid is represented by the type GridRow, a subcontainer element that is used to group grid cells together. Elements of this type can only be added into the Grid content element and their styling is limited (only properties which can be inherited from and background color value affect the appearance of the contained grid cells).

Links created using CrossReference or LinkUri classes and pointing to/from GridRow elements are also not supported. This behavior can be easily circumvented by adding a link to one of the child elements.

Each content element added into a grid row is being treated as a separate cell, so if you need multiple elements to be placed inside the single cell you should use a section to group them together. If you need the cell to be spanned vertically or horizontally you may use RowSpan or ColSpan properties respectively, these properties are taken into account by Grid element only. Both have value of 1 set by default.

Note: Every document has default style set by default for grid elements that uses single line InnerBorder and Black as default value for InnerBorderColor. This style is defined using a type selector (see the article describing the selectors and style matching and type selector usage sample for the details) and matches all grid elements whithin a particular document. If you override this default style, you will have to provide values for the properties mentioned above, otherwise their values won’t be set causing possibly unexpected results.

PageBreak

PageBreak content element controls the pagination behavior. It works on a page-level and has no effect being added to a container other than FlowDocument. It finalizes the page it appears on and generation continues on a new page.

Code for adding a page break:

// add page break
document.Add(new PageBreak());
 

PageCount

PageCount content element shows the number of pages generated while saving a particular FlowDocument instance. It can be used on any page, so you may use it to show the number of generated pages even on the first page of the document. By default this element is considered Inline.

Take a look at the sample code:

// create output file
using (Stream outputStream = File.Create(“pagecount.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument(){Margin = new Thickness(5)};
    document.Add(new TextBlock(“Page count in this document:”));
    
    // add page count element, set counter resolution to 2
    document.Add(new PageCount(2));
    
    // add page breaks, it will create blank pages
    document.Add(new PageBreak());
    document.Add(new PageBreak());
    document.Add(new PageBreak());
    document.Add(new PageBreak());
    
    // save as a PDF document with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

Generated file looks as follows:

PageCount content element usage

ContentReference

It’s a special content element that can be used to add reusable pieces of content on a PDF page. Such content may be defined using FlowContent, FixedContent or Image classes and registered in document's ResourceManager. By default this element is considered as Inline.

Code sample demonstrating the usage of ContentReference:

// create output file
using (Stream outputStream = File.Create(“contentreference.pdf”))
{
    // create document resource manager
    ResourceManager resourceManager = new ResourceManager();
    
    /* create form XObject (1)*/
    FixedContent fixedContent = new FixedContent(“rect”,new Boundary(0,0,50,50));
    
    // create rect path
    Path path = new Path();
    path.AppendRectangle(10,10,30,30);
    
    // select colors and draw path
    fixedContent.Content.SetDeviceNonStrokingColor(new double[]{1,0,0});
    fixedContent.Content.SetDeviceStrokingColor(new double[] {0,0.5,1});
    fixedContent.Content.FillAndStrokePath(path);
    
    // register fixed content
    resourceManager.RegisterResource(fixedContent);
    
    /* create a piece of flow content (2) */
    Section flowContentSection = new Section() { Color = RgbColors.Black};
    flowContentSection.Add(new TextBlock(“ContentReference element can be used”));
    flowContentSection.Add(new Br());
    flowContentSection.Add(new TextBlock(“to place reusable pieces of content on PDF page”));
    FlowContent flowContent = new FlowContent(“flowContentPiece”,flowContentSection,150,45);
    resourceManager.RegisterResource(flowContent);
    
    /* create and register image resource (3)*/
    resourceManager.RegisterResource(new Apitron.PDF.Kit.FixedLayout.Resources.Xobjects.Image(“logo”, “apitron.png”));    
    // create new flow document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    // register style for ContentReference elements using type selector
    document.StyleManager.RegisterStyle(“ContentReference”, new Style() { BorderColor = RgbColors.Magenta, Border = new Border(1) });
    
    // place reusable content on page    
    document.Add(new ContentReference(“rect”));
    document.Add(new ContentReference(“logo”));
    document.Add(new ContentReference(“flowContentPiece”));
    document.Add(new ContentReference(“flowContentPiece”));
    document.Add(new ContentReference(“rect”));
    document.Add(new ContentReference(“logo”));
    
    // save to output stream with page size A4
    document.Write(outputStream, resourceManager, new PageBoundary(Boundaries.A4));
}
 

In this example we have used all types of objects which can be referenced by the ContentReference element:

  • FixedContent, which is a form XObject. Drew a rectangle using Fixed layout API (1)
  • FlowContent, a static piece of content that can be created and reused multiple times and uses flow layout rules in opposite to FixedContent. Its bounds should be specified on creation and one or more elements (if section is used) can be used to define its content (2)
  • Image, an image XObject defining image to be used within the PDF document (3)

Furthermore, all elements have the same border defined by a matching style. It means that even if all these elements represent static content which can't be affected by the styles used by a particular FlowDocument instance, their placeholder can still be customized.

The image below shows the result:

Reusable content added using ContentReference element

TextBox

Represents a TextBox control linked to a text field (see the Fields article for details). Under the hood it hosts a widget annotation visualizing the field's content. It can be multiline and have various borders and styles assigned. By default this element gets processed like Block element.

Consider the code below, which creates two text boxes styled in different ways:

// create output file
using (Stream outputStream = File.Create(“textbox.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    document.StyleManager.RegisterStyle(“TextBox”,new Style(){Width = 100});

    // add text field and set its properties
    TextField textField1 = new TextField(“textField1”,”textbox”)
    {
        BorderStyle = new AnnotationBorderStyle(1),
        BorderColor = new double[]{0}
    };
    
    // add text field without setting any styles
    TextField textField2 = new TextField(“textField2”, “textbox”);
    
    // add fields to flow document
    document.Fields.Add(textField1);
    document.Fields.Add(textField2);
    
    // add text box linked to first text field
    document.Add(new TextBox(“textField1”));
    // add text box linked to second text field and style right here
    document.Add(new TextBox(“textField2”)
    {
        Border = new Border(1), BorderColor = RgbColors.Black, BorderRadius = 5
    });
    
    // save as a PDF document with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

As one may notice from the code above, we define the width for both text boxes using a style bound to a type selector. Other important difference is that we style the first text box using properties of a linked field while the second text box is being styled in situ by setting the corresponding content element properties.

Thus, in the first case, we set up actual field and all text boxes bound to this field (and there can be many) will inherit these properties as default for their widget annotation. All fields properties are defined in PDF specification.

In the second case we style a particular text box control, and actually we style it via its placeholder. Since PDF doesn’t define properties like border radius, etc. which are supported by the Apitron PDF Kit, only the placeholder gets styled and not the annotation itself.

It’s an important difference that one should consider if there's a need to copy annotations from one document to another in the future.

The image below demonstrates the result:

TextBox control usage

Highlight Existing Fields viewing mode was used to highlight form fields in the demonstrated document.

PushButton

PushButton represents a button control linked to a PushbuttonField. Any action defined in PDF specification can be assigned to it. See Actions for the details. Sample below demonstrates the creation of the PDF document containing a button with JavaScriptAction assigned. By default this element is processed as Block.

// create output file
using (Stream outputStream = File.Create(“pushbutton.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument(){Margin = new Thickness(5)};
    document.Fields.Add(new PushbuttonField(“buttonField”,”Click me!”));
    PushButton pushButton = new PushButton(“buttonField”,
        new JavaScriptAction(“app.alert(‘Hello world by Apitron PDF Kit!’);”))
        {
            Width = 100
        };
    
    // add new push button that shows the message box when clicked
    document.Add(pushButton);
    // save to the output stream as PDF document with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

Result looks as follows:

PushButton usage

RadioButton

RadioButton control element represents a radio button control linked to RadioButtonField. You can use single field to group radio buttons together. By default this element is considered as Block.

Check the code below:

using (Stream outputStream = File.Create(“radiobutton.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    
    // set all elements to have the same width
    document.StyleManager.RegisterStyle(“*”, new Style(){Width = 100});
    
    // add field and link option buttons
    document.Fields.Add(new RadioButtonField(“requiredRam”, “8gb”));
    document.Add(new RadioButton(“requiredRam”, “1 GB”, “gb1”));
    document.Add(new RadioButton(“requiredRam”, “2 GB”, “gb2”));
    document.Add(new RadioButton(“requiredRam”, “4 GB”, “gb4”));
    document.Add(new RadioButton(“requiredRam”, “8 GB”, “gb8”));

    // add button field and PushButton
    document.Fields.Add(new PushbuttonField(“showSelection”, “Show selection”));
    document.Add(new PushButton(“showSelection”,
        new JavaScriptAction(“app.alert(‘User selected: ‘ + this.getField(‘requiredRam’).value);”)));
    
    // save to output stream as PDF document with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

Results are shown on the image below, note that we used PushButton to display the selected value. It’s also possible to define different check marks instead of a circle used by default.

RadioButton usage

Choice (combobox and listbox)

Choice element is control linked to ChoiceField which contains several text items, one or more of which shall be selected as the field's value. The items may be presented to the user in one of the following two forms:

  • A combo box consisting of a drop-down list. The combo box may be accompanied by an editable text box in which the user can type a value other than the predefined choices if it’s allowed by the field author.
  • A scrollable list box

By default this element gets processed as Block.

Check the code below:

using (Stream outputStream = File.Create(“choice.pdf”))
{
    string [] options = new string[]{“1 GB”,”2 GB”,”3 GB”, “4 GB”};
    // create new document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    
    // set all elements to have the same width
    document.StyleManager.RegisterStyle(“*”, new Style() { Width = 100 });
    // add choice as list box
    document.Fields.Add(new ChoiceField(“requiredRam”, “1gb”, options){BorderColor = new double[0]});
    
    // add choice as combo
    document.Fields.Add(new ChoiceField(“requiredRam2”, “1gb”, options, ChoiceFieldType.ComboBox));
    
    // add Choice element to show first choice field
    document.Add(new Choice(“requiredRam”) { Display = Display.InlineBlock });
    // add Choice element to show second choice field
    document.Add(new Choice(“requiredRam2”) );
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

Resulting document is shown below:

Choice control usage

Checkbox

Checkbox element represents a checkbox control linked to a CheckBoxField. It supports various checkmarks and many checkboxes can be linked to a single field. By default this element is treated as Block.

See the code:

// create output file
using (Stream outputStream = File.Create(“checkbox.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument(){Margin = new Thickness(5)};
    
    // add checkbox field
    document.Fields.Add(new CheckBoxField(“121heckbox”,”I agree”));
    
    // add two checkbox controls linked to single field
    document.Add(new CheckBox(“121heckbox”, CheckBoxMark.Check));
    document.Add(new CheckBox(“121heckbox”, CheckBoxMark.Star));
    
    // save to output stream with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

The image below demonstrates the resulting document:

Checkbox usage

This document contains two checkboxes linked to a single CheckBoxField and they have different check marks set. When user selects one of them the other becomes checked as well.

SignatureControl

This element defines visual representation for the SignatureField in Flow layout API context. It’s possible to use SignatureFieldViewSettings class to define view settings which widget annotation created for the signature will use. You may assign it using ViewSettings property of the SignatureField. If no view settings are assigned then SignatureControl will show default signature representation which includes all signature properties related to the signier and read from the signing certificate. By default this element is processed as Block.

See also the signing sample from the Digital signatures in PDF section for other details and example on how to use image XObject instead of default visual representation.

The code for adding visual representation for a digital signature using flow layout api looks as follows:

// create output file
using (Stream outputStream = File.Create(“signature_control.pdf”))
{
    ResourceManager resourceManager = new ResourceManager();
    // create new document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    
    // add style for signature control using type selector
    document.StyleManager.RegisterStyle(“SignatureControl”, new Style(){Border = new Border(1),
    BorderColor = RgbColors.DarkBlue, BorderRadius = 5});
    
    // open digital certificate and create signature field
    using (Stream certificateStream = File.OpenRead( “johndoe.pfx”))
    {
        // add lender signature field
        document.Fields.Add(new SignatureField(“signature”)
        {
            Signature = Signature.Create(new Pkcs12Store(certificateStream, “password”))
        });
    }
    
    // add textblock and signature control to resulting document
    document.Add(new TextBlock(“This document was signed and approved by:”));
    document.Add(new SignatureControl(“signature”){Width = 100, Height = 50});
    
    // save as PDF document with page size A4
    document.Write(outputStream, resourceManager, new PageBoundary(Boundaries.A4));
}
 

Note that unlike in the sample from the Fixed layout API we didn’t use the image representing "handwritten" signature in this example. We relied on default visual representation generated by the Apitron PDF Kit for our signature field. Another thing to note is that we used style and matching type selector to create a custom border around the signature control.

Resulting file is shown on the image below:

SignatureControl in action

Page header and footer

Page header and footer are designed to host the content which should be repeated on top or bottom of each page, e.g. page number or company's title. There are no special content elements defined for header or footer and they both are of type Section. One may use PageHeader or PageFooter properties of FlowDocument object instance to set their content.

The code below shows how to use these properties:

// create output file
using (Stream outputStream = File.Create("header_and_footer.pdf"))
{
    // create document and set margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    
    // register style for gray elements
    document.StyleManager.RegisterStyle(".grayBg", new Style() { Background = RgbColors.LightGray });
    
    // register styles for header and footer
    document.StyleManager.RegisterStyle(".header",new Style(){Align = Align.Center, VerticalAlign = VerticalAlign.Middle});
    document.StyleManager.RegisterStyle(".footer",new Style(){Align = Align.Right, VerticalAlign = VerticalAlign.Bottom });
    
    // set header class and add content
    document.PageHeader.Class = "header grayBg";
    document.PageHeader.Add(new TextBlock("Big Company Inc."));
    
    // add document's content
    document.Add(new TextBlock("This document shows page header and footer usage. Both elements were styled using separate styles and class selectors.
    Common class selector was used to make them appear gray."));
    document.Add(new PageBreak());
    document.Add(new TextBlock("Notice that footer shows total page count."));
    
    // set footer class and add content
    document.PageFooter.Class = "footer grayBg";
    document.PageFooter.Add(new TextBlock((ctx)=> string.Format("Page {0} of ",Convert.ToString(ctx.CurrentPage+1))));
    document.PageFooter.Add(new PageCount(2));
    
    // save document
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A6));
}
 

We used two different styles in order to show header's and footer's appearance changed using the class selectors and also applied additional style which sets gray as a background color for both. The header contains a static TextBlock and the footer has dynamic page label placed on the right side of the page created using the dynamic TextBlock combined with PageCount element.

The content for header and footer gets generated for each page independently from the main document layout process. It lets you avoid code duplication and helps to easily define various repeating headers and footers separated from the main content generation routine.

The image below demonstrates the resulting document:

Page header and footer usage

Lists

There are two list kinds which can be generated using Flow layout API: ordered and unordered. The first is designed to contain numbered items while the second represents a plain list with item markers of chosen appearance. The list items themselves can have various list markers assigned and it’s possible to mix markers within one list. Nested lists are supported as well.

There is no special content element representing a list and lists are implemented using a Section class instead. Once you need to create a list you create a section object, set its ListStyle property to a desired list style and use other properties described in section List-specific properties to define the appearance of list items.

Content element added to the section marked as list, becomes a list item only if its ListStyle property is set to ListItem. List items are being always considered as Block elements.

Nested numbering e.g. 1.2, 1.2.3 (for ordered lists) can be implemented using several nested sections each marked as ordered list. Custom markers (for unordered lists) can be created using static member function defined for the ListMarker named FromResourceId(). It uses an id of the existing XObject defined in the same way as used for ContentReference element. One is required to set list item's marker otherwise it will appear unmarked by default.

See sections Ordered list sample and Unordered list sample for the detailed list creation code samples.

Ordered list sample

Consider the sample code provided below. It creates several numbered lists side by side and styles them using different numeric markers. Third list demonstrates how to start the numeration using explicitly specified counter, define multilevel lists and unmark one of the items.

// create output file
using (Stream outputStream = File.Create("ordered_list.pdf"))
{
    // create document and set margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };

    // defines style for elements with class "ol" - ordered list
    document.StyleManager.RegisterStyle(".ol",
        new Style()
        {
            ListStyle = ListStyle.Ordered, Display = Display.InlineBlock, Width = Length.FromPercentage(25)
        });
    
    // define style for all textblocks nested into the element marked with class "ol" (ordered list)
    document.StyleManager.RegisterStyle(".ol > TextBlock",
        new Style()
        {
            ListStyle = ListStyle.ListItem
        });
    
    // define marker classes for to be used with ordered lists
    document.StyleManager.RegisterStyle(".decimal",
        new Style()
        {
            ListMarker = ListMarker.Decimal
        });
    
    document.StyleManager.RegisterStyle(".roman", new Style()
        {
            ListMarker = ListMarker.UpperRoman
        });
        
    document.StyleManager.RegisterStyle(".latin",
        new Style()
        {
            ListMarker = ListMarker.UpperLatin, ListCounter = new ListCounter(0)
        });
    
    document.StyleManager.RegisterStyle(".decimal10",
        new Style()
        {
            ListMarker = ListMarker.Decimal, ListCounter = new ListCounter(10)
        });
        
    /* simple numbered list with decimal markers */
    Section list1 = new Section(
        new TextBlock("First item"),
        new TextBlock("Second item"),
        new TextBlock("Third item"),
        new TextBlock("Fourth item"),
        new TextBlock("Fifth item"))
    {
        Class = "ol decimal"
    };
    
    /* simple numbered list with roman markers */
    Section list2 = new Section(
        new TextBlock("First item"),
        new TextBlock("Second item"),
        new TextBlock("Third item"),
        new TextBlock("Fourth item"),
        new TextBlock("Fifth item"))
    {
        Class = "ol roman"
    };
    
    /* complex numbered list with decimal markers, numbering starts from 10 and it has nested list */
    // inner numbered list part 1
    Section innerList1 = new Section(
        new TextBlock("First item"),
        new TextBlock("Second item"),
        new TextBlock("Third item"),
        new TextBlock("Fourth item"),
        new TextBlock("Fifth item"))
    {
        Class = "ol decimal", Width = Length.Auto, Color = RgbColors.Red
    };
    
    // inner numbered list part 2
    Section innerList2 = new Section(
        new TextBlock("First item"),
        new TextBlock("Second item"),
        new TextBlock("Third item"),
        innerList1,
        new TextBlock("Fifth item"))
    {
        Class = "ol decimal", Width = Length.Auto, Color = RgbColors.Green
    }
    
    // outer list
    Section list3 = new Section(
        new TextBlock("First item"),
        new TextBlock("Second item") { ListMarker = ListMarker.None },
        new TextBlock("Third item"),
        new TextBlock("Fourth item"),
        innerList2,
        new TextBlock("Fifth item"))
    {
        Class = "ol decimal10" 
    };
    
    /* simple numbered list with latin markers*/
    Section list4 = new Section(
        new TextBlock("First item"),
        new TextBlock("Second item"),
        new TextBlock("Third item"),
        new TextBlock("Fourth item"),
        new TextBlock("Fifth item"))
    { Class = "ol latin" };

    // add lists to document
    document.Add(list1);
    document.Add(list2);
    document.Add(list3);
    document.Add(list4);
    
    // save document
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

The image below demonstrates the result. Notice different markers used by all lists. Also pay attention to the multilevel numeration, list counter starting from 10 and explicitly unmarked list item in the third list (“Second item”).

Ordered lists creation sample


Unordered list sample

The following code creates several unordered lists side by side and assigns different item markers to each of them. Child items are turned into list items using descendants selector based on the parent class.

using (Stream outputStream = File.Create("unordered_list.pdf"))
{
    // create document and set margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    
    // defines style for elements with class "ul" - unordered list
    document.StyleManager.RegisterStyle(".ul",
        new Style()
        {
            ListStyle = ListStyle.Unordered,
            Display = Display.InlineBlock,
            Width = Length.FromPercentage(25),
        });
        
    // define style for all elements nested into the element marked with class "ul"
    document.StyleManager.RegisterStyle(".ul > *",
        new Style()
        {
            ListStyle = ListStyle.ListItem 
        });
        
    // define unordered markers array used to create new lists
    ListMarker [] listMarkers = {ListMarker.Circle, ListMarker.Diamond, ListMarker.Triangle, ListMarker.Square};

    // create lists
    foreach (ListMarker listMarker in listMarkers)
    {
        // create list and set its marker using instance property
        Section list = new Section(
            new TextBlock("First item"),
            new TextBlock("Second item"),
            new TextBlock("Third item"),
            new TextBlock("Fourth item"),
            new TextBlock("Fifth item"))
        {
            Class = "ul", ListMarker = listMarker, ListMarkerPadding = new Thickness(5)
        };
        
        document.Add(list);
    }
    
    // save document
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

The image below shows the resulting PDF file generated by the given code:

Unordered list usage sample


Markers padding is being set using ListMarkerPadding property which defines how much space will be left around the marker. By default each marker doesn’t have any padding assigned and may look glued to its item. One may use marker padding to define its indent from the marked item and vertical offset that gives the complete control over its relative position.


Custom list markers sample

The following code sample shows how to use custom markers for list items:

using (Stream outputStream = File.Create("custom_markers.pdf"))
{
    // register image XObject resource for custom marker
    ResourceManager resourceManager = new ResourceManager();
    resourceManager.RegisterResource(new Apitron.PDF.Kit.FixedLayout.Resources.XObjects.Image("myMarker","marker.png"));
    
    // create document and set margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    
    // defines style for elements with class "ul" - ordered list
    document.StyleManager.RegisterStyle(".ul", new Style(){ ListStyle = ListStyle.Unordered});
    
    // define style for all elements nested into the element marked with class "ul"
    document.StyleManager.RegisterStyle(".ul > *", new Style() { ListStyle = ListStyle.ListItem });
    
    // create list and set its marker using instance property
    Section list = new Section( new TextBlock("First item"), new TextBlock("Second item"),
        new TextBlock("Third item"), new TextBlock("Fourth item"), new TextBlock("Fifth item"))
    {
        Class = "ul", ListMarker = ListMarker.FromResourceId("myMarker"), ListMarkerPadding = new Thickness(0,0,5,0)
    };
    
    document.Add(list);
    document.Write(outputStream, resourceManager, new PageBoundary(Boundaries.A4));
}
 

Resulting list with custom markers is shown on the image below:

Custom list markers usage

Navigation

Flow layout API supports all navigation features described in PDF specification and described in sections Document-level navigation and Add link annotations for quick navigation. Also there are many actions based samples in the chapter Actions usage samples. The concept behind the navigation techniques remain the same but, being different from Fixed layout API, Flow layout API provides its own implementation for these operations.

Each content element has two properties responsible for navigation:

  • Bookmark, which controls whether bookmark entry should be created for this element in the document bookmarks list. It’s of type BookmarkEntry and defines bookmark text as well as other properties needed to display it in bookmarks tab of the PDF viewer. Bookmarks set for content elements become naturally structured based on the element's nesting level and it in its turn will be reflected in the bookmarks tree of the document.
  • Link, which turns element to a link pointing to some destination. Can be of several types, see the chapter Links for the details. Please read the subparagraphs which contain descriptions for all mentioned properties and types with code samples and explanations.

Bookmarks

An object of type BookmarkEntry representing document bookmark can be attached to almost any content element; however there are a few exclusions: GridRow, Br and Hr elements. They were excluded because there makes no sense to create links to this content-less elements. The first one can be navigated using any of its cells, while the others have assume no meaningful content.

Bookmarks are organized hierarchically, so bookmark entries assigned to nested elements will be added as children to parent's element bookmark if it exists. If it doesn’t exist they’ll come to the upper level and so on, until they reach the root.

Every bookmark can have either static or dynamic caption assigned. Dynamic caption, as its name suggests, can be generated on the fly using a callback. It makes possible to customize the bookmarks creation according to the context state they are in. For example we can use current time or page number in bookmark's caption.

Consider the code below:

using (Stream outputStream = File.Create(“bookmarks.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5)};
    
    // add text element as immediate child of the document
    document.Add(new TextBlock(“Text element with bookmark”){Bookmark = new BookmarkEntry(“doc-level bookmark”)});
    
    // create nested text elements
    TextBlock nestedElement1 = new TextBlock(“Nested text element 1”){Bookmark = new BookmarkEntry(“child bookmark 1”)};
    TextBlock nestedElement2 = new TextBlock(“Nested text element 2”){Bookmark = new BookmarkEntry(“child bookmark 2”)};
    
    // add page break and move to second page
    document.Add(new PageBreak());
    
    // add section containing text elements and assign a bookmark to it
    document.Add(new Section(nestedElement1, new Br(), nestedElement2){Bookmark = new BookmarkEntry(“Section bookmark”)});
    
    // save to output stream with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

The image below demonstrates the result:

Bookmark usage sample

Links

Links can be used to embed navigation elements into document page. Same limitations apply as for bookmarks, so GridRow, Br and Hr elements can’t act as links. Other content elements can be turned into links by assigning a target to their Link property. Once user clicks on such link, navigation will occur. Currently two link types are supported:

  • CrossReference, navigates to a location within the same document
  • LinkUri, based on URI and navigates to external resource

Note: An element can have both Bookmark and Link properties set.

Let’s see the code:

using (Stream outputStream = File.Create(“links.pdf”))
{
    // create new document with margin
    FlowDocument document = new FlowDocument(){Margin = new Thickness(5)};

    // add link to other element in document
    document.Add(new TextBlock(“Click here to see the element on second page”){Link = new CrossReference(“destination”)});
    document.Add(new Br(){Height = 5});
    
    // add external link to a website
    document.Add(new TextBlock(“www.apitron.com”){Link = new LinkUri(“http://www.apitron.com”), Color = RgbColors.Blue});
    
    // add page break and move to second page
    document.Add(new PageBreak());
    
    // create destination text block, set its id so it could be referenced by link
    document.Add(new TextBlock(“Destination element”) { Id = “destination”});
    
    // save to pdf document with page size A4
    document.Write(outputStream, new ResourceManager(), new PageBoundary(Boundaries.A4));
}
 

And resulting image:

Links creation sample

Markup parsing

Content elements defined in Flow layout API can be created based on some markup to reflect the desired document structure. It becomes especially useful when you have to create paragraphs containing blocks of text having different styles. So we designed and implemented a simple way to do it. A ContentElement class which serves as a base for all content elements contains a static method called FromMarkup(), it creates a collection of content elements using given paramater string as a text with certain markup.

This markup looks similar to XML, given that each element will have its Class property assigned to a name or a set of names derived from the enclosing tags. The resulting list will contain parsed content elements (mostly TextBlocks) with assigned classes. A single markup element can contain the following attributes:

  • Link, affects the Link property of the created element. If link destination starts with # then it will be considered to be a CrossReference element otherwise a LinkUri. There is an additional href attribute supported for convenience that works exactly the same.
  • Bookmark, affects the Bookmark property of the created element.
  • Id, affects the Id property of the created element, so it can be linked to.

A few tags in this markup have special meaning:

  • <img>, can be used for placing images, e.g. <img src='[path to file or resource id]' width='[image width][unit]' height='[image height][unit]'/> creates Image element entry. Measure unit can be omitted or can be one of the following: auto, pt, in, px, cm, %.
  • <br/>, creates Br element entry.

Note: Given that markup text is in XML format and its parsing is implemented using XML-aware utils, entries which don't exist in XML like &nbsp; should be entered differently. So instead of ‘<tag> mytext.&nbsp;</tag>’ one should use ‘<tag> mytext.&#160;</tag>’ or ‘<tag> mytext.&amp;nbsp</tag>’ which will produce the same result.

For the detailed sample please take a look at the SimpleHtmlToPDF sample included into the [www.apitron.com/downloads | Apitron PDF Kit download package]. Below is a small piece of code that shows some of the features of markup parsing in action.

// create output file
using (Stream outputStream = File.Create("create_from_markup.pdf"))
{
    // create resource manager and register image to be used further
    ResourceManager resourceManager = new ResourceManager();
    resourceManager.RegisterResource(new Apitron.PDF.Kit.FixedLayout.Resources.XObjects.Image("logo","apitron.png"));

    // create document and set margin
    FlowDocument document = new FlowDocument(){Margin = new Thickness(10)};
    
    // header style
    document.StyleManager.RegisterStyle(".h1", 
       new Style()
       {
           Display = Display.Block, 
           Font = new Font(StandardFonts.HelveticaBold,16), 
           Margin = new Thickness(0,3,0,3)
       });
    
    // italic text style
    document.StyleManager.RegisterStyle(".i", 
       new Style()
       {
          Font = new Font(StandardFonts.HelveticaOblique,12)
       });
       
    // bold text style
    document.StyleManager.RegisterStyle(".b",
        new Style()
        {
                Font = new Font(StandardFonts.HelveticaBold,12)
        });

    // link style
    document.StyleManager.RegisterStyle(".a", new Style() { Color = RgbColors.Blue, 
        TextDecoration = new TextDecoration(TextDecorationOptions.Underline)});

    // image style
    document.StyleManager.RegisterStyle(".img", new Style() { Float = Float.Left});
    
    string markup = "<h1>Markup parsing</h1><img src='logo'></img>One of the advantages of using <i>markup parsing</i> is that" +
    " styled text can be produced quckly and resulting PDF looks awesome." +
    "Just call <b>ContentElement.FromMarkup</b> static function and it's done." +
    "Xml-based markup makes it esy to define own tags which are being transformed to classes and style them using class selectors."+
    "Create floating text, bookmarks, links, images and other PDF content with this style-based and very easy to use API." +
    "Read more about Apitron PDF Kit for .NET and its features on our website <a href='http://www.apitron.com'>www.apitron.com</a>.";
    
    document.Add(new Section(ContentElement.FromMarkup(markup)));
    document.Write(outputStream, resourceManager, new PageBoundary(Boundaries.A4));
}
 

The code from previous page registers several styles for the text markup representing a small piece of text containing an image with a text floating on the right. It also shows how to define a weblink, make text bold or italic. Special class for header becomes defined as well (“h1”). Resulting file is shown on the image below:

Markup parsing sample

XML templates

FlowDocument model supports XML export and import features, and one can use it to separate PDF documents creation from the code. As an example one may have a set of PDF forms stored as XML templates (supported by Apitron PDF Kit) which require regular updating. In this case application doesn't need to be rebuilt and updated, it will just load the updated templates and work as it did.

Limitations: some resource objects can’t be saved to XML, e.g. image data (because images can be linked only), FixedContent or FlowContent objects, external font files data.

The code which saves document to XML and loads it from template is as follows:

// save document to XML template (1)
using (Stream outputStream = File.Create("document_template.xml"))
{
    // create document resource manager
    ResourceManager resourceManager = new ResourceManager();

    // create new document with margin
    FlowDocument document = new FlowDocument() { Margin = new Thickness(5) };
    
    // add content elements
    document.Add(new TextBlock("text element"));
    
    //...create your document structure here
    document.SaveToXml(outputStream, resourceManager);
}

// load XML template from file (2)
using (Stream inputStream = File.OpenRead("document_template.xml"), outputStream = File.Create("fromxml.pdf"))
{
    // create resource manager which will hold loaded references
    ResourceManager resourceManager = new ResourceManager();
    
    // load document
    FlowDocument document = FlowDocument.LoadFromXml(inputStream, resourceManager);
    
    // save to pdf
    document.Write(outputStream,resourceManager,new PageBoundary(Boundaries.A4));
}
 

Here we used SaveToXml instance method to save document as XML template (1) and LoadFromXml static method to load this template (2).

XML template example

Consider the following template generated using the code from XML templates:

<?xml version="1.0" encoding="utf-8"?>
    <FlowDocument xmlns="Apitron.PDF.Kit.FlowLayout.v1">
        <Resources />
        <Styles>
            <Style selector="flowdocument">
                <Color value="Black" />
            </Style>
            <Style selector="grid">
                <InnerBorder thickness="1" />
                <InnerBorderColor value="Black" />
            </Style>
        </Styles>
        <Elements>
            <TextBlock>
                <Properties>
                    <Text value="text element" /> (1)
                </Properties>
            </TextBlock>
        </Elements>
        <Properties>
            <Margin value="5,5,5,5" />
        </Properties>
</FlowDocument>
 

If you change the template by adding a section:

<Style selector="textblock">
    <Color value="Red"/>
</Style>
 

to the <Styles> node of the template and load it using part (2) of the code from the main chapter you’ll get the following color change:

Create PDF document using modified XML template


Integration with Fixed layout API

One may have noted that sometimes Flow layout API samples make use of many classes defined for the Fixed layout API. The decision behind this was not to create duplicating entities serving as alternatives to the existing classes where it was appropriate. However, while working with FixedDocument it is sometimes advantageous to use the flexibility Flow layout API can offer.

So a special AppendContentElement() method was added to a ClippedContent class inherited by every drawing context in Fixed layout API. This method accepts ContentElement objects defined in Flow layout API and allows using them directly(without preprocessing) for the generation of PDF content with Fixed layout API.

Note: As there is no StyleManager in FixedDocument model, style support is limited to inline styles only. That's is to modify the appearance of ContentElement objects used in Fixed layout API one has to set element's properties directly.

Consider the code:

using (Stream outputStream = File.Create("flow_in_fixed.pdf"))
{
    // create document and add one page to it
    FixedDocument fixedDocument = new FixedDocument();
    fixedDocument.Pages.Add(new Page());
    
    ClippedContent content = fixedDocument.Pages[0].Content;
    // add TextBlock content element to the page's content
    content.Translate(10,790);
    content.AppendContentElement(new TextBlock("This multiline text box was created using TextBlock element."), 190,40);
    
    // add text using regular textobject, you’ll have to layout all text yourself
    content.Translate(210,28);
    TextObject textObject = new TextObject(StandardFonts.Helvetica, 12);
    textObject.AppendText("This multiline text box was created");
    textObject.MoveToNextLine(0,-15);
    textObject.AppendText("using TextObject.");  
    content.AppendText(textObject);
    
    // save document
    fixedDocument.Save(outputStream);
}
 

And the resulting image:

Using ContentElement objects in Fixed layout API