summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/font-syn.lua
diff options
context:
space:
mode:
authorTaco Hoekwater <taco@elvenkind.com>2009-08-23 11:11:32 +0000
committerTaco Hoekwater <taco@elvenkind.com>2009-08-23 11:11:32 +0000
commit8fc3039c82d48605b5ca8b2eda3f4fdd755681e1 (patch)
tree3cd9bbdd599bc4d1ac0409e167fee2136e4c0ec9 /Master/texmf-dist/tex/context/base/font-syn.lua
parent850fc99b7cd3ae7a20065531fe866ff7bae642ec (diff)
this is context 2009.08.19 17:10
git-svn-id: svn://tug.org/texlive/trunk@14827 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-syn.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/font-syn.lua549
1 files changed, 357 insertions, 192 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-syn.lua b/Master/texmf-dist/tex/context/base/font-syn.lua
index d9917771626..828808ec59d 100644
--- a/Master/texmf-dist/tex/context/base/font-syn.lua
+++ b/Master/texmf-dist/tex/context/base/font-syn.lua
@@ -6,57 +6,77 @@ if not modules then modules = { } end modules ['font-syn'] = {
license = "see context related readme files"
}
+local next = next
+local gsub, lower, match, find, lower, upper = string.gsub, string.lower, string.match, string.find, string.lower, string.upper
+
+local trace_names = false trackers.register("fonts.names", function(v) trace_names = v end)
+
--[[ldx--
<p>This module implements a name to filename resolver. Names are resolved
using a table that has keys filtered from the font related files.</p>
--ldx]]--
+local texsprint = (tex and tex.sprint) or print
+
fonts = fonts or { }
input = input or { }
texmf = texmf or { }
-fonts.names = { }
-fonts.names.filters = { }
-fonts.names.data = { }
-fonts.names.version = 1.04
-fonts.names.saved = false
-fonts.names.loaded = false
-fonts.names.be_clever = true
-fonts.names.enabled = true
-fonts.names.autoreload = toboolean(os.env['MTX.FONTS.AUTOLOAD'] or os.env['MTX_FONTS_AUTOLOAD'] or "no")
-fonts.names.cache = containers.define("fonts","data",fonts.names.version,true)
+fonts.names = fonts.names or { }
+fonts.names.filters = fonts.names.filters or { }
+fonts.names.data = fonts.names.data or { }
+
+local names = fonts.names
+local filters = fonts.names.filters
+
+names.version = 1.08 -- when adapting this, also changed font-dum.lua
+names.basename = "names"
+names.saved = false
+names.loaded = false
+names.be_clever = true
+names.enabled = true
+names.autoreload = toboolean(os.env['MTX.FONTS.AUTOLOAD'] or os.env['MTX_FONTS_AUTOLOAD'] or "no")
+names.cache = containers.define("fonts","data",names.version,true)
--[[ldx--
<p>It would make sense to implement the filters in the related modules,
but to keep the overview, we define them here.</p>
--ldx]]--
-fonts.names.filters.otf = fontforge.info
-fonts.names.filters.ttf = fontforge.info
-fonts.names.filters.ttc = fontforge.info
-
-function fonts.names.filters.afm(name)
- local f = io.open(name)
- if f then
- local hash = { }
- for line in f:lines() do
- local key, value = line:match("^(.+)%s+(.+)%s*$")
- if key and #key > 0 then
- hash[key:lower()] = value
- end
- if line:find("StartCharMetrics") then
- break
+filters.otf = fontloader.info
+filters.ttf = fontloader.info
+filters.ttc = fontloader.info
+filters.dfont = fontloader.info
+
+function filters.afm(name)
+ -- we could parse the afm file as well, and then report an error but
+ -- it's not worth the trouble
+ local pfbname = resolvers.find_file(file.removesuffix(name)..".pfb","pfb") or ""
+ if pfbname == "" then
+ pfbname = resolvers.find_file(file.removesuffix(file.basename(name))..".pfb","pfb") or ""
+ end
+ if pfbname ~= "" then
+ local f = io.open(name)
+ if f then
+ local hash = { }
+ for line in f:lines() do
+ local key, value = match(line,"^(.+)%s+(.+)%s*$")
+ if key and #key > 0 then
+ hash[lower(key)] = value
+ end
+ if find(line,"StartCharMetrics") then
+ break
+ end
end
+ f:close()
+ return hash
end
- f:close()
- return hash
- else
- return nil
end
+ return nil, "no matching pfb file"
end
-function fonts.names.filters.pfb(name)
- return fontforge.info(name)
+function filters.pfb(name)
+ return fontloader.info(name)
end
--[[ldx--
@@ -65,172 +85,320 @@ the file databases. Watch how we check not only for the names, but also
for combination with the weight of a font.</p>
--ldx]]--
-fonts.names.filters.list = {
- "otf", "ttf", "ttc", "afm" -- pfb is quite messy, too many messages, maybe broken
+filters.list = {
+ "otf", "ttf", "ttc", "dfont", "afm",
}
-fonts.names.filters.fixes = {
+filters.fixes = {
{ "reg$", "regular", },
{ "ita$", "italic", },
{ "ital$", "italic", },
{ "cond$", "condensed", },
+ { "book$", "", },
}
-fonts.names.xml_configuration_file = "fonts.conf" -- a bit weird format, bonus feature
-fonts.names.environment_path_variable = "osfontdir" -- the official way, in minimals etc
+names.xml_configuration_file = "fonts.conf" -- a bit weird format, bonus feature
+names.environment_path_variable = "OSFONTDIR" -- the official way, in minimals etc
+
+filters.paths = { }
+filters.names = { }
-function fonts.names.getpaths(instance)
+function names.getpaths(trace)
local hash, result = { }, { }
local function collect(t)
for i=1, #t do
- local v = input.clean_path(t[i])
- v = v:gsub("/+$","")
- local key = v:lower()
+ local v = resolvers.clean_path(t[i])
+ v = gsub(v,"/+$","")
+ local key = lower(v)
if not hash[key] then
hash[key], result[#result+1] = true, v
end
end
end
- local path = fonts.names.environment_path_variable
- if path and path ~= "" then
- collect(input.expanded_path_list(instance,path))
+ local path = names.environment_path_variable or ""
+ if path ~= "" then
+ collect(resolvers.expanded_path_list(path))
end
- local name = fonts.names.xml_configuration_file
- if name and not name == "" then
- local name = input.find_file(instance,name,"other")
- if name ~= "" then
- collect(xml.collect_texts(xml.load(name),"dir",true))
+ if xml then
+ local confname = names.xml_configuration_file or ""
+ if confname ~= "" then
+ -- first look in the tex tree
+ local name = resolvers.find_file(confname,"other")
+ if name == "" then
+ -- after all, fontconfig is a unix thing
+ name = file.join("/etc",confname)
+ if not lfs.isfile(name) then
+ name = "" -- force quit
+ end
+ end
+ if name ~= "" and lfs.isfile(name) then
+ if trace then
+ logs.report("fontnames","loading fontconfig file: %s",name)
+ end
+ local xmldata = xml.load(name)
+ -- begin of untested mess
+ xml.include(xmldata,"include","",true,function(incname)
+ if not file.is_qualified_path(incname) then
+ local path = file.dirname(name) -- main name
+ if path ~= "" then
+ incname = file.join(path,incname)
+ end
+ end
+ if lfs.isfile(incname) then
+ if trace then
+ logs.report("fontnames","merging included fontconfig file: %s",incname)
+ end
+ return io.loaddata(incname)
+ elseif trace then
+ logs.report("fontnames","ignoring included fontconfig file: %s",incname)
+ end
+ end)
+ -- end of untested mess
+ local fontdirs = xml.collect_texts(xmldata,"dir",true)
+ if trace then
+ logs.report("fontnames","%s dirs found in fontconfig",#fontdirs)
+ end
+ collect(fontdirs)
+ end
end
end
- function fonts.names.getpaths()
+ function names.getpaths()
return result
end
return result
end
-function fonts.names.identify()
- fonts.names.data = {
+function names.cleanname(name)
+ return (gsub(lower(name),"[^%a%d]",""))
+end
+
+function names.identify(verbose) -- lsr is for kpse
+ names.data = {
+ version = names.version,
mapping = { },
- version = fonts.names.version
+ -- sorted = { },
+ fallback_mapping = { },
+ -- fallback_sorted = { },
}
- local done, mapping, nofread, nofok = { }, fonts.names.data.mapping, 0, 0
- local function add(n,fontname,filename,suffix, sub)
- n = n:lower()
- if not mapping[n] then mapping[n], nofok = { suffix, fontname, filename, sub }, nofok + 1 end
- n = n:gsub("[^%a%d]","")
- if not mapping[n] then mapping[n], nofok = { suffix, fontname, filename, sub }, nofok + 1 end
- end
+ local done, mapping, fallback_mapping, nofread, nofok = { }, names.data.mapping, names.data.fallback_mapping, 0, 0
+ local cleanname = names.cleanname
local function check(result, filename, suffix, is_sub)
local fontname = result.fullname
if fontname then
- add(result.fullname, fontname, filename, suffix, is_sub)
+ local n = cleanname(result.fullname)
+ if not mapping[n] then
+ mapping[n], nofok = { lower(suffix), fontname, filename, is_sub }, nofok + 1
+ end
end
if result.fontname then
fontname = fontname or result.fontname
- add(result.fontname, fontname, filename, suffix, is_sub)
+ local n = cleanname(result.fontname)
+ if not mapping[n] then
+ mapping[n], nofok = { lower(suffix), fontname, filename, is_sub }, nofok + 1
+ end
end
- if result.familyname and result.weight then
+ if result.familyname and result.weight and result.italicangle == 0 then
local madename = result.familyname .. " " .. result.weight
fontname = fontname or madename
- add(madename, fontname, filename, suffix, is_sub)
+ local n = cleanname(fontname)
+ if not mapping[n] and not fallback_mapping[n] then
+ fallback_mapping[n], nofok = { lower(suffix), fontname, filename, is_sub }, nofok + 1
+ end
end
end
- local function identify(completename,name,suffix)
+ local trace = verbose or trace_names
+ local skip_paths = filters.paths
+ local skip_names = filters.names
+ local function identify(completename,name,suffix,storedname)
if not done[name] and io.exists(completename) then
nofread = nofread + 1
- logs.info("fontnames", "identifying " .. suffix .. " font " .. completename)
- logs.push()
- local result = fonts.names.filters[suffix](completename)
- logs.pop()
+ if #skip_paths > 0 then
+ local path = file.dirname(completename)
+ for i=1,#skip_paths do
+ if find(path,skip_paths[i]) then
+ if trace then
+ logs.report("fontnames","rejecting path of %s font %s",suffix,completename)
+ logs.push()
+ end
+ return
+ end
+ end
+ end
+ if #skip_names > 0 then
+ local base = file.basename(completename)
+ for i=1,#skip_paths do
+ if find(base,skip_names[i]) then
+ done[name] = true
+ if trace then
+ logs.report("fontnames","rejecting name of %s font %s",suffix,completename)
+ logs.push()
+ end
+ return
+ end
+ end
+ end
+ if trace_names then
+ logs.report("fontnames","identifying %s font %s",suffix,completename)
+ logs.push()
+ end
+ local result, message = filters[lower(suffix)](completename)
+ if trace then
+ logs.pop()
+ end
if result then
if not result[1] then
- check(result,name,suffix,false)
- else for _, r in ipairs(result) do
- check(r,name,suffix,true)
- end end
+ check(result,storedname,suffix,false) -- was name
+ else
+ for r=1,#result do
+ check(result[r],storedname,suffix,true) -- was name
+ end
+ end
+ if message and message ~= "" then
+ logs.report("fontnames","warning when identifying %s font %s: %s",suffix,completename,message)
+ end
+ else
+ logs.report("fontnames","error when identifying %s font %s: %s",suffix,completename,message or "unknown")
end
done[name] = true
end
end
+ local totalread, totalok = 0, 0
local function traverse(what, method)
- for n, suffix in pairs(fonts.names.filters.list) do
+ for n, suffix in ipairs(filters.list) do
nofread, nofok = 0, 0
local t = os.gettimeofday() -- use elapser
- logs.report("fontnames", string.format("identifying %s font files with suffix %s",what,suffix))
+ suffix = lower(suffix)
+ logs.report("fontnames", "identifying %s font files with suffix %s",what,suffix)
method(suffix)
- logs.report("fontnames", string.format("%s %s files identified, %s hash entries added, runtime %s seconds", nofread, what,nofok, os.gettimeofday()-t))
+ suffix = upper(suffix)
+ logs.report("fontnames", "identifying %s font files with suffix %s",what,suffix)
+ method(suffix)
+ logs.report("fontnames", "%s %s files identified, %s hash entries added, runtime %0.3f seconds",nofread,what,nofok,os.gettimeofday()-t)
+ totalread, totalok = totalread + nofread, totalok + nofok
end
end
- traverse("tree", function(suffix)
- input.with_files(texmf.instance,".*%." .. suffix .. "$", function(method,root,path,name)
- if method == "file" then
- identify(root .."/" .. path .. "/" .. name,name,suffix)
- end
- end)
- end)
- traverse("system", function(suffix)
- local pathlist = fonts.names.getpaths(texmf.instance) -- input.expanded_path_list(texmf.instance,"osfontdir")
+ local function walk_tree(pathlist,suffix)
if pathlist then
for _, path in ipairs(pathlist) do
- path = input.clean_path(path .. "/")
- path = path:gsub("/+","/")
- local pattern = path .. "*." .. suffix
- logs.report("fontnames", "globbing path " .. pattern)
+ path = resolvers.clean_path(path .. "/")
+ path = gsub(path,"/+","/")
+ local pattern = path .. "**." .. suffix -- ** forces recurse
+ logs.report("fontnames", "globbing path %s",pattern)
local t = dir.glob(pattern)
- for _, name in pairs(t) do -- ipairs
- local mode = lfs.attributes(name,'mode')
- if mode == "file" then
- identify(name,file.basename(name),suffix)
- end
+ for _, completename in pairs(t) do -- ipairs
+ identify(completename,file.basename(completename),suffix,completename)
end
end
end
+ end
+ traverse("tree", function(suffix) -- TEXTREE only
+ resolvers.with_files(".*%." .. suffix .. "$", function(method,root,path,name)
+ if method == "file" then
+ local completename = root .."/" .. path .. "/" .. name
+ identify(completename,name,suffix,name,name)
+ end
+ end)
end)
+ if texconfig.kpse_init then
+ -- we do this only for a stupid names run, not used for context itself,
+ -- using the vars is to clumsy so we just stick to a full scan instead
+ traverse("lsr", function(suffix) -- all trees
+ local pathlist = resolvers.split_path(resolvers.show_path("ls-R") or "")
+ walk_tree(pathlist,suffix)
+ end)
+ else
+ traverse("system", function(suffix) -- OSFONTDIR cum suis
+ walk_tree(names.getpaths(trace),suffix)
+ end)
+ end
local t = { }
- for _, f in ipairs(fonts.names.filters.fixes) do
+ for _, f in ipairs(filters.fixes) do
local expression, replacement = f[1], f[2]
- for k,v in pairs(mapping) do
- local fix, pos = k:gsub(expression,replacement)
+ for k,v in next, mapping do
+ local fix, pos = gsub(k,expression,replacement)
if pos > 0 and not mapping[fix] then
t[fix] = v
end
end
end
- for k,v in pairs(t) do
+ local n = 0
+ for k,v in next, t do
mapping[k] = v
+ n = n + 1
+ end
+ local rejected = 0
+ for k, v in next, mapping do
+ local kind, filename = v[1], v[3]
+ if not file.is_qualified_path(filename) and resolvers.find_file(filename,kind) == "" then
+ mapping[k] = nil
+ rejected = rejected + 1
+ end
+ end
+ if n > 0 then
+ logs.report("fontnames", "%s files read, %s normal and %s extra entries added, %s rejected, %s valid",totalread,totalok,n,rejected,totalok+n-rejected)
end
end
-function fonts.names.load(reload)
- if not fonts.names.loaded then
+function names.is_permitted(name)
+ return containers.is_usable(names.cache(), name)
+end
+function names.write_data(name,data)
+ containers.write(names.cache(),name,data)
+end
+function names.read_data(name)
+ return containers.read(names.cache(),name)
+end
+
+function names.load(reload,verbose)
+ if not names.loaded then
if reload then
- if containers.is_usable(fonts.names.cache(), "names") then
- fonts.names.identify()
- containers.write(fonts.names.cache(), "names", fonts.names.data)
+ if names.is_permitted(names.basename) then
+ names.identify(verbose)
+ names.write_data(names.basename,names.data)
+ else
+ logs.report("font table", "unable to access database cache")
end
- fonts.names.saved = true
+ names.saved = true
else
- fonts.names.data = containers.read(fonts.names.cache(), "names")
- if not fonts.names.saved then
- if table.is_empty(fonts.names.data) or table.is_empty(fonts.names.data.mapping) then
- fonts.names.load(true)
+ names.data = names.read_data(names.basename)
+ if not names.saved then
+ if table.is_empty(names.data) or table.is_empty(names.data.mapping) then
+ names.load(true)
end
- fonts.names.saved = true
+ names.saved = true
end
end
- fonts.names.loaded = true
+ local data = names.data
+ if data then
+ data.sorted = table.sortedkeys(data.mapping or { }) or { }
+ data.fallback_sorted = table.sortedkeys(data.fallback_mapping or { }) or { }
+ else
+ logs.report("font table", "accessing the data table failed")
+ end
+ names.loaded = true
end
end
-function fonts.names.list(pattern,reload)
- fonts.names.load(reload)
- if fonts.names.loaded then
+function names.list(pattern,reload)
+ names.load(reload)
+ if names.loaded then
local t = { }
- for k,v in pairs(fonts.names.data.mapping) do
- if k:find(pattern) then
- t[k] = v
+ local function list_them(mapping,sorted)
+ if mapping[pattern] then
+ t[pattern] = mapping[pattern]
+ else
+ for k,v in ipairs(sorted) do
+ if find(v,pattern) then
+ t[v] = mapping[v]
+ end
+ end
end
end
+ local data = names.data
+ if data then
+ list_them(data.mapping,data.sorted)
+ list_them(data.fallback_mapping,data.fallback_sorted)
+ end
return t
else
return nil
@@ -243,99 +411,81 @@ here is for testing purposes only (it deals with names prefixed by an
encoding name).</p>
--ldx]]--
-do
-
- local function found(name)
- if fonts.names.data then
- local result, mapping = nil, fonts.names.data.mapping
- local mn = mapping[name]
- if mn then
- return mn[2], mn[3], mn[4]
- end
- if fonts.names.be_clever then -- this will become obsolete
- local encoding, tag = name:match("^(.-)[%-%:](.+)$")
- local mt = mapping[tag]
- if tag and fonts.enc.is_known(encoding) and mt then
- return mt[1], encoding .. "-" .. mt[3], mt[4]
- end
- end
- -- name, type, file
- for k,v in pairs(mapping) do
- if k:find(name) then
- return v[2], v[3], v[4]
- end
- end
- local condensed = name:gsub("[^%a%d]","")
- local mc = mapping[condensed]
- if mc then
- return mc[2], mc[3], mc[4]
- end
- for k,v in pairs(mapping) do
- if k:find(condensed) then
- return v[2], v[3], v[4]
- end
- end
+local function found_indeed(mapping,sorted,name)
+ local mn = mapping[name]
+ if mn then
+ return mn[2], mn[3], mn[4]
+ end
+ if names.be_clever then -- this will become obsolete
+ local encoding, tag = match(name,"^(.-)[%-%:](.+)$")
+ local mt = mapping[tag]
+ if tag and fonts.enc.is_known(encoding) and mt then
+ return mt[1], encoding .. "-" .. mt[3], mt[4]
end
- return nil, nil, nil
end
-
- local reloaded = false
-
- function fonts.names.resolve(askedname, sub)
- if not askedname then
- return nil, nil
- elseif fonts.names.enabled then
- askedname = askedname:lower()
- fonts.names.load()
- local name, filename, is_sub = found(askedname)
- if not filename and not reloaded and fonts.names.autoreload then
- fonts.names.loaded = false
- reloaded = true
- io.flush()
- fonts.names.load(true)
- name, filename, is_sub = found(askedname)
- end
- if is_sub then
- return filename, name
- else
- return filename, sub
- end
- else
- return filename, sub
+ -- name, type, file
+ for k,v in next, mapping do
+ if find(k,name) then
+ return v[2], v[3], v[4]
end
end
+ local condensed = gsub(name,"[^%a%d]","")
+ local mc = mapping[condensed]
+ if mc then
+ return mc[2], mc[3], mc[4]
+ end
+ for k=1,#sorted do
+ local v = sorted[k]
+ if find(v,condensed) then
+ v = mapping[v]
+ return v[2], v[3], v[4]
+ end
+ end
+ return nil, nil, nil
+end
+local function found(name)
+ if name and name ~= "" and names.data then
+ name = names.cleanname(name)
+ local data = names.data
+ local fontname, filename, is_sub = found_indeed(data.mapping, data.sorted, name)
+ if not fontname or not filename then
+ fontname, filename, is_sub = found_indeed(data.fallback_mapping, data.fallback_sorted, name)
+ end
+ return fontname, filename, is_sub
+ else
+ return nil, nil, nil
+ end
end
---[[ldx--
-<p>A handy helper.</p>
---ldx]]--
+local reloaded = false
-function fonts.names.table(pattern,reload,all)
- local t = fonts.names.list(pattern,reload)
- if t then
- tex.sprint(tex.ctxcatcodes,"\\start\\nonknuthmode\\starttabulate[|T|T|T|T|T|]")
- tex.sprint(tex.ctxcatcodes,"\\NC hashname\\NC type\\NC fontname\\NC filename\\NC\\NR\\HL")
- for k,v in pairs(table.sortedkeys(t)) do
- if all or v == t[v][2]:lower() then
- local type, name, file = unpack(t[v])
- if type and name and file then
- tex.sprint(tex.ctxcatcodes,string.format("\\NC %s\\NC %s\\NC %s\\NC %s\\NC\\NR",v,type, name, file))
- else
- logs.report("font table", "skipping ".. v)
- end
- end
+function names.specification(askedname, sub)
+ if askedname and askedname ~= "" and names.enabled then
+ askedname = lower(askedname)
+ names.load()
+ local name, filename, is_sub = found(askedname)
+ if not filename and not reloaded and names.autoreload then
+ names.loaded = false
+ reloaded = true
+ io.flush()
+ names.load(true)
+ name, filename, is_sub = found(askedname)
end
- tex.sprint(tex.ctxcatcodes,"\\stoptabulate\\stop")
+ return name, filename, is_sub
end
end
+function names.resolve(askedname, sub)
+ local name, filename, is_sub = names.specification(askedname, sub)
+ return filename, (is_sub and name) or sub
+end
--[[ldx--
<p>Fallbacks, not permanent but a transition thing.</p>
--ldx]]--
-fonts.names.new_to_old = {
+names.new_to_old = {
["lmroman10-capsregular"] = "lmromancaps10-oblique",
["lmroman10-capsoblique"] = "lmromancaps10-regular",
["lmroman10-demi"] = "lmromandemi10-oblique",
@@ -377,4 +527,19 @@ fonts.names.new_to_old = {
["lmtypewritervarwd10-darkoblique"] = "lmmonoproplt10-boldoblique",
}
-fonts.names.old_to_new = table.swapped(fonts.names.new_to_old)
+names.old_to_new = table.swapped(names.new_to_old)
+
+function names.exists(name)
+ local fna, found = names.autoreload, false
+ names.autoreload = false
+ for k,v in ipairs(filters.list) do
+ found = (resolvers.find_file(name,v) or "") ~= ""
+ if found then
+ break
+ end
+ end
+ found = found or (resolvers.find_file(name,"tfm") or "") ~= ""
+ found = found or (names.resolve(name) or "") ~= ""
+ names.autoreload = fna
+ return found
+end