I'm writing a TTF parser. For a better understanding of the TTF format, I used TTX to extract the ".notdef" glyph data of C:\Windows\calibri.ttf as follow.
<TTGlyph name=".notdef" xMin="0" yMin="-397" xMax="978" yMax="1294">
<contour>
<pt x="978" y="1294" on="1"/>
<pt x="978" y="0" on="1"/>
<pt x="44" y="0" on="1"/>
<pt x="44" y="1294" on="1"/>
</contour>
<contour>
<pt x="891" y="81" on="1"/>
<pt x="891" y="1213" on="1"/>
<pt x="129" y="1213" on="1"/>
<pt x="129" y="81" on="1"/>
</contour>
<contour>
<pt x="767" y="855" on="1"/>
<pt x="767" y="796" on="0"/>
<pt x="732" y="704" on="0"/>
<pt x="669" y="641" on="0"/>
<pt x="583" y="605" on="0"/>
<pt x="532" y="602" on="1"/>
<pt x="527" y="450" on="1"/>
many more points
</contour>
...some other xml
</TTGlyph>
You can see more than one off-curve control points in a row. But I've learned that TrueType Font are made of Quadratic Beziers, each of which has two on-curve points (end points) and only one off-curve point (control point). How to interpret these consecutive off-curve points?
Bézier curves are widely used in computer graphics to model smooth curves. As the curve is completely contained in the convex hull of its control points, the points can be graphically displayed and used to manipulate the curve intuitively.
The benefit of Bezier curves is the ease of computation, stability at the lower degrees of control points (warning! they do become unstable at higher degrees) and a Bezier curve can be rotated and translated by performing the operations on the points.
Quadratic Bezier curve is a point-to-point linear interpolation of two Linear Bezier Curves. For given three points P0, P1 and P2, a quadratic bezier curve is a linear interpolation of two points, got from Linear Bezier curve of P0 and P1 and Linear Bezier Curve of P1 and P2.
A cubic Bezier curve has 2 control points, whereas the quadratic Bezier curve only has 1 control point. Cubic Bezier curves - 3 rd degree curves - to fully define such a curve, one will need to specify 4 points: two anchor points (P1 and P2) - the curve starts and, respectively, ends in these points.
TTF parsing requires applying http://www.microsoft.com/typography/otspec/glyf.htm as well as the tech docs about the TTF format from the microsoft site. These tell us that there are two types of points for a curve: on-curve and off-curve points. on-curve points are "real" points, through which a curve passes, and off-curve points are control points that guide the bezier curvature.
Now, what you describe as "a bezier curve" is correct: a single (quadratic) bezier curve goes from 1 real point, guided by 1 control point, to 1 real point (higher order curves like cubics, quartics, etc. have more control points between the real points). However, quadratic curves are generally terrible for design work because they are really bad at approximating circular arcs, but are cheaper to work with than higher order curves, so we're stuck with them for fonts that use TrueType for glyph outlines. To get around the downsize of quadratic curves, TrueType outlines generally use sequences of bezier curves rather than single curves in order to get decent-looking uniform curves, and those sequences tend to have a nice property: the on- and off-curve points are spaced in a way that we don't need to record every point in the sequence.
Consider this Bezier sequence:
P1 - C1 - P2 - C2 - P3 - C3 - P4
If we add the on
information, we'd encode it in TTF as:
P1 - C1 - P2 - C2 - P3 - C3 - P4
1 - 0 - 1 - 0 - 1 - 0 - 1
Now for the trick: if each Pn is an on-curve point, and each Cn is a control point, and P2 lies exactly midway between C1 and C2, P3 lies between C2 and C3, and so on, then this is a compactable curve: if we know C1 and C2, we know P2, so we don't have to list it explicitly, we can just leave that up to whatever parses the glyph outline.
So TTF will encode long bezier sequences for uniform curves as:
P1 - C1 - C2 - C3 - P4
1 - 0 - 0 - 0 - 1
saving considerable space, without loss of precision. If you look at your TTX dump, you'll see this reflected in the on
values for each point. To get the P2, P3, etc, all we do is this:
foreach(array as point):
// do we have an implied on-curve point?
if(mask_for_point == 0 && mask_for_previous_point == 0):
missing_point = midpoint(point, previous_point)
points.push(missing_point)
// add the explicitly encoded point
points.push(point)
after running this procedure, the points
array will have alternating on-curve and off-curve points, and the beziers are constructed as
for(i=0, i<arr.length, i+=2):
curve(array[i], array[i+1], array[i+2])
edit after a bit of searching, http://chanae.walon.org/pub/ttf/ttf_glyphs.htm covers how to work with the glyf
table data in pretty good detail (the ascii graphics are a bit silly, but still legible enough)
further edit after several years I managed to find documentation that actually explains (or, at least implies) it in the Apple documentation on TTF, over on https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html#necessary, which in "Figure 13" states that:
In particular the on-curve points, located at the midpoints of the tangents to the curve, add no extra information and might have been omitted.
even further edit ShreevatsaR points out that the text between Figures 2 and 3 in the apple documentation is also relevant:
It would also be possible to specify the curve shown in FIGURE 2 with one fewer point by removing point p2. Point p2 is not strictly needed to define the curve because its existence implied and its location can be reconstructed from the data given by the other points. After renumbering the remaining points, we have [FIGURE 3].
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With