C#

Architecture

A schematic diagram of Genero Report Writer for C#

Scalability

A schematic diagram illustrating the distributed nature of Genero Report Writer for C#

Highlights

Detailed feature overview

  • Easy to use, WYSIWYG editor,
  • Schema based, type-safe editing
  • Avoids run-time errors caused by failing string conversions,
  • Compile-time impact analysis,
  • Lists issues in a report design caused by changes to the schema. This feature is of  particular importance for end users that have customized or created their own report designs. Upon receipt of a software update they can check their reports against the new schemas shipped with the update to detect and fix any issues before making the update operational,
  • Batch upgrades.  A command line tool is available to perform batch upgrades of report design files from one version of the software to another,
  • Batch updates and software-assisted issue resolution in the report designer. A command line tool is available to update the report design files in cases where the schemas may have changed (e.g. during an application update and the end user’s site). The update algorithm is optimal in the sense that it finds a minimal patch so that in the general case no manual intervention is required. In the rare case that parts of a schema have been removed or renamed users are assisted in resolving the issues in form of issue list that is persistently stored with the report design file until all issues have been resolved.
  • Data stream and document formatting are separate,
  • Multiple presentations per data source,
  • Defines a clear interface between application vendors who are responsible for providing the data sources and their associated schemas and users who customize or add presentations to them.
  • Process level interface as opposed to accessing the database directly,
  • Less sensitive to database changes. Process level messages shield users from details of the database and allow application vendors to change the database schema without breaking customized or self made reports,
  • Prevents replication of business rules in report designs. By accessing “cooked” data, users are shielded from the need to know details on how things are stored in the database. It avoids the replication of central business logic in the report designs (e.g. rules about which address is actually used as a billing address of an invoice when there are multiple choices in the database),
  • Support for structured data as opposed to flat record sets returned by SQL queries. The data behind sophisticated reports such as invoices or delivery can rarely be represented as a flat list of records. The natural representation is a structured message allowing for arbitrary nested lists of different records. Using “sub reports” as in many ad hoc reporting tools is not an adequate solution since it adds unnecessary complexity and typically reverses the network communication causing performance problems due to network latency,
  • Novel, serial, graphical matching and transformation language. Conceptually similar to XSL-T, the RTL transformation language is limited in the type of transformations it can do (e.g. no recursion), but can be represented graphically. It works for documents of arbitrary size.
  • Single pass, unidirectional processing pipe. Since network latency can be regarded as a resource that does not scale it is important to avoid reversing the communication direction during a job. The engine can be used in a distributed environment with very high roundtrip times and low bandwidth without noticeable decrease in performance,
  • Serial processing of arbitrarily sized documents.
    • Thousands of pages, low and constant memory,
    • Distributed processing and multiple server option. The multi tier option allows to offload the report rendering to one or more separate machines leaving only the data source message creation for the server.
  • Pixel exact positioning,
  • Relative positioning vertical and horizontal,
  • Content propagation vertical and horizontal,
  • Multicolumn layout,
  • Tabular layout,
  • Pivot tables,
  •  Sub-reports,
  • Sophisticated headers and footers,
  • Sophisticated page breaking,
  • Label printing on varying media. The report design contains the description of a single label abstracting from the actual layout on the media removing the need to change the report design for different label printers or different label sheets,
  • Running totals in headers and footers,
  • Page n of m in headers and footers while retaining serial processing,
  • Self-layouting business graphs,
  • Large number of natively implemented bar codes,
  • Bitmap and SVG images. The lean scalable vector graphics (also those of other origin such as bar codes and business graphics) are preserved in all formats that support vector drawing such as PDF, browser or printer output. In all other formats they are rendered into bitmaps.
  • Design time API
    • API for schema introspection,
    • API for report design file introspection and generation,
    • API for template based report design generation,
    • API for pivot table configuration.
  • Run time API
    • Data level API,
      • Selection of source,
      • Configuration of localization,
      • Recommend JAXB for schema generation and marshalling,
    • Document level API,
      • Selection of design,
      • Configuration of page characteristics,
      • Configuration of label layout,
    • Device level API
      • Selection of device (e.g. PDF, server printer, client printer, ..),
      • Device specific configuration (e.g. Paper tray selection of printer).
  • Browser based report viewer
    • Low band-width,
    • 100% pixel exact rendering,
    • Uses cacheable WOFF fonts and SVG vector graphics,
    • Streaming capable for immediate rendering of huge reports,
    • Fast, random navigation in huge documents,
    • Bookmarkable and shareable reports
  • Native report viewer
    • Very fast processing, low, nearly fixed memory consumption,
    • 100% pixel exact rendering using embedded fonts,
    • Streaming capable for immediate rendering of huge reports,
    • Fast, random navigation in huge documents,
    • Support for silent printing
      • Viewer not visible,
      • Selecting printer via server API,
      • Configuration of printer via server API (e.g. selecting paper source),
  • Server side printing
    • Serial processing if supported by the operating system,
    • Programmatic control via IPP properties
  • File generation
    • PDF,
    • RTF (MS-Word),
    • XLS/XLSX (MS-Excel),
    • Postscript,
    • SVG
  • Single design for multiple languages. Primitives are provided to translate strings and format data in locale specific manner individually per document at runtime. Layouts can be defined that handle the resulting differences in text size well.
  • Asian language and alternative writing modes support.
  • Efficient handling of Asian fonts.
  • Support for self written document templates,
  • Type-safe unrolling of field lists and placeholders,
  • Support for complexly structured data sources.
  • Human readable and version controllable file formats,
  • Type safe report design editing,
  • Compile time impact analysis after changed to data sources,
  • Sophisticated report design upgrade algorithm (Similar to diff/patch).
    • 100 % tolerant to addition of fields and structures,
    • Good tolerance to removal,
    • Reports conflicts and allows for manual correction.
  • Graphical data source generator,
  • Creation of code for XML generation,
  • Creation of schema,
  • Type-safe unrolling of field lists and placeholders,
  • Support for complexly structured data sources.

All of the industry’s leading databases are supported:

  • DB2
  • Informix
  • MS SQL Server
  • MySQL
  • Oracle
  • PostrgreSQL

Getting started

In this example we use a plain C# object that is annotated for XML serialization as shown in the source below:

//The XmlRoot annotation causes a global element declaration to be produced in the schema.
    [Serializable]
    [XmlRoot]
    public class Sales
    {
        //The XmlElement annotation maps a property to an XML element.

        //The Genero Engine does not support optional variables so setting "IsNullable = true" is mandatory.
        //If a variable needs to be optional then set the variable to null whenthere is no value.
        //If a primitive type needs to be optional then use the c# nullable type 
        //instead (e.g "int?" for "int").

        [XmlElementAttribute(IsNullable = true)]
        public String shopName;
        public int zipCode;
        public DateTime day;

Graphical reports are designed based on a XML schema that matches the data to be serialized.
The command line tool “xsd.exe” can be used to generate the schema:

xsd.exe Sales.exe

The schema for the sample program reads as follows:

<?xml version="1.0" encoding="utf-8"?>
 <xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:element name="Sales" nillable="true" type="Sales" ></xs:element>
   <xs:complexType name="Sales">
     <xs:sequence>
       <xs:element minOccurs="1" maxOccurs="1" name="shopName" nillable="true" type="xs:string" ></xs:element>
       <xs:element minOccurs="1" maxOccurs="1" name="zipCode" type="xs:int" ></xs:element>
       <xs:element minOccurs="1" maxOccurs="1" name="nillable" nillable="true" type="xs:int" ></xs:element>
       <xs:element minOccurs="1" maxOccurs="1" name="day" type="xs:dateTime" ></xs:element>
       <xs:element minOccurs="0" maxOccurs="unbounded" name="items" nillable="true" type="SalesItem" ></xs:element>
     </xs:sequence>
   </xs:complexType>
   <xs:complexType name="SalesItem">
     <xs:sequence>
       <xs:element minOccurs="1" maxOccurs="1" name="articleName" nillable="true" type="xs:string" ></xs:element>
       <xs:element minOccurs="1" maxOccurs="1" name="category" type="Category" ></xs:element>
       <xs:element minOccurs="1" maxOccurs="1" name="price" type="xs:double" ></xs:element>
       <xs:element minOccurs="1" maxOccurs="1" name="runningTotal" type="xs:double" ></xs:element>
     </xs:sequence>
   </xs:complexType>
   <xs:simpleType name="Category">
     <xs:restriction base="xs:string">
       <xs:enumeration value="Furniture" ></xs:enumeration>
       <xs:enumeration value="Art" ></xs:enumeration>
       <xs:enumeration value="Supplies" ></xs:enumeration>
       <xs:enumeration value="Travelling" ></xs:enumeration>
     </xs:restriction>
   </xs:simpleType>
   <xs:element name="SalesItem" nillable="true" type="SalesItem" ></xs:element>
 </xs:schema>

Design the report using Report Designer. Choose your report template, associate the triggers and populate the report with data picked from the data view.

The image below shows “SalesReport.4rp” created in the Report Designer:

saleslist.4rp

Select report design

if (args.length == 0) {
     designFile = "SalesList.4rp";
} else {
      designFile = args[0];
}

Configure the output
Select PDF output

PDFRenderer renderer = new PDFRenderer(handler);

Serialize model
Use the .Net serializer to serialize the data.

report.runFromSerializable(data);

Find below the complete listing of the C# program:

using gre;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace Sales
{

    //The XmlRoot annotation causes an global element declaration to be produced in the schema.
    [Serializable]
    [XmlRoot]
    public class Sales
    {
        //The XmlElement annotation maps a property to an XML element.

        //The Genero Engine does not support optional variables so setting "IsNullable = true" is mandatory.
        //If a variable needs to be optional then set the variable to null whenthere is no value.
        //If a primitive type needs to be optional then use the c# nullable type 
        //instead (e.g "int?" for "int").

        [XmlElementAttribute(IsNullable = true)]
        public String shopName;
        public int zipCode;
        public DateTime day;


        //This and any other public collection will be serialized in the order they are declared descending //recursively into the classes contained in the collections.
        [XmlElementAttribute(IsNullable = true)]
        public List items = new List();

        public Sales(String shopName, int zipCode, DateTime day)
        {
            this.shopName = shopName;
            this.zipCode = zipCode;
            this.day = day;
            int i = 0;
            items.Add(new SalesItem("Tablelamp", SalesItem.Category.Furniture, 23.00, null));
            items.Add(new SalesItem("Tablelamp", SalesItem.Category.Furniture, 267.00, items[i++]));
            items.Add(new SalesItem("Officechair", SalesItem.Category.Furniture, 155.00, items[i++]));
            items.Add(new SalesItem("Grandfather clock", SalesItem.Category.Furniture, 329.00, items[i++]));
            items.Add(new SalesItem("Scissors", SalesItem.Category.Supplies, 19.00, items[i++]));
            items.Add(new SalesItem("Measuring tape", SalesItem.Category.Supplies, 23.00, items[i++]));
            items.Add(new SalesItem("Sunglasses", SalesItem.Category.Travelling, 15.95, items[i++]));
            items.Add(new SalesItem("Penknife", SalesItem.Category.Travelling, 6.25, items[i++]));
            items.Add(new SalesItem("Ornateangel", SalesItem.Category.Art, 1.95, items[i++]));
        }

        /** Default Constructor that is required for deserialization.
            In this program this is never used.
        
         */
        public Sales()
        {

        }

        /**
            Runs the report using the design file specified in args[0] or "SalesList.4rp" otherwise.
            The program creates the file "SalesList.pdf" and opens it using
            System.Diagnostics.Process.Start which will typically
            invoke the Acrobat Reader.
        */
        static void Main(string[] args)
        {

            String designFile;
            String outputFilename = "SalesList.pdf";

            if (args.Length == 0)
            {
                designFile = "SalesList.4rp";
            }
            else
            {
                designFile = args[0];
            }

            FormatHandler handler = new FormatWriter(outputFilename);
            PDFRenderer renderer = new PDFRenderer(handler);
            FourRpLayouter report = new FourRpLayouter(designFile, renderer);

            report.debugLevel = 9;
            Sales data = new Sales("Columbus Arts", 75038, new DateTime());

            report.runFromSerializable(data);

            // open the file
            System.Diagnostics.Process.Start(outputFilename);

        }

    }

    public class SalesItem
    {
        public enum Category { Furniture, Art, Supplies, Travelling };

        [XmlElementAttribute(IsNullable = true)]
        public String articleName;
        public Category category;
        public double price;
        public double runningTotal;

        //The previous item is passed to allow computing the running total.
        public SalesItem(String articleName, Category category, double price, SalesItem previousItem)
        {
            this.articleName = articleName;
            this.category = category;
            this.price = price;

            this.runningTotal = previousItem == null ? price : previousItem.runningTotal + price;
        }

        /** Default Constructor that is required for deserialization.
            In this program this is never used.
        */
        public SalesItem() { }

    }
}