SourceForge.net Logo Home Page Project Page Download CVS repository
Expressions Formulas

Expressions and Formulas

The TrueType engine is capable of performing simple arithmetical operations, and it is easy to combine these to perform more complex operations. You must always remember, however, that TrueType arithmetic operates on F26dot6 fixed-point numbers, and all operations return the same kind of fixed-point numbers. There is no way to obtain results with higher precision. Thus certain kinds of calculations are impossible in TrueType, and you must always be careful, when combining the operations that TrueType can do, to consider the limited precision of the intermediate results that get passed from operation to operation.

There are two ways to combine operations in Xgridfit. One is to write expressions rather like the expressions used in other programming languages (though fewer operators are available); the other is to use <formula> elements, within which operations can be chained.

Expressions

Expressions in Xgridfit resemble expressions in other programming languages: they consist of numbers and identifiers coordinated with operators; they can be simple (e.g. "bottom-pt + 1") or complex; Xgridfit parses them according to certain rules of precedence which are worth knowing; and the rules of precedence may be overridden by using parentheses, which can be nested.

There is little point in describing the syntax of expressions in detail, since they are so familiar to everyone who has done any programming; instead this section will list the operators, note a few peculiarities, and present some examples

Operators
First precedence
andLogical and. Example: pixels-per-em &gt; 10 and pixels-per-em &lt; 20
orLogical or. Example: pixels-per-em &lt; 10 or pixels-per-em &gt; 20
Second precedence
=Equals. Example: pixels-per-em = 15
&gt;Greater than. Example: control-value(lc-vert-stem) &gt; 1p
&lt;Less than. Example: control-value(lc-vert-stem) &lt; 1p
&gt;=Greater than or equal. Example (where v has previously been declared as a variable): v &gt;= 0.35
&lt;=Less than or equal. Example: v &lt;= 0.35
!=Not equal. Example: round(control-value(left-side)) != control-value(left-side)
Third precedence
+Addition. Example: top-point + 1
-Subtraction. Example: top-point - 1
*Multiplication. Example: lc-vert-stem * 1.2
/Division. Example: lc-vert-stem / 2.0
Fourth precedence
-- Treats the arguments on both sides of the operator as point numbers and returns the current distance (in pixels) between them, as measured on the projection vector. Ordinarily the argument on the left-hand side of the operator should be the point on the left or bottom; reverse the numbers to change the sign of the result.
Example: round(stem-left -- stem-right)
--- Like --, but returns the distance between points in the original outline.
Example: absolute((stem-left --- stem-right) - (stem-left -- stem-right))
Fifth precedence
oddTrue if the argument is odd. Example: odd(v)
evenTrue if the argument is even. Example: even v
notReverses the boolean value of the argument. Example: not(v &gt; 4.0)
floorThe greatest integer value less than the argument. Example: floor(control-value(lc-vert-stem))
ceilingThe smallest integer value greater than the argument. Example: ceiling(control-value(lc-vert-stem) / 2)
absoluteThe absolute value of the argument. Example: absolute(control-value(lc-vert-stem))
negativeThe negation of the argument. Example: negative(v)
round The argument rounded according to the current round state. The "color" is the <default> set with type="color" (gray if not set). Example: round(control-value(lc-vert-stem))
round-gray The argument rounded according to the current round state. The "color" is gray. Example: round-gray(control-value(lc-vert-stem))
round-black The argument rounded according to the current round state. The "color" is black. Example: round-black(control-value(lc-vert-stem))
round-white The argument rounded according to the current round state. The "color" is white. Example: round-white(control-value(lc-vert-stem))
indexReturns an index of (pointer to) a control value or variable. Example: index(lc-vert-stem)
control-value The control value at the index represented by the argument. Example: control-value(lc-vert-stem)
coord The current coordinate (x or y depending on the projection vector) of a point. Example: coord(stem-left)
initial-coord Like coord, but returns the original coordinate of a point: that is, its coordinate at the beginning of the glyph program. Example: initial-coord(bottom-point + 1)
x-coord Like coord, but always returns the current coordinate of a point on the x axis. The setting of the projection vector is the same after the operation as before. coord is more efficient than this and the following three operators when the coordinate of a point on the current projection vector is needed.
y-coord Like coord, but always returns the current coordinate of a point on the y axis. The setting of the projection vector is the same after the operation as before.
initial-x-coord Like initial-coord, but always returns the original coordinate of a point on the x axis. The setting of the projection vector is the same after the operation as before.
initial-y-coord Like initial-coord, but always returns the original coordinate of a point on the y axis. The setting of the projection vector is the same after the operation as before.
variable Treats the argument as an index of (pointer to) a variable and returns its value. Example: variable(v)
nan Returns true (1) if the argument cannot be resolved to a number at compile-time (e.g. it is the name of a variable, or the name of a <range>). If the argument is a number, returns false (0). Example: nan(v)
point Causes the compiler to regard the argument as a point number. In a <glyph> program with an offset parameter, the offset is automatically added to the argument. Use this operator in contexts where the compiler does not automatically recognize a number as a point number. Do not use it in the num attribute of a <point> element, as this will cause the offset to be added twice. Example: point(a) -- point(b)

Here is an example of precedence:

     <if test="pixels-per-em &lt; 10 or pixels-per-em &gt; 20 and
          round-state = to-grid">

Xgridfit breaks the expression at the "or" (which has the same precedence as the "and" but occurs farther to the left); it evaluates first "pixels-per-em &lt; 10", second "pixels-per-em &gt; 20 and round-state = to-grid", and finally executes OR on the two values. If that is not what you want, you may use parentheses to alter the order in which constituents are evaluated:

    <if test="(pixels-per-em &lt; 10 or pixels-per-em &gt; 20) and
          round-state = to-grid">

Now Xgridfit breaks the expression at the "and" and evaluates everything to the left of it (inside the parentheses), then everything to the right of it, and finally executes AND.

Fifth-precedence operators are all unary: they operate on a single value. If this is a simple value it may be separated from the operator by a space; if it is an expression it must be enclosed in parentheses.

Binary operators (those that operate on two values) must always be surrounded by whitespace. This will not work:

     <point num="top+2"/>

It must be like this:

     <point num="top + 2"/>

However, the whitespace may be any number of spaces, tabs, a line break, and so on, for the spacing of an expression is always normalized before it is evaluated.

Note that when all of the values in an expression are number literals, constants or other identifiers that can be resolved to numbers at compile time, and the only operators are first- or second-precedence operators and the arithmetic operators "+" and "-", Xgridfit resolves the whole expression to a single number at compile time. This optimizes the most common cases, where a point number is expressed by addition to or subtraction from a constant.

Formulas

A <formula> is a list of arithmetical operations whose result can be assigned to a variable, a control value, or any of those graphics variables that can be written to. The format is like this:

     <formula result-to="minimum-distance">
       <divide dividend="minimum-distance" divisor="2.0"/>
       <add value1="0.5"/>
     </formula>

The <formula> may contain any of the arithmetic elements: <add>, <subtract>, <divide>, <multiply>, <absolute>, <negate>, <floor>, <ceiling>, <minimum>, <maximum>, <round>. These can also occur outside the <formula>, but they behave differently there. When one of these elements outside the formula lacks a result-to attribute, Xgridfit tries to write the result back to one of the operands; failing that, it leaves the result on the run-time stack and prints a warning. But within the <formula> no attempt is made to write the result to one of the operands, and no warning is printed when the result is left on the stack; instead, it is assumed that the next arithmetic element will take one or both of its operands from the stack.

In the example above, the current minimum distance is divided by two, and the result of that operation is passed to the <add> element, where 0.5 is added to this new value; then the sum is passed back to the <formula> element, which writes it to the minimum-distance graphics variable, thus setting a new minimum distance.

The <formula> element was added to Xgridfit before the expression feature, and it looks rather clunky in comparison. The same operation could be performed more tersely thus:

     <set-minimum-distance value="(minimum-distance / 2.0) + 0.5"/>

and the code generated by Xgridfit would be the same.