-
Notifications
You must be signed in to change notification settings - Fork 3
/
luametalatex-font-tfm.lua
151 lines (150 loc) · 4.86 KB
/
luametalatex-font-tfm.lua
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
146
147
148
149
150
151
local readfile = require'luametalatex-readfile'
local upper_mask = (1<<20)-1<<44
local shifted_sign = 1<<43
local function scale(factor1, factor2)
local result = factor1*factor2 >> 20
if result & shifted_sign == shifted_sign then
return result | upper_mask
else
return result
end
end
local function read_scaled(buf, i, count, factor)
local result = {}
for j = 1, count do
result[j] = scale(factor, string.unpack(">i4", buf, i + (j-1)*4))
end
return result, i + count * 4
end
local function parse_ligkern(buf, offset, r_boundary, kerns)
local kerning, ligatures, done = {}, {}, {}
repeat
local skip, next, op, rem
skip, next, op, rem, offset = string.unpack("BBBB", buf, offset)
if skip > 128 then break end
if next == r_boundary then next = "right_boundary" end
if not done[next] then
done[next] = true
if op >= 128 then
kerning[next] = kerns[(op - 128 << 8) + rem + 1]
else
ligatures[next] = {
type = op,
char = rem,
}
end
end
until skip == 128
return next(kerning) and kerning or nil, next(ligatures) and ligatures or nil
end
local function parse_tfm(buf, i, size)
local lf, lh, bc, ec, nw, nh, nd, ni, nl, nk, ne, np
lf, lh, bc, ec, nw, nh, nd, ni, nl, nk, ne, np, i =
string.unpack(">HHHHHHHHHHHH", buf, i)
assert(bc-1 <= ec and ec <= 255)
assert(lf == 6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np)
assert(lh >= 2)
local checksum, designsize
checksum, designsize = string.unpack(">I4i4", buf, i)
i = i + 4*lh
designsize = designsize>>4 -- Adjust TFM sizes to sp
if size < 0 then
size = math.floor(-size*designsize/1000+.5)
end
-- In contrast to TeX, we will assume that multiplication of two 32 bit
-- integers never overflows. This is safe if Lua integers have 64 bit,
-- which is the default.
local ligatureoffset, r_boundary
local widths, heights, depths, italics, kerns, parameters
local extensibles = {}
do
local i = i + (ec - bc + 1) * 4
widths, i = read_scaled(buf, i, nw, size)
heights, i = read_scaled(buf, i, nh, size)
depths, i = read_scaled(buf, i, nd, size)
italics, i = read_scaled(buf, i, ni, size)
for k,v in ipairs(italics) do if v == 0 then italics[k] = nil end end
ligatureoffset = i
if nl ~= 0 and string.byte(buf, i, i) == 255 then
r_boundary = string.byte(buf, i+1, i+1)
end
i = i + nl * 4
kerns, i = read_scaled(buf, i, nk, size)
for j = 1, ne do
local ext = {}
ext.top, ext.middle, ext.bottom, ext.extender, i = string.unpack("BBBB", buf, i)
for k,v in pairs(ext) do
if v == 0 then ext[k] = nil end
end
extensibles[j] = ext
end
local slant = np ~= 0 and string.unpack(">i4", buf, i) >> 4 or nil
parameters = read_scaled(buf, i, np, size)
parameters[1] = slant
end
local characters = {}
for cc = bc,ec do
local charinfo
charinfo, i = string.unpack(">I4", buf, i)
if (charinfo >> 24) & 0xFF ~= 0 then
local char = {
width = widths[((charinfo >> 24) & 0xFF) + 1],
height = heights[((charinfo >> 20) & 0xF) + 1],
depth = depths[((charinfo >> 16) & 0xF) + 1],
italic = italics[((charinfo >> 10) & 0xF) + 1],
}
local tag = (charinfo >> 8) & 0x3
if tag == 0 then
elseif tag == 1 then
local offset = (charinfo & 0xFF) * 4 + ligatureoffset
if string.byte(buf, offset, offset) > 128 then
offset = string.unpack(">H", buf, offset + 2) * 4 + ligatureoffset
end
char.kerns, char.ligatures = parse_ligkern(buf, offset, r_boundary, kerns)
elseif tag == 2 then
char.next = charinfo & 0xFF
elseif tag == 3 then
char.extensible = extensibles[(charinfo & 0xFF) + 1]
end
characters[cc] = char
end
end
if nl ~= 0 and string.byte(buf, ligatureoffset + (nl-1) * 4) == 255 then
local char = {}
characters.left_boundary = char
local offset = string.unpack(">H", buf, ligatureoffset + nl * 4 - 2) * 4 + ligatureoffset
char.kerns, char.ligatures = parse_ligkern(buf, offset, r_boundary, kerns)
end
return {
checksum = checksum,
direction = 0,
embedding = "unknown",
-- encodingbytes = 0,
extend = 1000,
format = "unknown",
identity = "unknown",
mode = 0,
slant = 0,
squeeze = 1000,
oldmath = true,
streamprovider = 0,
tounicode = 0,
type = "unknown",
units_per_em = 0,
used = false,
width = 0,
writingmode = "unknown",
size = size,
designsize = designsize,
parameters = parameters,
characters = characters,
}
end
local basename = ((1-lpeg.S'\\/')^0*lpeg.S'\\/')^0*lpeg.C((1-lpeg.P'.tfm'*-1)^0)
return function(name, size)
local file <close> = readfile('tfm', name)
if not file then return end
local result = parse_tfm(file(), 1, size)
result.name = basename:match(name)
return result
end