forked from fontforge/fontforge.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
autowidth.html
271 lines (269 loc) · 11.4 KB
/
autowidth.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
<HTML>
<HEAD>
<!-- Created with AOLpress/2.0 -->
<!-- AP: Created on: 19-Jul-2002 -->
<!-- AP: Last modified: 15-Dec-2009 -->
<TITLE>Auto Width and Auto Kern</TITLE>
<LINK REL="icon" href="fftype16.png">
<LINK REL="stylesheet" TYPE="text/css" HREF="/assets/css/old/FontForge.css">
</HEAD>
>
<div style="margin:0; height: 4 em; padding: 0.5em; background: red; color:yellow; text-align:center; font-size:1em; font-family: sans-serif;">
<p><a href="http://fontforge.github.io" style="padding: 0.5em; color: yellow; font-weight: bold; text-decoration: none;" onmouseover="this.style.background='black';" onmouseout="this.style.background='red';" >This is part of the old website. New website begins at fontforge.github.io</a></p>
<p><a href="https://github.com/fontforge/fontforge.github.io" style="padding: 0.5em; color: yellow; font-weight: bold; text-decoration: none;" onmouseover="this.style.background='black';" onmouseout="this.style.background='red';" >Are you a web developer? Help us migrate this page on Github</a></p>
</div>
<DIV id="in">
<H2 ALIGN=Center>
<A NAME="AutoWidth">Auto
Width</A><IMG SRC="/assets/img/old/autowidth.png" WIDTH="386" HEIGHT="240" ALIGN="Right">
</H2>
<P>
The Auto Width command will attempt to guess reasonable widths (more accurately
reasonable left and right side bearings) for your font. The command splits
the selection into scripts and looks at all possible combinations of selected
glyphs within each script.
<P>
The Separation text box allows you to say how close you want the glyphs to
be. If you look at a vertical bar (or sanserif "I"), and you autowidth it
the sum of the left and right side bearings will be equal to the spacing.
The min and max values will prevent sidebearings from becoming unrealistically
large (these rarely matter).
<P>
Adjusting the left and right bearings of a letter will also adjust the left
and right bearings of all accented letters based (by reference) on it. You
should not try to use AutoWidth on accented letters directly (unless those
glyphs do not use references).<BR Clear=Right>
<H2 ALIGN=Center>
<A NAME="AutoKern">Auto Kern</A>
</H2>
<P>
There used to be an Auto Kern dialog. There is no longer, this is now done
in the <A HREF="lookups.html#Pair">kerning subtable</A> dialog and the
<A HREF="metricsview.html#kernclass">kerning class </A>dialog.
<HR>
<H3>
The python
<CODE><A NAME="GlyphSeparationHook">GlyphSeparationHook</A></CODE>
</H3>
<P>
When FontForge does AutoKerning (and in a more complicated fashion, when
it does AutoWidthing) it tries to guess the optical separation between two
glyphs, and then it applies an appropriate kern value to make the optical
separation be the desired spacing.
<P>
FontForge assumes that the optical spacing is linear (so if you increase
the separation between two glyphs by 3 em-units, then the optical spacing
will also increase by 3 em-units).
<P>
FF guesses the optical separation between two glyphs by figuring a value
for the separation of two glyphs when their bounding boxes are adjacent.
For two rectangles (where the bounding box is the same as the glyph itself)
the separation will be 0 as the two glyphs will be touching. For a combination
like "To" the optical separation will be quite large.
<TABLE BORDER CELLPADDING="2" ALIGN="center">
<TR>
<TD><IMG SRC="/assets/img/old/GlyphSep-rectangles.png" WIDTH="129" HEIGHT="122"></TD>
<TD><IMG SRC="/assets/img/old/GlyphSep-To.png" WIDTH="105" HEIGHT="111"></TD>
</TR>
</TABLE>
<P>
The GlyphSeparationHook is a python routine which will be called with a bunch
of information about two glyphs, and is expected to return an integer
representing the optical separation between the two. It is a low-level routine;
the actual calculation of kerning offsets or setting glyph widths is done
elsewhere, but this routine provides the information those higher level routines
need. Of course, if you don't provide a routine, FontForge has a built-in
routine which will provide a default behavior. Here is a python version of
that default routine:
<BLOCKQUOTE>
<PRE>import fontforge;
def GlyphSeparation(onLeft,onRight,context) :
# the goal is to give a weighted average that expresses the visual
# separation between two glyphs when they are placed so their bounding
# boxes are adjacent. The separation between two rectangles would be 0
# While the separation between "T" and "o" would be fairly large
# The trick is to guess a good weighting function. My guess is that
# things that look close are more important than those which look far
# So "T" and "O" should be dominated by the crossbar of the "T"...
#
# Find the area the two glyphs have in common (so when comparing "o" and
# "l" or "g", ignore the ascenders and descenders since that's outside
# the range of "o" and won't affect its visual proximity.
imin_y = max(onRight.iminY,onLeft.iminY);
imax_y = min(onRight.imaxY,onLeft.imaxY);
#
# Some glyph combinations will have no overlap -- the grave accent and the
# letter "a" will have none. So they don't interact visually.
if imax_y < imin_y :
return( 0 )
#
# Otherwise compute some sort of weighted average of the separations between
# the two glyphs at various heights.
tot,cnt = 0,0
j = imin_y
while j<=imax_y :
if onRight.left[j] < 32767 and onLeft.right[j] > -32767 :
# beware of gaps such as those in "i" or "aaccute"
# a gap has a left or right value which is huge
# so ignore any such, again it doesn't contribute to the
# visual separation.
sep = onRight.left[j] - onLeft.right[j]
weight = 1.0/(sep + context.denom)
weight *= weight
tot += weight*sep
cnt += weight
j += 1
if cnt!=0 :
tot /= cnt
return( int(round( tot )) );
fontforge.registerGlyphSeparationHook(GlyphSeparation)
</PRE>
</BLOCKQUOTE>
<P>
The hook will be called with three arguments, a structure with information
about the glyph on the left, a structure with information about the glyph
on the right, and a structure containing general information about the operation
as a whole.
<P>
The context argument contains the following fields:
<P>
<TABLE BORDER CELLPADDING="2">
<TR>
<TD>font</TD>
<TD>The font being worked on</TD>
</TR>
<TR>
<TD>emSize</TD>
<TD>The emsize of the font</TD>
</TR>
<TR>
<TD>layer</TD>
<TD>The active layer for this operation</TD>
</TR>
<TR>
<TD>regionHeight</TD>
<TD>(explained below) by default emSize/100</TD>
</TR>
<TR>
<TD>denom </TD>
<TD>1/50th of the emSize. A number I found handy.</TD>
</TR>
</TABLE>
<P>
FontForge preprocess all the glyphs, extracting pertinant information from
each and storing it in a separate per-glyph structure than the normal python
Glyph object. The most important information is a representation of the leftmost
and rightmost edges of the glyph. Basically, for every value of y, FontForge
finds the smallest x coordinate on any of the glyph's contours at that height
and stores that information in an array. There is a similar array containing
the largest x coordinates. Of course there are an infinite number of y values,
so that isn't quite accurate. FontForge divides the vertical axis into many
regions, each regionHeight high (this is usually 1/100th of the emsize).
It then finds the smallest x coordinate achieved by any contour within that
region.
<P>
The value stored in the array representing the leftmost edge of the glyph
is not quite the smallest x coordinate. Instead we normalize the numbers
so that the x coordinate of the left side bearing of the glyph is 0. None
of the numbers in the left array will be negative.
<P>
Similarly the value stored in the rightmost array is normalized so that the
glyph's right side bearing is 0. None of the numbers in the right array will
be positive.
<P>
There can be gaps in a glyph -- for example in a lower case "i" there is
a hole in the body of the glyph between the dot on top and the rest down
below. In this case the left array will hold the special value 32767, and
the right array will hold -32767.
<P>
So I don't pass a standard glyph object. The underlying glyph object can
be obtained through the <CODE>glyph</CODE> member. The most important things
in this structure are the arrays representing the left and right contours
called <CODE>left</CODE> and <CODE>right</CODE>. The structure also contains
the upper and lower bounds of the arrays (both arrays have the same bounds).
The glyph structures contain the following members:
<TABLE BORDER CELLPADDING="2">
<TR>
<TD>glyph</TD>
<TD>This is the standard python glyph object. This can (rarely) be None.</TD>
</TR>
<TR>
<TD>boundingbox</TD>
<TD>A tuple of 4 values (minx,miny,maxx,maxy) for the glyph</TD>
</TR>
<TR>
<TD>iminY</TD>
<TD>The low bound of the arrays.</TD>
</TR>
<TR>
<TD>imaxY</TD>
<TD>The high bound of the arrays</TD>
</TR>
<TR>
<TD>left</TD>
<TD>an array of integers representing the separation between the left edge
of the bounding box and the leftmost contour at that height (never negative)</TD>
</TR>
<TR>
<TD>right</TD>
<TD>an array of integers representing the separation between the right edge
of the bounding box and the rightmost contour at that height (never positive)</TD>
</TR>
</TABLE>
<P>
<CODE>iminY</CODE> and <CODE>imaxY</CODE> are scaled by
<CODE>regionHeight</CODE> from the y units in the glyph itself. The value
contain at left[0] would represent minimum separation between boundingbox.minx
and any the x location of any contour when the y location was between 0 and
regionHeight. Similarly left[1] would be the minimum separation with the
y location between regionHeight and 2*regionHeight. And so forth.
<BLOCKQUOTE>
<PRE> imin_y = max(onRight.iminY,onLeft.iminY);
imax_y = min(onRight.imaxY,onLeft.imaxY);
if imax_y < imin_y :
return( 0 )
</PRE>
</BLOCKQUOTE>
<P>
Here we figure out the range along the y axis where the glyphs both exist.
If the two glyphs don't share any area along the y axis (as, for example,
the glyph "a" and the grave accent might not) then we assume they do not
interact and return a visual separation of 0.
<BLOCKQUOTE>
<PRE> tot,cnt = 0,0
j = imin_y
while j<=imax_y :
if onRight.left[j] < 32767 and onLeft.right[j] > -32767 :
sep = onRight.left[j] - onLeft.right[j]
weight = 1.0/(sep + context.denom)
weight *= weight
tot += weight*sep
cnt += weight
j += 1
</PRE>
</BLOCKQUOTE>
<P>
Otherwise loop over the y range where both glyphs are active. Checking to
make sure there are no holes.
<P>
We find the actual separation between the two glyphs at this y value.
<P>
Then we calculate some magic weighting function (this is handwaving. your
routine will have a better weighting function)
<BLOCKQUOTE>
<PRE> if cnt!=0 :
tot /= cnt
return( int(round( tot )) );
</PRE>
</BLOCKQUOTE>
<P>
And finally we take the weighted average of those separations, and return
that as the optical separation.
<BLOCKQUOTE>
<PRE>fontforge.registerGlyphSeparationHook(GlyphSeparation)
</PRE>
</BLOCKQUOTE>
<P>
And this routine will tell FontForge to use the hook you provide.
</DIV>
</BODY></HTML>