How GX does Justification
Introduction
The justification process in GX is divided into several phases: the
factor phase, the assignment phase, and the postcomp phase. Note that some
of the justification actions may take place even on unjustified lines of
text: for example, if loose tracking is applied to a line, enough letterspacing
may be added so that ligatures should be broken apart. Ligature decomposition
happens in the postcomp phase, so this phase needs to happen even if the
line itself is not justified.
Irrespective of justification, a line of text has a "natural"
width, called the unjustified width, which is roughly the sum of the widths
of all the glyphs making up the line. I say "roughly" here, since
there are other factors taken into account in GX typography which affect
this width. For instance, the line may start with a hung punctuation glyph;
in this case, the width of that glyph is not counted in the line's unjustified
width. Similarly, any kerning, tracking or manual letterspacing the user
has specified alter the effective widths of glyphs, and thus the total
unjustified width.
The State Table Phase
Before doing any processing on a line, GX first sees if the justification
table contains a state table. If it does, then that state table is processed
to determine the justification class for each glyph on the line. In all
the following phases, this class (which is just a number) is used in addition
to the glyphcode to look up values or determine what actions are taken.
By allowing a state table to assist with the processing, it becomes possible
to make the same glyph have different justification characteristics in
different parts of the line.
The Factor Phase
The factor phase begins by computing the unjustified width of the line,
and sees what justified width the user has specified. The unjustified width
is subtracted from the justified width to get the base gap that needs to
be filled. For example if the unjustified width of the line is 300 points,
and the justified width is specified as being 450 points, then the base
gap is +150 points. When the base gap is positive, it is called the grow
case, because the line needs to grow to fill the gap. When the base gap
is negative, it is called the shrink case. In this case, the line needs
to be squeezed to fit the specified width.
In either the grow or shrink cases, the user may not want all the gap
to be filled; an example of this is specifying, say, an 80% ragged right
edge. In GX this is done by setting the justification for the line to some
value other than 0 or 1. The base gap is adjusted by one minus this value,
resulting in the final gap to be filled.
Once the final gap is calculated, GX consults with the justification
table in the font, as well as any user overrides, to determine how much
distance is available on each side of each glyph for justification purposes.
This distance is specified in ems -- that is, in points for one-point text.
This gets multiplied by the pointsize of the actual glyphs to get the actual
available distance. The resulting numbers (one for the left side of each
glyph and one for the right side) are called the justification factors.
To take an example, if the font designates a left-side factor of 0.2 and
a right-side factor of 0.3 for a particular glyph, then that glyph at 10-point
will have available (0.2 times 10) or 2 points on the left and (0.3 times
10) or 3 points on the right. Note that GX doesn't do anything with this
distance yet; it represents a potential, a means of helping fill the final
gap.
As well as determining the justification factors, the factor phase also
determines two other critical pieces of information for each glyph on the
line: the justification priority, and the unlimited status. The justification
priority controls the order in which glyphs will be changed later in the
assignment phase. There are four priorities, called by convention the kashida
priority, whitespace priority, intercharacter priority, and the null priority,
with kashida priority the highest and null priority the lowest. All glyphs
of a higher priority will be adjusted before any glyphs of a lower priority
are considered. If a glyph has the unlimited status, then that glyph is
permitted to absorb all of the remaining gap, even in excess of its justification
factors. If a line contains one or more unlimited glyphs of a given priority
level, then any glyphs of lower priority levels won't be touched during
the assignment phase.
The Assignment Phase
With these data (the left and right factors, the priority and the unlimited
status), the factor phase is finished and the assignment phase begins.
The assignment phase is needed to allow for cases where the total justification
factors available on the line exceed the needed final gap, or are insufficient
to cover that gap. The assignment phase starts at the highest priority
present on the line, adds up the total factors at that level, and sees
if that amount is enough to satisfy the final gap. If it is then the final
gap is apportioned proprtionally to the factors, and the assignment phase
is done. If there is still gap left over, then the next lowest priority
level is considered, and so on. If all the priority levels are used up,
and there is still gap left over (and there were no unlimited glyphs),
then the assignment phase goes back to the highest priority that was present
on the line and gives the remaining gap to those glyphs; note that this
will violate their factors, but this case only happens in extremis.
The Postcomp Phase
Once the factors have been apportioned in this way, the glyph positions
are modified accordingly. By positioning the glyphs further apart, whitespace
is introduced. In many cases this suffices; however, there are cases (like
connected scripts) where whitespace is not permitted between glyphs. These
cases are handled by the final phase, the postcomp phase. Here decisions
are made about creative alternatives to just separating glyphs by whitespace.
For example, an extra glyph can be inserted between two glyphs in order
to change the whitespace added into something else. A wider version of
a glyph can be substituted for an existing glyph to help swallow some of
the space. Ligatures can decompose if needed. A variation axis can be used
to change the widths of all the glyphs (called copyfitting).
Change History