diff options
Diffstat (limited to 'src/documentation/content/xdocs/DnI/properties.xml')
-rw-r--r-- | src/documentation/content/xdocs/DnI/properties.xml | 2455 |
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 <=> property ID => property maker + | | + s_htSubPropNames (<-) s_htGeneric + s_htPropIds (->) +</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 << COMPOUND_SHIFT; + int CP_MINIMUM = 6 << COMPOUND_SHIFT; + int CP_OPTIMUM = 7 << 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><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 && +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 && 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><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 → 0, length → 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><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: --> |