Each glyph in the CharStrings
dictionary, and each subroutine in Subrs, is a charstring: a compact
program of numbers and operators that draws the outline with straight lines and cubic
Bézier curves and marks the stems for hinting. It works like a tiny stack machine:
numbers are pushed, each operator takes its arguments from the stack, and the
drawing and hint operators then clear it. This is the Type 1 format; the CFF table of
OpenType fonts uses the related
Type 2 format.
Charstring encryption
Every charstring is encrypted on its own, with the same cipher as eexec but a starting key of 4330:
r = 4330
for each byte c:
plain = c XOR (r >> 8)
r = ((c + r) × 52845 + 22719) mod 65536
The first lenIV decrypted bytes (4 unless the Private dictionary says
otherwise) are random and are dropped (Type 1 specification, sections 7.1 and 7.3).
So a glyph is decrypted twice: once as part of the eexec section, and again as a
charstring.
Numbers
| First byte v | Bytes | Value |
|---|---|---|
| 0 to 31 | 1 or 2 | An operator, not a number (12 starts a two-byte operator). |
| 32 to 246 | 1 | v − 139: −107 to 107. |
| 247 to 250 | 2 | (v − 247) × 256 + w + 108: 108 to 1131. |
| 251 to 254 | 2 | −(v − 251) × 256 − w − 108: −108 to −1131. |
| 255 | 5 | The next four bytes as a signed 32-bit big-endian integer. |
There are only integers; a fraction is made with the div operator.
Operators
Arguments are listed in the order they are pushed. Coordinates are relative to the current point (“d” values) unless stated.
| Code | Operator | Arguments | Effect |
|---|---|---|---|
13 | hsbw | sbx wx | Starts the glyph: left side bearing sbx (the current point becomes (sbx, 0)) and advance width wx. |
12 7 | sbw | sbx sby wx wy | Like hsbw with vertical components. |
1 | hstem | y dy | Horizontal stem hint from y to y + dy, relative to the side bearing’s y. |
3 | vstem | x dx | Vertical stem hint, relative to the side bearing’s x. |
12 2 | hstem3 | y0 dy0 y1 dy1 y2 dy2 | Three evenly spaced horizontal stems (as in “≡”) to be kept evenly spaced. |
12 1 | vstem3 | x0 dx0 x1 dx1 x2 dx2 | Three evenly spaced vertical stems (as in “m”). |
12 0 | dotsection | — | Brackets a small dot or similar feature, so it is not hinted away; used in pairs. |
21 | rmoveto | dx dy | Moves to a new point, starting a new contour. |
22 | hmoveto | dx | Horizontal move. |
4 | vmoveto | dy | Vertical move. |
5 | rlineto | dx dy | Line. |
6 | hlineto | dx | Horizontal line. |
7 | vlineto | dy | Vertical line. |
8 | rrcurveto | dx1 dy1 dx2 dy2 dx3 dy3 | Cubic curve; each point relative to the previous one. |
30 | vhcurveto | dy1 dx2 dy2 dx3 | Curve that starts vertical and ends horizontal (0 dy1 dx2 dy2 dx3 0 rrcurveto). |
31 | hvcurveto | dx1 dx2 dy2 dy3 | Curve that starts horizontal and ends vertical (dx1 0 dx2 dy2 0 dy3 rrcurveto). |
9 | closepath | — | Closes the contour with a line back to its start. |
14 | endchar | — | Ends the glyph. |
12 6 | seac | asb adx ady bchar achar | Standard encoding accented character: draws the glyphs whose StandardEncoding codes are bchar (base) and achar (accent), the accent shifted by (adx, ady) allowing for its side bearing asb. Ends the glyph. |
12 12 | div | num1 num2 | Pushes num1 ÷ num2; the only arithmetic. |
10 | callsubr | … subr# | Runs Subrs[subr#] and continues; the stack is shared. |
11 | return | — | Returns from a subroutine. |
12 16 | callothersubr | arg1 … argn n othersubr# | Calls PostScript procedure OtherSubrs[othersubr#] with n arguments; its results are fetched with pop. |
12 17 | pop | — | Moves one result from the PostScript stack to the charstring stack. |
12 33 | setcurrentpoint | x y | Sets the current point to absolute coordinates; used only after a flex. |
Every other code from 0 to 31, and every other second byte after 12, is undefined.
A glyph must begin with hsbw or sbw, and must end with
endchar or seac.
Hint replacement and flex
- Hint replacement. Stem hints may overlap in a complex glyph, so a
charstring can change them part-way with
subr# 1 3 callothersubr pop callsubr, where Subrs[subr#] holds the newhstemandvstemset.OtherSubrs3 hands subr# back, so the new hints are run; an interpreter that cannot replace hints hands back 3 instead, and Subrs 3 is justreturn(section 8.1). The hint Subr may be any entry except 0 to 3 and holds only stem hints. - Flex. A pair of shallow curves (such as a cupped serif) can be
marked so that at small sizes it is drawn as a straight line. The charstring calls
Subrs 1, then gives seven points each followed by a call to Subrs 2, then pushes
the flex height in hundredths of a pixel and the end point and calls Subrs 0, which
uses
OtherSubrs0 andsetcurrentpoint. Each of the seven points is anrmoveto: the reference point first, then the six points of the two curves. A flex height of 50 (half a pixel) is the usual value for cupped serifs (section 8.3).
Example
A square 500 units wide, written out as charstring bytes before encryption (an encrypted copy would start with four random bytes), with a left side bearing of 50 and an advance width of 600:
BD F8 EC 0D 50 600 hsbw 189 − 139 = 50; (248 − 247) × 256 + 236 + 108 = 600
8B 8B 15 0 0 rmoveto 139 − 139 = 0; the contour starts at (50, 0)
F8 88 06 500 hlineto (248 − 247) × 256 + 136 + 108 = 500
F8 88 07 500 vlineto
FC 88 06 -500 hlineto −(252 − 251) × 256 − 136 − 108 = −500
09 closepath back down to (50, 0)
0E endchar