1. Using the Library

Layout is designed for the flexible composition of document layouts, particularly complex charts, and multi-page documents of algorithmically calculated data. It isn’t designed to be used as a framework to flow in large amounts of body text, as the sizing algorithms used don’t take account of text flows.

1.1. Overview

There are three stages in using the library:

1. Define classes that will render the content you need to render. There are a selection of classes in the layout.elements package that cover some basic needs, but often custom classes are needed.

2. Compose your layout in a series of managers, subclasses of layout.managers.root.LayoutManager, such as layout.managers.margins.PaddedMarginsLM, which will position the correct piece of content in the correct position on the page.

3. Render your layout by calling the layout.managers.root.LayoutElement.render() method of the manager at the top level of your hierarchy. This will render all its children.

1.2. Step By Step

The above steps will now be considered in more detail.

1.2.1. Define Classes

Classes should subclass layout.managers.root.LayoutElement, and override two methods: get_minimum_size() should return a layout.datatypes.position.Point instance, which specifies the smallest size this item can be rendered at; and render() which takes a layout.datatypes.position.Rectangle instance as the location for its rendering, and should perform the necessary drawing.

Both of these methods additionally take a data dictionary, which can contain any additional information about the render that is required.

1.2.2. Compose Your Layout

To compose your layout, select appropriate managers and create a data structure from their instantiation. By convention, all managers take either a single child, or a list of their children (depending on whether the manager accepts one or more children) as the last element in their constructors. The most notable exception to this is the layout.managers.grid.GridLM class, which needs extra data on each child, so expects you to call its layout.managers.grid.GridLM.add_element() method for each child you wish to add.

A sample composition might be:

from layout.managers import *

top_level_manager = margins.MarginsLM(
    10*mm, 10*mm, 10*mm, 10*mm,
    element=transform.ScaleLM(
        element=directional.HorizontalLM(
            margin=10*mm,
            vertical_align=directional.HorizontalLM.ALIGN_MIDDLE,
            elements=[
                MyElement(a),
                MyElement(b)
                ]
            )
        )
    )

Which is two MyElement instances (I assume these are defined by you) side by side (layout.managers.directional.HorizontalLM) on a page with at least 10mm margins all around (layout.managers.margins.MarginsLM). If the two elements are too large to fit in this space, then they will be scaled down, keeping their relative proportion, until they can fit (layout.managers.transform.ScaleLM). Compositions can get much more complex, and may have to be assembled in stages.

1.2.3. Create the Output

With the top level manager created, we can build our PDF by calling its render() method. Assuming we’re using Report Lab, we could do this manually:

canvas = Canvas(filename, papersize)
output = rl_utils.ReportlabOutput(canvas)
top_level_manager.render(Rectangle(0, 0, *papersize), dict(output=output))
canvas.showPage()
canvas.save()

Or we can rely on the utility functions in the layout.rl_utils module:

canvas = Canvas(filename, papersize)
rl_utils.render_to_reportlab_canvas(canvas, papersize, top_level_manager)
canvas.save()

or even shorter:

rl_utils.render_to_reportlab_document(filename, papersize, top_level_manager)

Similar methods exist for the Cairo renderer. Cairo uses a different coordinate system, however, so it is not usually practical to use the manual approach unless you reconfigure the coordinate system manually. See the implementation of cairo_utils.render_to_cairo_context() for details.