xgridfit
Author: Peter S. Baker

Home Page Project Page Download CVS repository

<if> <compile-if> Looping

# Conditional Execution and Looping

## The <if> element

Xgridfit's mechanism for executing code conditionally is simple: the <if> element contains code that is executed if the expression in the `test` attribute evaluates as true (non-zero):

        <if test="pixels-per-em &lt; 20">
          <!-- execute code -->
        </if>

The <if> element may also contain an <else> element at the end; this is executed only if `test` evaluates as false (zero):

        <if test="pixels-per-em &lt; 20">
          <!-- do this if test is true -->
          <else>
            <!-- do this if test is false -->
          </else>
        </if>

Xgridfit's <if> element resembles the XSLT's <if> (though <else> is an addition). Remember, when writing the `test` attribute, that Xgridfit requires operators in expressions to be surrounded by whitespace.

You should also be aware that the TrueType engine frequently executes code conditionally even without the explicit use of <if>. The control value cut-in is consulted to determine whether to use a measurement from the original outline or one from the Control Value Table in moving points, and the (apparently) little-used single-width cut-in is similar. Every <delta-set> represents code that is executed conditionally--a point is moved only at a particular resolution.

## The <compile-if> element

The <compile-if> element provides for conditional compilation: the code inside the element is compiled only if the expression in the `test` attribute evaluates to "true" (non-zero) at compile time. The `test` attribute may contain only expressions that can be evaluated at compile time: constants, number literals, control values (indexes only). Only a few operators are permitted: `+ - = != &gt; &lt; &gt;= &lt;= or and not`.

<compile-if> is allowed anywhere that other programming is allowed (except within a <move> element), and can contain any other programming (including other <compile-if> elements). Here's a simple example:

        <macro name="anchor-to-baseline">
          <param name="pt"/>
          <param name="pt2" value="-1"/>
          <param name="pt3" value="-1"/>
          <move distance="baseline" round="no" cut-in="no">
            <point num="pt"/>
          </move>
          <compile-if test="pt2 &gt;= 0">
            <align>
              <point num="pt2"/>
            </align>
          </compile-if>
          <compile-if test="pt3 &gt;= 0">
            <align>
              <point num="pt3"/>
            </align>
          </compile-if>
        </macro>

This macro anchors one, two or three points to the baseline so that they won't be moved later. The param "pt" is required: it is a point that is always moved by the first "move" element. Params "pt2" and "pt3" are optional; they have impossible default values. But if reasonable point numbers are passed to the macro for "pt2" and "pt3" then the two "align" elements are compiled. Here are a couple of calls to the macro:

          <call-macro name="anchor-to-baseline">
            <with-param name="pt" value="bottom-left"/>
          </call-macro>

          <call-macro name="anchor-to-baseline">
            <with-param name="pt" value="bottom-left"/>
            <with-param name="pt2" value="bottom-mid"/>
            <with-param name="pt3" value="bottom-right"/>
          </call-macro>

Another way to compile code conditionally is to include the `compile-if` attribute on a <move>, <align>, <interpolate>, <shift> or <delta> element. These are the elements that can be nested inside a <move>, where the <compile-if> element is not allowed. Using this attribute, we can rewrite the "anchor-to-baseline" macro as follows:

        <macro id="anchor-to-baseline">
          <param name="pt"/>
          <param name="pt2" value="-1"/>
          <param name="pt3" value="-1"/>
          <move distance="baseline" round="no" cut-in="no">
            <point num="pt"/>
    	<align compile-if="pt2 &gt;= 0 and pt3 &lt; 0">
    	  <point num="pt2"/>
    	</align>
    	<align compile-if="pt2 &gt;= 0 and pt3 &gt;= 0">
    	  <point num="pt2"/>
    	  <point num="pt3"/>
    	</align>
          </move>
        </macro>

The advantage of this method is that it enables the Good Programming Practice of building blocks of code around the visible features of a glyph while still permitting conditional compilation. It also produces slightly more compact and efficient code than the earlier example. The disadvantage is that it is somewhat more awkward to write three different <align> elements. However, it may well be worth doing so in a macro.

You may include, as the last child of a <compile-if> element, an <else> element containing code to compile if `test` evaluates as false. This works the same way as the <else> clause in an <if> element.

## Looping

Xgridfit does not have an explicit looping mechanism such as the for-loop of C and other languages. Certain TrueType instructions can loop through a collection of points; Xgridfit exploits this capability and extends it by allowing more than one <point> or <range> element to be included in an <align>, <shift>, <interpolate>, or <toggle-points> element (the <range> element is itself a looping mechanism). Xgridfit exploits other looping mechanisms of TrueType when it allows more than one <delta-set> in a <delta> or <control-value-delta> element and more than one <param-set> in a <call-function> element.