Current File : //usr/share/texlive/texmf-dist/tex/luatex/luaotfload/fontloader-font-shp.lua |
if not modules then modules = { } end modules ['font-shp'] = {
version = 1.001,
comment = "companion to font-ini.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
local tonumber, next = tonumber, next
local concat = table.concat
local formatters, lower = string.formatters, string.lower
local otf = fonts.handlers.otf
local afm = fonts.handlers.afm
local pfb = fonts.handlers.pfb
local hashes = fonts.hashes
local identifiers = hashes.identifiers
local version = 0.009
local shapescache = containers.define("fonts", "shapes", version, true)
local streamscache = containers.define("fonts", "streams", version, true)
-- shapes (can be come a separate file at some point)
local compact_streams = false
directives.register("fonts.streams.compact", function(v) compact_streams = v end)
local function packoutlines(data,makesequence)
local subfonts = data.subfonts
if subfonts then
for i=1,#subfonts do
packoutlines(subfonts[i],makesequence)
end
return
end
local common = data.segments
if common then
return
end
local glyphs = data.glyphs
if not glyphs then
return
end
if makesequence then
for index=0,#glyphs do
local glyph = glyphs[index]
if glyph then
local segments = glyph.segments
if segments then
local sequence = { }
local nofsequence = 0
for i=1,#segments do
local segment = segments[i]
local nofsegment = #segment
-- why last first ... needs documenting
nofsequence = nofsequence + 1
sequence[nofsequence] = segment[nofsegment]
for i=1,nofsegment-1 do
nofsequence = nofsequence + 1
sequence[nofsequence] = segment[i]
end
end
glyph.sequence = sequence
glyph.segments = nil
end
end
end
else
local hash = { }
local common = { }
local reverse = { }
local last = 0
for index=0,#glyphs do
local glyph = glyphs[index]
if glyph then
local segments = glyph.segments
if segments then
for i=1,#segments do
local h = concat(segments[i]," ")
hash[h] = (hash[h] or 0) + 1
end
end
end
end
for index=0,#glyphs do
local glyph = glyphs[index]
if glyph then
local segments = glyph.segments
if segments then
for i=1,#segments do
local segment = segments[i]
local h = concat(segment," ")
if hash[h] > 1 then -- minimal one shared in order to hash
local idx = reverse[h]
if not idx then
last = last + 1
reverse[h] = last
common[last] = segment
idx = last
end
segments[i] = idx
end
end
end
end
end
if last > 0 then
data.segments = common
end
end
end
local function unpackoutlines(data)
local subfonts = data.subfonts
if subfonts then
for i=1,#subfonts do
unpackoutlines(subfonts[i])
end
return
end
local common = data.segments
if not common then
return
end
local glyphs = data.glyphs
if not glyphs then
return
end
for index=0,#glyphs do
local glyph = glyphs[index]
if glyph then
local segments = glyph.segments
if segments then
for i=1,#segments do
local c = common[segments[i]]
if c then
segments[i] = c
end
end
end
end
end
data.segments = nil
end
-- todo: loaders per format
local readers = otf.readers
local cleanname = otf.readers.helpers.cleanname
local function makehash(filename,sub,instance)
local name = cleanname(file.basename(filename))
if instance then
return formatters["%s-%s-%s"](name,sub or 0,cleanname(instance))
else
return formatters["%s-%s"] (name,sub or 0)
end
end
local function loadoutlines(cache,filename,sub,instance)
local base = file.basename(filename)
local name = file.removesuffix(base)
local kind = file.suffix(filename)
local attr = lfs.attributes(filename)
local size = attr and attr.size or 0
local time = attr and attr.modification or 0
local sub = tonumber(sub)
-- fonts.formats
if size > 0 and (kind == "otf" or kind == "ttf" or kind == "tcc") then
local hash = makehash(filename,sub,instance)
data = containers.read(cache,hash)
if not data or data.time ~= time or data.size ~= size then
data = otf.readers.loadshapes(filename,sub,instance)
if data then
data.size = size
data.format = data.format or (kind == "otf" and "opentype") or "truetype"
data.time = time
packoutlines(data)
containers.write(cache,hash,data)
data = containers.read(cache,hash) -- frees old mem
end
end
unpackoutlines(data)
elseif size > 0 and (kind == "pfb") then
local hash = containers.cleanname(base) -- including suffix
data = containers.read(cache,hash)
if not data or data.time ~= time or data.size ~= size then
data = afm.readers.loadshapes(filename)
if data then
data.size = size
data.format = "type1"
data.time = time
packoutlines(data)
containers.write(cache,hash,data)
data = containers.read(cache,hash) -- frees old mem
end
end
unpackoutlines(data)
else
data = {
filename = filename,
size = 0,
time = time,
format = "unknown",
units = 1000,
glyphs = { }
}
end
return data
end
local function cachethem(cache,hash,data)
containers.write(cache,hash,data,compact_streams) -- arg 4 aka fast
return containers.read(cache,hash) -- frees old mem
end
local function loadstreams(cache,filename,sub,instance)
local base = file.basename(filename)
local name = file.removesuffix(base)
local kind = lower(file.suffix(filename))
local attr = lfs.attributes(filename)
local size = attr and attr.size or 0
local time = attr and attr.modification or 0
local sub = tonumber(sub)
if size > 0 and (kind == "otf" or kind == "ttf" or kind == "ttc") then
local hash = makehash(filename,sub,instance)
data = containers.read(cache,hash)
if not data or data.time ~= time or data.size ~= size then
data = otf.readers.loadshapes(filename,sub,instance,true)
if data then
local glyphs = data.glyphs
local streams = { }
if glyphs then
for i=0,#glyphs do
local glyph = glyphs[i]
if glyph then
streams[i] = glyph.stream or ""
else
streams[i] = ""
end
end
end
data.streams = streams
data.glyphs = nil
data.size = size
data.format = data.format or (kind == "otf" and "opentype") or "truetype"
data.time = time
data = cachethem(cache,hash,data)
end
end
elseif size > 0 and (kind == "pfb") then
local hash = makehash(filename,sub,instance)
data = containers.read(cache,hash)
if not data or data.time ~= time or data.size ~= size then
local names, encoding, streams, metadata = pfb.loadvector(filename,false,true)
if streams then
local fontbbox = metadata.fontbbox or { 0, 0, 0, 0 }
for i=0,#streams do
streams[i] = streams[i].stream or "\14"
end
data = {
filename = filename,
size = size,
time = time,
format = "type1",
streams = streams,
fontheader = {
fontversion = metadata.version,
units = 1000, -- can this be different?
xmin = fontbbox[1],
ymin = fontbbox[2],
xmax = fontbbox[3],
ymax = fontbbox[4],
},
horizontalheader = {
ascender = 0,
descender = 0,
},
maximumprofile = {
nofglyphs = #streams + 1,
},
names = {
copyright = metadata.copyright,
family = metadata.familyname,
fullname = metadata.fullname,
fontname = metadata.fontname,
subfamily = metadata.subfamilyname,
trademark = metadata.trademark,
notice = metadata.notice,
version = metadata.version,
},
cffinfo = {
familyname = metadata.familyname,
fullname = metadata.fullname,
italicangle = metadata.italicangle,
monospaced = metadata.isfixedpitch and true or false,
underlineposition = metadata.underlineposition,
underlinethickness = metadata.underlinethickness,
weight = metadata.weight,
},
}
data = cachethem(cache,hash,data)
end
end
else
data = {
filename = filename,
size = 0,
time = time,
format = "unknown",
streams = { }
}
end
return data
end
local loadedshapes = { }
local loadedstreams = { }
local function loadoutlinedata(fontdata,streams)
local properties = fontdata.properties
local filename = properties.filename
local subindex = fontdata.subindex
local instance = properties.instance
local hash = makehash(filename,subindex,instance)
local loaded = loadedshapes[hash]
if not loaded then
loaded = loadoutlines(shapescache,filename,subindex,instance)
loadedshapes[hash] = loaded
end
return loaded
end
hashes.shapes = table.setmetatableindex(function(t,k)
local f = identifiers[k]
if f then
return loadoutlinedata(f)
end
end)
local function getstreamhash(fontid)
local fontdata = identifiers[fontid]
if fontdata then
local properties = fontdata.properties
return makehash(properties.filename,properties.subfont,properties.instance)
end
end
local function loadstreamdata(fontdata)
local properties = fontdata.properties
local shared = fontdata.shared
local rawdata = shared and shared.rawdata
local metadata = rawdata and rawdata.metadata
local filename = properties.filename
local subindex = metadata and metadata.subfontindex
local instance = properties.instance
local hash = makehash(filename,subindex,instance)
local loaded = loadedstreams[hash]
if not loaded then
loaded = loadstreams(streamscache,filename,subindex,instance)
loadedstreams[hash] = loaded
end
return loaded
end
hashes.streams = table.setmetatableindex(function(t,k)
local f = identifiers[k]
if f then
return loadstreamdata(f)
end
end)
otf.loadoutlinedata = loadoutlinedata -- not public
otf.loadstreamdata = loadstreamdata -- not public
otf.loadshapes = loadshapes
otf.getstreamhash = getstreamhash -- not public, might move to other namespace
local streams = fonts.hashes.streams
-- we can now assume that luatex has this one
callback.register("glyph_stream_provider",function(id,index,mode)
if id > 0 then
local streams = streams[id].streams
-- print(id,index,streams[index])
if streams then
return streams[index] or ""
end
end
return ""
end)