aboutsummaryrefslogtreecommitdiffstats
path: root/src/documentation/content/xdocs/DnI/properties.xml
diff options
context:
space:
mode:
Diffstat (limited to 'src/documentation/content/xdocs/DnI/properties.xml')
-rw-r--r--src/documentation/content/xdocs/DnI/properties.xml2455
1 files changed, 2455 insertions, 0 deletions
diff --git a/src/documentation/content/xdocs/DnI/properties.xml b/src/documentation/content/xdocs/DnI/properties.xml
new file mode 100644
index 000000000..632ccf946
--- /dev/null
+++ b/src/documentation/content/xdocs/DnI/properties.xml
@@ -0,0 +1,2455 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<!-- $Id$ -->
+
+<!--
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "docbookx.dtd">
+ -->
+
+ <chapter id="ch.properties">
+ <title>Properties</title>
+
+<section>
+ <title>The basic setup of the property subsystem</title>
+
+ <section>
+ <title>Property values</title>
+
+<para>The FO nodes in the FO tree contain property values as specified
+by the user. Each property value is represented by a object of type
+<literal>org.apache.fop.fo.Property</literal>, or of a subtype
+thereof.</para>
+
+<para>There are various types of property values:
+<literal>CharacterProperty</literal>,
+<literal>ColorTypeProperty</literal>,
+<literal>CondLengthProperty</literal>,
+<literal>EnumProperty</literal>, <literal>KeepProperty</literal>,
+<literal>LengthPairProperty</literal>,
+<literal>LengthProperty</literal>,
+<literal>LengthRangeProperty</literal>,
+<literal>ListProperty</literal>, <literal>NCnameProperty</literal>,
+<literal>NumberProperty</literal>, <literal>NumericProperty</literal>,
+<literal>RelativeNumericProperty</literal>,
+<literal>StringProperty</literal>. The type
+<literal>ToBeImplementedProperty</literal> is used for properties that
+are not yet implemented. Some of these types have subtypes:
+<literal>AutoLength</literal>, <literal>FixedLength</literal>,
+<literal>PercentLength</literal>, <literal>TableColLength</literal>
+are subclasses of <literal>LengthProperty</literal>;
+<literal>SpaceProperty</literal> is a subclass of
+<literal>LengthRangeProperty</literal>. Each of these types is a
+subtype of <literal>org.apache.fop.fo.Property</literal>.
+</para>
+
+ <para>Property values may implement one or more of the interfaces
+defined in the package <literal>org.apache.fop.datatypes</literal>:
+<literal>Numeric</literal>, <literal>Length</literal>,
+<literal>ColorType</literal></para>
+
+ <para>Some properties actually represent a set of properties, such
+as a minimum, an optimum and a maximum. These are represented by a
+property value which implements the
+<literal>CompoundDatatype</literal> interface. They contain a property
+value for each member property.</para>
+
+ </section>
+
+ <section>
+ <title>The property list of an FO node</title>
+
+ <para>Property values are held by the FO node corresponding to
+the fo element on which they are specified by the user. FO nodes
+contain a property list of type <literal>PropertyList</literal>, which
+extends <literal>HashMap</literal>.</para>
+
+ <para>The property types are known by the property
+name as used on the FO element, e.g. <literal>font-size</literal>. For
+efficiency of implementation, each type of property type is also known by
+an <literal>integer</literal>, the <literal>propID</literal>. The
+<literal>propID</literal>s are defined in the interface
+<literal>org.apache.fop.fo.Constants</literal>, which gives them a
+symbolic name of the form <literal>PR_</literal> + property name in
+capitals and spaces replaced by underscores,
+e.g. <literal>PR_FONT_SIZE</literal>. Wherever possible, the code uses
+the <literal>propID</literal>s instead of the property names.</para>
+
+<para>When an FO requests a property, it does so by
+<literal>propId</literal>. The request is eventually answered by
+<literal>PropertyList.getExplicitBaseProp</literal>, but before it can
+do so, it has to retrieve the property name from
+<literal>FOPropertyMapping.getPropertyName</literal>. A particular
+inefficiency as a consequence is found in
+<filename>FopPropValFunction.java</filename> and some other classes in
+the <literal>fo.expr</literal> package:
+
+<screen>
+return pInfo.getPropertyList().get(FOPropertyMapping.getPropertyId(propName))
+</screen>
+
+Here <literal>propName</literal> -> <literal>propId</literal> mapping
+is done, which later is reverted again.</para>
+
+<screen>
+ [1] org.apache.fop.fo.FOPropertyMapping.getPropertyName (FOPropertyMapping.java:486)
+ [2] org.apache.fop.fo.PropertyList.getExplicitBaseProp (PropertyList.java:230)
+ [3] org.apache.fop.fo.Property$Maker.findProperty (Property.java:281)
+ [4] org.apache.fop.fo.Property$Maker.get (Property.java:314)
+ [5] org.apache.fop.fo.PropertyList.get (PropertyList.java:281)
+ [6] org.apache.fop.fo.PropertyList.get (PropertyList.java:267)
+ [7] org.apache.fop.fo.FObj.getProperty (FObj.java:261)
+</screen>
+
+ </section>
+
+ <section>
+ <title>Property makers</title>
+
+ <para>Property value objects are created by a property maker of
+type <literal>org.apache.fop.fo.PropertyMaker</literal>, or of a
+subtype thereof. For each property type there is a property maker
+object, which knows the property type, its default value, and some
+other characteristics.</para>
+
+ <para>The types of property makers are:
+<literal>CharacterProperty.Maker</literal>,
+<literal>ColorTypeProperty.Maker</literal>,
+<literal>CompoundPropertyMaker</literal>,
+<literal>EnumProperty.Maker</literal>,
+<literal>LengthProperty.Maker</literal>,
+<literal>ListProperty.Maker</literal>,
+<literal>NumberProperty.Maker</literal>,
+<literal>StringProperty.Maker</literal>, and
+<literal>ToBeImplementedProperty.Maker</literal>.</para>
+
+<para>The property makers are lazily constructed when the
+<literal>FObj</literal> constructor wants to create its static member
+<literal>propertyListTable</literal>. The constructor calls
+<literal>FOPropertyMapping.getGenericMappings()</literal>, which
+constructs and returns
+<literal>Property.Maker[Constants.PROPERTY_COUNT+1]
+s_generics</literal>. The FObj constructor then copies this array of
+<literal>PropertyMaker</literal>s into
+<literal>propertyListTable</literal>.</para>
+
+ <para><literal>public static PropertyMaker[]
+getGenericMappings()</literal> first creates the shorthand property
+makers, so that they can be used in the creation of the makers of the
+real properties, and a set of generic property makers, which act as
+templates for the real property makers. Next it creates the makers for
+all property types. Related property types are grouped,
+e.g. <literal>createFontProperties()</literal>.</para>
+
+ <para>An example is the creation of the maker for the
+<literal>font-size</literal> property type:
+
+<screen>
+ m = new LengthProperty.Maker(PR_FONT_SIZE);
+ m.setInherited(true);
+ m.setDefault("12pt");
+ m.setPercentBase(LengthBase.INH_FONTSIZE);
+ addPropertyMaker("font-size", m);
+</screen>
+
+Since <literal>font-size</literal> is a length, its maker is a
+<literal>LengthProperty.Maker</literal>. It is inherited, and its
+default value is 12 pt. If the user specifies the
+<literal>font-size</literal> value as a percentage, then the actual
+value is calculated from the <literal>font-size</literal> value
+inherited from the parent FO node.</para>
+
+ </section>
+
+ <section>
+ <title>Shorthand properties</title>
+
+ <section>
+ <title>Overview</title>
+
+ <para>Shorthand properties are properties which are shorthand
+for a number of properties. In other words, they specify the value of
+a number of properties in a single attribute. All shorthand properties
+can take a list of values, which are space separated in the FO
+file. The FO spec specifies how this list of values determines the
+values of the properties for which this is the shorthand (the target
+properties.). The length of the list of values for a single shorthand
+property may vary. For each length the attribution of these values to
+the target properties is different.</para>
+
+ <para>When the FO tree is constructed, shorthand property
+values are parsed and stored like any other property value. Because
+the value can be a list, it is always of type
+<literal>ListProperty</literal>.</para>
+
+<para>The meaning of shorthand properties is only dealt with when the
+value of one of the target properties is retrieved. For that purpose
+each target property maker knows the shorthand properties that may set
+its value, and when the target property value is retrieved, its maker
+checks with each of its shorthand property makers if it has a
+value. Note that the value of a shorthand property is never retrieved
+directly, because shorthand properties have no direct meaning for the
+layout.</para>
+
+ <para>When the shorthand property value has been retrieved,
+the value for the target property must be extracted from the
+list. That is done by a shorthand parser, which implements
+<literal>ShorthandParser</literal>. There are two implementing types:
+<literal>GenericShorthandParser</literal> and
+<literal>BoxPropShorthandParser</literal>. Their method
+<literal>convertValueForProperty</literal> knows how each specified value
+determines the value of the possible target properties. A shorthand
+parser object is added to the shorthand property maker when the maker
+is created.</para>
+
+ <para>Note that <literal>CompoundPropertyMaker</literal> also
+has a member <literal>shorthandMaker</literal>. I am not sure if this
+has anything to do with shorthand properties. It seems more related to
+<literal>CompoundPropertyMaker</literal> delegating certain tasks to a
+subproperty maker, viz. the one which is the
+<literal>shorthandMaker</literal>.</para>
+
+
+ </section>
+
+ <section>
+ <title>Example of a shorthand property</title>
+
+ <para>The property <literal>margin</literal> is shorthand for
+the four properties <literal>margin-top</literal>,
+<literal>margin-right</literal>, <literal>margin-bottom</literal>,
+<literal>margin-left</literal>. Its value can consist of 1 to 4 width
+values.</para>
+
+<para>When the property maker for <literal>margin</literal> is
+created, it gets a <literal>BoxPropShorthandParser</literal> as
+shorthand parser:
+<screen>
+m = new ListProperty.Maker(PR_MARGIN);
+m.setInherited(false);
+m.setDefault("");
+m.setDatatypeParser(new BoxPropShorthandParser());
+m.setPercentBase(LengthBase.BLOCK_WIDTH);
+addPropertyMaker("margin", m);
+</screen>
+
+When the property maker for <literal>margin-top</literal> is created,
+the <literal>margin</literal> maker is registered with it as a
+shorthand maker:
+<screen>
+m = new LengthProperty.Maker(PR_MARGIN_TOP);
+m.setInherited(false);
+m.setDefault("0pt");
+m.addShorthand(s_generics[PR_MARGIN]);
+m.setPercentBase(LengthBase.BLOCK_WIDTH);
+addPropertyMaker("margin-top", m);
+</screen>
+</para>
+
+ <para>The maker for <literal>border-top-width</literal> has
+three shorthands: <literal>border-top</literal>,
+<literal>border-width</literal>, and <literal>border</literal>:
+<screen>
+ this.shorthands = instance of org.apache.fop.fo.properties.PropertyMaker[3] (id=772)
+ this.shorthands[0] = "org.apache.fop.fo.properties.ListProperty$Maker@1e1dadb"
+ this.shorthands[0].propId = 52
+ this.shorthands[1] = "org.apache.fop.fo.properties.ListProperty$Maker@bac9b9"
+ this.shorthands[1].propId = 56
+ this.shorthands[2] = "org.apache.fop.fo.properties.ListProperty$Maker@8ceeea"
+ this.shorthands[2].propId = 18
+</screen></para>
+
+ </section>
+
+ <section>
+ <title>Parsing a shorthand property</title>
+
+ <para>The value of a shorthand property is parsed and the value of a
+target property is extracted in this call stack:
+
+<screen>
+ [1] org.apache.fop.fo.BoxPropShorthandParser.convertValueForProperty (BoxPropShorthandParser.java:80)
+ [2] org.apache.fop.fo.GenericShorthandParser.getValueForProperty (GenericShorthandParser.java:93)
+ [3] org.apache.fop.fo.properties.PropertyMaker.getShorthand (PropertyMaker.java:617)
+ [4] org.apache.fop.fo.properties.PropertyMaker.findProperty (PropertyMaker.java:277)
+ [5] org.apache.fop.fo.properties.PropertyMaker.get (PropertyMaker.java:305)
+ [6] org.apache.fop.fo.PropertyList.get (PropertyList.java:282)
+ [7] org.apache.fop.fo.PropertyList.get (PropertyList.java:268)
+ [8] org.apache.fop.fo.PropertyManager.getMarginProps (PropertyManager.java:301)
+</screen>
+</para>
+
+ <para>The extraction proceeds as follows:
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>PropertyMaker.getShorthand</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>parser.getValueForProperty(propId,
+listprop, propertyMaker, propertyList)</literal>;
+<literal>propId</literal> is the ID of the target property,
+<literal>listprop</literal> is the shorthand property value, of type
+<literal>ListProperty</literal>, which was retrieved <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>if the shorthand value is
+<literal>inherit</literal>, get the value for the target property from
+the parent.</para>
+ </listitem>
+ <listitem>
+ <para>else
+<literal>convertValueForProperty(propId, listProperty, maker,
+propertyList)</literal> <itemizedlist spacing="compact">
+ <listitem>
+ <para>get from the shorthand list of
+values the value that corresponds to the target property</para>
+ </listitem>
+ <listitem>
+ <para>if the retrieved value is not
+<literal>null</literal>, convert the property,
+<literal>maker.convertShorthandProperty(propertyList, p,
+null)</literal> <itemizedlist spacing="compact">
+ <listitem>
+ <para>first try to convert it in
+the normal way: <literal>maker.convertProperty(prop, propertyList,
+fo)</literal></para>
+ </listitem>
+ <listitem>
+ <para>if this gives a
+<literal>null</literal> value, test if the value is an enumerated
+value or a keyword; if so, process it.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Corresponding properties</title>
+
+ <para>A number of traits can be specified by two alternative
+properties, e.g. <literal>border-left-width</literal> and
+<literal>border-start-width</literal>. These are called corresponding
+properties. One of a pair of corresponding properties is an absolute
+property, the other is a relative property. The meaning of the
+relative property depends on the writing mode. When the value of a
+property is retrieved that has a corresponding property, the value of
+that corresponding property should also be taken into account.</para>
+
+ <para>Corresponding properties are registered with the property
+maker when it is created:
+
+<screen>
+bwm = new BorderWidthPropertyMaker(PR_BORDER_LEFT_WIDTH);
+bwm.useGeneric(genericBorderWidth);
+bwm.setBorderStyleId(PR_BORDER_LEFT_STYLE);
+bwm.addShorthand(s_generics[PR_BORDER_LEFT]);
+bwm.addShorthand(s_generics[PR_BORDER_WIDTH]);
+bwm.addShorthand(s_generics[PR_BORDER]);
+corr = new CorrespondingPropertyMaker(bwm);
+corr.setCorresponding(PR_BORDER_START_WIDTH, PR_BORDER_END_WIDTH,
+ PR_BORDER_AFTER_WIDTH);
+addPropertyMaker("border-left-width", bwm);
+</screen>
+
+There are always three corresponding properties, for the three writing
+modes <literal>lr_tb</literal>, <literal>rl_tb</literal>,
+<literal>tb_rl</literal>, in this order:
+
+<screen>
+ corr = {
+ baseMaker: instance of org.apache.fop.fo.properties.BorderWidthPropertyMaker(id=702)
+ lr_tb: 50
+ rl_tb: 36
+ tb_rl: 22
+ useParent: false
+ relative: false
+}
+</screen>
+</para>
+
+ <para>When a property value is retrieved, the value of the
+corresponding property may have priority. This is determined by the
+method <literal>corresponding.isCorrespondingForced()</literal>. This
+is true if
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>this is a relative property</para>
+ </listitem>
+ <listitem>
+ <para>and the corresponding property has been explicitly
+specified on this FO node</para>
+ </listitem>
+ </itemizedlist>
+Relative properties are marked by the fact that their corresponding
+property maker has its member <literal>relative</literal> set to
+<literal>true</literal>; this is set when the property is
+created.</para>
+
+ <para>If the corresponding property has priority, its value is
+computed. Otherwise, if the value of the property itself has been
+explicitly specified on this FO node, it is used. Otherwise, the
+corresponding property is computed. Computation in this connection
+means that also the shorthand properties are checked.</para>
+
+<para>Because shorthand properties only exist for absolute properties,
+the values are effectively checked in this order:
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>An absolute property <itemizedlist spacing="compact">
+ <listitem>
+ <para>The explicit value of this property.</para>
+ </listitem>
+ <listitem>
+ <para>The explicit value of the corresponding
+property.</para>
+ </listitem>
+ <listitem>
+ <para>The value of this property from the shorthand
+properties.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+
+ <listitem>
+ <para>A relative property <itemizedlist spacing="compact">
+ <listitem>
+ <para>The explicit value of the corresponding
+property.</para>
+ </listitem>
+ <listitem>
+ <para>The explicit value of this property.</para>
+ </listitem>
+ <listitem>
+ <para>The value of the corresponding property from
+the shorthand properties.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist>
+</para>
+
+ </section>
+
+ <section>
+ <title>Mapping between property names, IDs and makers</title>
+
+<para>The property subsystem is set up in the class
+<literal>FOPropertyMapping</literal>. It creates a property maker
+object for each property type, and it creates mappings of
+the names, IDs and makers of the property types. It holds the
+following static maps:
+
+<screen>
+property name &lt;=&gt; property ID =&gt; property maker
+ | |
+ s_htSubPropNames (&lt;-) s_htGeneric
+ s_htPropIds (-&gt;)
+</screen>
+
+Each type of FObj holds a copy of <literal>s_htGeneric</literal> as
+its static member <literal>FObj.propertyListTable</literal>. According
+to design documents an FObj type may have its own specific makers for
+certain property types. Probably this is the reason that
+<literal>FObj</literal> holds its own copy of the list of makers. This
+allows subclasses to hold their own modified copy. As far as I know,
+this is not currently the case.</para>
+
+<para>The mappings are filled in the static method
+
+<screen>
+ private static void addPropertyMaker(String name, Property.Maker maker) {
+ s_generics[maker.getPropId()] = maker;
+ s_htPropNames.put(name, new Integer(maker.getPropId()));
+ s_htPropIds.put(new Integer(maker.getPropId()), name);
+ }
+</screen>
+
+which is called for each property type.</para>
+
+<para>The constants for property IDs are defined in the interface
+<literal>org.apache.fop.fo.Constants</literal>:
+
+<screen>
+ int PR_ABSOLUTE_POSITION = 1;
+ int PR_ACTIVE_STATE = 2;
+ ...
+ int PR_FONT_SIZE = 94;
+ ...
+ int PROPERTY_COUNT = 247;
+</screen>
+</para>
+
+<para>Composite properties are defined by a compound number:
+
+<screen>
+ int COMPOUND_SHIFT = 9;
+ int CP_MAXIMUM = 5 &lt;&lt; COMPOUND_SHIFT;
+ int CP_MINIMUM = 6 &lt;&lt; COMPOUND_SHIFT;
+ int CP_OPTIMUM = 7 &lt;&lt; COMPOUND_SHIFT;
+ ...
+</screen>
+</para>
+
+ <para>Enumerated property values are also defined here:
+
+<screen>
+ int ABSOLUTE = 1;
+ int ABSOLUTE_COLORMETRIC = 2;
+ ...
+ int VISIBLE = 105;
+ int WRAP = 106;
+</screen></para>
+
+ <para>For fast access to important characteristic of property
+inheritance, <literal>PropertyList</literal> maintains a static array
+<literal>boolean[Constants.PROPERTY_COUNT + 1]
+inheritableProperty</literal>, which lists for each property type if
+it is inherited. It is constructed by asking the maker of each
+property type if it is inherited.</para>
+
+<para>A few members of the array of <literal>PropertyMaker</literal>s
+<literal>s_generics</literal>. It is indexed by the
+<literal>propID</literal>. Member 0 is <literal>null</literal>, and
+serves for unknown property types. Member 1 is for
+<literal>absolute-position</literal>, member 94 for
+<literal>font-size</literal>.
+
+<screen>
+main[1] print org.apache.fop.fo.FOPropertyMapping.s_generics
+ org.apache.fop.fo.FOPropertyMapping.s_generics = instance of org.apache.fop.fo.Property$Maker[248] (id=651)
+main[1] print org.apache.fop.fo.FOPropertyMapping.s_generics[0]
+ org.apache.fop.fo.FOPropertyMapping.s_generics[0] = null
+main[1] print org.apache.fop.fo.FOPropertyMapping.s_generics[1]
+ org.apache.fop.fo.FOPropertyMapping.s_generics[1] = "org.apache.fop.fo.EnumProperty$Maker@12884e0"
+main[1] print org.apache.fop.fo.FOPropertyMapping.s_generics[94]
+ org.apache.fop.fo.FOPropertyMapping.s_generics[94] = "org.apache.fop.fo.LengthProperty$Maker@32efa7"
+</screen>
+</para>
+
+ <para>A few members of the mapping <literal>s_htPropIds</literal>
+from <literal>propID</literal> to property name. The
+<literal>s_htPropIds</literal> for compound properties are shifted:
+
+<screen>
+main[1] print org.apache.fop.fo.FOPropertyMapping.s_htPropIds
+ org.apache.fop.fo.FOPropertyMapping.s_htPropIds = "{
+ 1=absolute-position
+ ...
+ 94=font-size
+ ...
+ 247=z-index
+ 512=block-progression-direction
+ 1024=conditionality
+ ...
+ 5632=within-page
+ }"
+</screen>
+</para>
+
+<para>A few members of the mappings <literal>s_htPropNames</literal>
+and <literal>s_htSubPropNames</literal> from property name to
+<literal>propID</literal>. The <literal>propIds</literal> for
+compound properties are shifted:
+
+<screen>
+main[1] print org.apache.fop.fo.FOPropertyMapping.s_htPropNames
+ org.apache.fop.fo.FOPropertyMapping.s_htPropNames = "{
+ absolute-position=1
+ ...
+ font-size=94
+ ...
+ z-index=247
+ }"
+</screen>
+
+<screen>
+main[1] print org.apache.fop.fo.FOPropertyMapping.s_htSubPropNames
+ org.apache.fop.fo.FOPropertyMapping.s_htSubPropNames = "{
+ block-progression-direction=512
+ conditionality=1024
+ ...
+ within-page=5632
+ }"
+</screen>
+</para>
+
+ </section>
+
+ <section>
+ <title>Storing the property values based on their
+<literal>PropID</literal></title>
+
+ <para>The class <literal>PropertySets</literal> contains a setup
+by which property values may be retrieved by their
+<literal>PropId</literal> instead of their name. In this setup
+<literal>PropertyList</literal> no longer extends
+<literal>HashMap</literal> but contains an array of property objects,
+called <literal>values</literal>. In order to prevent that each FObj
+should contain an array of size
+<literal>Constants.PROPERTY_COUNT</literal>, a mapping is setup from a
+static array for all FO types and all property types to an index for
+the possible properties of an FO type,
+<literal>PropertySets.mapping</literal>.</para>
+
+<para><literal>PropertySets.mapping</literal> is a
+<literal>short[Constants.ELEMENT_COUNT+1][]</literal> matrix, which
+for each FO type contains a mapping from <literal>PropID</literal> to
+a sparse array of indices, which enumerates the properties that are
+valid for this FO type.</para>
+
+<para>For an element <literal>fo:bar</literal> which supports 2
+properties, <literal>foo</literal>, whose <literal>PropID</literal> is
+21, and <literal>baz</literal>, whose <literal>PropID</literal> is
+137, the array of indices has the values
+
+<screen>
+ indices[21] = 1
+ indices[137] = 2
+</screen>
+
+and all other values are 0. Here indices denotes the row in
+<literal>mapping</literal> which corresponds to FO type bar.</para>
+
+<para>The values are indices into the array
+<literal>PropertyList.values</literal>, which then looks like this:
+
+<screen>
+ values[0] = null // always null.
+ values[1] = reference to a 'foo' Property instance
+ values[2] = reference to a 'baz' Property instance
+</screen>
+</para>
+
+<para>Example of <literal>PropertySets.mapping</literal>:
+
+<screen>
+PropID -> | 0 1 2 3 4 5 ... (Contants.PR_XXX)
+Element | |
+ v |
+-----------------|--------------------------------------------------
+FO_BASIC_LINK | 2 0 1 0 2 0
+FO_BIDI_OVERRIDE | 3 0 0 1 2 3
+FO_BLOCK | 2 1 0 0 2 0
+... | ....
+ |
+</screen>
+</para>
+
+<para>A property value of an <literal>FONode</literal> can then be
+retrieved as <literal>PropertyList.values[indices[propId]]</literal>,
+where <literal>indices = PropertySets.getPropertySet(elementId) =
+PropertySets.mapping[elementId].</literal></para>
+
+<para>The matrix <literal>PropertySets.mapping</literal> is
+constructed in the routine
+<literal>PropertySets.initialize()</literal>.</para>
+
+<para>First it constructs the <literal>elements</literal> array. For
+each FO type this array contains an <literal>Element</literal>
+object. This object contains a list of properties which are valid for
+this type of FO, and a list of child <literal>Element</literal>
+objects. Each child <literal>Element</literal> object corresponds to
+an FO type that may occur as a child of this FO type.</para>
+
+<para>Then the <literal>for (boolean dirty = true; dirty; )</literal>
+loop is executed. It effect is as follows (from an email by Finn
+Bock). For each FO type the <literal>BitSet</literal> of allowed
+properties is merged with the <literal>BitSet</literal> of allowed
+properties of its possible direct children. When for any FO type the
+<literal>merge</literal> subroutine modifies its
+<literal>BitSet</literal>, it sets the boolean variable
+<literal>dirty</literal> to <literal>true</literal> to signal that
+another iteration of the loop is required. By iterating over the loop
+until no further modifications are made, one makes sure that the
+merging process propagates from below to the top, that is, from any FO
+type to its farthest possible ancestor. This ensures that every FO
+type registers the allowed properties of itself and of all FO types
+that may ever appear in its subtree.</para>
+
+<para>The matrix <literal>PropertySets.mapping</literal> is still not
+used in <literal>PropertyList</literal>, and the array
+<literal>values</literal> does not yet exist (19 May 2004). The
+properties are held by name in <literal>PropertyList</literal>, which
+extends <literal>HashMap</literal>.</para>
+
+ </section>
+
+ </section>
+
+ <section id="sec.properties.create">
+ <title>Creating a property value</title>
+
+ <section>
+ <title>General</title>
+
+ <para>A property value is created by the maker for the property type,
+in its method
+<literal>PropertyMaker.make(PropertyList, String, FObj)</literal>,
+where the second argument is the property value as a string:
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>If the specified value is <literal>inherit</literal>,
+get the property value from the parent FObj.</para>
+ </listitem>
+ <listitem>
+ <para>Else if the value is an enumerated value, the corresponding
+property value is retrieved (for each possible enumerated value only
+one property value object exists, of type EnumProperty).</para>
+ </listitem>
+ <listitem>
+ <para>If this does not retrieve a property value, <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>If the value is a shorthand keyword, it is
+converted to the corresponding value.</para>
+ </listitem>
+ <listitem>
+ <para>The value is parsed, and a property value is
+created.</para>
+ </listitem>
+ <listitem>
+ <para>The method
+<literal>PropertyMaker.convertProperty</literal> is called, which is overridden
+in subclasses of PropertyMaker. CompoundPropertyMaker uses this method
+to convert the simple property value constructed to a compound
+property value: <itemizedlist spacing="compact">
+ <listitem>
+ <para>Make a compound property value based on
+default values: <literal>PropertyMaker.makeCompound</literal>,
+overridden in CompoundPropertyMaker.</para>
+ </listitem>
+ <listitem>
+ <para>Set all components equal to the
+simple property value that is being converted.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>If this is a compound property maker, convert it to
+a compound property as above. (Is this the second time the property
+value is converted?)</para>
+ </listitem>
+ </itemizedlist>
+The property may also record the value as specified in the fo element,
+as this may influence the traits of the areas created by this FO node
+and FO nodes in the subtree.</para>
+
+<para>Subclasses of <literal>PropertyMaker</literal> may override this
+method. For example, <literal>StringProperty.Maker</literal> has a
+much simpler method.</para>
+
+<para>Attributes of the fo elements are converted to property value
+objects in <literal>PropertyList.convertAttributeToProperty</literal>:
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>If the property is not a component of a compound
+property, <itemizedlist spacing="compact">
+ <listitem>
+ <para>Ask the maker for the property to create the
+property value.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>Else if the property is a component of a compound
+property, <itemizedlist spacing="compact">
+ <listitem>
+ <para>Find the base property by a call to
+<literal>Propertylist.findBaseProperty</literal>: <itemizedlist
+ spacing="compact">
+ <listitem>
+ <para>If the base property value already exists,
+return it to <literal>convertAttributeToProperty</literal>.</para>
+ </listitem>
+ <listitem>
+ <para>If the base attribute is also specified
+(later) in the list of attributes, retrieve it, ask the maker for the
+base property to create the base property value, and return it to
+<literal>convertAttributeToProperty</literal>.</para>
+ </listitem>
+ <listitem>
+ <para>Return
+null to <literal>convertAttributeToProperty</literal>.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>Ask the maker for the subproperty to create the
+subproperty value by a call to <literal>PropertyMaker.make(Property,
+int, PropertyList, String, FObj)</literal>, where the second argument
+is the subproperty ID and the fourth argument is the specified
+value. This method is overridden in
+<literal>CompoundPropertyMaker</literal>: <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>If the base property value does not yet
+exist, ask its maker to create it with default values for the
+components: <literal>PropertyMaker.makeCompound</literal>, which is
+overridden in <literal>CompoundPropertyMaker</literal>: <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>Create an empty property value.</para>
+ </listitem>
+ <listitem>
+ <para>Create property values for the
+subproperties with default values, and insert them into the compound
+property value.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>Create the specified subproperty value and
+insert it into the compound property value, where it replaces the
+default subproperty value.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>Add the property to the property list.
+</para>
+ </listitem>
+ </itemizedlist></para>
+
+ </section>
+
+ <section>
+ <title>Example of a compound property</title>
+
+<para>In this example we illustrate the case where first the
+base attribute of a compound property is specified, and then the
+attribute for one of the components.</para>
+
+<para>First the user specifies the attribute value
+<literal>leader-length="120pt"</literal>.</para>
+
+<para>A simple length property value is constructed first:
+<screen>
+ p.getClass() = "class org.apache.fop.fo.properties.FixedLength"
+ p = {
+ millipoints: 120000
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+ p = "120000mpt"
+</screen></para>
+
+<para>Then it is converted into a compound property value. First a
+compound property with default component values is created:
+<screen>
+ p.getClass() = "class org.apache.fop.fo.properties.LengthRangeProperty"
+ p = {
+ minimum: instance of org.apache.fop.fo.properties.FixedLength(id=759)
+ optimum: instance of org.apache.fop.fo.properties.FixedLength(id=760)
+ maximum: instance of org.apache.fop.fo.properties.PercentLength(id=761)
+ bfSet: 0
+ bChecked: true
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+ p = "LengthRange[min:0mpt, max:100.0%, opt:12000mpt]"
+</screen>
+
+Then all components are set equal to the simple property value:
+<screen>
+ prop.getClass() = "class org.apache.fop.fo.properties.LengthRangeProperty"
+ prop = {
+ minimum: instance of org.apache.fop.fo.properties.FixedLength(id=744)
+ optimum: instance of org.apache.fop.fo.properties.FixedLength(id=744)
+ maximum: instance of org.apache.fop.fo.properties.FixedLength(id=744)
+ bfSet: 7
+ bChecked: true
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+ prop = "LengthRange[min:120000mpt, max:120000mpt, opt:120000mpt]"
+</screen></para>
+
+<para>The property makers involved:
+<screen>
+ this = "org.apache.fop.fo.properties.LengthRangeProperty$Maker@55a338"
+ this.subproperties = instance of org.apache.fop.fo.properties.PropertyMaker[11] (id=766)
+ getSubpropMaker(org.apache.fop.fo.Constants.CP_MINIMUM) = "org.apache.fop.fo.properties.LengthProperty$Maker@955cd5"
+ getSubpropMaker(org.apache.fop.fo.Constants.CP_MAXIMUM) = "org.apache.fop.fo.properties.LengthProperty$Maker@1bde4"
+ getSubpropMaker(org.apache.fop.fo.Constants.CP_OPTIMUM) = "org.apache.fop.fo.properties.LengthProperty$Maker@a77106"
+</screen></para>
+
+<para>Stack dump when making the compound property:
+<screen>
+ [1] org.apache.fop.fo.properties.CompoundPropertyMaker.makeCompound (CompoundPropertyMaker.java:258)
+ [2] org.apache.fop.fo.properties.CompoundPropertyMaker.convertProperty (CompoundPropertyMaker.java:173)
+ [3] org.apache.fop.fo.properties.LengthRangeProperty$Maker.convertProperty (LengthRangeProperty.java:68)
+ [4] org.apache.fop.fo.properties.PropertyMaker.make (PropertyMaker.java:392)
+ [5] org.apache.fop.fo.properties.CompoundPropertyMaker.make (CompoundPropertyMaker.java:206)
+ [6] org.apache.fop.fo.PropertyList.convertAttributeToProperty (PropertyList.java:418)
+ [7] org.apache.fop.fo.PropertyList.addAttributesToList (PropertyList.java:374)
+ [8] org.apache.fop.fo.FObj.addProperties (FObj.java:133)
+ [9] org.apache.fop.fo.FObj.processNode (FObj.java:96)
+ [10] org.apache.fop.fo.FOTreeBuilder.startElement (FOTreeBuilder.java:234)
+</screen>
+</para>
+
+<para>Subsequently, the user specifies a component,
+<literal>leader-length.maximum="200pt"</literal>.</para>
+
+<para>First the subproperty is constructed as a simple length property:
+<screen>
+ p.getClass() = "class org.apache.fop.fo.properties.FixedLength"
+ p = {
+ millipoints: 200000
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+ p = "200000mpt"
+</screen>
+</para>
+
+ <para>Then it is added to the compound property as the component
+<literal>maximum</literal>:
+<screen>
+ prop.getClass() = "class org.apache.fop.fo.properties.LengthRangeProperty"
+ prop = {
+ minimum: instance of org.apache.fop.fo.properties.FixedLength(id=755)
+ optimum: instance of org.apache.fop.fo.properties.FixedLength(id=755)
+ maximum: instance of org.apache.fop.fo.properties.FixedLength(id=767)
+ bfSet: 7
+ bChecked: true
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+ prop = "LengthRange[min:120000mpt, max:200000mpt, opt:120000mpt]"
+</screen>
+</para>
+
+<para>Stack dump when making the property:
+<screen>
+ [1] org.apache.fop.fo.properties.PropertyMaker.make (PropertyMaker.java:378)
+ [2] org.apache.fop.fo.properties.CompoundPropertyMaker.make (CompoundPropertyMaker.java:235)
+ [3] org.apache.fop.fo.PropertyList.convertAttributeToProperty (PropertyList.java:423)
+ [4] org.apache.fop.fo.PropertyList.addAttributesToList (PropertyList.java:374)
+ [5] org.apache.fop.fo.FObj.addProperties (FObj.java:133)
+ [6] org.apache.fop.fo.FObj.processNode (FObj.java:96)
+ [7] org.apache.fop.fo.FOTreeBuilder.startElement (FOTreeBuilder.java:234)
+</screen></para>
+
+ </section>
+
+<section>
+ <title>Enumerated property values</title>
+
+ <para>The interface <literal>Constants</literal> defines values
+for each possible enumerated value of a property:
+<screen>
+ int ABSOLUTE = 1;
+ int ABSOLUTE_COLORMETRIC = 2;
+ ...
+ int VISIBLE = 105;
+ int WRAP = 106;
+</screen></para>
+
+ <para>In <literal>FOPropertyMapping</literal> a property value
+object is constructed for each possible enumerated value. See the
+<literal>Property</literal> array <literal>enums</literal> and the
+method <literal>makeEnumProperty</literal>. During its construction,
+each property maker that can have enumerated values gets a member
+<literal>enums</literal>, which, for each of its possible enumerated
+values, gets a reference to the appropriate enumerated property value
+object. See the method <literal>PropertyMaker.addEnum</literal>.</para>
+
+ <para>Example: The properties <literal>hyphenate</literal> and
+<literal>precedence</literal> both have the possible value
+<literal>true</literal>. Their makers have a reference to the same
+property value object:
+<screen>
+ org.apache.fop.fo.FOPropertyMapping.s_generics
+ [org.apache.fop.fo.Constants.PR_HYPHENATE].
+ enums.get("true").hashCode() = 9236202
+ org.apache.fop.fo.FOPropertyMapping.s_generics
+ [org.apache.fop.fo.Constants.PR_PRECEDENCE].
+ enums.get("true").hashCode() = 9236202
+ org.apache.fop.fo.FOPropertyMapping.s_generics
+ [org.apache.fop.fo.Constants.PR_HYPHENATE].enums.get("true") = "100"
+ org.apache.fop.fo.FOPropertyMapping.s_generics
+ [org.apache.fop.fo.Constants.PR_HYPHENATE].enums.get("true") = {
+ value: 100
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+</screen></para>
+
+<para>Example: <literal>leader-pattern="rule"</literal>.
+<screen>
+ this = "org.apache.fop.fo.properties.EnumProperty$Maker@25c828"
+ this.enums = "{rule=82, use-content=104, dots=21, space=88}"
+ this.enums.get("rule").getClass() = "class org.apache.fop.fo.properties.EnumProperty"
+ this.enums.get("rule") = "82"
+ this.enums.get("rule") = {
+ value: 82
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+</screen></para>
+
+<para>The maker's method <literal>checkEnumValues</literal> returns
+the appropriate property value object
+<literal>enums.get(value)</literal>: <literal>newProp =
+"82"</literal>.</para>
+
+ <screen>
+ [1] org.apache.fop.fo.properties.PropertyMaker.checkEnumValues (PropertyMaker.java:480)
+ [2] org.apache.fop.fo.properties.EnumProperty$Maker.checkEnumValues (EnumProperty.java:50)
+ [3] org.apache.fop.fo.properties.PropertyMaker.make (PropertyMaker.java:383)
+ [4] org.apache.fop.fo.PropertyList.convertAttributeToProperty (PropertyList.java:418)
+ [5] org.apache.fop.fo.PropertyList.addAttributesToList (PropertyList.java:374)
+</screen>
+
+ </section>
+
+<section>
+ <title>Example of a property with keywords</title>
+
+<para>The value of the property <literal>border-top-width</literal>
+can be set to a width, but it can also be indicated by one of the
+keywords <literal>thin</literal>, <literal>medium</literal> and
+<literal>thick</literal>. The width values to which these keywords
+correspond are by default set by the implementation. When the property
+maker is constructed in <literal>FOPropertyMapping</literal>, it gets
+a hash map of keyword values.
+<screen>
+ this = "org.apache.fop.fo.properties.BorderWidthPropertyMaker@1cf4a2c"
+ this.propId = 55
+ this.keywords = "{medium=1pt, thin=0.5pt, thick=2pt}"
+}
+</screen>
+</para>
+
+<para>The method <literal>checkValueKeywords</literal> returns the
+mapped value: <literal>value = "1pt"</literal>. Subsequently a
+property value object is created as if that value had been specified.
+
+ <screen>
+ [1] org.apache.fop.fo.properties.PropertyMaker.checkValueKeywords (PropertyMaker.java:499)
+ [2] org.apache.fop.fo.properties.PropertyMaker.make (PropertyMaker.java:387)
+ [3] org.apache.fop.fo.PropertyList.convertAttributeToProperty (PropertyList.java:418)
+ [4] org.apache.fop.fo.PropertyList.addAttributesToList (PropertyList.java:374)
+ [5] org.apache.fop.fo.FObj.addProperties (FObj.java:133)
+</screen>
+</para>
+
+ </section>
+
+ <section>
+ <title>Parsing a property with an absolute value</title>
+
+ <para>Property values are parsed in
+<literal>PropertyParser.parseProperty</literal>:
+ <screen>
+ [1] org.apache.fop.fo.expr.PropertyParser.parseProperty (PropertyParser.java:111)
+ [2] org.apache.fop.fo.expr.PropertyParser.parse (PropertyParser.java:88)
+ [3] org.apache.fop.fo.properties.PropertyMaker.make (PropertyMaker.java:389)
+ [4] org.apache.fop.fo.PropertyList.convertAttributeToProperty (PropertyList.java:418)
+ [5] org.apache.fop.fo.PropertyList.addAttributesToList (PropertyList.java:374)
+ [6] org.apache.fop.fo.FObj.addProperties (FObj.java:133)
+</screen></para>
+
+ <para>Example: <literal>&lt;fo:simple-page-master
+master-name="simpleA4" margin="4pt"></literal>, property being parsed:
+<literal>margin="4pt"</literal>, <literal>propId</literal> = 134.</para>
+
+<para>The PropertyParser object:
+<screen>
+ this = "org.apache.fop.fo.expr.PropertyParser@8530b8"
+ this = {
+ propInfo: instance of org.apache.fop.fo.expr.PropertyInfo(id=729)
+ org.apache.fop.fo.expr.PropertyTokenizer.currentToken: 0
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenValue: null
+ org.apache.fop.fo.expr.PropertyTokenizer.currentUnitLength: 0
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenStartIndex: 0
+ org.apache.fop.fo.expr.PropertyTokenizer.expr: "4pt"
+ org.apache.fop.fo.expr.PropertyTokenizer.exprIndex: 0
+ org.apache.fop.fo.expr.PropertyTokenizer.exprLength: 3
+ org.apache.fop.fo.expr.PropertyTokenizer.recognizeOperator: false
+}
+</screen></para>
+
+ <para>It has a member of type <literal>PropertyInfo</literal>,
+which contains contextual information:
+<screen>
+ propInfo = "org.apache.fop.fo.expr.PropertyInfo@1abcc03"
+ propInfo = {
+ maker: instance of org.apache.fop.fo.properties.ListProperty$Maker(id=705)
+ plist: instance of org.apache.fop.fo.PropertyList(id=737)
+ fo: instance of org.apache.fop.fo.pagination.LayoutMasterSet(id=738)
+ stkFunction: null
+}
+</screen>
+<literal>fo</literal> is the parent FO.</para>
+
+<para>The property list of the current FO node:
+<screen>
+ propInfo.plist = {
+ writingModeTable: null
+ writingMode: 0
+ inheritableProperty: null
+ parentPropertyList: instance of org.apache.fop.fo.PropertyList(id=743)
+ namespace: "http://www.w3.org/1999/XSL/Format"
+ elementName: "fo:simple-page-master"
+ fobj: instance of org.apache.fop.fo.pagination.SimplePageMaster(id=746)
+}
+</screen>
+The property list up to now:
+<screen>
+ propInfo.plist = "{master-name=simpleA4}"
+</screen>
+</para>
+
+ <para>Property <literal>master-name</literal>'s maker is
+<literal>StringPropertyMaker</literal>, which does not parse its
+value.</para>
+
+ <para>PropertyParser.parseProperty <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>next(), which scans the next token; at its return:
+<screen>
+ this = {
+ propInfo: instance of org.apache.fop.fo.expr.PropertyInfo(id=667)
+ org.apache.fop.fo.expr.PropertyTokenizer.currentToken: 12
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenValue: "4pt"
+ org.apache.fop.fo.expr.PropertyTokenizer.currentUnitLength: 2
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenStartIndex: 0
+ org.apache.fop.fo.expr.PropertyTokenizer.expr: "4pt"
+ org.apache.fop.fo.expr.PropertyTokenizer.exprIndex: 3
+ org.apache.fop.fo.expr.PropertyTokenizer.exprLength: 3
+ org.apache.fop.fo.expr.PropertyTokenizer.recognizeOperator: true
+}
+</screen>
+i.e. the whole expression is a single token, it is of type
+<literal>PropertyTokenizer.TOK_NUMERIC</literal> (= 12), the unit is 2
+chars long.</para>
+ </listitem>
+ <listitem>
+ <para>Loop forever. <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>Analyse the expression. Start with
+<literal>parseAdditiveExpr</literal> <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>parseMultiplicativeExpr</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>parseUnaryExpr</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>parsePrimaryExpr</literal>;
+the unit may be a relative unit (em) which must be resolved against
+the font size <itemizedlist spacing="compact">
+ <listitem>
+ <para>construct a property
+value object:
+<screen>
+ prop = "4000mpt"
+ prop.getClass() = "class org.apache.fop.fo.properties.FixedLength"
+</screen></para>
+ </listitem>
+ <listitem>
+ <para><literal>next()</literal>:
+scan the next token;
+<screen>
+ this = {
+ propInfo: instance of org.apache.fop.fo.expr.PropertyInfo(id=729)
+ org.apache.fop.fo.expr.PropertyTokenizer.currentToken: 0
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenValue: null
+ org.apache.fop.fo.expr.PropertyTokenizer.currentUnitLength: 2
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenStartIndex: 3
+ org.apache.fop.fo.expr.PropertyTokenizer.expr: "4pt"
+ org.apache.fop.fo.expr.PropertyTokenizer.exprIndex: 3
+ org.apache.fop.fo.expr.PropertyTokenizer.exprLength: 3
+ org.apache.fop.fo.expr.PropertyTokenizer.recognizeOperator: true
+}
+</screen>
+the next token is of type <literal>currentToken =
+PropertyTokenizer.TOK_EOF</literal>.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>If <literal>currentToken =
+PropertyTokenizer.TOK_EOF</literal>, break the loop.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist>
+Return the property: <literal>p = "4000mpt"</literal>.</para>
+ </section>
+
+ </section>
+
+<section>
+ <title>Retrieving a property value</title>
+
+ <section>
+ <title>Overview</title>
+
+ <para>For all FO types the FO spec specifies a large number of
+property types for which the user may specify a value in order to
+modify the resulting layout of the document. Many of these properties
+have a default value, many others inherit their value from the parent
+FO if they are not specified. In principle the layout process must
+retrieve the value of each possible property type of an FO node in
+order to determine the appropriate value of the corresponding
+trait.</para>
+
+ <para>Retrieving a property value goes through these steps:
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>First determine if the property value was specified
+in one or other way. This is done in the method
+<literal>propertyMaker.findProperty</literal> <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>if this property has a corresponding property
+and if the corresponding property is forced, i.e. if this property is
+relative and if a value for the corresponding property was explicitly
+specified, compute and return it.</para>
+ </listitem>
+ <listitem>
+ <para>else <itemizedlist spacing="compact">
+ <listitem>
+ <para>if a value for this property was
+explicitly specified, compute and return it</para>
+ </listitem>
+ <listitem>
+ <para>else if this property has a
+corresponding property, compute its value; if it is not null return
+it</para>
+ </listitem>
+ <listitem>
+ <para>else if a value for a relevant shorthand
+property was specified, compute and return it</para>
+ </listitem>
+ <listitem>
+ <para>else if this property is inheritable,
+find it at the parent; this repeats the whole process on the parent,
+and possibly its parents, up to the root node; if successful, return
+the found property value</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>If no property value is found, a default property
+value object is made; the default value is stored in the property
+maker as <literal>defaultValue</literal>: <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>if the default property value object was
+calculated earlier, it was cached as
+<literal>defaultProperty</literal>; return it</para>
+ </listitem>
+ <listitem>
+ <para>else make it in the same way as property value
+objects are made when the FO tree is constructed, in
+<literal>propertyMaker.make</literal>.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+
+ <para>Compute corresponding, as used in
+<literal>findProperty</literal>, proceeds as follows: <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>use parent's property list or this property
+list?</para>
+ </listitem>
+ <listitem>
+ <para>check explicit or shorthand for corresponding</para>
+ </listitem>
+ <listitem>
+ <para>convert property</para>
+ </listitem>
+ </itemizedlist></para>
+
+<para><literal>PropertyManager</literal>,
+<literal>CommonBorderAndPadding</literal>,
+<literal>CommonBackground</literal>,
+<literal>CommonMarginBlock</literal>,
+<literal>CommonHyphenation</literal> are convenience classes used in
+the calculation of the traits. <literal>PropertyManager</literal> has
+methods to return objects of these types. A
+<literal>CommonBorderAndPadding</literal> object and a
+<literal>CommonHyphenation</literal> object are cached. A
+<literal>CommonBackground</literal> object and a
+<literal>CommonMarginBlock</literal> object are calculated when
+requested. Similarly for many other convenience classes
+<literal>Common*</literal> for which
+<literal>PropertyManager</literal> can return an object. These classes
+are in package <literal>fo.properties</literal>. Similar classes
+called <literal>*Props</literal> are in package
+<literal>traits</literal>. Of these <literal>PropertyManager</literal>
+can only return a <literal>BlockProps</literal> object.</para>
+
+ </section>
+
+ <section>
+ <title>Detailed overview</title>
+
+ <para>The retrieval of a property value is started with a call
+to <literal>PropertyList.get(int propId)</literal>, which calls
+<literal>PropertyList.get(propId, true, true)</literal>.</para>
+
+<para><literal>PropertyList.get(int propId, boolean bTryInherit,
+boolean bTryDefault)</literal>:
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>Find the maker for this property as
+<literal>FObj.propertyListTable[propId]</literal>, variable
+<literal>propertyMaker</literal></para>
+ </listitem>
+ <listitem>
+ <para><literal>propertyMaker.get(int subpropId,
+PropertyList propertyList, boolean bTryInherit, boolean
+bTryDefault)</literal> <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>propertyMaker.findProperty(PropertyList
+propertyList, boolean bTryInherit)</literal> <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>if corresponding and corresponding is
+forced <itemizedlist spacing="compact">
+ <listitem>
+ <para>evaluate condition:
+<literal>CorrespondingPropertyMaker.isCorrespondingForced
+(PropertyList propertyList)</literal> <itemizedlist spacing="compact">
+ <listitem>
+ <para> <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>return false if this is
+not a relative property
+(<literal>corresponding.relative</literal>)</para>
+ </listitem>
+ <listitem>
+ <para>return true if
+corresponding property was explicitly specified on this node or on its
+parent, depending on the type of property
+(<literal>corresponding.useParent</literal>)</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para><literal>corresponding.compute(PropertyList
+propertyList)</literal>; this subroutine and the subroutines it calls
+refer to the corresponding property <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>propertyList.getExplicitOrShorthand(correspondingId)</literal>;
+propertyList is that of this node or of the parent, depending on the
+type of property (corresponding.useParent) <itemizedlist
+spacing="compact">
+ <listitem>
+ <para><literal>propertyList.getExplicitBaseProp(int
+correspondingId)</literal> (see below)</para>
+ </listitem>
+ <listitem>
+ <para>if
+<literal>(null)</literal> <literal>propertyList.getShorthand(int
+correspondingId)</literal> <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>propertyMaker.getShorthand(this)</literal>
+(see below)</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>if <literal>(not
+null)</literal> convert property</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>else <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>propertyList.getExplicitBaseProp(int
+propId)</literal> from <literal>propertyList</literal> as a hash map;
+note that this requires a conversion from <literal>propId</literal> to
+<literal>propName</literal> via <literal>s_htPropNames</literal>;
+example: <literal>propertyList =
+"{master-name=simpleA4}"</literal></para>
+ </listitem>
+ <listitem>
+ <para>if <literal>(null)</literal>
+<literal>propertyMaker.compute(PropertyList propertyList)</literal>:
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>if
+<literal>(corresponding)</literal>
+<literal>corresponding.compute(PropertyList propertyList)</literal>,
+as above</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>if <literal>(null)</literal>
+<literal>propertyMaker.getShorthand(PropertyList
+propertyList)</literal> <itemizedlist spacing="compact">
+ <listitem>
+ <para>if
+<literal>(maker.shorthands)</literal> then for each shorthand
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>propertyList.getExplicit(int
+shorthand.propId)</literal>; a shorthand must be
+<literal>ListProperty</literal></para>
+ </listitem>
+ <listitem>
+ <para>if <literal>(not
+null)</literal> parse the shorthand property value,
+<literal>parser.getValueForProperty(propId, listprop, propertyMaker,
+this, propertyList)</literal>; here <literal>propId</literal> is the
+<literal>propId</literal> of this property, not of the shorthand; a
+shorthand may contain values for several properties, and this method
+retrieves the value for the current property; if <literal>(not
+null)</literal> return it</para>
+ </listitem>
+ </itemizedlist>Note: the first
+shorthand property maker in <literal>maker.shorthands</literal> that
+returns a good property is used</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>if <literal>(null &amp;&amp;
+bTryInherit)</literal>
+<literal>propertyMaker.findProperty(parentPropertyList,
+true)</literal>: the whole process is repeated on the parent, and
+possibly its parents, up to the root node.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>if <literal>(null &amp;&amp; bTryDefault)</literal>
+<literal>propertyMaker.make(PropertyList propertyList)</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>if there is a cached value,
+<literal>defaultProperty</literal>, return it</para>
+ </listitem>
+ <listitem>
+ <para><literal>propertyMaker.make(PropertyList
+propertyList, String value, FObj fo)</literal>;
+<literal>value</literal> is the default value stored in the property
+maker, <literal>fo</literal> is the parent FO <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>if default value is
+<literal>inherit</literal>,
+propertyList.getFromParent(<literal>propId</literal>)
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>if
+<literal>(parentPropertyList != null)</literal>
+<literal>parentPropertyList.get(propId)</literal>; the whole process
+is repeated on the parent, and possibly its parents, up to the root
+node.</para>
+ </listitem>
+ <listitem>
+ <para><literal>propertyMaker.make(PropertyList
+propertyList)</literal>; this seems to create an endless recursion;
+<literal>parentPropertyList == null</literal> in the root node.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>else check enumerated values
+<literal>propertyMaker.checkEnumValues(value)</literal>: get the
+enumerated property from <literal>propertyMaker.enums</literal></para>
+ </listitem>
+ <listitem>
+ <para>if <literal>(null)</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>propertyMaker.checkValueKeywords(String
+value)</literal>; check if the value is a keyword in
+<literal>propertyMaker.keywords</literal>; if so, substitute the value
+with the value for which the keyword stands</para>
+ </listitem>
+ <listitem>
+ <para>parse the value <literal>p =
+PropertyParser.parse(pvalue, new PropertyInfo(propertyMaker,
+propertyList, fo))</literal></para>
+ </listitem>
+ <listitem>
+ <para>convert the property
+<literal>propertyMaker.convertProperty(p, propertyList,
+fo)</literal></para>
+ </listitem>
+ <listitem>
+ <para>if <literal>(null)</literal>
+throw <literal>org.apache.fop.fo.expr.PropertyException</literal>,
+which immediately is catched and rethrown as
+<literal>FOPException</literal> (why?)</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>catch <literal>FOPException</literal>; this
+means that this method may return a <literal>null</literal>
+property</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>catch <literal>FOPException</literal>; this means that
+this method may return a <literal>null</literal> property</para>
+ </listitem>
+ </itemizedlist></para>
+
+ <para>The call stack at the <literal>findProperty</literal> and
+<literal>make</literal>calls is:
+<screen>
+ [1] org.apache.fop.fo.properties.PropertyMaker.findProperty (PropertyMaker.java:240)
+ [2] org.apache.fop.fo.properties.PropertyMaker.get (PropertyMaker.java:282)
+ [3] org.apache.fop.fo.PropertyList.get (PropertyList.java:252)
+ [4] org.apache.fop.fo.PropertyList.get (PropertyList.java:238)
+ [5] org.apache.fop.fo.FObj.getProperty (FObj.java:163)
+ [6] org.apache.fop.layoutmgr.PageLayoutManager.createPageAreas (PageLayoutManager.java:745)
+</screen>
+<screen>
+ [1] org.apache.fop.fo.properties.PropertyMaker.make (PropertyMaker.java:387)
+ [2] org.apache.fop.fo.properties.PropertyMaker.make (PropertyMaker.java:369)
+ [3] org.apache.fop.fo.properties.PropertyMaker.get (PropertyMaker.java:285)
+ [4] org.apache.fop.fo.PropertyList.get (PropertyList.java:252)
+ [5] org.apache.fop.fo.PropertyList.get (PropertyList.java:238)
+ [6] org.apache.fop.fo.FObj.getProperty (FObj.java:163)
+ [7] org.apache.fop.layoutmgr.PageLayoutManager.createPageAreas (PageLayoutManager.java:745)
+</screen>
+For properties whose maker is compound property maker:
+<screen>
+ [1] org.apache.fop.fo.properties.PropertyMaker.findProperty (PropertyMaker.java:240)
+ [2] org.apache.fop.fo.properties.PropertyMaker.get (PropertyMaker.java:282)
+ [3] org.apache.fop.fo.properties.CompoundPropertyMaker.get (CompoundPropertyMaker.java:146)
+ [4] org.apache.fop.fo.PropertyList.get (PropertyList.java:252)
+ [5] org.apache.fop.fo.PropertyList.get (PropertyList.java:238)
+ [6] org.apache.fop.fo.flow.Leader.getLength (Leader.java:135)
+ [7] org.apache.fop.layoutmgr.AddLMVisitor.getLeaderAllocIPD (AddLMVisitor.java:305)
+ [8] org.apache.fop.layoutmgr.AddLMVisitor$2.getAllocationIPD (AddLMVisitor.java:290)
+ [9] org.apache.fop.layoutmgr.LeafNodeLayoutManager.getNextBreakPoss (LeafNodeLayoutManager.java:143)
+</screen></para>
+
+ </section>
+
+ <section>
+ <title>Examples: Retrieving border and padding values</title>
+
+<para>In this section we follow in detail how the border and padding
+values for the body region are retrieved. The relevant part of the
+input FO file is:
+
+<screen>
+<![CDATA[<fo:simple-page-master master-name="simpleA4" margin="4pt">
+<fo:region-body margin="4pt+20%" border="3pt solid black"
+ border-before-width="2pt" border-right-width="4pt"
+ border-start-width="inherit"/>
+</fo:simple-page-master>]]>
+</screen>
+</para>
+
+ <para>This section was written after I added the cache to the
+look-up of property values.</para>
+
+ <section>
+
+ <title>Retrieving the <literal>margin-top</literal> value on
+<literal>region-body</literal></title>
+
+<para>This what we are retrieving:
+
+<screen>
+ propertyList.getFOName() = "fo:region-body"
+ org.apache.fop.fo.FOPropertyMapping.getPropertyName(propId) = "margin-top"
+</screen>
+
+The margin values are retrieved by the method
+<literal>PropertyManager.getMarginProps</literal>. This is the call
+stack that leads up to it and on to the retrieval of the value of
+<literal>margin-top</literal>:
+
+<screen>
+ [1] org.apache.fop.fo.properties.PropertyMaker.findProperty (PropertyMaker.java:240)
+ [2] org.apache.fop.fo.PropertyList.findProperty (PropertyList.java:289)
+ [3] org.apache.fop.fo.properties.PropertyMaker.get (PropertyMaker.java:291)
+ [4] org.apache.fop.fo.PropertyList.get (PropertyList.java:261)
+ [5] org.apache.fop.fo.PropertyList.get (PropertyList.java:247)
+ [6] org.apache.fop.fo.PropertyManager.getMarginProps (PropertyManager.java:264)
+ [7] org.apache.fop.fo.pagination.RegionBody.getViewportRectangle (RegionBody.java:58)
+ [8] org.apache.fop.layoutmgr.PageLayoutManager.makeRegionViewport (PageLayoutManager.java:811)
+ [9] org.apache.fop.layoutmgr.PageLayoutManager.createPageAreas (PageLayoutManager.java:784)
+ [10] org.apache.fop.layoutmgr.PageLayoutManager.createPage (PageLayoutManager.java:721)
+ [11] org.apache.fop.layoutmgr.PageLayoutManager.makeNewPage (PageLayoutManager.java:441)
+ [12] org.apache.fop.layoutmgr.PageLayoutManager.doLayout (PageLayoutManager.java:191)
+</screen>
+</para>
+
+<para>The retrieval proceeds as follows:
+<itemizedlist spacing="compact">
+<listitem>
+<para><literal>PropertyList.findProperty</literal>: Is the value in
+the cache? No.</para>
+</listitem>
+<listitem>
+<para><literal>PropertyMaker.findProperty</literal>:
+<literal>corresponding != null</literal>? No.</para>
+</listitem>
+<listitem>
+<para><literal>PropertyMaker.findProperty</literal>: Is this property
+explicitly specified? No.</para>
+</listitem>
+<listitem>
+<para><literal>PropertyMaker.findProperty</literal>: Can the
+corresponding property be computed? No, there is no corresponding
+property.</para>
+</listitem>
+<listitem>
+<para><literal>PropertyMaker.findProperty</literal>: Is a shorthand
+property for this property specified? Yes, <literal>margin</literal>
+is a shorthand for it. The property value is retrieved as:
+<screen>
+ listprop = "[(4000mpt +20.0%)]"
+</screen>
+It is a list property as are all shorthand properties. The
+value for <literal>margin-top</literal> is extracted from it as:
+<screen>
+ p = "(4000mpt +20.0%)"
+</screen>
+</para>
+</listitem>
+<listitem>
+<para><literal>PropertyList.findProperty</literal>: Add the value to
+the cache.</para>
+</listitem>
+ </itemizedlist>
+</para>
+ </section>
+
+ <section>
+ <title>Retrieving the <literal>border-before-style</literal>
+value on <literal>region-body</literal></title>
+
+<para>This what we are retrieving:
+
+<screen>
+ propertyList.getFOName() = "fo:region-body"
+ org.apache.fop.fo.FOPropertyMapping.getPropertyName(propId) = "border-before-style"
+</screen>
+
+The border values are retrieved by the method
+<literal>PropertyManager.getBorderAndPadding</literal>. This is the call
+stack that leads up to it and on to the retrieval of the value of
+<literal>border-before-style</literal>:
+
+<screen>
+ [1] org.apache.fop.fo.properties.PropertyMaker.findProperty (PropertyMaker.java:240)
+ [2] org.apache.fop.fo.PropertyList.findProperty (PropertyList.java:289)
+ [3] org.apache.fop.fo.properties.PropertyMaker.get (PropertyMaker.java:291)
+ [4] org.apache.fop.fo.PropertyList.get (PropertyList.java:261)
+ [5] org.apache.fop.fo.PropertyList.get (PropertyList.java:247)
+ [6] org.apache.fop.fo.PropertyManager.initBorderInfo (PropertyManager.java:155)
+ [7] org.apache.fop.fo.PropertyManager.getBorderAndPadding (PropertyManager.java:143)
+ [8] org.apache.fop.fo.PropertyManager.getMarginProps (PropertyManager.java:289)
+ [9] org.apache.fop.fo.pagination.RegionBody.getViewportRectangle (RegionBody.java:58)
+ [10] org.apache.fop.layoutmgr.PageLayoutManager.makeRegionViewport (PageLayoutManager.java:811)
+ [11] org.apache.fop.layoutmgr.PageLayoutManager.createPageAreas (PageLayoutManager.java:784)
+ [12] org.apache.fop.layoutmgr.PageLayoutManager.createPage (PageLayoutManager.java:721)
+ [13] org.apache.fop.layoutmgr.PageLayoutManager.makeNewPage (PageLayoutManager.java:441)
+ [14] org.apache.fop.layoutmgr.PageLayoutManager.doLayout (PageLayoutManager.java:191)
+</screen>
+</para>
+
+<para>The retrieval proceeds as follows:
+<itemizedlist spacing="compact">
+<listitem>
+<para><literal>PropertyList.findProperty</literal>: Is the value in
+the cache? No.</para>
+</listitem>
+<listitem>
+<para><literal>PropertyMaker.findProperty</literal>:
+<literal>corresponding != null</literal>, but
+<literal>corresponding.isCorrespondingForced()</literal> returns
+<literal>false</literal>. The corresponding property is
+<literal>border-top-style</literal>, which is not specified:
+
+<screen>
+ org.apache.fop.fo.FOPropertyMapping.getPropertyName(correspondingId) = "border-top-style"
+</screen>
+This is the corresponding property maker:
+<screen>
+ this = {
+ baseMaker: instance of org.apache.fop.fo.properties.EnumProperty$Maker(id=816)
+ lr_tb: 54
+ rl_tb: 54
+ tb_rl: 43
+ useParent: false
+ relative: true
+}
+</screen>
+</para>
+ </listitem>
+<listitem>
+<para><literal>PropertyMaker.findProperty</literal>: Is this property
+explicitly specified? No.</para>
+</listitem>
+<listitem>
+<para><literal>PropertyMaker.findProperty</literal>: Can the
+corresponding property be computed? Yes, it can be derived from the
+property <literal>border</literal>, which is a shorthand for the
+corresponding property <literal>border-top-style</literal>. The
+returned value is <literal>87</literal>, which stands for
+<literal>SOLID</literal>.</para>
+<para>Note that the shorthand was not used in the
+computation of
+<literal>corresponding.isCorrespondingForced()</literal>, but it is in
+the computation of the specified value of the corresponding
+property.</para>
+</listitem>
+<listitem>
+<para><literal>PropertyList.findProperty</literal>: Add the value to
+the cache.</para>
+</listitem>
+ </itemizedlist></para>
+ </section>
+
+ <section>
+ <title>Retrieving the <literal>border-before-width</literal>
+value on <literal>region-body</literal></title>
+
+<para>This what we are retrieving:
+
+<screen>
+ propertyList.getFOName() = "fo:region-body"
+ org.apache.fop.fo.FOPropertyMapping.getPropertyName(propId) = "border-before-width"
+</screen>
+
+The border values are retrieved by the method
+<literal>PropertyManager.getBorderAndPadding</literal>. This is the call
+stack that leads up to it and on to the retrieval of the value of
+<literal>border-before-width</literal>:
+
+<screen>
+main[1] where
+ [1] org.apache.fop.fo.properties.PropertyMaker.findProperty (PropertyMaker.java:240)
+ [2] org.apache.fop.fo.PropertyList.findProperty (PropertyList.java:289)
+ [3] org.apache.fop.fo.properties.PropertyMaker.get (PropertyMaker.java:291)
+ [4] org.apache.fop.fo.properties.CompoundPropertyMaker.get (CompoundPropertyMaker.java:146)
+ [5] org.apache.fop.fo.PropertyList.get (PropertyList.java:261)
+ [6] org.apache.fop.fo.PropertyList.get (PropertyList.java:247)
+ [7] org.apache.fop.fo.PropertyManager.initBorderInfo (PropertyManager.java:157)
+ [8] org.apache.fop.fo.PropertyManager.getBorderAndPadding (PropertyManager.java:143)
+ [9] org.apache.fop.fo.PropertyManager.getMarginProps (PropertyManager.java:289)
+ [10] org.apache.fop.fo.pagination.RegionBody.getViewportRectangle (RegionBody.java:58)
+ [11] org.apache.fop.layoutmgr.PageLayoutManager.makeRegionViewport (PageLayoutManager.java:811)
+ [12] org.apache.fop.layoutmgr.PageLayoutManager.createPageAreas (PageLayoutManager.java:784)
+ [13] org.apache.fop.layoutmgr.PageLayoutManager.createPage (PageLayoutManager.java:721)
+ [14] org.apache.fop.layoutmgr.PageLayoutManager.makeNewPage (PageLayoutManager.java:441)
+ [15] org.apache.fop.layoutmgr.PageLayoutManager.doLayout (PageLayoutManager.java:191)
+</screen>
+
+The difference with the call stack for
+<literal>border-before-style</literal> is that
+<literal>border-before-width</literal> is a compound property, with
+a <literal>minimum</literal>, an <literal>optimum</literal> and a
+<literal>maximum</literal> value.
+</para>
+
+<para>The retrieval proceeds as follows:
+<itemizedlist spacing="compact">
+<listitem>
+<para><literal>PropertyList.findProperty</literal>: Is the value in
+the cache? No.</para>
+</listitem>
+ <listitem>
+<para><literal>PropertyMaker.findProperty</literal>:
+<literal>corresponding != null</literal>, but
+<literal>corresponding.isCorrespondingForced()</literal> returns
+<literal>false</literal>. The corresponding property is
+<literal>border-top-width</literal>, which is not specified:
+
+<screen>
+ org.apache.fop.fo.FOPropertyMapping.getPropertyName(correspondingId) = "border-top-width"
+</screen>
+</para>
+ </listitem>
+ <listitem>
+ <para><literal>PropertyMaker.findProperty</literal>: Is
+this property explicitly specified? Yes. The property value is
+retrieved as:
+<screen>
+ p = "CondLength[2000mpt]"
+</screen>
+The specified value was <literal>2pt</literal>. When this attribute
+value was added to the property list, it was converted to a
+<literal>CondLength</literal> type.
+</para>
+ </listitem>
+
+<listitem>
+<para><literal>PropertyList.findProperty</literal>: Add the value to
+the cache.</para>
+</listitem>
+ </itemizedlist>
+</para>
+ </section>
+
+ <section>
+ <title>Retrieving the <literal>border-end-width</literal>
+value on <literal>region-body</literal></title>
+
+<para>This what we are retrieving:
+
+<screen>
+ propertyList.getFOName() = "fo:region-body"
+ org.apache.fop.fo.FOPropertyMapping.getPropertyName(propId) = "border-end-width"
+</screen>
+
+The border values are retrieved by the method
+<literal>PropertyManager.getBorderAndPadding</literal>. The call stack
+that leads up to it and on to the retrieval of the value of
+<literal>border-end-width</literal> is identical to the call stack for
+<literal>border-before-width</literal>.
+</para>
+
+<para>The retrieval proceeds as follows:
+<itemizedlist spacing="compact">
+<listitem>
+<para><literal>PropertyList.findProperty</literal>: Is the value in
+the cache? No.</para>
+</listitem>
+<listitem>
+<para><literal>PropertyMaker.findProperty</literal>:
+<literal>corresponding != null</literal>, and
+<literal>corresponding.isCorrespondingForced()</literal> returns
+<literal>true</literal>. The corresponding property is
+<literal>border-right-width</literal>, which is explicitly specified:
+
+<screen>
+ org.apache.fop.fo.FOPropertyMapping.getPropertyName(correspondingId) = "border-right-width"
+</screen>
+</para>
+ </listitem>
+<listitem>
+<para><literal>PropertyMaker.findProperty</literal>: Compute the
+corresponding property value. It is retrieved as:
+<screen>
+ p = "CondLength[discard, 4000mpt]"
+</screen>
+The specified value was <literal>4pt</literal>. When this attribute
+value was added to the property list, it was converted to a
+<literal>CondLength</literal> type.</para>
+ </listitem>
+<listitem>
+<para><literal>PropertyList.findProperty</literal>: Add the value to
+the cache.</para>
+</listitem>
+ </itemizedlist>
+</para>
+ </section>
+
+ </section>
+
+ </section>
+
+<!--
+The implementation before bug 26778: Properties leader-length
+
+leader-length.maximum="30%"
+leader-length.minimum="20%"
+leader-length.optimum="25%"
+
+basePropertyName = "leader-length"
+subPropertyName = "maximum"
+propId = 124
+subpropId = 2560
+
+main[1] where
+ [1] org.apache.fop.fo.flow.Leader.getLength (Leader.java:167)
+ [2] org.apache.fop.layoutmgr.AddLMVisitor.getLeaderAllocIPD (AddLMVisitor.java:361)
+ [3] org.apache.fop.layoutmgr.AddLMVisitor$2.getAllocationIPD (AddLMVisitor.java:342)
+ [4] org.apache.fop.layoutmgr.LeafNodeLayoutManager.getNextBreakPoss (LeafNodeLayoutManager.java:166)
+ [5] org.apache.fop.layoutmgr.LineLayoutManager.getNextBreakPoss (LineLayoutManager.java:215)
+ [6] org.apache.fop.layoutmgr.BlockLayoutManager.getNextBreakPoss (BlockLayoutManager.java:240)
+ [7] org.apache.fop.layoutmgr.FlowLayoutManager.getNextBreakPoss (FlowLayoutManager.java:111)
+ [8] org.apache.fop.layoutmgr.PageLayoutManager.getNextBreakPoss (PageLayoutManager.java:261)
+ [9] org.apache.fop.layoutmgr.PageLayoutManager.doLayout (PageLayoutManager.java:226)
+
+propId = Constants.PR_LEADER_LENGTH | Constants.CP_MAXIMUM
+ = 2684 = 124 + 2560
+
+In this older implementation the base value is obtained from the
+layout context and propagated to the calculation:
+
+LeafNodeLayoutManager.getNextBreakPoss:
+ ipd = getAllocationIPD(context.getRefIPD());
+AddLMVisitor.getLeaderAllocIPD:
+ node.getLength(Constants.PR_LEADER_LENGTH | Constants.CP_MAXIMUM, ipd);
+Leader.getLength:
+ length = (int)(((PercentLength) maxlength).value() * dim);
+
+This is wrong because it does not find out at which node the property
+is specified and from which node the base length should be
+obtained. In the new implementation LengthBase contains this
+information as member parentFO:
+main[1] dump maxlength
+ maxlength = {
+ factor: 0.3
+ lbase: instance of org.apache.fop.datatypes.LengthBase(id=773)
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+main[1] dump maxlength.lbase
+ maxlength.lbase = {
+ parentFO: instance of org.apache.fop.fo.flow.Block(id=776)
+ propertyList: instance of org.apache.fop.fo.PropertyList(id=777)
+ iBaseType: 3
+}
+
+Result:
+ipd = "MinOptMax: min=67520; opt=84400; max=101280"
+-->
+
+ <section>
+ <title>Percent-based and mixed property values</title>
+
+ <section>
+ <title>Overview</title>
+
+ <para>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.</para>
+
+ <para>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 <literal>percentBase</literal>. This is set when the
+maker is created, see <literal>FOPropertyMapping</literal>.</para>
+
+ <para>When the maker creates a relative property value object,
+it stores its own member <literal>percentBase</literal> in the
+property as member <literal>iBaseType</literal>. 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.</para>
+
+ <para>The possible values of the member
+<literal>percentBase</literal> are listed in class
+<literal>LengthBase</literal>. Both the interface
+<literal>PercentBase</literal> and the class
+<literal>LengthBase</literal> 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 <literal>PercentBase.XXX</literal>
+types name the stored values and the <literal>LengthBase.XXX</literal>
+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, <literal>margin-top</literal>
+should really use an algorithm like
+<literal>BLOCK_IPD_OR_PAGEHEIGHT</literal>, which would look for
+either <literal>PercentBase.BLOCK_IPD</literal> or
+<literal>PercentBase.PAGE_HEIGHT</literal>, depending on the FO
+element.</para>
+
+ <para>A <literal>LengthBase</literal> object contains a
+reference to a parent FO node and a reference to a property list. In
+this manner the <literal>LengthBase</literal> 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.</para>
+
+ <para>The method <literal>LengthBase.getBaselength</literal>
+uses the base type, member <literal>iBaseType</literal>, 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.</para>
+
+ <para>The interface <literal>Numeric</literal>, which is
+implemented by all classes that can participate in numeric operations,
+uses the notion of <literal>dimension</literal>, which denotes the
+type of numeric: for integers <literal>dimension = 0</literal>, for
+lengths <literal>dimension = 1</literal>.</para>
+
+ <para>The constructor of
+<literal>RelativeNumericProperty</literal> calculates the dimension of
+the new property value object as a combination of the dimensions of
+the operands, depending on the operation:
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>multiplication adds dimensions: 0+0=0, 0+1=1+0=1,</para>
+ </listitem>
+ <listitem>
+ <para>division subtracts dimensions: 0-0=0, 1-0=1, 1-1=0,</para>
+ </listitem>
+ <listitem>
+ <para>other (addition, subtraction) does not change the
+dimensions</para>
+ </listitem>
+ </itemizedlist></para>
+
+ <para>A <literal>RelativeProperty</literal> contains the
+operation between the operands in its member
+<literal>operation</literal>. It is an integer. Names for the possible
+integer values are listed as static members of class
+<literal>RelativeProperty</literal>.</para>
+
+ <para>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
+<literal>getValue</literal> of <literal>Property</literal> and its
+subclasses.</para>
+
+ <para>A <literal>RelativeNumericProperty</literal> is resolved
+as follows: <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>RelativeNumericProperty.getValue</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>RelativeNumericProperty.getNumericValue</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>RelativeNumericProperty.getResolved</literal>.
+This evaluates the expression tree by recursion. The nodes in the tree
+are <literal>RelativeNumericProperty</literal> objects. The leaf nodes
+are <literal>FixedLength</literal> and
+<literal>PercentLength</literal> objects. On each node
+<literal>getNumericValue</literal> is called. <itemizedlist
+spacing="compact">
+ <listitem>
+ <para>The relative numeric property
+values call <literal>getResolved</literal> again, which descends the
+tree and calls <literal>getNumericValue</literal> on its child
+nodes.</para>
+ </listitem>
+ <listitem>
+ <para>The fixed lengths return their
+millipoints.</para>
+ </listitem>
+ <listitem>
+ <para>The percent lengths return
+<literal>factor * lbase.getBaseLength()</literal> <itemizedlist
+spacing="compact">
+ <listitem>
+ <para><literal>factor</literal> is
+a member of the percent length object.</para>
+ </listitem>
+ <listitem>
+ <para><literal>LengthBase.getBaselength</literal>
+gets the base length from the parent FO <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>FObj.getLayoutDimension</literal>.
+The value is retrieved from the <literal>Map
+layoutDimension</literal>. If that does not contain the desired layout
+dimension, then its parent is consulted, all the way up to
+<literal>fo:root</literal>.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+
+ </section>
+
+ <section>
+ <title>Parsing a mixed property value</title>
+
+ <para>Example: <literal>&lt;fo:region-body
+margin="4pt+20%"/></literal>, property being parsed:
+<literal>margin="4pt+20%"</literal>, <literal>propId</literal> =
+134.</para>
+
+<para><literal>PropertyParser.parseProperty</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>next()</literal>, which scans the next token;
+at its return:
+<screen>
+ this = {
+ propInfo: instance of org.apache.fop.fo.expr.PropertyInfo(id=736)
+ org.apache.fop.fo.expr.PropertyTokenizer.currentToken: 12
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenValue: "4pt"
+ org.apache.fop.fo.expr.PropertyTokenizer.currentUnitLength: 2
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenStartIndex: 0
+ org.apache.fop.fo.expr.PropertyTokenizer.expr: "4pt+20%"
+ org.apache.fop.fo.expr.PropertyTokenizer.exprIndex: 3
+ org.apache.fop.fo.expr.PropertyTokenizer.exprLength: 7
+ org.apache.fop.fo.expr.PropertyTokenizer.recognizeOperator: true
+}
+</screen>
+i.e. the whole expression is a single token, it is of type
+<literal>PropertyTokenizer.TOK_NUMERIC</literal> (= 12), the unit is
+2 chars long.</para>
+ </listitem>
+ <listitem>
+ <para>Loop forever. <itemizedlist spacing="compact">
+ <listitem>
+ <para>Analyse the expression. Start with
+<literal>parseAdditiveExpr</literal> <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>parseMultiplicativeExpr</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>parseUnaryExpr</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>parsePrimaryExpr</literal>;
+the unit may be a relative unit (em) which must be resolved against
+the font size <itemizedlist spacing="compact">
+ <listitem>
+ <para>construct a property
+value object:
+<screen>
+prop = "4000mpt"
+prop.getClass() = "class org.apache.fop.fo.properties.FixedLength"
+</screen></para>
+ </listitem>
+ <listitem>
+ <para><literal>next()</literal>:
+scan for the next token;
+<screen>
+ this = {
+ propInfo: instance of org.apache.fop.fo.expr.PropertyInfo(id=736)
+ org.apache.fop.fo.expr.PropertyTokenizer.currentToken: 8
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenValue: null
+ org.apache.fop.fo.expr.PropertyTokenizer.currentUnitLength: 2
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenStartIndex: 3
+ org.apache.fop.fo.expr.PropertyTokenizer.expr: "4pt+20%"
+ org.apache.fop.fo.expr.PropertyTokenizer.exprIndex: 4
+ org.apache.fop.fo.expr.PropertyTokenizer.exprLength: 7
+ org.apache.fop.fo.expr.PropertyTokenizer.recognizeOperator: false
+}
+</screen>
+the next token is of type <literal>currentToken =
+PropertyTokenizer.TOK_PLUS</literal>.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para><literal>next()</literal>:
+scan for the next token;
+<screen>
+ this = {
+ propInfo: instance of org.apache.fop.fo.expr.PropertyInfo(id=736)
+ org.apache.fop.fo.expr.PropertyTokenizer.currentToken: 14
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenValue: "20%"
+ org.apache.fop.fo.expr.PropertyTokenizer.currentUnitLength: 2
+ org.apache.fop.fo.expr.PropertyTokenizer.currentTokenStartIndex: 4
+ org.apache.fop.fo.expr.PropertyTokenizer.expr: "4pt+20%"
+ org.apache.fop.fo.expr.PropertyTokenizer.exprIndex: 7
+ org.apache.fop.fo.expr.PropertyTokenizer.exprLength: 7
+ org.apache.fop.fo.expr.PropertyTokenizer.recognizeOperator: true
+}
+</screen>
+the next token is of type <literal>currentToken =
+PropertyTokenizer.TOK_PERCENT</literal>.
+The currentTokenValue <literal>20%</literal> is analysed:</para>
+ </listitem>
+ <listitem>
+ <para><literal>parseMultiplicativeExpr</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>parseUnaryExpr</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>parsePrimaryExpr</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>propInfo.getPercentBase</literal>:
+create a PercentBase property
+<itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>propInfo.getFunctionPercentBase</literal>
+uses a stack of functions stkFunction, which currently <literal>==
+null</literal></para>
+ </listitem>
+ <listitem>
+ <para><literal>if (null)
+maker.getPercentBase(fo, plist)</literal>: create and return a
+<literal>LengthBase</literal> property, which implements
+<literal>PercentBase</literal>
+<screen>
+ pcBase = "org.apache.fop.datatypes.LengthBase@171f189"
+ pcBase = {
+ parentFO: instance of org.apache.fop.fo.pagination.SimplePageMaster(id=786)
+ propertyList: instance of org.apache.fop.fo.PropertyList(id=807)
+ iBaseType: 5
+}
+</screen>
+the value of <literal>iBaseType</literal> is derived from
+<literal>maker.percentBase == 5 ==
+LengthBase.BLOCK_WIDTH</literal>.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para><literal>pcBase.getDimension</literal>;
+dimension: type of integer, int &rarr; 0, length &rarr; 1; used by
+<literal>PercentBase</literal> and <literal>NumericProperty</literal>;
+<literal>LengthBase</literal> has a dimension of 1</para>
+ </listitem>
+ <listitem>
+ <para>create a
+<literal>PercentLength(pcval, pcBase)</literal>:
+<screen>
+ prop = "20.0%"
+ prop = {
+ factor: 0.2
+ lbase: instance of org.apache.fop.datatypes.LengthBase(id=751)
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+</screen>
+<literal>factor</literal> comes from <literal>pcval</literal>,
+<literal>lbase = pcBase</literal></para>
+ </listitem>
+ <listitem>
+ <para><literal>next()</literal>:
+scan for the next token; the next token is of type
+<literal>currentToken = PropertyTokenizer.TOK_EOF</literal>.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>return the <literal>LengthBase</literal>
+property value object</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para><literal>evalAddition(NumericProperty op1,
+NumericProperty op2)</literal>. <literal>op1</literal> and
+<literal>op2</literal> are now <literal>Numeric</literal>, which is an
+interface implemented by <literal>LengthProperty</literal>, of which
+<literal>FixedLength</literal> and <literal>PercentLength</literal>
+are subclasses:
+<screen>
+op1 = instance of org.apache.fop.fo.properties.FixedLength(id=744)
+op2 = instance of org.apache.fop.fo.properties.PercentLength(id=757)
+</screen> <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>NumericOp.addition</literal>
+<itemizedlist spacing="compact">
+ <listitem>
+ <para>Construct a new
+<literal>RelativeNumericProperty</literal> by adding the two
+properties:
+<literal>RelativeNumericProperty(RelativeNumericProperty.ADDITION,
+op1, op2)</literal></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ <listitem>
+ <para>If <literal>currentToken =
+PropertyTokenizer.TOK_EOF</literal>, break the loop.</para>
+ </listitem>
+ </itemizedlist></para>
+ </listitem>
+ </itemizedlist>
+return the <literal>RelativeNumericProperty</literal>:
+<screen>
+ prop = "(4000mpt +20.0%)"
+ prop = {
+ operation: 1
+ op1: instance of org.apache.fop.fo.properties.FixedLength(id=744)
+ op2: instance of org.apache.fop.fo.properties.PercentLength(id=757)
+ dimension: 1
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+</screen>
+The value 1 for the operation corresponds to
+<literal>RelativeProperty.ADDITION</literal>. The value 1 for the
+dimension indicates that this is a length. PropertyList:
+<screen>
+ this = "{margin=[(4000mpt +20.0%)]}"
+</screen></para>
+ </section>
+
+ <section>
+ <title>Resolving a mixed property value</title>
+
+ <para>Example: Resolving the value of the property
+<literal>margin-left</literal> of the FO node
+<literal>fo:region-body</literal>. This value was specified as
+<literal>&lt;fo:region-body margin="4pt+20%"/></literal>.
+<literal>margin</literal> is a shorthand property for
+<literal>margin-left</literal>.</para>
+
+<para>The property list of <literal>fo:region-body</literal> reads:
+<screen>
+ this = "{margin=[(4000mpt +20.0%)]}"
+</screen>
+The retrieved property value is a
+<literal>RelativeNumericProperty</literal>:
+<screen>
+ prop = "(4000mpt +20.0%)"
+ prop = {
+ operation: 1
+ op1: instance of org.apache.fop.fo.properties.FixedLength(id=817)
+ op2: instance of org.apache.fop.fo.properties.PercentLength(id=818)
+ dimension: 1
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+</screen>
+The value 1 for the operation corresponds to
+<literal>RelativeProperty.ADDITION</literal>. The value 1 for the
+dimension indicates that this is a length.
+<screen>
+ op2 = "20.0%"
+ op2 = {
+ factor: 0.2
+ lbase: instance of org.apache.fop.datatypes.LengthBase(id=751)
+ org.apache.fop.fo.properties.Property.specVal: null
+}
+ lbase = "org.apache.fop.datatypes.LengthBase@171f189"
+ lbase = {
+ parentFO: instance of org.apache.fop.fo.pagination.SimplePageMaster(id=786)
+ propertyList: instance of org.apache.fop.fo.PropertyList(id=807)
+ iBaseType: 5
+}
+</screen>
+</para>
+
+<para>The <literal>RelativeNumericProperty</literal> is resolved in
+the method call
+<screen>
+ props.marginLeft =
+ this.propertyList.get(PR_MARGIN_LEFT).getLength().getValue();
+</screen>
+in <literal>PropertyManager.getMarginProps()</literal>.
+<literal>getLength()</literal> is a sort of cast; it returns the
+property value if it is a length. The <literal>getValue()</literal>
+method invoked is
+<literal>RelativeNumericProperty.getValue()</literal>.
+This calls
+<literal>RelativeNumericProperty.getNumericValue()</literal>, which
+calls <literal>RelativeNumericProperty.getResolved()</literal>. This
+invokes the operation on its two operands,
+<literal>NumericOp.addition2</literal>, which invokes
+<literal>getNumericValue()</literal> on each
+operand. <literal>PercentLength.getNumericValue()</literal> calls
+<literal>LengthBase.getBaseLength</literal> on its member
+<literal>lbase</literal>.</para>
+
+<para>Due to its value <literal>iBaseType == 5 ==
+LengthBase.BLOCK_WIDTH</literal> this invokes
+<literal>parentFO.getLayoutDimension(PercentBase.BLOCK_IPD).intValue()</literal>
+on its parent FO. The simple page master FO node does not have any
+layout dimensions, its member <literal>layoutDimension</literal> is
+<literal>null</literal>. Therefore it consults its parent FO. This
+goes all the way up to the root FO node.</para>
+
+<para>The root FO node does have the required layout dimensions, which
+are the page dimensions. These have been set on it
+by the <literal>PageLayoutManager</literal> when the page was created
+in its
+method <literal>createPageAreas</literal>:
+<screen>
+((FObj) fobj.getParent()).setLayoutDimension(PercentBase.BLOCK_IPD,pageWidth)
+((FObj) fobj.getParent()).setLayoutDimension(PercentBase.BLOCK_BPD,pageHeight)
+PercentBase.BLOCK_IPD = 2, PercentBase.BLOCK_BPD = 3
+</screen>
+As a result:
+<screen>
+ layoutDimension = "{2=576000, 3=792000}"
+ key = "2"
+ getName() = "fo:root"
+</screen>
+</para>
+
+<screen>
+ [1] org.apache.fop.fo.FObj.getLayoutDimension (FObj.java:241)
+ [2] org.apache.fop.datatypes.LengthBase.getBaseLength (LengthBase.java:120)
+ [3] org.apache.fop.fo.properties.PercentLength.getNumericValue (PercentLength.java:82)
+ [4] org.apache.fop.fo.expr.NumericOp.addition2 (NumericOp.java:52)
+ [5] org.apache.fop.fo.expr.RelativeNumericProperty.getResolved (RelativeNumericProperty.java:105)
+ [6] org.apache.fop.fo.expr.RelativeNumericProperty.getNumericValue (RelativeNumericProperty.java:132)
+ [7] org.apache.fop.fo.expr.RelativeNumericProperty.getValue (RelativeNumericProperty.java:170)
+ [8] org.apache.fop.fo.PropertyManager.getMarginProps (PropertyManager.java:267)
+</screen>
+
+<para><literal>PercentLength.getNumericValue()</literal> returns the
+page width, which is multiplied by the requested factor of 0.2, and
+added to the value of the fixed length, 4000. The resulting value is
+returned and used for the variable <literal>marginLeft</literal>:
+
+<screen>
+ value = 119200.0
+ props.marginLeft = 119200
+</screen>
+</para>
+
+ </section>
+
+ </section>
+
+</chapter>
+
+<!-- Local Variables: -->
+<!-- current-language-environment: UTF-8 -->
+<!-- coding: utf-8 -->
+<!-- default-input-method: TeX -->
+<!-- End: -->