luametalatex/luametalatex-font-cff.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