4. Percent-based and mixed property values

4.1. Overview

Properties may have relative values, expressed as a percentage. The value is relative to a trait of the layout. Therefore relative values can only be evaluated when the layout is created. The FO tree must store them as an expression.

The FO spec specifies for each property that can have a relative value, which trait is the basis for the evaluation of the relative value. FOP maintains that information in the property maker, in its member percentBase. This is set when the maker is created, see FOPropertyMapping.

When the maker creates a relative property value object, it stores its own member percentBase in the property as member iBaseType. In this way the property value contains the information that is needed at layout time to find the trait that is the basis for the calculation of the actual value.

The possible values of the member percentBase are listed in class LengthBase. Both the interface PercentBase and the class LengthBase define static constants which are used as types. The difference is as follows (from an email by Finn Bock, edited by me): The idea is that the PercentBase.XXX types name the stored values and the LengthBase.XXX types name the algorithms for looking up a base value. Most of the time these map one-to-one to each other, but for some I imaged that they would be different. For example, margin-top should really use an algorithm like BLOCK_IPD_OR_PAGEHEIGHT, which would look for either PercentBase.BLOCK_IPD or PercentBase.PAGE_HEIGHT, depending on the FO element.

A LengthBase object contains a reference to a parent FO node and a reference to a property list. In this manner the LengthBase value contains the information that is needed at layout time to locate the FO node or property list which contains the trait that is the basis for the calculation of the actual value, irrespective of the FO node at which the property is resolved.

The method LengthBase.getBaselength uses the base type, member iBaseType, to determine relative to which layout trait the value should be resolved. If the trait is a property value, it is retrieved from the property list, in the usual manner. If it is a layout dimension, it is taken from the parent FO.

The interface Numeric, which is implemented by all classes that can participate in numeric operations, uses the notion of dimension, which denotes the type of numeric: for integers dimension = 0, for lengths dimension = 1.

The constructor of RelativeNumericProperty calculates the dimension of the new property value object as a combination of the dimensions of the operands, depending on the operation:

  • multiplication adds dimensions: 0+0=0, 0+1=1+0=1,

  • division subtracts dimensions: 0-0=0, 1-0=1, 1-1=0,

  • other (addition, subtraction) does not change the dimensions

A RelativeProperty contains the operation between the operands in its member operation. It is an integer. Names for the possible integer values are listed as static members of class RelativeProperty.

Relative and mixed property values are retrieved in the usual manner. After retrieval they must be resolved against the relevant layout traits. This happens in the method getValue of Property and its subclasses.

A RelativeNumericProperty is resolved as follows:

  • RelativeNumericProperty.getValue

    • RelativeNumericProperty.getNumericValue

      • RelativeNumericProperty.getResolved. This evaluates the expression tree by recursion. The nodes in the tree are RelativeNumericProperty objects. The leaf nodes are FixedLength and PercentLength objects. On each node getNumericValue is called.

        • The relative numeric property values call getResolved again, which descends the tree and calls getNumericValue on its child nodes.

        • The fixed lengths return their millipoints.

        • The percent lengths return factor * lbase.getBaseLength()

          • factor is a member of the percent length object.

          • LengthBase.getBaselength gets the base length from the parent FO

            • FObj.getLayoutDimension. The value is retrieved from the Map layoutDimension. If that does not contain the desired layout dimension, then its parent is consulted, all the way up to fo:root.