Generating PDF or XPS documents with Infragistics Documents in ASP.NET MVC applications (Part 1)

Damyan Petev / Friday, March 2, 2012

Need to provide your users with a way to generate documents? Quality is a must and the business report must be requested / customized by and delivered to a remote client? We have the thing for you then – the Infragistics Document Engine™ allows you to publish in both Portable Document Format (PDF) and XML Paper Specification (XPS) file formats. Those are the two most common format when the original state of your content does matter - PDF requires Adobe Acrobat Reader (which is free and we can safely assume it is already installed on almost every running computer out there). Microsoft’s XPS format is also supported by a large number of machines – Vista and 7 come with built-in support and it is available along with .NET 3.0 for XP. That makes for a target group about as large as it gets and you can provide the users of your application with a way to generate reports from your application’s content and deliver them a file - with quality and in a format you can almost be sure would be recognized or already supported.

Infragistics NetAdvantage for jQuery Document Engine: Document Generation

Now, of course preserving fidelity and generating reports in popular format is not quite the full story. This product allows you to tap into the full power of the Document Object Model (DOM), which means there are hardly any limitations for what you can do with it. Creating the document goes through steps, along the way of which you can customize almost every little detail that would appear in the finished document, so there’s always a way to customize the content.

The Infragistics Document Engine also provide means to set preferences ( like preferred printing layout for your report) and security.  Once you have your content set, should you want to add some color or decorative elements – you can do that with the supported graphics.

The Document Engine

The Infragistics Document Engine is a large assembly with multiple namespaces and interfaces in each. Since the goal is to provide as much tools to handle almost any scenario, it’s very likely that a few blogs won’t be enough to go in depth with all that is supported and can be done with this library so we will try to make the essential introduction just for now.

The Layout Elements

Whatever content is to be presented in the report needs to have some… layout. There are quite a few layout elements available -  it’s a little bit overwhelming at first, because there are some limitations on content for each element as well as which can be used inside other containers. Our Help Topics have designated a whole section on how those can interact presenting them in a comparison table. To elaborate a bit – the Report is, as it should be clear by now, your main object – the one that contains all other elements and properties and where the actual document is generated from. The general rule is that the Report can contain only Section elements. But no worries – they can be containers of almost any other element ( sans another section ). There are two elements that are reserved for other containers, but naturally the latter themselves can be included in the section. Sounds somewhat confusing? If it does then that table above would be the source of enlightenment you seek. The range of things you can add to those elements is also quite wide – text (as you would imagine), images, graphics, tables, lists, etc.

So for our example we would have a report instance with a section in it. We would add paging to the section as well as set the page orientation to be portrait:

  1. //create new Report instance
  2. Report report = new Report();
  3.  
  4. //Adding a section to the Report
  5. ISection section = report.AddSection();
  6.  
  7. //set the page orientation
  8. section.PageOrientation = PageOrientation.Portrait;
  9. //add nice margins to all sides
  10. section.PageMargins.All = 20;
  11.  
  12. //add paging
  13. section.PageNumbering.Template = "Page [Page #] of [TotalPages]";
  14. //bring it up abit to be above the margin
  15. section.PageNumbering.OffsetY = -22;
  16.  
  17. //add footer
  18. ISectionFooter sectionFooter = section.AddFooter();
  19. sectionFooter.Repeat = true;
  20. sectionFooter.Height = 50;
  21. //add text to the footer
  22. IText sectionFooterText = sectionFooter.AddText(0, 0);
  23. sectionFooterText.Paddings.All = 10;
  24. sectionFooterText.Alignment = new TextAlignment(Alignment.Center, Alignment.Middle);
  25. sectionFooterText.Height = new RelativeHeight(100);
  26. sectionFooterText.Background = new Background(Brushes.Gainsboro);
  27. sectionFooterText.AddContent("Intellectual property related info goes here.");

This is so far nothing but an empty document with paging and a footer. As you can see adding a section to the report actually creates a new object that implements the ISection interface and adding more elements or content to them is done in very much the same fashion, including adding a footer directly to the section. Also paging can be templated and you have two placeholders work with, but you should also be aware of its positioning.

We can now add layout elements to the section and we have plenty to choose from, but here are some highlights:

  • Container and Condition – The Container element’s unique feature is that it provides the ability to inject content loaded directly from a XML file, which is very neat, but also its contents can be subject to conditional displaying. What this means is that you can define a Condition based on whether the container’s content fits the page or not. The engine will then pick one of those conditions and if the content doesn’t fit display the ‘fall-back’ text.
  • Flow one of the major layout elements which separates your content in columns you can define with relative size.
  • You can create blocks of text that would be inseparable with the Chain element, which just means text in the Chain will never be divided in two parts when reaching the page’s end, but instead be transferred as a whole to the new page.
  • Separators can be added in various ways (some containers have specific ones to fit their needs) – the Rule produces a line separator, the Gap is a empty space with either set or even relative (to the remaining space) height and there’s the Page Break that just fills the rest of the page.
  • The Site element is especially exciting for fine-tuning, because it allows for positioning the elements inside using X and Y coordinate system,. That in turns also allows for random angle rotations, where the Rotator element only allows 90 degrees ones.
  • And then there’s the suggested element to go for – the Band. Much like the Section it can contain various other elements and also comes with Header, Footer and Divider at your disposal.

For our examples we have a single Section and to keep it simple, inside we will use a Container along with a Condition to show the user a message if the content did not fit inside the page:

  1. //Add the container element
  2. IContainer container = section.AddContainer("Container");
  3.  
  4. //**** Add main content here ****
  5.  
  6. //Add a condition
  7. ICondition condition = section.AddCondition(container, false);
  8. //Add a warning message for when content doesn't fit:
  9. IText condIsFalseText = condition.AddText();
  10. condIsFalseText.AddContent("Content doesn't fit the page!");
  11.  
  12. //Optional: Add text for when it does fit
  13. //This text will appear under the content itself
  14. condition = section.AddCondition(container, true);
  15. IText condIsTrueText = condition.AddText();
  16. condIsTrueText.AddContent("\n\t (Content fits the remaining space on the page.)");

The Main Stars

What is referred to as the Pattern content elements in the Document Engine are in the spotlight of your document, so to speak. They offer ways to present data you data and in case that didn’t send the right message – yes, it means nothing else but grids, tables, lists, trees or even plain text. You are also provided with a few ‘quick’ versions of those element to just get that content there without much hassle.

Since we already have our Container we would add our content to it. Here is the place to give some insight for what’s ahead, for those wondering where the ASP.NET MVC part is in all this. Of course content should be driven by what the user is seeing. What that means is that we would present the user with a client-side control to interact with ( or even request some settings) and based on that we would generate a document. Since we already have an amazing sample showing generating a report with our jQuery Grid, for this demo we will take on a scenario where the user is presented with a jQuery Tree and the ability to generate a permanent digital copy( the document) of it’s current state. Again adding a Tree to layout element is done in an already familiar manner – by calling the element's AddTree() method,  which would return your ITree implementing object to add data and setting to:

  1. ITree tree = container.AddTree();

Beautifiers

Want to add a little color or even that “Wow!” factor to your document? The library has to offer.

There’s a pretty large Graphics namespace allowing access to Brushes, Pens, Colors and even the Dash styles for the borders. All of those have been used so far to set various properties throughout the code. Also included are a Fonts, Canvas and Shapes and various utilities for all mentioned so far. Shapes include all the basic ones you’d expect as well as polygons, paths, etc. The canvas also supports drawing pretty much all the shapes along with curves (Bezier including), arcs and rounded rectangles. In addition, it also allows for filling and clipping them. These can used to really go a long way making your final document look appealing.

The Section itself can contain two unique types of elements that can add some major styling points – those are the Stationery and Decoration elements. The first is a watermark-like element that stays behind all other content and the latter is a stamp-like element that is usually on top. Both can only be added to a section and adding such in code looks like this:

  1. //stationery and decoration elements
  2. IStationery stationery = section.AddStationery();
  3. //AddText takes two numbers for left and top margin and an angle of rotation
  4. IText stationeryText = stationery.AddText(50, 450, -45);
  5. stationeryText.Style = new Style(new Font("Verdana", 72), Brushes.Silver);
  6. stationeryText.AddContent("Your Company Name Here");
  7.  
  8. IDecoration decoration = section.AddDecoration();
  9. IText decorationText = decoration.AddText(330, 720, -15);
  10. decorationText.Style = new Style(new Font("Verdana", 30), Brushes.Red);
  11. decorationText.Width = new RelativeWidth(40);
  12. decorationText.Borders = new Borders(new Pen(Colors.Red, 3, DashStyle.Solid), 10);
  13. decorationText.AddContent("CONFIDENTIAL");

To better get a clue what the result of that is:

Infragistics NetAdvantage for jQuery Document Engine: Document decoration, footer and paging.

Settings

The report element supports a wide array of properties you can use to forge the document to your liking. Of course you will find all the default things like Author, Creator, Company, Modified, Title and even Comments and Keywords in the report’s Info. Then you have Preferences to set like printing layout and paper size:

  1. //settings:
  2. report.Info.Author = "You";
  3. report.Info.Creator = "Infragistics Document Engine";
  4. report.Preferences.Printing.PaperSize = Infragistics.Documents.Reports.Report.Preferences.Printing.PaperSize.A4;

The preferences also contain settings for both PDF and XPS formats.

No document would offer great usability without some Navigational aid – You can create Index elements and even Table of Contents to quickly guide users to the parts they need when those documents’ pages start growing in numbers. There is also the ability to add Bookmarks for PDF files.

Generating your document

There are a few different ways to publish your document, and by publishing we mean writing a PDF or XPS file. You have the option to call Print directly but that is of little to no use in our case. Then you can save the report as XML to load it for later use to make modifications and such. Of course, there is a Publish method that has two overloads – one that writes to a file (which can be used to send that file afterwards), but the other overload is even better – writing directly to a stream. And what that means is that you can write directly to the outgoing stream of the response we send back to the client – that is exactly the method we would use. The second parameter the method accepts is always the file type – in our case that would be a PDF:

  1. report.Publish(Response.OutputStream, FileFormat.PDF);

Generating documents from the report has one more perk to offer – calling the Generate() method will create a collection of output pages that can be very handy for thumbnail creation.

Preparation

With some knowledge about the Document engine, it’s time lay some foundations. As mentioned we would present the user with a NetAdvantage for jQuery Tree and for that we need data – in my case I took the idea for an organization chart, which is something the tree is capable of presenting, and following that I also took a ready XML file called DepartmentGroups.xml from a sample of our own XAML OrgChart control. The hierarchy in the data is “Department > Employee Position > Employee” and therefore we would create a Model based on that and add a method that would read the xml file and return an IEnumerable with the Departments so that we can bind that to the Tree. Here is the complete code for that:

  1. public class Data
  2. {
  3.     public class Department
  4.     {
  5.         public string Text { get; set; }
  6.         public IEnumerable<EmployeePosition> Positions { get; set; }
  7.     }
  8.  
  9.     public class EmployeePosition
  10.     {
  11.         public string Text { get; set; }
  12.         public IEnumerable<Employee> Employees { get; set; }
  13.     }
  14.  
  15.     public class Employee
  16.     {
  17.         public string FirstName { get; set; }
  18.         public string LastName { get; set; }
  19.         public string ImagePath { get; set; }
  20.     }
  21.  
  22.     /// <summary>
  23.     /// Creates an hiererchical Ienumerable collection from the xml document data.
  24.     /// </summary>
  25.     public static IEnumerable<Department> GetData()
  26.     {
  27.         //load data from xml file
  28.         var ctx = XDocument.Load(AppDomain.CurrentDomain.BaseDirectory + "DepartmentGroups.xml");
  29.        
  30.         IEnumerable<Department> data = from item in ctx.Root.Element("DepartmentGroup").Elements("Department")
  31.                                        select new Department
  32.                                        {
  33.                                            Text = item.Attribute("DepartmentName").Value,
  34.                                            Positions = from i1 in item.Elements("EmployeePosition")
  35.                                                        select new EmployeePosition
  36.                                                        {
  37.                                                            Text = i1.Attribute("PositionName").Value,
  38.                                                            Employees = from emps in i1.Elements("Employee")
  39.                                                                        select new Employee
  40.                                                                        {
  41.                                                                            FirstName = emps.Attribute("FirstName").Value,
  42.                                                                            LastName = emps.Attribute("LastName").Value,
  43.                                                                            ImagePath = emps.Attribute("ImagePath").Value
  44.                                                                        }
  45.  
  46.                                                        }
  47.                                        };
  48.         return data;
  49.     }
  50. }

The respective xml document you will find included in the demo project along with a folder containing the images for each employee.

Moving on

What is left – to get that data to an APS.NET MVC View and bind a tree to it. Save user actions to send back as settings for the report. Make a Controller action that would call the report generating method and return the ready document to the client.

This will be continued in a second blog. A link to the second part will be added to this blog as soon as it is up and if you are looking for the sample project it will also be included there.

I strongly encourage you to visit our Online Documentation for the Document Engine and also the NetAdvantage for jQuery Online Samples and give the Infragistics Documents sample a go.

Stay tuned!