Merge x/exp/shiny/font to x/image/font.
diff --git a/example/font/main.go b/example/font/main.go
new file mode 100644
index 0000000..10909f4
--- /dev/null
+++ b/example/font/main.go
@@ -0,0 +1,105 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+//
+// This build tag means that "go install golang.org/x/exp/shiny/..." doesn't
+// install this example program. Use "go run main.go" to run it.
+
+// Font is a basic example of using fonts.
+package main
+
+import (
+	"flag"
+	"image"
+	"image/color"
+	"image/draw"
+	"image/png"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"golang.org/x/exp/shiny/font"
+	"golang.org/x/exp/shiny/font/plan9font"
+	"golang.org/x/image/math/fixed"
+)
+
+var (
+	fontFlag = flag.String("font", "",
+		`filename of the Plan 9 font or subfont file, such as "lucsans/unicode.8.font" or "lucsans/lsr.14"`)
+	firstRuneFlag = flag.Int("firstrune", 0, "the Unicode code point of the first rune in the subfont file")
+)
+
+func pt(p fixed.Point26_6) image.Point {
+	return image.Point{
+		X: int(p.X+32) >> 6,
+		Y: int(p.Y+32) >> 6,
+	}
+}
+
+func main() {
+	flag.Parse()
+
+	// TODO: mmap the files.
+	if *fontFlag == "" {
+		flag.Usage()
+		log.Fatal("no font specified")
+	}
+	var face font.Face
+	if strings.HasSuffix(*fontFlag, ".font") {
+		fontData, err := ioutil.ReadFile(*fontFlag)
+		if err != nil {
+			log.Fatal(err)
+		}
+		dir := filepath.Dir(*fontFlag)
+		face, err = plan9font.ParseFont(fontData, func(name string) ([]byte, error) {
+			return ioutil.ReadFile(filepath.Join(dir, filepath.FromSlash(name)))
+		})
+		if err != nil {
+			log.Fatal(err)
+		}
+	} else {
+		fontData, err := ioutil.ReadFile(*fontFlag)
+		if err != nil {
+			log.Fatal(err)
+		}
+		face, err = plan9font.ParseSubfont(fontData, rune(*firstRuneFlag))
+		if err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	dst := image.NewRGBA(image.Rect(0, 0, 800, 300))
+	draw.Draw(dst, dst.Bounds(), image.Black, image.Point{}, draw.Src)
+
+	d := &font.Drawer{
+		Dst:  dst,
+		Src:  image.White,
+		Face: face,
+	}
+	ss := []string{
+		"The quick brown fox jumps over the lazy dog.",
+		"Hello, 世界.",
+		"U+FFFD is \ufffd.",
+	}
+	for i, s := range ss {
+		d.Dot = fixed.P(20, 100*i+80)
+		dot0 := pt(d.Dot)
+		d.DrawString(s)
+		dot1 := pt(d.Dot)
+		dst.SetRGBA(dot0.X, dot0.Y, color.RGBA{0xff, 0x00, 0x00, 0xff})
+		dst.SetRGBA(dot1.X, dot1.Y, color.RGBA{0x00, 0x00, 0xff, 0xff})
+	}
+
+	out, err := os.Create("out.png")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer out.Close()
+	if err := png.Encode(out, dst); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/font/font.go b/font/font.go
new file mode 100644
index 0000000..a9b9729
--- /dev/null
+++ b/font/font.go
@@ -0,0 +1,204 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package font defines an interface for font faces, for drawing text on an
+// image.
+//
+// Other packages provide font face implementations. For example, a truetype
+// package would provide one based on .ttf font files.
+package font
+
+// TODO: move this from golang.org/x/exp to golang.org/x/image ??
+
+import (
+	"image"
+	"image/draw"
+	"io"
+
+	"golang.org/x/image/math/fixed"
+)
+
+// TODO: who is responsible for caches (glyph images, glyph indices, kerns)?
+// The Drawer or the Face?
+
+// Face is a font face. Its glyphs are often derived from a font file, such as
+// "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and
+// hinting. For example, the 12pt and 18pt versions of Comic Sans are two
+// different faces, even if derived from the same font file.
+//
+// A Face is not safe for concurrent use by multiple goroutines, as its methods
+// may re-use implementation-specific caches and mask image buffers.
+//
+// To create a Face, look to other packages that implement specific font file
+// formats.
+type Face interface {
+	io.Closer
+
+	// Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
+	// glyph at the sub-pixel destination location dot, and that glyph's
+	// advance width.
+	//
+	// It returns !ok if the face does not contain a glyph for r.
+	//
+	// The contents of the mask image returned by one Glyph call may change
+	// after the next Glyph call. Callers that want to cache the mask must make
+	// a copy.
+	Glyph(dot fixed.Point26_6, r rune) (
+		dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool)
+
+	// GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal
+	// to the origin, and that glyph's advance width.
+	//
+	// It returns !ok if the face does not contain a glyph for r.
+	//
+	// The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y. A
+	// visual depiction of what these metrics are is at
+	// https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
+	GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool)
+
+	// GlyphAdvance returns the advance width of r's glyph.
+	//
+	// It returns !ok if the face does not contain a glyph for r.
+	GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool)
+
+	// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
+	// positive kern means to move the glyphs further apart.
+	Kern(r0, r1 rune) fixed.Int26_6
+
+	// TODO: per-font Metrics.
+	// TODO: ColoredGlyph for various emoji?
+	// TODO: Ligatures? Shaping?
+}
+
+// TODO: Drawer.Layout or Drawer.Measure methods to measure text without
+// drawing?
+
+// Drawer draws text on a destination image.
+//
+// A Drawer is not safe for concurrent use by multiple goroutines, since its
+// Face is not.
+type Drawer struct {
+	// Dst is the destination image.
+	Dst draw.Image
+	// Src is the source image.
+	Src image.Image
+	// Face provides the glyph mask images.
+	Face Face
+	// Dot is the baseline location to draw the next glyph. The majority of the
+	// affected pixels will be above and to the right of the dot, but some may
+	// be below or to the left. For example, drawing a 'j' in an italic face
+	// may affect pixels below and to the left of the dot.
+	Dot fixed.Point26_6
+
+	// TODO: Clip image.Image?
+	// TODO: SrcP image.Point for Src images other than *image.Uniform? How
+	// does it get updated during DrawString?
+}
+
+// TODO: should DrawString return the last rune drawn, so the next DrawString
+// call can kern beforehand? Or should that be the responsibility of the caller
+// if they really want to do that, since they have to explicitly shift d.Dot
+// anyway?
+//
+// In general, we'd have a DrawBytes([]byte) and DrawRuneReader(io.RuneReader)
+// and the last case can't assume that you can rewind the stream.
+//
+// TODO: how does this work with line breaking: drawing text up until a
+// vertical line? Should DrawString return the number of runes drawn?
+
+// DrawString draws s at the dot and advances the dot's location.
+func (d *Drawer) DrawString(s string) {
+	var prevC rune
+	for i, c := range s {
+		if i != 0 {
+			d.Dot.X += d.Face.Kern(prevC, c)
+		}
+		dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c)
+		if !ok {
+			// TODO: is falling back on the U+FFFD glyph the responsibility of
+			// the Drawer or the Face?
+			// TODO: set prevC = '\ufffd'?
+			continue
+		}
+		draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
+		d.Dot.X += advance
+		prevC = c
+	}
+}
+
+// MeasureString returns how far dot would advance by drawing s.
+func (d *Drawer) MeasureString(s string) (advance fixed.Int26_6) {
+	var prevC rune
+	for i, c := range s {
+		if i != 0 {
+			advance += d.Face.Kern(prevC, c)
+		}
+		a, ok := d.Face.GlyphAdvance(c)
+		if !ok {
+			// TODO: is falling back on the U+FFFD glyph the responsibility of
+			// the Drawer or the Face?
+			// TODO: set prevC = '\ufffd'?
+			continue
+		}
+		advance += a
+		prevC = c
+	}
+	return advance
+}
+
+// Hinting selects how to quantize a vector font's glyph nodes.
+//
+// Not all fonts support hinting.
+type Hinting int
+
+const (
+	HintingNone Hinting = iota
+	HintingVertical
+	HintingFull
+)
+
+// Stretch selects a normal, condensed, or expanded face.
+//
+// Not all fonts support stretches.
+type Stretch int
+
+const (
+	StretchUltraCondensed Stretch = -4
+	StretchExtraCondensed Stretch = -3
+	StretchCondensed      Stretch = -2
+	StretchSemiCondensed  Stretch = -1
+	StretchNormal         Stretch = +0
+	StretchSemiExpanded   Stretch = +1
+	StretchExpanded       Stretch = +2
+	StretchExtraExpanded  Stretch = +3
+	StretchUltraExpanded  Stretch = +4
+)
+
+// Style selects a normal, italic, or oblique face.
+//
+// Not all fonts support styles.
+type Style int
+
+const (
+	StyleNormal Style = iota
+	StyleItalic
+	StyleOblique
+)
+
+// Weight selects a normal, light or bold face.
+//
+// Not all fonts support weights.
+type Weight int
+
+const (
+	WeightThin       Weight = 100
+	WeightExtraLight Weight = 200
+	WeightLight      Weight = 300
+	WeightNormal     Weight = 400
+	WeightMedium     Weight = 500
+	WeightSemiBold   Weight = 600
+	WeightBold       Weight = 700
+	WeightExtraBold  Weight = 800
+	WeightBlack      Weight = 900
+)
diff --git a/font/plan9font/example_test.go b/font/plan9font/example_test.go
new file mode 100644
index 0000000..071fcfa
--- /dev/null
+++ b/font/plan9font/example_test.go
@@ -0,0 +1,93 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package plan9font_test
+
+import (
+	"image"
+	"image/draw"
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+	"path/filepath"
+
+	"golang.org/x/exp/shiny/font"
+	"golang.org/x/exp/shiny/font/plan9font"
+	"golang.org/x/image/math/fixed"
+)
+
+func ExampleParseFont() {
+	readFile := func(name string) ([]byte, error) {
+		return ioutil.ReadFile(filepath.FromSlash(path.Join("../testdata/fixed", name)))
+	}
+	fontData, err := readFile("unicode.7x13.font")
+	if err != nil {
+		log.Fatal(err)
+	}
+	face, err := plan9font.ParseFont(fontData, readFile)
+	if err != nil {
+		log.Fatal(err)
+	}
+	// TODO: derive the ascent from the face's metrics.
+	const ascent = 11
+
+	dst := image.NewRGBA(image.Rect(0, 0, 4*7, 13))
+	draw.Draw(dst, dst.Bounds(), image.Black, image.Point{}, draw.Src)
+	d := &font.Drawer{
+		Dst:  dst,
+		Src:  image.White,
+		Face: face,
+		Dot:  fixed.P(0, ascent),
+	}
+	// Draw:
+	// 	- U+0053 LATIN CAPITAL LETTER S
+	//	- U+03A3 GREEK CAPITAL LETTER SIGMA
+	//	- U+222B INTEGRAL
+	//	- U+3055 HIRAGANA LETTER SA
+	// The testdata does not contain the CJK subfont files, so U+3055 HIRAGANA
+	// LETTER SA (さ) should be rendered as U+FFFD REPLACEMENT CHARACTER (�).
+	//
+	// The missing subfont file will trigger an "open
+	// ../testdata/shinonome/k12.3000: no such file or directory" log message.
+	// This is expected and can be ignored.
+	d.DrawString("SΣ∫さ")
+
+	// Convert the dst image to ASCII art.
+	var out []byte
+	b := dst.Bounds()
+	for y := b.Min.Y; y < b.Max.Y; y++ {
+		out = append(out, '0'+byte(y%10), ' ')
+		for x := b.Min.X; x < b.Max.X; x++ {
+			if dst.RGBAAt(x, y).R > 0 {
+				out = append(out, 'X')
+			} else {
+				out = append(out, '.')
+			}
+		}
+		// Highlight the last row before the baseline. Glyphs like 'S' without
+		// descenders should not affect any pixels whose Y coordinate is >= the
+		// baseline.
+		if y == ascent-1 {
+			out = append(out, '_')
+		}
+		out = append(out, '\n')
+	}
+	os.Stdout.Write(out)
+
+	// Output:
+	// 0 ..................X.........
+	// 1 .................X.X........
+	// 2 .XXXX..XXXXXX....X.....XXX..
+	// 3 X....X.X.........X....XX.XX.
+	// 4 X.......X........X....X.X.X.
+	// 5 X........X.......X....XXX.X.
+	// 6 .XXXX.....X......X....XX.XX.
+	// 7 .....X...X.......X....XX.XX.
+	// 8 .....X..X........X....XXXXX.
+	// 9 X....X.X.........X....XX.XX.
+	// 0 .XXXX..XXXXXX....X.....XXX.._
+	// 1 ...............X.X..........
+	// 2 ................X...........
+}
diff --git a/font/plan9font/plan9font.go b/font/plan9font/plan9font.go
new file mode 100644
index 0000000..89f0a2d
--- /dev/null
+++ b/font/plan9font/plan9font.go
@@ -0,0 +1,556 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package plan9font implements font faces for the Plan 9 font and subfont file
+// formats. These formats are described at
+// http://plan9.bell-labs.com/magic/man2html/6/font
+package plan9font
+
+// TODO: have a subface use an *image.Alpha instead of plan9Image implementing
+// the image.Image interface? The image/draw code has a fast path for
+// *image.Alpha masks.
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"image"
+	"image/color"
+	"log"
+	"strconv"
+	"strings"
+
+	"golang.org/x/exp/shiny/font"
+	"golang.org/x/image/math/fixed"
+)
+
+// fontchar describes one character glyph in a subfont.
+//
+// For more detail, look for "struct Fontchar" in
+// http://plan9.bell-labs.com/magic/man2html/2/cachechars
+type fontchar struct {
+	x      uint32 // X position in the image holding the glyphs.
+	top    uint8  // First non-zero scan line.
+	bottom uint8  // Last non-zero scan line.
+	left   int8   // Offset of baseline.
+	width  uint8  // Width of baseline.
+}
+
+func parseFontchars(p []byte) []fontchar {
+	fc := make([]fontchar, len(p)/6)
+	for i := range fc {
+		fc[i] = fontchar{
+			x:      uint32(p[0]) | uint32(p[1])<<8,
+			top:    uint8(p[2]),
+			bottom: uint8(p[3]),
+			left:   int8(p[4]),
+			width:  uint8(p[5]),
+		}
+		p = p[6:]
+	}
+	return fc
+}
+
+// subface implements font.Face for a Plan 9 subfont.
+type subface struct {
+	firstRune rune        // First rune in the subfont.
+	n         int         // Number of characters in the subfont.
+	height    int         // Inter-line spacing.
+	ascent    int         // Height above the baseline.
+	fontchars []fontchar  // Character descriptions.
+	img       *plan9Image // Image holding the glyphs.
+}
+
+func (f *subface) Close() error                   { return nil }
+func (f *subface) Kern(r0, r1 rune) fixed.Int26_6 { return 0 }
+
+func (f *subface) Glyph(dot fixed.Point26_6, r rune) (
+	dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
+
+	r -= f.firstRune
+	if r < 0 || f.n <= int(r) {
+		return image.Rectangle{}, nil, image.Point{}, 0, false
+	}
+	i := &f.fontchars[r+0]
+	j := &f.fontchars[r+1]
+
+	minX := int(dot.X+32)>>6 + int(i.left)
+	minY := int(dot.Y+32)>>6 + int(i.top) - f.ascent
+	dr = image.Rectangle{
+		Min: image.Point{
+			X: minX,
+			Y: minY,
+		},
+		Max: image.Point{
+			X: minX + int(j.x-i.x),
+			Y: minY + int(i.bottom) - int(i.top),
+		},
+	}
+	return dr, f.img, image.Point{int(i.x), int(i.top)}, fixed.Int26_6(i.width) << 6, true
+}
+
+func (f *subface) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
+	r -= f.firstRune
+	if r < 0 || f.n <= int(r) {
+		return fixed.Rectangle26_6{}, 0, false
+	}
+	i := &f.fontchars[r+0]
+	j := &f.fontchars[r+1]
+
+	bounds = fixed.R(
+		int(i.left),
+		int(i.top)-f.ascent,
+		int(i.left)+int(j.x-i.x),
+		int(i.bottom)-f.ascent,
+	)
+	return bounds, fixed.Int26_6(i.width) << 6, true
+}
+
+func (f *subface) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
+	r -= f.firstRune
+	if r < 0 || f.n <= int(r) {
+		return 0, false
+	}
+	return fixed.Int26_6(f.fontchars[r].width) << 6, true
+}
+
+// runeRange maps a single rune range [lo, hi] to a lazily loaded subface. Both
+// ends of the range are inclusive.
+type runeRange struct {
+	lo, hi      rune
+	offset      rune // subfont index that the lo rune maps to.
+	relFilename string
+	subface     *subface
+	bad         bool
+}
+
+// face implements font.Face for a Plan 9 font.
+//
+// It maps multiple rune ranges to *subface values. Rune ranges may overlap;
+// the first match wins.
+type face struct {
+	height     int
+	ascent     int
+	readFile   func(relFilename string) ([]byte, error)
+	runeRanges []runeRange
+}
+
+func (f *face) Close() error                   { return nil }
+func (f *face) Kern(r0, r1 rune) fixed.Int26_6 { return 0 }
+
+func (f *face) Glyph(dot fixed.Point26_6, r rune) (
+	dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
+
+	if s, rr := f.subface(r); s != nil {
+		return s.Glyph(dot, rr)
+	}
+	return image.Rectangle{}, nil, image.Point{}, 0, false
+}
+
+func (f *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
+	if s, rr := f.subface(r); s != nil {
+		return s.GlyphBounds(rr)
+	}
+	return fixed.Rectangle26_6{}, 0, false
+}
+
+func (f *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
+	if s, rr := f.subface(r); s != nil {
+		return s.GlyphAdvance(rr)
+	}
+	return 0, false
+}
+
+func (f *face) subface(r rune) (*subface, rune) {
+	// Fall back on U+FFFD if we can't find r.
+	for _, rr := range [2]rune{r, '\ufffd'} {
+		// We have to do linear, not binary search. plan9port's
+		// lucsans/unicode.8.font says:
+		//	0x2591  0x2593  ../luc/Altshades.7.0
+		//	0x2500  0x25ee  ../luc/FormBlock.7.0
+		// and the rune ranges overlap.
+		for i := range f.runeRanges {
+			x := &f.runeRanges[i]
+			if rr < x.lo || x.hi < rr || x.bad {
+				continue
+			}
+			if x.subface == nil {
+				data, err := f.readFile(x.relFilename)
+				if err != nil {
+					log.Printf("plan9font: couldn't read subfont %q: %v", x.relFilename, err)
+					x.bad = true
+					continue
+				}
+				sub, err := ParseSubfont(data, x.lo-x.offset)
+				if err != nil {
+					log.Printf("plan9font: couldn't parse subfont %q: %v", x.relFilename, err)
+					x.bad = true
+					continue
+				}
+				x.subface = sub.(*subface)
+			}
+			return x.subface, rr
+		}
+	}
+	return nil, 0
+}
+
+// ParseFont parses a Plan 9 font file. data is the contents of that font file,
+// which gives relative filenames for subfont files. readFile returns the
+// contents of those subfont files. It is similar to io/ioutil's ReadFile
+// function, except that it takes a relative filename instead of an absolute
+// one.
+func ParseFont(data []byte, readFile func(relFilename string) ([]byte, error)) (font.Face, error) {
+	f := &face{
+		readFile: readFile,
+	}
+	// TODO: don't use strconv, to avoid the conversions from []byte to string?
+	for first := true; len(data) > 0; first = false {
+		i := bytes.IndexByte(data, '\n')
+		if i < 0 {
+			return nil, errors.New("plan9font: invalid font: no final newline")
+		}
+		row := string(data[:i])
+		data = data[i+1:]
+		if first {
+			height, s, ok := nextInt32(row)
+			if !ok {
+				return nil, fmt.Errorf("plan9font: invalid font: invalid header %q", row)
+			}
+			ascent, s, ok := nextInt32(s)
+			if !ok {
+				return nil, fmt.Errorf("plan9font: invalid font: invalid header %q", row)
+			}
+			if height < 0 || 0xffff < height || ascent < 0 || 0xffff < ascent {
+				return nil, fmt.Errorf("plan9font: invalid font: invalid header %q", row)
+			}
+			f.height, f.ascent = int(height), int(ascent)
+			continue
+		}
+		lo, s, ok := nextInt32(row)
+		if !ok {
+			return nil, fmt.Errorf("plan9font: invalid font: invalid row %q", row)
+		}
+		hi, s, ok := nextInt32(s)
+		if !ok {
+			return nil, fmt.Errorf("plan9font: invalid font: invalid row %q", row)
+		}
+		offset, s, _ := nextInt32(s)
+
+		f.runeRanges = append(f.runeRanges, runeRange{
+			lo:          lo,
+			hi:          hi,
+			offset:      offset,
+			relFilename: s,
+		})
+	}
+	return f, nil
+}
+
+func nextInt32(s string) (ret int32, remaining string, ok bool) {
+	i := 0
+	for ; i < len(s) && s[i] <= ' '; i++ {
+	}
+	j := i
+	for ; j < len(s) && s[j] > ' '; j++ {
+	}
+	n, err := strconv.ParseInt(s[i:j], 0, 32)
+	if err != nil {
+		return 0, s, false
+	}
+	for ; j < len(s) && s[j] <= ' '; j++ {
+	}
+	return int32(n), s[j:], true
+}
+
+// ParseSubfont parses a Plan 9 subfont file.
+//
+// firstRune is the first rune in the subfont file. For example, the
+// Phonetic.6.0 subfont, containing glyphs in the range U+0250 to U+02E9, would
+// set firstRune to '\u0250'.
+func ParseSubfont(data []byte, firstRune rune) (font.Face, error) {
+	data, m, err := parseImage(data)
+	if err != nil {
+		return nil, err
+	}
+	if len(data) < 3*12 {
+		return nil, errors.New("plan9font: invalid subfont: header too short")
+	}
+	n := atoi(data[0*12:])
+	height := atoi(data[1*12:])
+	ascent := atoi(data[2*12:])
+	data = data[3*12:]
+	if len(data) != 6*(n+1) {
+		return nil, errors.New("plan9font: invalid subfont: data length mismatch")
+	}
+	return &subface{
+		firstRune: firstRune,
+		n:         n,
+		height:    height,
+		ascent:    ascent,
+		fontchars: parseFontchars(data),
+		img:       m,
+	}, nil
+}
+
+// plan9Image implements that subset of the Plan 9 image feature set that is
+// used by this font file format.
+//
+// Some features, such as the repl bit and a clip rectangle, are omitted for
+// simplicity.
+type plan9Image struct {
+	depth int             // Depth of the pixels in bits.
+	width int             // Width in bytes of a single scan line.
+	rect  image.Rectangle // Extent of the image.
+	pix   []byte          // Pixel bits.
+}
+
+func (m *plan9Image) byteoffset(x, y int) int {
+	a := y * m.width
+	if m.depth < 8 {
+		// We need to always round down, but Go rounds toward zero.
+		np := 8 / m.depth
+		if x < 0 {
+			return a + (x-np+1)/np
+		}
+		return a + x/np
+	}
+	return a + x*(m.depth/8)
+}
+
+func (m *plan9Image) Bounds() image.Rectangle { return m.rect }
+func (m *plan9Image) ColorModel() color.Model { return color.AlphaModel }
+
+func (m *plan9Image) At(x, y int) color.Color {
+	if (image.Point{x, y}).In(m.rect) {
+		b := m.pix[m.byteoffset(x, y)]
+		switch m.depth {
+		case 1:
+			// CGrey, 1.
+			mask := uint8(1 << uint8(7-x&7))
+			if (b & mask) != 0 {
+				return color.Alpha{0xff}
+			}
+			return color.Alpha{0x00}
+		case 2:
+			// CGrey, 2.
+			shift := uint(x&3) << 1
+			// Place pixel at top of word.
+			y := b << shift
+			y &= 0xc0
+			// Replicate throughout.
+			y |= y >> 2
+			y |= y >> 4
+			return color.Alpha{y}
+		}
+	}
+	return color.Alpha{0x00}
+}
+
+var compressed = []byte("compressed\n")
+
+func parseImage(data []byte) (remainingData []byte, m *plan9Image, retErr error) {
+	if !bytes.HasPrefix(data, compressed) {
+		return nil, nil, errors.New("plan9font: unsupported uncompressed format")
+	}
+	data = data[len(compressed):]
+
+	const hdrSize = 5 * 12
+	if len(data) < hdrSize {
+		return nil, nil, errors.New("plan9font: invalid image: header too short")
+	}
+	hdr, data := data[:hdrSize], data[hdrSize:]
+
+	// Distinguish new channel descriptor from old ldepth. Channel descriptors
+	// have letters as well as numbers, while ldepths are a single digit
+	// formatted as %-11d.
+	new := false
+	for m := 0; m < 10; m++ {
+		if hdr[m] != ' ' {
+			new = true
+			break
+		}
+	}
+	if hdr[11] != ' ' {
+		return nil, nil, errors.New("plan9font: invalid image: bad header")
+	}
+	if !new {
+		return nil, nil, errors.New("plan9font: unsupported ldepth format")
+	}
+
+	depth := 0
+	switch s := strings.TrimSpace(string(hdr[:1*12])); s {
+	default:
+		return nil, nil, fmt.Errorf("plan9font: unsupported pixel format %q", s)
+	case "k1":
+		depth = 1
+	case "k2":
+		depth = 2
+	}
+	r := ator(hdr[1*12:])
+	if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y {
+		return nil, nil, errors.New("plan9font: invalid image: bad rectangle")
+	}
+
+	width := bytesPerLine(r, depth)
+	m = &plan9Image{
+		depth: depth,
+		width: width,
+		rect:  r,
+		pix:   make([]byte, width*r.Dy()),
+	}
+
+	miny := r.Min.Y
+	for miny != r.Max.Y {
+		if len(data) < 2*12 {
+			return nil, nil, errors.New("plan9font: invalid image: data band too short")
+		}
+		maxy := atoi(data[0*12:])
+		nb := atoi(data[1*12:])
+		data = data[2*12:]
+
+		if len(data) < nb {
+			return nil, nil, errors.New("plan9font: invalid image: data band length mismatch")
+		}
+		buf := data[:nb]
+		data = data[nb:]
+
+		if maxy <= miny || r.Max.Y < maxy {
+			return nil, nil, fmt.Errorf("plan9font: bad maxy %d", maxy)
+		}
+		// An old-format image would flip the bits here, but we don't support
+		// the old format.
+		rr := r
+		rr.Min.Y = miny
+		rr.Max.Y = maxy
+		if err := decompress(m, rr, buf); err != nil {
+			return nil, nil, err
+		}
+		miny = maxy
+	}
+	return data, m, nil
+}
+
+// Compressed data are sequences of byte codes. If the first byte b has the
+// 0x80 bit set, the next (b^0x80)+1 bytes are data. Otherwise, these two bytes
+// specify a previous string to repeat.
+const (
+	compShortestMatch = 3    // shortest match possible.
+	compWindowSize    = 1024 // window size.
+)
+
+var (
+	errDecompressBufferTooSmall = errors.New("plan9font: decompress: buffer too small")
+	errDecompressPhaseError     = errors.New("plan9font: decompress: phase error")
+)
+
+func decompress(m *plan9Image, r image.Rectangle, data []byte) error {
+	if !r.In(m.rect) {
+		return errors.New("plan9font: decompress: bad rectangle")
+	}
+	bpl := bytesPerLine(r, m.depth)
+	mem := make([]byte, compWindowSize)
+	memi := 0
+	omemi := -1
+	y := r.Min.Y
+	linei := m.byteoffset(r.Min.X, y)
+	eline := linei + bpl
+	datai := 0
+	for {
+		if linei == eline {
+			y++
+			if y == r.Max.Y {
+				break
+			}
+			linei = m.byteoffset(r.Min.X, y)
+			eline = linei + bpl
+		}
+		if datai == len(data) {
+			return errDecompressBufferTooSmall
+		}
+		c := data[datai]
+		datai++
+		if c >= 128 {
+			for cnt := c - 128 + 1; cnt != 0; cnt-- {
+				if datai == len(data) {
+					return errDecompressBufferTooSmall
+				}
+				if linei == eline {
+					return errDecompressPhaseError
+				}
+				m.pix[linei] = data[datai]
+				linei++
+				mem[memi] = data[datai]
+				memi++
+				datai++
+				if memi == len(mem) {
+					memi = 0
+				}
+			}
+		} else {
+			if datai == len(data) {
+				return errDecompressBufferTooSmall
+			}
+			offs := int(data[datai]) + ((int(c) & 3) << 8) + 1
+			datai++
+			if memi < offs {
+				omemi = memi + (compWindowSize - offs)
+			} else {
+				omemi = memi - offs
+			}
+			for cnt := (c >> 2) + compShortestMatch; cnt != 0; cnt-- {
+				if linei == eline {
+					return errDecompressPhaseError
+				}
+				m.pix[linei] = mem[omemi]
+				linei++
+				mem[memi] = mem[omemi]
+				memi++
+				omemi++
+				if omemi == len(mem) {
+					omemi = 0
+				}
+				if memi == len(mem) {
+					memi = 0
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func ator(b []byte) image.Rectangle {
+	return image.Rectangle{atop(b), atop(b[2*12:])}
+}
+
+func atop(b []byte) image.Point {
+	return image.Pt(atoi(b), atoi(b[12:]))
+}
+
+func atoi(b []byte) int {
+	i := 0
+	for ; i < len(b) && b[i] == ' '; i++ {
+	}
+	n := 0
+	for ; i < len(b) && '0' <= b[i] && b[i] <= '9'; i++ {
+		n = n*10 + int(b[i]) - '0'
+	}
+	return n
+}
+
+func bytesPerLine(r image.Rectangle, depth int) int {
+	if depth <= 0 || 32 < depth {
+		panic("invalid depth")
+	}
+	var l int
+	if r.Min.X >= 0 {
+		l = (r.Max.X*depth + 7) / 8
+		l -= (r.Min.X * depth) / 8
+	} else {
+		// Make positive before divide.
+		t := (-r.Min.X*depth + 7) / 8
+		l = t + (r.Max.X*depth+7)/8
+	}
+	return l
+}
diff --git a/font/testdata/fixed/7x13.0000 b/font/testdata/fixed/7x13.0000
new file mode 100644
index 0000000..9509cdf
--- /dev/null
+++ b/font/testdata/fixed/7x13.0000
Binary files differ
diff --git a/font/testdata/fixed/7x13.0100 b/font/testdata/fixed/7x13.0100
new file mode 100644
index 0000000..0a79f55
--- /dev/null
+++ b/font/testdata/fixed/7x13.0100
Binary files differ
diff --git a/font/testdata/fixed/7x13.0200 b/font/testdata/fixed/7x13.0200
new file mode 100644
index 0000000..e25247e
--- /dev/null
+++ b/font/testdata/fixed/7x13.0200
Binary files differ
diff --git a/font/testdata/fixed/7x13.0300 b/font/testdata/fixed/7x13.0300
new file mode 100644
index 0000000..86eb33f
--- /dev/null
+++ b/font/testdata/fixed/7x13.0300
Binary files differ
diff --git a/font/testdata/fixed/7x13.0400 b/font/testdata/fixed/7x13.0400
new file mode 100644
index 0000000..43300ad
--- /dev/null
+++ b/font/testdata/fixed/7x13.0400
Binary files differ
diff --git a/font/testdata/fixed/7x13.0500 b/font/testdata/fixed/7x13.0500
new file mode 100644
index 0000000..2d93267
--- /dev/null
+++ b/font/testdata/fixed/7x13.0500
Binary files differ
diff --git a/font/testdata/fixed/7x13.0E00 b/font/testdata/fixed/7x13.0E00
new file mode 100644
index 0000000..7c51a1e
--- /dev/null
+++ b/font/testdata/fixed/7x13.0E00
Binary files differ
diff --git a/font/testdata/fixed/7x13.1000 b/font/testdata/fixed/7x13.1000
new file mode 100644
index 0000000..019698c
--- /dev/null
+++ b/font/testdata/fixed/7x13.1000
Binary files differ
diff --git a/font/testdata/fixed/7x13.1600 b/font/testdata/fixed/7x13.1600
new file mode 100644
index 0000000..f69a977
--- /dev/null
+++ b/font/testdata/fixed/7x13.1600
Binary files differ
diff --git a/font/testdata/fixed/7x13.1E00 b/font/testdata/fixed/7x13.1E00
new file mode 100644
index 0000000..3bc5068
--- /dev/null
+++ b/font/testdata/fixed/7x13.1E00
Binary files differ
diff --git a/font/testdata/fixed/7x13.1F00 b/font/testdata/fixed/7x13.1F00
new file mode 100644
index 0000000..43b320b
--- /dev/null
+++ b/font/testdata/fixed/7x13.1F00
Binary files differ
diff --git a/font/testdata/fixed/7x13.2000 b/font/testdata/fixed/7x13.2000
new file mode 100644
index 0000000..f9244e1
--- /dev/null
+++ b/font/testdata/fixed/7x13.2000
Binary files differ
diff --git a/font/testdata/fixed/7x13.2100 b/font/testdata/fixed/7x13.2100
new file mode 100644
index 0000000..c565abb
--- /dev/null
+++ b/font/testdata/fixed/7x13.2100
Binary files differ
diff --git a/font/testdata/fixed/7x13.2200 b/font/testdata/fixed/7x13.2200
new file mode 100644
index 0000000..a992d35
--- /dev/null
+++ b/font/testdata/fixed/7x13.2200
Binary files differ
diff --git a/font/testdata/fixed/7x13.2300 b/font/testdata/fixed/7x13.2300
new file mode 100644
index 0000000..8ff099d
--- /dev/null
+++ b/font/testdata/fixed/7x13.2300
Binary files differ
diff --git a/font/testdata/fixed/7x13.2400 b/font/testdata/fixed/7x13.2400
new file mode 100644
index 0000000..99927a1
--- /dev/null
+++ b/font/testdata/fixed/7x13.2400
Binary files differ
diff --git a/font/testdata/fixed/7x13.2500 b/font/testdata/fixed/7x13.2500
new file mode 100644
index 0000000..60dc224
--- /dev/null
+++ b/font/testdata/fixed/7x13.2500
Binary files differ
diff --git a/font/testdata/fixed/7x13.2600 b/font/testdata/fixed/7x13.2600
new file mode 100644
index 0000000..1b393c2
--- /dev/null
+++ b/font/testdata/fixed/7x13.2600
Binary files differ
diff --git a/font/testdata/fixed/7x13.2700 b/font/testdata/fixed/7x13.2700
new file mode 100644
index 0000000..c39a572
--- /dev/null
+++ b/font/testdata/fixed/7x13.2700
Binary files differ
diff --git a/font/testdata/fixed/7x13.2800 b/font/testdata/fixed/7x13.2800
new file mode 100644
index 0000000..c7572de
--- /dev/null
+++ b/font/testdata/fixed/7x13.2800
Binary files differ
diff --git a/font/testdata/fixed/7x13.2A00 b/font/testdata/fixed/7x13.2A00
new file mode 100644
index 0000000..71791ac
--- /dev/null
+++ b/font/testdata/fixed/7x13.2A00
Binary files differ
diff --git a/font/testdata/fixed/7x13.3000 b/font/testdata/fixed/7x13.3000
new file mode 100644
index 0000000..fb830f4
--- /dev/null
+++ b/font/testdata/fixed/7x13.3000
Binary files differ
diff --git a/font/testdata/fixed/7x13.FB00 b/font/testdata/fixed/7x13.FB00
new file mode 100644
index 0000000..3a0b30a
--- /dev/null
+++ b/font/testdata/fixed/7x13.FB00
Binary files differ
diff --git a/font/testdata/fixed/7x13.FE00 b/font/testdata/fixed/7x13.FE00
new file mode 100644
index 0000000..3989d26
--- /dev/null
+++ b/font/testdata/fixed/7x13.FE00
Binary files differ
diff --git a/font/testdata/fixed/7x13.FF00 b/font/testdata/fixed/7x13.FF00
new file mode 100644
index 0000000..78ed398
--- /dev/null
+++ b/font/testdata/fixed/7x13.FF00
Binary files differ
diff --git a/font/testdata/fixed/README b/font/testdata/fixed/README
new file mode 100644
index 0000000..a39f8a5
--- /dev/null
+++ b/font/testdata/fixed/README
@@ -0,0 +1,9 @@
+These font files were copied from the Plan 9 Port's font/fixed directory. The
+README in that directory states that: "These fonts are converted from the BDFs
+in the XFree86 distribution. They were all marked as public domain."
+
+The Plan 9 Port is at https://github.com/9fans/plan9port and the copy was made
+from commit a78b1841 (2015-08-18).
+
+The unicode.7x13.font file also refers to a ../shinonome directory, but this
+testdata does not include those subfont files.
diff --git a/font/testdata/fixed/unicode.7x13.font b/font/testdata/fixed/unicode.7x13.font
new file mode 100644
index 0000000..337b428
--- /dev/null
+++ b/font/testdata/fixed/unicode.7x13.font
@@ -0,0 +1,68 @@
+13 10
+0x0000 0x001F 7x13.2400
+0x0000 0x00FF 7x13.0000
+0x0100 0x01FF 7x13.0100
+0x0200 0x02FF 7x13.0200
+0x0300 0x03FF 7x13.0300
+0x0400 0x04FF 7x13.0400
+0x0500 0x05FF 7x13.0500
+0x0E00 0x0EFF 7x13.0E00
+0x1000 0x10FF 7x13.1000
+0x1600 0x16FF 7x13.1600
+0x1E00 0x1EFF 7x13.1E00
+0x1F00 0x1FFF 7x13.1F00
+0x2000 0x20FF 7x13.2000
+0x2100 0x21FF 7x13.2100
+0x2200 0x22FF 7x13.2200
+0x2300 0x23FF 7x13.2300
+0x2400 0x24FF 7x13.2400
+0x2500 0x25FF 7x13.2500
+0x2600 0x26FF 7x13.2600
+0x2700 0x27FF 7x13.2700
+0x2800 0x28FF 7x13.2800
+0x2A00 0x2AFF 7x13.2A00
+0x3000 0x30fe ../shinonome/k12.3000
+0x4e00 0x4ffe ../shinonome/k12.4e00
+0x5005 0x51fe ../shinonome/k12.5005
+0x5200 0x53fa ../shinonome/k12.5200
+0x5401 0x55fe ../shinonome/k12.5401
+0x5606 0x57fc ../shinonome/k12.5606
+0x5800 0x59ff ../shinonome/k12.5800
+0x5a01 0x5bff ../shinonome/k12.5a01
+0x5c01 0x5dfe ../shinonome/k12.5c01
+0x5e02 0x5fff ../shinonome/k12.5e02
+0x600e 0x61ff ../shinonome/k12.600e
+0x6200 0x63fa ../shinonome/k12.6200
+0x6406 0x65fb ../shinonome/k12.6406
+0x6602 0x67ff ../shinonome/k12.6602
+0x6802 0x69ff ../shinonome/k12.6802
+0x6a02 0x6bf3 ../shinonome/k12.6a02
+0x6c08 0x6dfb ../shinonome/k12.6c08
+0x6e05 0x6ffe ../shinonome/k12.6e05
+0x7001 0x71ff ../shinonome/k12.7001
+0x7206 0x73fe ../shinonome/k12.7206
+0x7403 0x75ff ../shinonome/k12.7403
+0x7601 0x77fc ../shinonome/k12.7601
+0x7802 0x79fb ../shinonome/k12.7802
+0x7a00 0x7bf7 ../shinonome/k12.7a00
+0x7c00 0x7dfb ../shinonome/k12.7c00
+0x7e01 0x7ffc ../shinonome/k12.7e01
+0x8000 0x81fe ../shinonome/k12.8000
+0x8201 0x83fd ../shinonome/k12.8201
+0x8403 0x85fe ../shinonome/k12.8403
+0x8602 0x87fe ../shinonome/k12.8602
+0x8805 0x89f8 ../shinonome/k12.8805
+0x8a00 0x8b9a ../shinonome/k12.8a00
+0x8c37 0x8dff ../shinonome/k12.8c37
+0x8e08 0x8ffd ../shinonome/k12.8e08
+0x9000 0x91ff ../shinonome/k12.9000
+0x920d 0x93e8 ../shinonome/k12.920d
+0x9403 0x95e5 ../shinonome/k12.9403
+0x961c 0x97ff ../shinonome/k12.961c
+0x9801 0x99ff ../shinonome/k12.9801
+0x9a01 0x9bf5 ../shinonome/k12.9a01
+0x9c04 0x9dfd ../shinonome/k12.9c04
+0x9e1a 0x9fa0 ../shinonome/k12.9e1a
+0xFB00 0xFBFF 7x13.FB00
+0xFE00 0xFEFF 7x13.FE00
+0xFF00 0xFFFF 7x13.FF00