~sbinet/star-tex

ref: 01f50749024c419b3b1800893d74a02994b9ecbb star-tex/font/pkf/font.go -rw-r--r-- 3.1 KiB
01f50749Sebastien Binet {cmd/pk2bm,font/pkf}: first import 1 year, 1 month ago
                                                                                
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
// Copyright ©2021 The star-tex 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 pkf

import (
	"fmt"
	"io"

	"star-tex.org/x/tex/font/fixed"
	"star-tex.org/x/tex/internal/iobuf"
)

// Units are an integral number of abstract, scalable "font units". The em
// square is typically 1000 or 2048 "font units". This would map to a certain
// number (e.g. 30 pixels) of physical pixels, depending on things like the
// display resolution (DPI) and font size (e.g. a 12 point font).
type Units int32

// Font is a Packed Font.
type Font struct {
	hdr    CmdPre
	glyphs []Glyph
}

// Parse parses a Packed Font file.
func Parse(r io.Reader) (*Font, error) {
	raw, err := io.ReadAll(r)
	if err != nil {
		return nil, err
	}
	rr := iobuf.NewReader(raw)

	if opCode(rr.PeekU8()) != opPre {
		return nil, fmt.Errorf("pkf: invalid PK header")
	}

	var fnt Font
	fnt.hdr.read(rr)

specials:
	for {
		op := opCode(rr.PeekU8())
		if op < opXXX1 || op == opPost {
			break specials
		}
		switch op {
		case opXXX1, opXXX2, opXXX3, opXXX4:
			op.cmd().read(rr)
		case opYYY:
			op.cmd().read(rr)
		case opNOP:
			op.cmd().read(rr)
		case 247, 248, 249, 250, 251, 252, 253, 254, 255:
			return nil, fmt.Errorf("pkf: unexpected PK flagbyte 0x%x (%d)", op, op)
		}
	}

loop:
	for {
		op := opCode(rr.PeekU8())
		switch op {
		case opPost:
			break loop
		case opNOP:
			op.cmd().read(rr)
		case opXXX1, opXXX2, opXXX3, opXXX4:
			op.cmd().read(rr)
		case opYYY:
			op.cmd().read(rr)
		default:
			switch {
			case op < opXXX1:
				glyph, err := readGlyph(rr)
				if err != nil {
					return nil, err
				}
				fnt.glyphs = append(fnt.glyphs, glyph)
			default:
				return nil, fmt.Errorf("pkf: invalid opcode 0x%x (%d)", op, op)
			}
		}
	}
	return &fnt, nil
}

// UnitsPerEm returns the number of units per em for that font.
func (fnt *Font) UnitsPerEm() Units {
	// FIXME(sbinet): extract or infer from TFM.body.param ?
	return 1000
}

// DesignSize returns the TFM/PK font's design size.
func (fnt *Font) DesignSize() fixed.Int12_20 {
	return fixed.Int12_20(fnt.hdr.Design)
}

// Checksum returns the PK font checksum of that font.
// Checksum should be equal to the TFM checksum.
func (fnt *Font) Checksum() uint32 {
	return fnt.hdr.Checksum
}

// NumGlyphs returns the number of glyphs in this font.
func (fnt *Font) NumGlyphs() int {
	return len(fnt.glyphs)
}

// GlyphAt returns the i-th glyph from the PK font.
func (fnt *Font) GlyphAt(i int) *Glyph {
	if i < 0 || len(fnt.glyphs) <= i {
		return nil
	}
	return &fnt.glyphs[i]
}

// Glyph returns the glyph corresponding to the provided rune r,
// or nil if it is not present in the PK font.
func (fnt *Font) Glyph(r rune) *Glyph {
	g, ok := fnt.glyph(r)
	if !ok {
		return nil
	}
	return g
}

func (fnt *Font) index(r rune) int {
	for i := range fnt.glyphs {
		if fnt.glyphs[i].code == uint32(r) {
			return i
		}
	}
	return -1
}

func (fnt *Font) glyph(r rune) (*Glyph, bool) {
	for i := range fnt.glyphs {
		g := &fnt.glyphs[i]
		if g.code == uint32(r) {
			return g, true
		}
	}
	return nil, false
}