pdf links

PDF Rendering
Convert PDF to Image (.NET)
Convert PDF to image on Android (Xamarin)
Convert PDF to image on iOS (Xamarin)
Convert PDF to image in Windows Store apps (.NET)
Convert PDF to image in Windows Phone apps (.NET)
PDF to image in Universal Windows Store apps (.NET)
Free PDF Viewer control for Windows Forms (.NET)
How to integrate PDF Viewer control in WPF app (.NET)
Creating WPF PDF Viewer supporting bookmarks (.NET)
Cross-platform PDF Viewer using GTK# (MONO)
Silverlight PDF viewer control (Silverlight 5)
Multithreaded PDF rendering (.NET)
Convert pdf to image in Silverlight app (C# sample)
How to set fallback fonts for PDF rendering (C#)
Avoiding the out-of-memory exception on rendering (C#)
PDF viewer single page application (WebAPI, AngularJS)
PDF viewer control for Windows 10 universal applications
Use custom ICC profile for CMYK to RGB conversion
PDF layers - separate images, text, annotations, graphics

PDF Forms Creation PDF Security
Conversion to PDF/A
Other topics
PDF Document Manipulation
PDF Content Generation
Fixed and Flow layout document API (.NET)
Creation of grids and tables in PDF (C# sample)
How to create interactive documents using Actions (C# sample)
Text flow effects in PDF (C# sample)
How to generate ordered and bulleted lists in PDF (C# sample)
Convert HTML to PDF using flow layout API (C# sample)
How to use custom fonts for PDF generation (.NET)
Create document with differently sized pages (C#)
Create PDF documents using MONO (C#/MONO/Windows/OSX)
How to use background images for content elements (C#/PDF Kit/FlowLayout)
Add transparent images to PDF document (C#)
Draw round rect borders in PDF documents(C#)
ICC color profiles and and ICC based colors in PDF (C#)
How to use bidirectional and right to left text in PDF (C#)
Create PDF documents from XML templates (C# sample)
How to resize PDF pages and use custom stamps (C#)
Add header and footer to PDF page (.NET sample)
How to use clipping mask for drawing on PDF page
Fill graphics path with gradient brushes in PDF (Shadings)
Apitron PDF Kit and Rasterizer engine settings
Add layers to PDF page (optional content, C# sample)
How to create free text annotation with custom appearance

PDF Content Extraction
PDF Navigation

PDF to TIFF conversion
Contact us if you have a PDF related question and we'll cover it in our blog.

2015-05-07

How to create grids and tables in PDF

1. Grid and table elements in PDF


If you were to generate reports or similar documents, containing extensive amounts of ordered data, you would probably think whether there is a default, PDF - specific way to do it. We should disappoint you and assert that there’s no such way and PDF specification simply allows us to put drawing commands on PDF page, encouraging to generate the desired representation ourselves.

It should be noted, however, that PDF outlines the special markup technique named “Marked content” that you can use to define the nature of the graphical objects sets put on page. It could help you to identify these objects later and provide some application-specific viewing or processing logic. See section 14.6. Marked Content in PDF specification for details. But you still have to generate the content yourself, marked or not.

Manual generation of grids and tables can appear as a boring task one could rather prefer to be automated. As a part of this article, we’ll dive into details of automatic grid creation and generation and show how to create perfect tables and grids using a few small pieces of C# code and Apitron PDF Kit for .NET component as the main tool.

2. Grid element in flow layout model


The grid element in flow layout model is represented by the Grid class and helps to automate the creation of tabulated data in PDF. Its rows are represented by the GridRow objects and cells can be of any supported content element type, mostly TextBlocks. To place more than one content element into grid cell, you’d have to use one of the available container objects e.g. Section. When content element object is being added into a grid row to represent a cell, one important modification to its style is being made – it becomes a block element. Even if its Display property had the value different from Block before this addition, it would be set to Block and content element would be treated as a grid cell. This modification also assumes that when the column and row sizes are set explicitly, the element representing the cell will be sized accordingly.

The grid element supports automatic splitting and can produce multiple pages if needed. Read more about grids and flow layout model in our book Apitron PDF Kit in action.

2.1 Grid borders and paddings

As any other content elements, grids can have standard outer borders and paddings set. In addition, InnerBorder and CellPadding properties can be used to define one of the main grid's appearance characteristics:  thickness and color of lines separating the cells, and inner cell padding. 

2.2 Simple grid with uniform cells

Let’s create the document containing a simple grid with uniform cells, using the code below:

/// <summary>
/// Creates simple grid with default inner border.
/// </summary>
public void CreateSimpleGrid()
{
    // create resource manager and document object
    ResourceManager resourceManager = new ResourceManager();
    FlowDocument doc = new FlowDocument(){Margin = new Thickness(5)};

    // create grid with 4 autosized columns taking 50% of the available space
    Grid grid = new Grid(Length.Auto,Length.Auto,Length.Auto,Length.Auto);
    grid.Width = Length.FromPercentage(50);

    // add 2 grid rows with data
    grid.Add(new GridRow(new TextBlock("Order"), new TextBlock("Date"),
      new TextBlock("Customer"), new TextBlock("Paid")));
    grid.Add(new GridRow(new TextBlock("1184A"), new TextBlock("01/02/2015"),
      new TextBlock("Jon Doe"), new TextBlock("No")));
    // add grid to document
    doc.Add(grid);

    // save document
    using (Stream stream = File.Create("simple_grid.pdf"))
    {
        doc.Write(stream, resourceManager);
    }
}

The result looks as follows:

Pic. 1 Simple pdf grid with uniform cells

Pic. 1 Simple grid with uniform cells

Created grid has uniform cells and takes 50% of the available page space; it has single inner border and two data rows (including header).

2.3 Simple grid with col spans and row spans

If the “customer” from previous example had more than one order, we could group them all using a row span. To show a col span, we will add a table header spanning across the table.

See the code:

public void CreateSimpleGridWithSpans()
{
    // create resource manager and document object
    ResourceManager resourceManager = new ResourceManager();
    FlowDocument doc = new FlowDocument(){Margin = new Thickness(5)};

    // create grid with 4 autosized columns taking 50% of the available space
    Grid grid = new Grid(Length.Auto,Length.Auto,Length.Auto,Length.Auto);
    grid.Width = Length.FromPercentage(50);

    // add header row spanned across the grid
    grid.Add(new GridRow(new TextBlock("Orders and payments") { ColSpan = 4,
      Align = Align.Center }));

    // add col headers
    grid.Add(new GridRow(new TextBlock("Order"), new TextBlock("Date"),
      new TextBlock("Customer"), new TextBlock("Paid")));
    // add data row with 3d column spanned across 3 rows below
    grid.Add(new GridRow(new TextBlock("1184A"), new TextBlock("01/02/2015"),
      new TextBlock("Jon Doe"){RowSpan = 3}, new TextBlock("No")));
    grid.Add(new GridRow(new TextBlock("1184B"), new TextBlock("03/02/2015"), 
      new TextBlock("No")));
    grid.Add(new GridRow(new TextBlock("1184C"), new TextBlock("05/02/2015"), 
      new TextBlock("No")));

    // add grid to document
    doc.Add(grid);   

    // save document
    using (Stream stream = File.Create("simple_grid_with_spans.pdf"))
    {       
doc.Write(stream, resourceManager);   
    }
}


And resulting image:

Pic. 2 Simple pdf grid with col spans and row spans

Pic. 2 Simple grid with col spans and row spans

Notice the last two rows having they columns shifted to the right because of the row span in the middle of the row above them.


2.4 Simple grid with composite cells

If you were to put several content elements in one cell, you would have to use a container element. Suitable existing containers are Section and Grid itself. Let’s add image and text into the single cell. The code:

public void CreateSimpleGridWithCompositeCells()
{
    // create resource manager and register images
    ResourceManager resourceManager = new ResourceManager();
    resourceManager.RegisterResource(
         new Apitron.PDF.Kit.FixedLayout.Resources.XObjects.Image("flag_red",           
           "flag_red.png"));
    resourceManager.RegisterResource(
         new Apitron.PDF.Kit.FixedLayout.Resources.XObjects.Image("flag_green",
           "flag_green.png"));
    FlowDocument doc = new FlowDocument(){Margin = new Thickness(5)};

    // create grid with 4 autosized columns taking 50% of the available space
    Grid grid = new Grid(Length.Auto,Length.Auto,Length.Auto,Length.Auto);
    grid.Width = Length.FromPercentage(50);

    // add col headers
    grid.Add(new GridRow(new TextBlock("Order"), new TextBlock("Date"), 
      new TextBlock("Customer"), new TextBlock("Paid")));
    // create section for status cell
    Section paymentStatusNo = new Section(
         new Apitron.PDF.Kit.FlowLayout.Content.Image("flag_red"),
         new TextBlock("No") );
    grid.Add(new GridRow(new TextBlock("1184A"), new TextBlock("01/02/2015"),
         new TextBlock("Jon Doe"),
         paymentStatusNo));        
    Section paymentStatusYes = new Section(
         new Apitron.PDF.Kit.FlowLayout.Content.Image("flag_green"),
         new TextBlock("Yes") );
    grid.Add(new GridRow(new TextBlock("1184B"), new TextBlock("03/02/2015"),
         new TextBlock("Jon Doe"), paymentStatusYes));   

    doc.Add(grid);   
    // save document
    using (Stream stream = File.Create("simple_grid_with_composite_cells.pdf"))
    {
        doc.Write(stream, resourceManager);
    }
}


Resulting image:

Pic. 3 Simple pdf grid with composite cells

Pic. 3 Simple grid with composite cells


2.5 Multipage grid


What if you data records take more than one page and have to be paginated? Grid element will handle it automatically unless it’s explicitly sized. Sample below shows what happens if we rely on this behavior and add more records than a page can fit.

Code sample:

/// <summary>
/// Creates multipage grid.
/// </summary>
public void CreateMultipageGrid()
{
    // create resource manager and document object
    ResourceManager resourceManager = new ResourceManager();
    FlowDocument doc = new FlowDocument() { Margin = new Thickness(5) };

    // create grid with 4 autosized columns taking 100% of the available space
    Grid grid = new Grid(Length.Auto, Length.Auto, Length.Auto, Length.Auto);
    // add col headers
    grid.Add(new GridRow(new TextBlock("Order"), new TextBlock("Date"), 
      new TextBlock("Customer"),
      new TextBlock("Paid")));
   
    // add data records
    for (int rowIndex = 0; rowIndex < 45; rowIndex++)
    {
        grid.Add(new GridRow(new TextBlock(rowIndex.ToString()), new TextBlock("01/02/2015"),
          new TextBlock("Jon Doe"), new TextBlock("Yes")));
    }

    // add grid to document
    doc.Add(grid);

    // save document
    using (Stream stream = File.Create("multipage_grid.pdf"))
    {
        doc.Write(stream, resourceManager, new PageBoundary(Boundaries.A6));
    }     
}

This code creates a grid split on two parts because it simply doesn’t fit on one page.  If we add more records or change output page boundary, the number of generated pages will change accordingly. Notice that you don’t have to do anything special for this pagination effect to happen, it’s fully auto. If you set the grid height explicitly, you will limit the number of visible rows (if their accumulated height exceeds the specified grid height).

The image below shows the resulting PDF document containing multipage grid.

Pic. 4 Multipage grid pdf

Pic. 4 Multipage grid

We have created only two pages here, but the idea is clear. Whenever you need a multipage grid in PDF, this flow layout code will do the job.

3. Grid rows and cells styling


Flow layout API offers rich styling capabilities in a form of inline or selector-based styles. It is possible to style individual cell elements as well as grid rows with minimum efforts, and create PDF documents with highly customized data grids within minutes.


3.1 Vertical and horizontal content alignment

The most often needed operation is positioning of the content within cell’s boundaries.  There are two properties designed for that, named Align and VerticalAlign respectively.

Align property, affects only Block or InlineBlock elements, just like its analog in HTML. Because table cells are being implicitly treated as block elements, it’s possible to align their content using Align property exposed by each ContentElement descendant. This setting can be inherited by inner block elements, making it possible to set the horizontal alignment for the entire row by setting it for the GridRow container object instead of individual cell elements.

VerticalAlign property, can be used to define positioning of inline elements within a line of content inside the parent, and is, therefore, dependent on the line height of the parent element. To vertically align an element, one would need to place it inside the Section or similar container, and then set element’s VerticalAlign property to the desired value.

In general, to align a piece of content vertically inside the grid cell, you would have to:
  1. create a Section representing a cell, add it into GridRow 
  2. set its LineHeight to be equal to the height of the parent row 
  3. add the element to this section, and set its VerticalAlign to the desired value
Otherwise, if you had to vertically align single line text block inside the grid row, you could just set its LineHeight equal to the parent row height without the use of the intermediate section.

Let’s see the sample code showing how to create a grid with variously aligned cells content:

/// <summary>
/// Creates a grid with aligned content.
/// </summary>
public void CreateGridWithAlignedContent()
{
    // create resource manager and document object
    ResourceManager resourceManager = new ResourceManager();
    FlowDocument doc = new FlowDocument() { Margin = new Thickness(5) };

    // create border styles for textblocks and sections
    doc.StyleManager.RegisterStyle("TextBlock",
      new Style(){Border = new Border(1), BorderColor = RgbColors.Red});
    doc.StyleManager.RegisterStyle("Section",
      new Style(){Border = new Border(1), BorderColor = RgbColors.Green});

    // create grid with 4 autosized columns taking 100% of the available space
    Grid grid = new Grid(Length.Auto, Length.Auto, Length.Auto, Length.Auto);

    // add header row
    GridRow header = new GridRow(new TextBlock("Order"), new TextBlock("Date"),
      new TextBlock("Customer"), new TextBlock("Paid"));
    // set text alignment, all cells will inherit this setting
    header.Align = Align.Center;
    header.Height = 30;   
    grid.Add(header);

    // add content row
    GridRow contentRow = new GridRow(new Section(new TextBlock("1237AS")
        { VerticalAlign = VerticalAlign.Bottom,LineHeight = Length.FromPercentage(110)})
          { Align = Align.Left, LineHeight = 48 },
        new Section(new TextBlock("01/02/2015")
        { VerticalAlign = VerticalAlign.Top,LineHeight = Length.FromPercentage(110)})        
          { Align = Align.Right, LineHeight = 48 },
        new TextBlock("Jon Doe"){LineHeight = 48},
        new TextBlock("Yes") {Align = Align.Center});
   
    contentRow.Height = 50;
    grid.Add(contentRow);
   
    // add grid to document
    doc.Add(grid);

    // save document
    using (Stream stream = File.Create("grid_content_alignment.pdf"))
    {
        doc.Write(stream, resourceManager, new PageBoundary(Boundaries.A6));
    }
}


Take a look at the image representing the results created by the piece of code above:

Pic. 5 Cell's content alignment in pdf grid

Pic. 5 Cell's content alignment

Sections and TextBlocks have color borders around them to make their boundaries visible, and provide visual markers for further analysis. See the annotated image below, it highlights the key elements and explains the structure of the grid shown above.

Pic. 6 Cells' content alignment annotated

Pic. 6 Cells' content alignment annotated 

All content elements have borders added using matching styles. See that vertically aligned text blocks are positioned within sections added as cells and with their LineHeight set to row height. TextBlocks, used as cells, align their content differently. The one, containing “John Doe”, has its LineHeight set to row height; the others have default line heights (based on font height), and their content  is vertically aligned within the lines defined by these values. 


3.2 Highlighting a row with a background color

Highlighting a row with some color is as simple as setting its background color. Another way for achieving the same result is to set background color for all elements in the row. Whichever way you choose, you can either set the Background property of the element explicitly, or use a style object matching one or more elements if needed.

Let’s take the code from multipage grid sample and slightly modify it, turning our grid to zebra:

/// <summary>
/// Creates a grid with even rows highlighted.
/// </summary>
public static void CreateZebraGrid()
{
    // create resource manager and document object
    ResourceManager resourceManager = new ResourceManager();
    FlowDocument doc = new FlowDocument() { Margin = new Thickness(5) };
    // register style for even rows using class selector
    doc.StyleManager.RegisterStyle("gridrow.gray"new Style(){
      Background = RgbColors.LightGray});

    // create grid with 4 autosized columns taking 100% of the available space
    Grid grid = new Grid(Length.Auto, Length.Auto, Length.Auto, Length.Auto);
    // add col headers
    grid.Add(new GridRow(new TextBlock("Order"), new TextBlock("Date"),
      new TextBlock("Customer"),
      new TextBlock("Paid")));

    // add data records
    for (int rowIndex = 0; rowIndex < 45; rowIndex++)
    {
        GridRow dataRow = new GridRow(new TextBlock(rowIndex.ToString()), 
          new TextBlock("01/02/2015"),
          new TextBlock("Jon Doe"),new TextBlock("Yes"));

        if ((rowIndex & 0x1) == 0)
        {
            dataRow.Class = "gray";
        }

        grid.Add(dataRow);              
    }

    // add grid to document
    doc.Add(grid);

    // save document
    using (Stream stream = File.Create("zebra_grid.pdf"))
    {
        doc.Write(stream, resourceManager, new PageBoundary(Boundaries.A6));
    }
}


Resulting grid is shown on the image below:

Pic. 7 Grid with even rows highlighted (pdf)

Pic. 7 Grid with even rows highlighted

Note that we have added only a simple style based on a class selector matching rows with class “gray”, and assigned this class to each even GridRow of the grid. Not much code really, but the final grid changes nicely. It’s possible to define other styles, e.g. for odd rows, every 3rd row and so on. All you need is a right style.


3.3 Cell styling

When it comes to cell styling, the whole arsenal of content element properties is at your disposal. As usual in flow layout, elements inside the grid row can be styled using their exposed properties or matching styles. Let’s create a styled version of the very simple grid from section 2.1 Grid borders and paddings. See the code:

// Creates simple grid with styled cells.
public void CreateGridWithStyledCells()
{
    // create resource manager and document object
    ResourceManager resourceManager = new ResourceManager();
    FlowDocument doc = new FlowDocument() { Margin = new Thickness(5) };
    // register styles:
    // style for all grid rows, sets horizontal content alignment
    doc.StyleManager.RegisterStyle("GridRow", new Style() {Align = Align.Center});

    // style for all child text blocks of the element with class "header",
    // we use it for grid header
    doc.StyleManager.RegisterStyle(".header > TextBlock",
        new Style(){
            Font = new Apitron.PDF.Kit.Styles.Text.Font(StandardFonts.HelveticaBold, 14),
            Color = RgbColors.White,
            Background =  RgbColors.Gray,
        });   

    // styles for textblock elements having classes "no" and "yes" respectively
    doc.StyleManager.RegisterStyle("TextBlock.no",
       new Style(){Background = RgbColors.Red, Color = RgbColors.White});
    doc.StyleManager.RegisterStyle("TextBlock.yes",
       new Style(){Background = RgbColors.Green, Color = RgbColors.White});

    // create grid with 4 autosized columns taking 100% of the available space
    Grid grid = new Grid(Length.Auto, Length.Auto, Length.Auto, Length.Auto);
    // create round border
    grid.BorderRadius = 5;

    // add header
    GridRow header = new GridRow(new TextBlock("Order"), new TextBlock("Date"),
        new TextBlock("Customer"), new TextBlock("Paid"));
    header.Class = "header";           
    grid.Add(header);

    // add data rows
    grid.Add(new GridRow(new TextBlock("1184A"), new TextBlock("01/02/2015"),
        new TextBlock("Jon Doe"),
        new TextBlock("No"){Class = "no"}));
    grid.Add(new GridRow(new TextBlock("1184B"), new TextBlock("01/02/2015"), 
        new TextBlock("Jon Doe"),
        new TextBlock("Yes"){Class = "yes"}));

    // add grid to document
    doc.Add(grid);
    // save document
    using (Stream stream = File.Create("styled_cells.pdf"))
    {
        doc.Write(stream, resourceManager, new PageBoundary(Boundaries.A6));
    }
}


This code creates a grid with a colored header and two rows. Each of the data rows has its last cell styled according to the state of the payment using one of the defined styles. So, the non-paid one becomes red, and the paid one turns green. In addition, the grid has a round-rect border which, indeed, adds some coolness.

 See the image below:

Pic. 8 Grid cells styling in pdf

Pic. 8 Grid cells styling

Styling cells using build-in styling mechanism offers unlimited flexibility and provides an easy and convenient way to achieve complex visual effects in your grids. You can style any element in any cell as you want, and it’s incredibly useful for data visualization.

4. Conclusion


We demonstrated how to use Apitron PDF Kit to create PDF documents containing complex tabular data structures such as grids. This .NET PDF component is available for many existing platforms and can be used in desktop, mobile, web, and cloud applications and services. We’d like to hear your feedback and see your comments regarding the library and API it offers. Contact our support or ask us here and we’ll be happy to answer your questions.

No comments:

Post a Comment