iText PDF Renderer Framework

Tags: iText 7AGPLsupportRenderer Frameworkrenderershiearchy

Many of you are aware that iText Software provides dedicated support to its paying customers. However, we also have a very large user base that complies with the AGPL license. Since they don't pay for technical support, they have to rely on their own debugging skills and, as a fallback channel, on Stack Overflow to solve their iText problems.

Those who have stared into the abyss of iText 5's ColumnText::go method and survived, know that debugging our good old rendering engine could be a daunting task because of that method's recursive nature and opaque variable names. ColumnText::go was the organic result of almost 20 years of refactoring of the original iText code, and it's one of the prime examples why we chose to reimplement our library.

The main innovation of the PDF document generation module of iText 7 is the Renderer Framework. Its architecture is explained in detail in the introduction to the technical tutorial for iText 7. The framework defines three general concepts:

  • an Element is a high-level layout object that will be written to a PDF document

  • a RootElement is the background upon which layout objects are drawn. It functions as an element container

  • a Renderer draws an Element onto a RootElement

    • RootElement objects also have a special RootRenderer, which will perform calculations for layout positionings

The flexibility in the framework lies in the configurability and pluggability of the Renderer objects. All Renderer types implement the methods defined in the IRenderer interface:

  • layout(): will calculate how much of the object will fit onto the RootElement

  • getOccupiedArea(): gets the area that the Element will try to occupy

  • draw(): will write the graphical instructions to the RootElement

  • addChild(), setParent(), getChildRenderers(), getNextRenderer(): methods for constructing and querying the hierarchy of the Renderer tree

Every Element has its own renderer implementation, and uses it by default. It is easy to customize the rendering rules for a specific layout object, by plugging in a homebrewn Renderer class, or a subclass of the Element's default renderer with a few modifications.

  1. // in C#
  2. var element = new Div();
  3. element.SetNextRenderer(new MyDivRenderer(element));
  4. document.Add(element);

It is also possible to let all objects of a certain type use your custom Renderer, by subclassing the Element and creating these objects instead of the default type.

  1. // in Java
  2. class MyDiv extends Div {
  3. @Override
  4. protected IRenderer makeNewRenderer() {
  5. return new MyDivRenderer(this);
  6. }
  7. }

As already hinted at in the introduction to this post, another great advantage lies in debugging your application. In iText 5, users debugging ColumnText encountered a turtles-all-the-way-down stack of go() calls.

Debugging a nested table in iText 5

By contrast, in iText 7, you clearly see where, and how deep, in the Renderer hierarchy you are:

Debugging a nested table in iText 7