2019-07-17 21:14:34 +02:00
|
|
|
local fontcmds = {
|
|
|
|
[243] = ">I1I4I4I4BB",
|
|
|
|
[244] = ">I2I4I4I4BB",
|
|
|
|
[245] = ">I3I4I4I4BB",
|
|
|
|
[246] = ">I4I4I4I4BB",
|
|
|
|
}
|
|
|
|
local function read_fonts(buf, i, fonts, size)
|
|
|
|
local cmd = fontcmds[string.byte(buf, i)]
|
|
|
|
if not cmd then return i end
|
|
|
|
local fid, check, scale, designsize, arealen, namelen, i =
|
|
|
|
string.unpack(cmd, buf, i + 1)
|
2020-06-18 01:13:00 +02:00
|
|
|
fid = fid + 1 -- We prefer 1-based arrays
|
2019-07-17 21:14:34 +02:00
|
|
|
local fsize = size * scale >> 20
|
|
|
|
if fonts[fid] then error[[font number reused in VF file]] end
|
|
|
|
fonts[fid] = {
|
2020-06-18 01:13:00 +02:00
|
|
|
area = arealen > 0 and string.sub(buf, i, i+arealen-1) or nil,
|
2019-07-17 21:14:34 +02:00
|
|
|
name = string.sub(buf, i+arealen, i+arealen+namelen-1),
|
|
|
|
size = fsize,
|
|
|
|
designsize = designsize >> 4,
|
|
|
|
checksum = check,
|
|
|
|
}
|
|
|
|
return read_fonts(buf, i+arealen+namelen, fonts, size)
|
|
|
|
end
|
|
|
|
local Cmds = {
|
|
|
|
[1] = ">I1",
|
|
|
|
[2] = ">I2",
|
|
|
|
[3] = ">I3",
|
|
|
|
[4] = ">I4",
|
|
|
|
}
|
|
|
|
local cmds = {
|
|
|
|
[1] = ">i1",
|
|
|
|
[2] = ">i2",
|
|
|
|
[3] = ">i3",
|
|
|
|
[4] = ">i4",
|
|
|
|
}
|
|
|
|
local xxx = {
|
|
|
|
[239] = ">s1",
|
|
|
|
[240] = ">s2",
|
|
|
|
[241] = ">s3",
|
|
|
|
[242] = ">s4",
|
|
|
|
}
|
|
|
|
local function read_chars(buf, i, characters, size)
|
|
|
|
local cmd = string.byte(buf, i)
|
|
|
|
if cmd > 242 then return i end
|
|
|
|
local code, tfmwidth
|
|
|
|
if cmd == 242 then
|
|
|
|
cmd, code, tfmwidth, i = string.unpack(">I4I4I4", buf, i + 1)
|
|
|
|
else
|
|
|
|
code, tfmwidth, i = string.unpack(">BI3", buf, i + 1)
|
|
|
|
end
|
|
|
|
local commands = {}
|
|
|
|
local character = {
|
|
|
|
width = tfmwidth, -- Unscaled for compatibility with LuaTeX
|
|
|
|
commands = commands,
|
|
|
|
}
|
|
|
|
characters[code] = character
|
|
|
|
local after = i + cmd
|
|
|
|
local w, x, y, z, stack = 0, 0, 0, 0, {}
|
|
|
|
while i < after do
|
|
|
|
local cmd = string.byte(buf, i)
|
|
|
|
if cmd <= 131 then
|
|
|
|
if cmd >= 128 then
|
|
|
|
cmd, i = string.unpack(Cmds[cmd-127], buf, i + 1)
|
|
|
|
else
|
|
|
|
i = i + 1
|
|
|
|
end
|
|
|
|
commands[#commands + 1] = { "char", cmd }
|
|
|
|
elseif cmd == 132 then
|
|
|
|
local height, width
|
|
|
|
height, width, i = string.unpack(">I4I4", buf, i + 1)
|
|
|
|
commands[#commands + 1] =
|
|
|
|
{ "rule", height * size >> 20, width * size >> 20 }
|
|
|
|
elseif cmd <= 136 then
|
|
|
|
cmd, i = string.unpack(Cmds[cmd-132], buf, i + 1)
|
|
|
|
commands[#commands + 1] = { "push" }
|
|
|
|
commands[#commands + 1] = { "char", cmd }
|
|
|
|
commands[#commands + 1] = { "pop" }
|
|
|
|
elseif cmd == 137 then
|
|
|
|
local height, width
|
|
|
|
height, width, i = string.unpack(">I4I4", buf, i + 1)
|
|
|
|
commands[#commands + 1] = { "push" }
|
|
|
|
commands[#commands + 1] =
|
|
|
|
{ "rule", height * size >> 20, width * size >> 20 }
|
|
|
|
commands[#commands + 1] = { "pop" }
|
|
|
|
elseif cmd == 138 then -- NOP
|
|
|
|
i = i + 1
|
|
|
|
elseif cmd <= 140 then
|
|
|
|
error[[Invalid command in packet]]
|
|
|
|
elseif cmd == 141 then
|
|
|
|
stack[#stack+1] = {w, x, y, z}
|
|
|
|
commands[#commands + 1] = { "push" }
|
|
|
|
i = i + 1
|
|
|
|
elseif cmd == 142 then
|
|
|
|
local top = stack[#stack]
|
|
|
|
if not top then error[[Attempt to pop with empty stack]] end
|
|
|
|
stack[#stack] = nil
|
|
|
|
w, x, y, z = top[1], top[2], top[3], top[4]
|
|
|
|
commands[#commands + 1] = { "pop" }
|
|
|
|
i = i + 1
|
|
|
|
elseif cmd <= 146 then
|
|
|
|
cmd, i = string.unpack(cmds[cmd-142], buf, i + 1)
|
|
|
|
commands[#commands + 1] = { "right", (cmd * size >> 20) | (cmd < 0 and 0xFFFFFFFF00000000 or 0) }
|
|
|
|
elseif cmd == 147 then
|
|
|
|
commands[#commands + 1] = { "right", w }
|
|
|
|
i = i + 1
|
|
|
|
elseif cmd <= 151 then
|
|
|
|
cmd, i = string.unpack(cmds[cmd-147], buf, i + 1)
|
|
|
|
w = (cmd * size >> 20) | (cmd < 0 and 0xFFFFFFFF00000000 or 0)
|
|
|
|
commands[#commands + 1] = { "right", w }
|
|
|
|
elseif cmd == 152 then
|
|
|
|
commands[#commands + 1] = { "right", x }
|
|
|
|
i = i + 1
|
|
|
|
elseif cmd <= 156 then
|
|
|
|
cmd, i = string.unpack(cmds[cmd-152], buf, i + 1)
|
|
|
|
x = (cmd * size >> 20) | (cmd < 0 and 0xFFFFFFFF00000000 or 0)
|
|
|
|
commands[#commands + 1] = { "right", x }
|
|
|
|
elseif cmd <= 160 then
|
|
|
|
cmd, i = string.unpack(cmds[cmd-156], buf, i + 1)
|
|
|
|
commands[#commands + 1] = { "down", (cmd * size >> 20) | (cmd < 0 and 0xFFFFFFFF00000000 or 0) }
|
|
|
|
elseif cmd == 161 then
|
|
|
|
commands[#commands + 1] = { "down", y }
|
|
|
|
i = i + 1
|
|
|
|
elseif cmd <= 165 then
|
|
|
|
cmd, i = string.unpack(cmds[cmd-161], buf, i + 1)
|
|
|
|
y = (cmd * size >> 20) | (cmd < 0 and 0xFFFFFFFF00000000 or 0)
|
|
|
|
commands[#commands + 1] = { "down", y }
|
|
|
|
elseif cmd == 166 then
|
|
|
|
commands[#commands + 1] = { "down", z }
|
|
|
|
i = i + 1
|
|
|
|
elseif cmd <= 170 then
|
|
|
|
cmd, i = string.unpack(cmds[cmd-166], buf, i + 1)
|
|
|
|
z = (cmd * size >> 20) | (cmd < 0 and 0xFFFFFFFF00000000 or 0)
|
|
|
|
commands[#commands + 1] = { "down", z }
|
|
|
|
elseif cmd <= 238 then
|
|
|
|
if cmd >= 235 then
|
|
|
|
cmd, i = string.unpack(Cmds[cmd-234], buf, i + 1)
|
|
|
|
else
|
2020-06-18 01:13:00 +02:00
|
|
|
cmd, i = cmd - 171, i + 1
|
2019-07-17 21:14:34 +02:00
|
|
|
end
|
2020-06-18 01:13:00 +02:00
|
|
|
commands[#commands + 1] = { "font", cmd + 1 } -- 1-based fonts
|
2019-07-17 21:14:34 +02:00
|
|
|
elseif xxx[cmd] then
|
|
|
|
cmd, i = string.unpack(xxx[cmd], buf, i + 1)
|
|
|
|
commands[#commands + 1] = { "special", cmd }
|
|
|
|
else
|
|
|
|
error[[Invalid command in packet]]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if i > after then error[[Ill-formed packet]] end
|
|
|
|
return read_chars(buf, after, characters, size)
|
|
|
|
end
|
|
|
|
local function parse_vf(buf, i, size)
|
|
|
|
local font = {}
|
|
|
|
local magic, designsize
|
|
|
|
magic, font.header, font.checksum, designsize, i =
|
|
|
|
string.unpack(">Hs1I4I4", buf, i)
|
|
|
|
if magic ~= 247*256+202 then error[[Not a VF file]] end
|
|
|
|
font.designsize = designsize >> 4
|
|
|
|
|
|
|
|
local fonts, characters = {}, {}
|
|
|
|
font.fonts, font.characters = fonts, characters
|
|
|
|
|
|
|
|
i = read_fonts(buf, i, fonts, size)
|
|
|
|
i = read_chars(buf, i, characters, size)
|
|
|
|
|
2020-06-18 01:13:00 +02:00
|
|
|
return font
|
2019-07-17 21:14:34 +02:00
|
|
|
end
|
|
|
|
local basename = ((1-lpeg.S'\\/')^0*lpeg.S'\\/')^0*lpeg.C((1-lpeg.P'.tfm'*-1)^0)
|
|
|
|
return function(name, size, must_exist)
|
|
|
|
local filename = kpse.find_file(name, 'vf', must_exist)
|
2020-06-18 01:13:00 +02:00
|
|
|
if not filename then return end
|
2020-07-09 14:53:17 +02:00
|
|
|
local f = io.open(filename, 'rb')
|
2019-07-17 21:14:34 +02:00
|
|
|
if not f then return end
|
|
|
|
local buf = f:read'*a'
|
|
|
|
f:close()
|
2020-06-18 01:13:00 +02:00
|
|
|
local result = parse_vf(buf, 1, size)
|
2019-07-17 21:14:34 +02:00
|
|
|
result.name = basename:match(name)
|
|
|
|
return result
|
|
|
|
end
|