|
|||||||||||||||
Using Justification Input FilesIntroductionOne of the tables added automatically by the AAT Font Tool controls how the glyphs in a font will look in justified lines of text. This table is called the Justification Table, and controls many different aspects of how the text will look when it is justified, including how much whitespace (if any) will be added between letters, whether certain glyphs should take up more space than others, how much space should be added before ligatures break apart, and so on. While the default behaviors which the AAT Font Tool adds are good enough in many cases, there may be times when you wish to specify particular behaviors. In order to do that, you prepare a special text file, called a "Justification Input File" (or JIF, for short). This tutorial will review how AAT justification works, and then describe how to make your own JIFs to allow your fonts to behave in different ways. How AAT Justification WorksBefore describing how to make a JIF, it will be useful to describe how AAT line layout does justification. This section describes this process, as well as defining some terminology which will be extensively used in later sections. AAT Line Layout happens in two broad passes. First, a nonpositional pass alters the identities of glyphs, fusing glyphs into ligatures or changing them into swashes. Second, a positional pass adjusts the positions of the glyphs with respect to one another. This positional pass comprises two parts: the non-justification part, and the justification part. In the non-justification part of the positioning pass, adjustments to glyph positions are made using kerning, tracking, baseline, optical edge and manual letterspacing controls. Once these are done, the justification part may do further processing. This justification part is itself subdivided 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 AAT 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 Factor PhaseNow let's look at the three phases of justification in more detail. First is the factor phase. This 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 AAT 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, AAT 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 AAT 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 PhaseWith 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 PhaseOnce 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). The Justification ClassOne of the most powerful aspects of AAT Line Layout is its ability to make decisions based on context. This means that the same glyph might behave differently in different contexts: for instance, a glyph at the end of the line or in a particular word might be changed into a swash variant glyph. This ability to make decisions based on context is present for justification as well. In the factor phase and the postcomp phase, the values associated with a particular glyph are looked up not only by glyph index but also by a contextually determined value called the justification class. This value is a number from 0 to 127 which can be used to give the same glyph different behaviors. If you don't need contextual behavior in your justification table, you can just use a justification class of zero everywhere. If you wish to have contextual behaviors, then you can include a justification class state table in your font. Overall JIF FormatThe format of a JIF starts with a direction header line, and is followed by table parts which describe one or more of the factors, classes and postcomp actions. To get things started, let's look at a simple JIF: ----------------------------------------- Direction H TablePart Factors Whitespace space Interchar (all others) Interchar 0 BeforeGrow 0.05 BeforeShrink -0.02 AfterGrow 0.05 AfterShrink -0.02 GrowFlags intercharacterPriority ShrinkFlags whitespacePriority Whitespace 0 BeforeGrow 0.2 BeforeShrink -0.02 AfterGrow 0.2 AfterShrink -0.02 GrowFlags whitespacePriority unlimited ShrinkFlags whitespacePriority ----------------------------------------- The effect of this JIF is to divide the glyphs in the font into two categories: whitespace (the space glyph), and everything else. When space is added (the grow case), the space gets whitespace priority and everything else gets intercharacter priority. As described above, this means that any spaces on the line will be the first things to be expanded when the line is being justified. Furthermore, since the unlimited flag is also specified, whitespace will be the only thing expanded on the line; intercharacter spacing won't ever be used (unless the line contains no whitespace at all). The HeaderThe first line in a JIF is an indication of which direction the following table parts are for. The choices are "H" or "V" -- in our example, we're looking at the data for a horizontal justification table. You may include both directions in a single JIF: after the last table part for the first direction, include another "Direction" line with the other direction, followed by its table parts. Each table part starts with the "TablePart" identifier. There are three different table parts you may include in a JIF: Factors, Classes, and Postcomp. You should always include at least the Factors table part in your JIFs; the other two are optional, and less frequently used. Each table part is described in more detail in the following sections. You may use lines of all hyphens as separators in your JIF. You may include comments in your JIF by placing them between /* and */ delimiters, or at the end of a line after a // delimiter. The "Factors" table partThe "Factors" table part contains information describing the characteristics of glyphs when they are justified. It has two main pieces: the grouping piece and the values pieces. The grouping piece allows you to gather together glyphs which have the same behaviors, and to give a single name to this collection of glyphs. The name of the group appears in the leftmost column, and it is followed by a list of the members of the group, which may be specified by either their names or their glyph numbers. If your class is very long, you may continue it over multiple lines by including a plus-sign ('+') at the start of each continuation line, like this: ------------------------------------------------ UpperCaseLetters A B C D E F G H I J K L + M N O P Q R S T U V W X Y Z ------------------------------------------------ The values piece lets you associate justification values with the different groups you just defined. Each part of the values piece starts with the name of the group for which values are being specified in the leftmost column. This is followed on the same line by the justification class for which these values are to be used. There are six lines after this containing the actual values for this combination of group and justification class. The "Classes" table partThe concept of a justification class was described above in the description of how AAT justification works. You can specify a state table for the contextual determination of the justification class for a glyph in your JIF by included a "Classes" table part. To focus the discussion, let's look at an example of a state table used to give a justification class of zero to "fi" and "fl" ligatures which occur in words, and a justification class of one if these same ligatures appear by themselves (that is, surrounded by non letters): ------------------------------------------------ TablePart Classes Forward yes Liggie fi fl Letter A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d + e f g h i j k l m n o p q r s t u v w x y z Adieresis Aring + Ccedilla Eacute Ntilde Odieresis Udieresis aacute agrave + acircumflex adieresis atilde aring ccedilla eacute egrave + ecircumflex edieresis iacute igrave icircumflex idieresis + ntilde oacute ograve ocircumflex odieresis otilde uacute + ugrave ucircumflex udieresis germandbls AE Oslash ae oslash + Agrave Atilde Otilde OE oe ydieresis Ydieresis fi fl + Acircumflex Ecircumflex Aacute Edieresis Egrave Iacute + Icircumflex Idieresis Igrave Oacute Ocircumflex Ograve + Uacute Ucircumflex Ugrave Lslash lslash Scaron scaron + Zcaron zcaron Eth eth Yacute yacute Thorn thorn Gbreve + gbreve Idotaccent Scedilla scedilla Cacute cacute Ccaron + ccaron dbar EOT OOB DEL EOL Liggie Letter StartText 1 1 1 1 2 3 StartLine 1 1 1 1 2 3 SawLig 5 5 4 5 3 3 SawLetter 1 1 3 1 3 3 GoTo Mark? Advance? MarkClass CurrentClass 1 StartText no yes 0 0 2 SawLig yes yes 0 0 3 SawLetter no yes 0 0 4 SawLig no yes 0 0 5 StartText no yes 1 0 ------------------------------------------------ This table works by walking through the line, looking for either a letter or a ligature. If it sees a letter first, it goes to the SawLetter state, where it scans through and ignores both letters and ligatures until it sees something that is neither (that is, the end of the word -- this is the "1" in the OOB column of the SawLetter state). If it sees a ligature first, it goes to the SawLig state and marks the ligature it just saw. Once in that state, it ignores any deleted glyphs (leftovers from the process that formed the ligature), and only sets the justification class of the marked ligature to 1 if it sees no further letters. The "Postcomp" table partThere are many different ways of filling up space in a line of text which is being justified. The "Factors" table part, described above, lets you control how extra gap is distributed within a line, but it says nothing about how that gap actually appears. In Latin text, the default behavior is to add whitespace, but in other writing systems, there are other approaches. The "Postcomp" table part allows you to control how gap is filled in the line. To get the discussion started, here's a sample "Postcomp" table part: ------------------------------------------------ TablePart Postcomp fiLig fi flLig fl fiLig 0 Decomposition LowerLimit 0 UpperLimit 0.5 Order 1 DecomposeInto f i fiLig 0 Decomposition LowerLimit -0.1 UpperLimit 0 Order 1 DecomposeInto f dotlessi flLig 0 Decomposition LowerLimit -0.1 UpperLimit 0.5 Order 1 DecomposeInto f l ------------------------------------------------ You can see the standard "TablePart" tag, which identifies this as the postcomp table part. This is always followed by a list of glyph group names, along with which glyphs belong to those groups. This is followed by the list of one or more specific actions, which are associated with a particular group and a particular justification class (as described above). Ligature Decomposition actionsIn this example we're specifying how the 'fi' and 'fl' ligatures should decompose under various justification conditions. The standard TablePart tag is followed by a list of glyph group names, along with which glyphs belong to those groups. Then, for each group, one or more action specifications are made. In the 'fi' ligature case, there are two specifications, so that the ligature will break apart into its component "f" and "i" glyphs under expansion, but will differently decompose into an "f" followed by a dotless "i" under compression. The lower and upper limits are expressed in ems, and represent the amount of gap (cumulated on both sides of the ligature) which will trigger the specified action. The order is a number controlling which ligatures decompose first on a line; lower order numbers decompose before higher ones. Unconditional Add actionsThere may be times when, in order to fill some justification gap, you wish to insert an extra glyph after an existing one. In Arabic, for instance, extra glyphs called "kashidas" are inserted when text is justified, in order to avoid the connected letters from being broken apart by whitespace. You can accomplish this by specifying an Unconditional Add action. Here's an example: ------------------------------------------------ TablePart Postcomp lower a b c d e f g h i j k l m n o p q r s t u v w x y z lower 0 UnconditionalAdd AddGlyph period ------------------------------------------------ Here, a period will be added after every lower-case letter to help fill in the gap. Conditional Add actionsThe Unconditional Add action is useful, but sometimes you'd like more subtle control over the process. Specifically, you may use the Conditional Add action to control the threshold beyond which a glyph will be added, and also whether the glyph in question can itself be changed into another glyph. To take a simple example, let's say you wish to change a simple 'W' into a wider swash 'W' when the line is being expanded under justification. Here's a postcomp table part that does this: ------------------------------------------------ TablePart Postcomp W W W 0 ConditionalAdd Threshold 0.05 AddGlyph (none) SubstGlyph Wswash ------------------------------------------------ Here the 'W' will change into the swash 'W' if 0.05 ems (or more) are being added around the 'W'. The special "(none)" value means no glyph will be added here, so this is a simple substitution. The Threshold factor only applies to the substitution; if there's an AddGlyph specified, it will always be added. Stretch actionsThere may be times when, in order to fill in gap, you wish to stretch an existing glyph. An example here might be an em-dash, which the font designer adds with square edges so it can be stretched horizontally without distortion. The stretch action is very simple; here's an example with the em-dash: ------------------------------------------------ TablePart Postcomp dash emdash endash dash 0 Stretch ------------------------------------------------ Note that there is no extra information, beyond the kind of action (i.e. "Stretch"). Ductility actionsA more sophisticated form of stretching can also be specified if the font has been designed with variation axes (a Multiple Masters font, say, or a TrueType variations font). In this case, you may designate a variation axis which will be modified to copyfit glyphs when the line is being justified. Here's an example: ------------------------------------------------ TablePart Postcomp toCopyFit A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + a b c d e f g h i j k l m n o p q r s t u v w x y z toCopyFit 0 Ductility VariationAxis duct Minimum 0.5 NoStretch 1.0 Maximum 2.0 ------------------------------------------------ In this example the upper-case and lower-case Latin letters are specified as being allowed to vary under justification. The axis being used has the 4-character tag 'duct', and has the specified minimum, default, and maximum values. For more information on how postcomp information is used by the line layout justification algorithm, please see the description of the 'just' table, or the justification algorithm documentation. Some ExamplesWhile the above sections described all the possible inputs for the various table parts, it is always more instructive to take some real examples. This section will include some examples of real justification tables, showing how to put together the information described above in creative ways. Simple Latin, no intercharacter spacingTo start off, let's do an example for a Latin font where you don't ever want to see intercharacter spacing when a line is expanded. If a line must be shrunk, you want space to be removed uniformly from both intercharacter and whitespace. To accomplish this, you'll need to make the space glyph a higher priority than any of the other glyphs, and you'll need to designate it as an "unlimited" glyph. Here's a JIF that would give you this effect: ------------------------------------------------ Direction H TablePart Factors Whitespace space Interchar (all others) Interchar 0 BeforeGrow 0 BeforeShrink -0.05 AfterGrow 0 AfterShrink -0.05 GrowFlags intercharacterPriority ShrinkFlags intercharacterPriority Whitespace 0 BeforeGrow 1 BeforeShrink -0.05 AfterGrow 1 AfterShrink -0.05 GrowFlags whitespacePriority unlimited ShrinkFlags intercharacterPriority ------------------------------------------------ This JIF specifies factors for horizontal text. There is only one table part, a factors part. Two groups are defined: one named "Whitespace" comprising solely the space glyph, and one named "Interchar" comprising all other glyphs in the font. The "(all others)" should be typed exactly as it is here. This special wording is recognized by the AAT Font Tool and it will group all glyphs not already named into the specified group. The factors for intercharacter and whitespace in the shrink case are the same, and the shrink flags indicate that glyphs of both groups will get the same priority (intercharacterPriority) assigned to them. Notice that the shrink factors are negative, and that they are the same for whitespace and intercharacter. This means that if a line needs to shrink, uniform amounts of space will be removed from between every pair of glyphs, irrespective of whitespace considerations. In the grow case, the space glyph gets a nonzero value (1 in this case, although this value doesn't really matter in this example); it also gets assigned to a higher priority (whitespacePriority) and gets the unlimited flag. This means that the layout process won't even bother looking at any lower priority glyphs on the line -- everything will be added to the spaces only. Adding intercharacterLet's modify this example slightly to permit a very small amount of intercharacter spacing to be added: ------------------------------------------------ Direction H TablePart Factors Whitespace space Interchar (all others) Interchar 0 BeforeGrow 0.01 BeforeShrink -0.05 AfterGrow 0.01 AfterShrink -0.05 GrowFlags intercharacterPriority ShrinkFlags intercharacterPriority Whitespace 0 BeforeGrow 1 BeforeShrink -0.05 AfterGrow 1 AfterShrink -0.05 GrowFlags whitespacePriority ShrinkFlags intercharacterPriority ------------------------------------------------ The only change between this new JIF and the previous one is the removal of the "unlimited" flag from the whitespace's grow flags, and the changing of the intercharacter grow factors from zero to 0.01. What does this mean in practical terms? Let's say we're layout out a line of 12-point text whose natural width is 350 points and we need to justify it to 400 points. Because 400 is greater than 350, this is the grow case, so AAT will only look at the grow numbers and flags to fill the 50 point gap. The values of 1 for both before and after factors for whitespace mean that (1 times 12) or 12 points of space will be added both before and after any space glyphs on the line. Let's imagine that this line has two space glyphs on it. Each of these will have a total of 24 points of padding added (12 before and 12 after), so that takes up 48 points of the 50 point gap. After whitespace priority is finished, AAT moves down to the next priority level, intercharacter. The factors of 0.01 for before and after mean that no more than (0.01 times 12) or 0.12 points of padding may be added on either side of a glyph. If there are enough intercharacter positions available to fill up the 2 points remaining, then the gap is evenly distributed and processing stops. |