2019-07-17 21:14:34 +02:00
|
|
|
local function check(buf, i, afterI)
|
|
|
|
local checksum = 0
|
|
|
|
while i < afterI do
|
|
|
|
if i+60 < afterI then
|
|
|
|
local num1, num2, num3, num4, num5, num6, num7, num8, num9, num10, num11, num12, num13, num14, num15, num16, newI = string.unpack(">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", buf, i)
|
|
|
|
i = newI
|
|
|
|
checksum = checksum + num1 + num2 + num3 + num4 + num5 + num6 + num7 + num8 + num9 + num10 + num11 + num12 + num13 + num14 + num15 + num16
|
|
|
|
elseif i+28 < afterI then
|
|
|
|
local num1, num2, num3, num4, num5, num6, num7, num8, newI = string.unpack(">I4I4I4I4I4I4I4I4", buf, i)
|
|
|
|
i = newI
|
|
|
|
checksum = checksum + num1 + num2 + num3 + num4 + num5 + num6 + num7 + num8
|
|
|
|
elseif i+12 < afterI then
|
|
|
|
local num1, num2, num3, num4, newI = string.unpack(">I4I4I4I4", buf, i)
|
|
|
|
i = newI
|
|
|
|
checksum = checksum + num1 + num2 + num3 + num4
|
|
|
|
else
|
|
|
|
local num
|
|
|
|
num, i = string.unpack(">I4", buf, i)
|
|
|
|
checksum = checksum + num
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return checksum & 0xFFFFFFFF
|
|
|
|
end
|
|
|
|
local function log2floor(i)
|
|
|
|
local j = 0
|
|
|
|
if i>>8 ~= 0 then
|
|
|
|
j = j + 8
|
|
|
|
end
|
|
|
|
if i>>(j+4) ~= 0 then
|
|
|
|
j = j + 4
|
|
|
|
end
|
|
|
|
if i>>(j+2) ~= 0 then
|
|
|
|
j = j + 2
|
|
|
|
end
|
|
|
|
if i>>(j+1) ~= 0 then
|
|
|
|
j = j + 1
|
|
|
|
end
|
|
|
|
return j
|
|
|
|
end
|
|
|
|
return {
|
|
|
|
write = function(magic, tables)
|
|
|
|
local tabdata = {}
|
|
|
|
for t, val in next, tables do
|
2020-07-23 09:01:03 +02:00
|
|
|
tabdata[#tabdata+1] = {string.format("%-4s", t), val .. string.rep("\0", (#val+3&~3)-#val), #val}
|
2019-07-17 21:14:34 +02:00
|
|
|
end
|
|
|
|
table.sort(tabdata, function(a,b)return a[1]<b[1]end)
|
|
|
|
local logtabs = log2floor(#tabdata)
|
|
|
|
local tabs = {string.pack(">c4I2I2I2I2", magic, #tabdata, 1<<logtabs+4, logtabs, #tabdata-(1<<logtabs)<<4)}
|
|
|
|
local offset = #tabs[1]+#tabdata*16
|
|
|
|
local checksum, headindex = check(tabs[1], 1, 1+#tabs[1])
|
|
|
|
for i=1,#tabdata do
|
|
|
|
local tab = tabdata[i]
|
|
|
|
local data = tab[2]
|
|
|
|
if tab[1] == "head" then
|
|
|
|
headindex = i+1+#tabdata
|
|
|
|
data = data:sub(1,8) .. '\0\0\0\0' .. data:sub(13) -- Benchmarking suggests that this is faster than a LPEG version
|
|
|
|
end
|
|
|
|
local thischeck = check(data, 1, 1+tab[3])
|
|
|
|
tabs[i+1] = string.pack(">c4I4I4I4", tab[1], thischeck, offset, tab[3])
|
|
|
|
checksum = checksum + check(tabs[i+1], 1, 17) + thischeck
|
|
|
|
offset = offset + #data
|
|
|
|
tabs[i+1+#tabdata] = data
|
|
|
|
end
|
|
|
|
if headindex then
|
|
|
|
local data = tabs[headindex]
|
|
|
|
data = data:sub(1,8) .. string.pack(">I4", 0xB1B0AFBA-checksum&0xFFFFFFFF) .. data:sub(13) -- Benchmarking suggests that this is faster than a LPEG version
|
|
|
|
tabs[headindex] = data
|
|
|
|
end
|
|
|
|
return table.concat(tabs)
|
|
|
|
end,
|
|
|
|
parse = function(buf, off, fontid)
|
|
|
|
off = off or 1
|
|
|
|
local headMagic, numTables
|
|
|
|
headMagic, numTables, off = string.unpack(">c4I2", buf, off)
|
|
|
|
if headMagic == "ttcf" then
|
|
|
|
if numTables > 2 then -- numTables is actually the major version here, 1&2 are basically equal
|
|
|
|
error[[Unsupported TTC header version]]
|
|
|
|
end
|
|
|
|
headMagic, numTables, off = string.unpack(">I2I4", buf, off)
|
|
|
|
fontid = fontid or 1
|
|
|
|
if numTables < fontid then
|
|
|
|
error[[There aren't that many fonts in this file]]
|
|
|
|
end
|
|
|
|
off = string.unpack(">I4", buf, off + (fontid-1)*4)+1
|
|
|
|
headMagic, numTables, off = string.unpack(">c4I2", buf, off)
|
|
|
|
end
|
|
|
|
off = off+6
|
|
|
|
local tables = {}
|
|
|
|
for i=1,numTables do
|
|
|
|
local tag, check, offset, len, newOff = string.unpack(">c4I4I4I4", buf, off)
|
|
|
|
off = newOff
|
|
|
|
tables[tag] = {offset+1, len}
|
|
|
|
if offset+len > #buf then
|
|
|
|
error[[Font file too small]]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return headMagic, tables
|
|
|
|
end,
|
|
|
|
}
|