322 lines
11 KiB
Lua
322 lines
11 KiB
Lua
|
local pack = string.pack
|
||
|
local strings = require'luametalatex-font-cff-data'
|
||
|
|
||
|
local function getstring(cff, str)
|
||
|
local i = strings[str] or cff.strings[str]
|
||
|
if not i then
|
||
|
i = #strings + #cff.strings + 1
|
||
|
cff.strings[str] = i
|
||
|
cff.strings[i - #strings] = str
|
||
|
end
|
||
|
return i
|
||
|
end
|
||
|
local function serialize_index(index, element_serializer)
|
||
|
local sizes = {1}
|
||
|
local length = 1
|
||
|
local data = {}
|
||
|
for i=1,#index do
|
||
|
data[i] = element_serializer(index[i])
|
||
|
length = length + #data[i]
|
||
|
sizes[#sizes+1] = length
|
||
|
end
|
||
|
data = table.concat(data)
|
||
|
if data == "" then return "\0\0" end
|
||
|
local offSize = length < 2^8 and 1 or length < 2^16 and 2 or length < 2^24 and 3 or 4
|
||
|
local offsetfmt = string.format(">I%i", offSize)
|
||
|
local offsets = ""
|
||
|
for i = #sizes,1,-1 do
|
||
|
sizes[i+1] = pack(offsetfmt, sizes[i])
|
||
|
end
|
||
|
sizes[1] = pack(">I2B", #index, offSize)
|
||
|
sizes[#sizes+1] = data
|
||
|
return table.concat(sizes)
|
||
|
end
|
||
|
local function ident(...)
|
||
|
return ...
|
||
|
end
|
||
|
local real_lookup = {
|
||
|
['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4, ['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9,
|
||
|
['.'] = 0xa, ['-'] = 0xe
|
||
|
}
|
||
|
local function dictInt(n)
|
||
|
local num = math.floor(n)
|
||
|
if num ~= n then
|
||
|
num = tostring(n)
|
||
|
local i, result, tmp = 1, string.char(0x1e)
|
||
|
while i <= #num do
|
||
|
local c = real_lookup[num:sub(i, i)]
|
||
|
if not c then -- We got an 'e'
|
||
|
c = num:sub(i+1, i+1) == '+' and 0xb or 0xc
|
||
|
repeat
|
||
|
i = i + 1
|
||
|
until num:sub(i+1, i+1) ~= '0'
|
||
|
end
|
||
|
if tmp then
|
||
|
result = result .. string.char(tmp * 16 + c)
|
||
|
tmp = nil
|
||
|
else
|
||
|
tmp = c
|
||
|
end
|
||
|
i = i + 1
|
||
|
end
|
||
|
return result .. string.char((tmp or 0xf) * 16 + 0xf)
|
||
|
elseif num >= -107 and num <= 107 then
|
||
|
return string.char(num + 139)
|
||
|
elseif num >= 108 and num <= 1131 then
|
||
|
num = num - 108
|
||
|
return string.char(247 + ((num >> 8) & 0xFF), num & 0xFF)
|
||
|
elseif num >= -1131 and num <= -108 then
|
||
|
num = -num - 108
|
||
|
return string.char(251 + ((num >> 8) & 0xFF), num & 0xFF)
|
||
|
elseif num >= -32768 and num <= 32767 then
|
||
|
return string.char(28, (num >> 8) & 0xFF, num & 0xFF)
|
||
|
else
|
||
|
return string.char(29, (num >> 24) & 0xFF, (num >> 16) & 0xFF,
|
||
|
(num >> 8) & 0xFF, num & 0xFF)
|
||
|
end
|
||
|
end
|
||
|
local function serialize_top(cff)
|
||
|
local data = dictInt(getstring(cff, cff.registry or 'Adobe'))
|
||
|
.. dictInt(getstring(cff, cff.ordering or 'Identity'))
|
||
|
.. dictInt( cff.supplement or 0)
|
||
|
.. string.char(12, 30)
|
||
|
if cff.version then
|
||
|
data = data .. dictInt(getstring(cff, cff.version)) .. string.char(0)
|
||
|
end
|
||
|
if cff.Notice then
|
||
|
data = data .. dictInt(getstring(cff, cff.Notice)) .. string.char(1)
|
||
|
end
|
||
|
if cff.FullName then
|
||
|
data = data .. dictInt(getstring(cff, cff.FullName)) .. string.char(2)
|
||
|
end
|
||
|
if cff.FamilyName then
|
||
|
data = data .. dictInt(getstring(cff, cff.FamilyName)) .. string.char(3)
|
||
|
end
|
||
|
if cff.Weight then
|
||
|
data = data .. dictInt(getstring(cff, cff.Weight)) .. string.char(4)
|
||
|
end
|
||
|
if cff.isFixedPitch then
|
||
|
data = data .. dictInt(1) .. string.char(12, 1)
|
||
|
end
|
||
|
if cff.ItalicAngle and cff.ItalicAngle ~= 0 then
|
||
|
data = data .. dictInt(cff.ItalicAngle) .. string.char(12, 2)
|
||
|
end
|
||
|
if cff.UnderlinePosition then
|
||
|
data = data .. dictInt(cff.UnderlinePosition) .. string.char(12, 3)
|
||
|
end
|
||
|
if cff.UnderlineThickness then
|
||
|
data = data .. dictInt(cff.UnderlineThickness) .. string.char(12, 4)
|
||
|
end
|
||
|
if cff.FontMatrix then
|
||
|
data = data .. dictInt(cff.FontMatrix[1]) .. dictInt(cff.FontMatrix[2])
|
||
|
.. dictInt(cff.FontMatrix[3]) .. dictInt(cff.FontMatrix[4])
|
||
|
.. dictInt(cff.FontMatrix[5]) .. dictInt(cff.FontMatrix[6])
|
||
|
.. string.char(12, 7)
|
||
|
end
|
||
|
if cff.FontBBox then
|
||
|
data = data .. dictInt(cff.FontBBox[1]) .. dictInt(cff.FontBBox[2])
|
||
|
.. dictInt(cff.FontBBox[3]) .. dictInt(cff.FontBBox[4])
|
||
|
.. string.char(5)
|
||
|
end
|
||
|
if cff.PostScript then
|
||
|
data = data .. dictInt(getstring(cff, cff.PostScript)) .. string.char(12, 21)
|
||
|
end
|
||
|
data = data .. dictInt(cff.charset_offset) .. string.char(15)
|
||
|
data = data .. dictInt(cff.charstrings_offset) .. string.char(17)
|
||
|
data = data .. dictInt(cff.fdarray_offset) .. string.char(12, 36)
|
||
|
data = data .. dictInt(cff.fdselect_offset) .. string.char(12, 37)
|
||
|
-- data = data .. dictInt(cff.private_size) .. dictInt(cff.private_offset) .. string.char(18)
|
||
|
return data
|
||
|
end
|
||
|
local function serialize_font(cff, offset0) return function(private)
|
||
|
local data = dictInt(private[3]) .. string.char(12, 38)
|
||
|
data = data .. dictInt(private[2]) .. dictInt(offset0 + private[1]) .. string.char(18)
|
||
|
return data
|
||
|
end end
|
||
|
local function va_minone(...)
|
||
|
if select('#', ...) ~= 0 then
|
||
|
return (...+1), select(2, ...)
|
||
|
end
|
||
|
end
|
||
|
-- local function serialize_fdselect(cff)
|
||
|
-- local fdselect = cff.FDSelect or {format=3, {0,1}}
|
||
|
-- if not fdselect then
|
||
|
-- return '\3\0\1\0\0\0' .. string.pack('>I2', #cff.glyphs)
|
||
|
-- end
|
||
|
-- if fdselect.format == 0 then
|
||
|
-- return string.char(0, va_minone(table.unpack(fdselect)))
|
||
|
-- elseif fdselect.format == 3 then
|
||
|
-- local fdparts = {string.pack(">BI2", 3, #fdselect)}
|
||
|
-- for i=1,#fdselect do
|
||
|
-- fdparts[i+1] = string.pack(">I2B", fdselect[i][1], fdselect[i][2]-1)
|
||
|
-- end
|
||
|
-- fdparts[#fdselect+2] = string.pack(">I2", #cff.glyphs)
|
||
|
-- return table.concat(fdparts)
|
||
|
-- else
|
||
|
-- error[[Confusion]]
|
||
|
-- end
|
||
|
-- end
|
||
|
local function serialize_fdselect(cff)
|
||
|
local fdselect = {""}
|
||
|
local lastfont = -1
|
||
|
for i, g in ipairs(cff.glyphs) do
|
||
|
local font = g.cidfont or 1
|
||
|
if font ~= lastfont then
|
||
|
fdselect[#fdselect+1] = string.pack(">I2B", i-1, font-1)
|
||
|
lastfont = font
|
||
|
end
|
||
|
end
|
||
|
if #fdselect*3+2 > #cff.glyphs+1 then
|
||
|
fdselect[1] = string.pack("B", 0)
|
||
|
for i, g in ipairs(cff.glyphs) do
|
||
|
local font = g.cidfont or 1
|
||
|
fdselect[i+1] = string.pack("B", font-1)
|
||
|
end
|
||
|
else
|
||
|
fdselect[1] = string.pack(">BI2", 3, #fdselect-1)
|
||
|
fdselect[#fdselect+1] = string.pack(">I2", #cff.glyphs)
|
||
|
end
|
||
|
return table.concat(fdselect)
|
||
|
end
|
||
|
local function serialize_private(private, subrsoffset)
|
||
|
local data = ""
|
||
|
if not private.BlueValues then
|
||
|
private.BlueValues = { }
|
||
|
end
|
||
|
local last = 0
|
||
|
for _, v in ipairs(private.BlueValues) do
|
||
|
data = data .. dictInt(v - last)
|
||
|
last = v
|
||
|
end
|
||
|
data = data .. '\6'
|
||
|
if private.OtherBlues and #private.OtherBlues ~= 0 then
|
||
|
last = 0
|
||
|
for _, v in ipairs(private.OtherBlues) do
|
||
|
data = data .. dictInt(v - last)
|
||
|
last = v
|
||
|
end
|
||
|
data = data .. '\7'
|
||
|
end
|
||
|
if private.BlueScale then
|
||
|
data = data .. dictInt(private.BlueScale) .. '\12\9'
|
||
|
end
|
||
|
if private.BlueShift then
|
||
|
data = data .. dictInt(private.BlueShift) .. '\12\10'
|
||
|
end
|
||
|
if private.BlueFuzz then
|
||
|
data = data .. dictInt(private.BlueFuzz) .. '\12\11'
|
||
|
end
|
||
|
if private.ForceBold then
|
||
|
data = data .. dictInt(1) .. '\12\14'
|
||
|
end
|
||
|
if private.StdHW then
|
||
|
data = data .. dictInt(private.StdHW) .. '\10'
|
||
|
end
|
||
|
if private.StdVW then
|
||
|
data = data .. dictInt(private.StdVW) .. '\11'
|
||
|
end
|
||
|
if private.StemSnapH and #private.StemSnapH ~= 0 then
|
||
|
last = 0
|
||
|
for _, v in ipairs(private.StemSnapH) do
|
||
|
data = data .. dictInt(v - last)
|
||
|
last = v
|
||
|
end
|
||
|
data = data .. '\12\12'
|
||
|
end
|
||
|
if private.StemSnapV and #private.StemSnapV ~= 0 then
|
||
|
last = 0
|
||
|
for _, v in ipairs(private.StemSnapV) do
|
||
|
data = data .. dictInt(v - last)
|
||
|
last = v
|
||
|
end
|
||
|
data = data .. '\12\13'
|
||
|
end
|
||
|
if subrsoffset and subrsoffset ~= 0 then
|
||
|
data = data .. dictInt(subrsoffset) .. '\19'
|
||
|
end
|
||
|
if private.defaultWidthX then
|
||
|
data = data .. dictInt(private.defaultWidthX) .. '\20'
|
||
|
end
|
||
|
if private.nominalWidthX then
|
||
|
data = data .. dictInt(private.nominalWidthX) .. '\21'
|
||
|
end
|
||
|
return data
|
||
|
end
|
||
|
local function serialize_charset(cff)
|
||
|
local data = string.char(2)
|
||
|
local last, count = -42, -1
|
||
|
for _, glyph in ipairs(cff.glyphs) do
|
||
|
if not glyph.cid then glyph.cid = glyph.index end
|
||
|
if glyph.cid ~= 0 then
|
||
|
if glyph.cid ~= last + 1 or count == 0xFFFF then
|
||
|
if count >= 0 then
|
||
|
data = data .. string.pack(">I2", count)
|
||
|
count = -1
|
||
|
end
|
||
|
data = data .. string.pack(">I2", glyph.cid)
|
||
|
end
|
||
|
last = glyph.cid
|
||
|
count = count + 1
|
||
|
end
|
||
|
end
|
||
|
if count >= 0 then
|
||
|
data = data .. string.pack(">I2", count)
|
||
|
count = -1
|
||
|
end
|
||
|
return data
|
||
|
end
|
||
|
return function(cff)
|
||
|
cff.strings = {}
|
||
|
cff.private_offset = 0
|
||
|
cff.charstrings_offset = 0
|
||
|
cff.fdarray_offset = 0
|
||
|
cff.fdselect_offset = 0
|
||
|
cff.charset_offset = 0
|
||
|
local top = serialize_index({serialize_top(cff)}, ident)
|
||
|
local data = string.char(1, 0, 4, 2) -- Assuming 16Bit offsets (Where are they used?)
|
||
|
local name = serialize_index({cff.FontName}, ident)
|
||
|
local globalsubrs = serialize_index(cff.GlobalSubrs or {}, ident)
|
||
|
local private_offsets, privates = {}, {}
|
||
|
local privates_size = 0 -- These include the localsubrs sizes
|
||
|
for i, p in ipairs(cff.Privates or {cff}) do
|
||
|
local subrs = p.Subrs
|
||
|
if not subrs or subrs and #subrs == 0 then
|
||
|
subrs = ""
|
||
|
else
|
||
|
subrs = serialize_index(subrs, ident)
|
||
|
end
|
||
|
local serialized = ""
|
||
|
if subrs ~= "" then
|
||
|
local last_size = 0
|
||
|
repeat
|
||
|
last_size = #serialized
|
||
|
serialized = serialize_private(p, last_size)
|
||
|
until last_size == #serialized
|
||
|
else
|
||
|
serialized = serialize_private(p)
|
||
|
end
|
||
|
-- serialized = serialize_private(p, -#subrs)
|
||
|
privates[i] = serialized .. subrs
|
||
|
private_offsets[i] = {privates_size + 0*#subrs, #serialized, getstring(cff, p.FontName or (cff.FontName .. '-' .. i))}
|
||
|
privates_size = privates_size + #subrs + #serialized
|
||
|
end
|
||
|
if cff.glyphs[1].index ~= 0 then
|
||
|
table.insert(cff.glyphs, 1, {index = 0, cs = string.char(14), cidfont = cff.glyphs[1].cidfont})
|
||
|
end
|
||
|
local strings = serialize_index(cff.strings, ident)
|
||
|
local charset = serialize_charset(cff)
|
||
|
local fdselect = serialize_fdselect(cff)
|
||
|
local charstrings = serialize_index(cff.glyphs, function(g) return g.cs end)
|
||
|
local pre_private, top_size = #data + #name + #strings + #globalsubrs
|
||
|
repeat
|
||
|
top_size = #top
|
||
|
cff.charstrings_offset = pre_private + top_size + privates_size
|
||
|
cff.charset_offset = cff.charstrings_offset + #charstrings
|
||
|
cff.fdselect_offset = cff.charset_offset + #charset
|
||
|
cff.fdarray_offset = cff.fdselect_offset + #fdselect
|
||
|
top = serialize_index({serialize_top(cff)}, ident)
|
||
|
until top_size == #top
|
||
|
local fdarray = serialize_index(private_offsets, serialize_font(cff, top_size + pre_private), ident)
|
||
|
return data .. name .. top .. strings .. globalsubrs .. table.concat(privates) .. charstrings .. charset .. fdselect .. fdarray
|
||
|
end
|