Merge x/exp/shiny/font to x/image/font.
This commit is contained in:
commit
00f88f596f
105
example/font/main.go
Normal file
105
example/font/main.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
204
font/font.go
Normal file
204
font/font.go
Normal file
|
@ -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
|
||||||
|
)
|
93
font/plan9font/example_test.go
Normal file
93
font/plan9font/example_test.go
Normal file
|
@ -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 (<28>).
|
||||||
|
//
|
||||||
|
// 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...........
|
||||||
|
}
|
556
font/plan9font/plan9font.go
Normal file
556
font/plan9font/plan9font.go
Normal file
|
@ -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
|
||||||
|
}
|
BIN
font/testdata/fixed/7x13.0000
vendored
Normal file
BIN
font/testdata/fixed/7x13.0000
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.0100
vendored
Normal file
BIN
font/testdata/fixed/7x13.0100
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.0200
vendored
Normal file
BIN
font/testdata/fixed/7x13.0200
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.0300
vendored
Normal file
BIN
font/testdata/fixed/7x13.0300
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.0400
vendored
Normal file
BIN
font/testdata/fixed/7x13.0400
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.0500
vendored
Normal file
BIN
font/testdata/fixed/7x13.0500
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.0E00
vendored
Normal file
BIN
font/testdata/fixed/7x13.0E00
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.1000
vendored
Normal file
BIN
font/testdata/fixed/7x13.1000
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.1600
vendored
Normal file
BIN
font/testdata/fixed/7x13.1600
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.1E00
vendored
Normal file
BIN
font/testdata/fixed/7x13.1E00
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.1F00
vendored
Normal file
BIN
font/testdata/fixed/7x13.1F00
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2000
vendored
Normal file
BIN
font/testdata/fixed/7x13.2000
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2100
vendored
Normal file
BIN
font/testdata/fixed/7x13.2100
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2200
vendored
Normal file
BIN
font/testdata/fixed/7x13.2200
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2300
vendored
Normal file
BIN
font/testdata/fixed/7x13.2300
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2400
vendored
Normal file
BIN
font/testdata/fixed/7x13.2400
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2500
vendored
Normal file
BIN
font/testdata/fixed/7x13.2500
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2600
vendored
Normal file
BIN
font/testdata/fixed/7x13.2600
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2700
vendored
Normal file
BIN
font/testdata/fixed/7x13.2700
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2800
vendored
Normal file
BIN
font/testdata/fixed/7x13.2800
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.2A00
vendored
Normal file
BIN
font/testdata/fixed/7x13.2A00
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.3000
vendored
Normal file
BIN
font/testdata/fixed/7x13.3000
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.FB00
vendored
Normal file
BIN
font/testdata/fixed/7x13.FB00
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.FE00
vendored
Normal file
BIN
font/testdata/fixed/7x13.FE00
vendored
Normal file
Binary file not shown.
BIN
font/testdata/fixed/7x13.FF00
vendored
Normal file
BIN
font/testdata/fixed/7x13.FF00
vendored
Normal file
Binary file not shown.
9
font/testdata/fixed/README
vendored
Normal file
9
font/testdata/fixed/README
vendored
Normal file
|
@ -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.
|
68
font/testdata/fixed/unicode.7x13.font
vendored
Normal file
68
font/testdata/fixed/unicode.7x13.font
vendored
Normal file
|
@ -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
|
Loading…
Reference in New Issue
Block a user