summaryrefslogtreecommitdiff
path: root/otfl-font-nms.lua
diff options
context:
space:
mode:
Diffstat (limited to 'otfl-font-nms.lua')
-rw-r--r--otfl-font-nms.lua770
1 files changed, 0 insertions, 770 deletions
diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua
deleted file mode 100644
index 8290e17..0000000
--- a/otfl-font-nms.lua
+++ /dev/null
@@ -1,770 +0,0 @@
-if not modules then modules = { } end modules ['font-nms'] = {
- version = 1.002,
- comment = "companion to luaotfload.lua",
- author = "Khaled Hosny and Elie Roux",
- copyright = "Luaotfload Development Team",
- license = "GNU GPL v2"
-}
-
-fonts = fonts or { }
-fonts.names = fonts.names or { }
-
-local names = fonts.names
-local names_dir = "luatex-cache/generic/names"
-names.version = 2.010 -- not the same as in context
-names.data = nil
-names.path = {
- basename = "otfl-names.lua",
- dir = file.join(kpse.expand_var("$TEXMFVAR"), names_dir),
-}
-
-local success = pcall(require, "luatexbase.modutils")
-if success then
- success = pcall(luatexbase.require_module, "lualatex-platform", "2011/03/30")
-end
-local get_installed_fonts
-if success then
- get_installed_fonts = lualatex.platform.get_installed_fonts
-else
- function get_installed_fonts()
- end
-end
-
-local splitpath, expandpath = file.split_path, kpse.expand_path
-local glob, basename = dir.glob, file.basename
-local extname = file.extname
-local upper, lower, format = string.upper, string.lower, string.format
-local gsub, match, rpadd = string.gsub, string.match, string.rpadd
-local gmatch, sub, find = string.gmatch, string.sub, string.find
-local utfgsub = unicode.utf8.gsub
-
-local trace_short = false --tracing adapted to rebuilding of the database inside a document
-local trace_search = false --trackers.register("names.search", function(v) trace_search = v end)
-local trace_loading = false --trackers.register("names.loading", function(v) trace_loading = v end)
-
-local function sanitize(str)
- if str then
- return utfgsub(lower(str), "[^%a%d]", "")
- else
- return str -- nil
- end
-end
-
-local function fontnames_init()
- return {
- mappings = { },
- status = { },
- version = names.version,
- }
-end
-
-local function make_name(path)
- return file.replacesuffix(path, "lua"), file.replacesuffix(path, "luc")
-end
-
-local function load_names()
- local path = file.join(names.path.dir, names.path.basename)
- local luaname, lucname = make_name(path)
- local foundname
- local data
- if file.isreadable(lucname) then
- data = dofile(lucname)
- foundname = lucname
- elseif file.isreadable(luaname) then
- data = dofile(luaname)
- foundname = luaname
- end
- if data then
- logs.info("Font names database loaded", "%s", foundname)
- else
- logs.info([[Font names database not found, generating new one.
- This can take several minutes; please be patient.]])
- data = names.update(fontnames_init())
- names.save(data)
- end
- texio.write_nl("")
- return data
-end
-
-local synonyms = {
- regular = { "normal", "roman", "plain", "book", "medium" },
- bold = { "boldregular", "demi", "demibold" },
- italic = { "regularitalic", "normalitalic", "oblique", "slanted" },
- bolditalic = { "boldoblique", "boldslanted", "demiitalic", "demioblique", "demislanted", "demibolditalic" },
-}
-
-local loaded = false
-local reloaded = false
-
-function names.resolve(_,_,specification) -- the 1st two parameters are used by ConTeXt
- local name = sanitize(specification.name)
- local style = sanitize(specification.style) or "regular"
-
- local size
- if specification.optsize then
- size = tonumber(specification.optsize)
- elseif specification.size then
- size = specification.size / 65536
- end
-
-
- if not loaded then
- names.data = names.load()
- loaded = true
- end
-
- local data = names.data
- if type(data) == "table" and data.version == names.version then
- if data.mappings then
- local found = { }
- for _,face in next, data.mappings do
- local family = sanitize(face.names.family)
- local subfamily = sanitize(face.names.subfamily)
- local fullname = sanitize(face.names.fullname)
- local psname = sanitize(face.names.psname)
- local fontname = sanitize(face.fontname)
- local pfullname = sanitize(face.fullname)
- local optsize, dsnsize, maxsize, minsize
- if #face.size > 0 then
- optsize = face.size
- dsnsize = optsize[1] and optsize[1] / 10
- -- can be nil
- maxsize = optsize[2] and optsize[2] / 10 or dsnsize
- minsize = optsize[3] and optsize[3] / 10 or dsnsize
- end
- if name == family then
- if subfamily == style then
- if optsize then
- if dsnsize == size
- or (size > minsize and size <= maxsize) then
- found[1] = face
- break
- else
- found[#found+1] = face
- end
- else
- found[1] = face
- break
- end
- elseif synonyms[style] and
- table.contains(synonyms[style], subfamily) then
- if optsize then
- if dsnsize == size
- or (size > minsize and size <= maxsize) then
- found[1] = face
- break
- else
- found[#found+1] = face
- end
- else
- found[1] = face
- break
- end
- elseif subfamily == "regular" or
- table.contains(synonyms.regular, subfamily) then
- found.fallback = face
- end
- else
- if name == fullname
- or name == pfullname
- or name == fontname
- or name == psname then
- if optsize then
- if dsnsize == size
- or (size > minsize and size <= maxsize) then
- found[1] = face
- break
- else
- found[#found+1] = face
- end
- else
- found[1] = face
- break
- end
- end
- end
- end
- if #found == 1 then
- if kpse.lookup(found[1].filename[1]) then
- logs.report("load font",
- "font family='%s', subfamily='%s' found: %s",
- name, style, found[1].filename[1])
- return found[1].filename[1], found[1].filename[2]
- end
- elseif #found > 1 then
- -- we found matching font(s) but not in the requested optical
- -- sizes, so we loop through the matches to find the one with
- -- least difference from the requested size.
- local closest
- local least = math.huge -- initial value is infinity
- for i,face in next, found do
- local dsnsize = face.size[1]/10
- local difference = math.abs(dsnsize-size)
- if difference < least then
- closest = face
- least = difference
- end
- end
- if kpse.lookup(closest.filename[1]) then
- logs.report("load font",
- "font family='%s', subfamily='%s' found: %s",
- name, style, closest.filename[1])
- return closest.filename[1], closest.filename[2]
- end
- elseif found.fallback then
- return found.fallback.filename[1], found.fallback.filename[2]
- end
- -- no font found so far
- if not reloaded then
- -- try reloading the database
- names.data = names.update(names.data)
- names.save(names.data)
- reloaded = true
- return names.resolve(_,_,specification)
- else
- -- else, fallback to filename
- -- XXX: specification.name is empty with absolute paths, looks
- -- like a bug in the specification parser
- return specification.name, false
- end
- end
- else
- if not reloaded then
- names.data = names.update()
- names.save(names.data)
- reloaded = true
- return names.resolve(_,_,specification)
- else
- return specification.name, false
- end
- end
-end
-
-names.resolvespec = names.resolve
-
-function names.set_log_level(level)
- if level == 2 then
- trace_loading = true
- elseif level >= 3 then
- trace_loading = true
- trace_search = true
- end
-end
-
-local lastislog = 0
-
-local function log(category, fmt, ...)
- lastislog = 1
- if fmt then
- texio.write_nl(format("luaotfload | %s: %s", category, format(fmt, ...)))
- elseif category then
- texio.write_nl(format("luaotfload | %s", category))
- else
- texio.write_nl(format("luaotfload |"))
- end
- io.flush()
-end
-
-logs = logs or { }
-logs.report = logs.report or log
-logs.info = logs.info or log
-
-local function font_fullinfo(filename, subfont, texmf)
- local t = { }
- local f = fontloader.open(filename, subfont)
- if not f then
- if trace_loading then
- logs.report("error", "failed to open %s", filename)
- end
- return
- end
- local m = fontloader.to_table(f)
- fontloader.close(f)
- collectgarbage('collect')
- -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size
- if m.fontstyle_name then
- for _,v in next, m.fontstyle_name do
- if v.lang == 1033 then
- t.fontstyle_name = v.name
- end
- end
- end
- if m.names then
- for _,v in next, m.names do
- if v.lang == "English (US)" then
- t.names = {
- -- see
- -- http://developer.apple.com/textfonts/
- -- TTRefMan/RM06/Chap6name.html
- fullname = v.names.compatfull or v.names.fullname,
- family = v.names.preffamilyname or v.names.family,
- subfamily= t.fontstyle_name or v.names.prefmodifiers or v.names.subfamily,
- psname = v.names.postscriptname
- }
- end
- end
- else
- -- no names table, propably a broken font
- if trace_loading then
- logs.report("broken font rejected", "%s", basefile)
- end
- return
- end
- t.fontname = m.fontname
- t.fullname = m.fullname
- t.familyname = m.familyname
- t.filename = { texmf and basename(filename) or filename, subfont }
- t.weight = m.pfminfo.weight
- t.width = m.pfminfo.width
- t.slant = m.italicangle
- -- don't waste the space with zero values
- t.size = {
- m.design_size ~= 0 and m.design_size or nil,
- m.design_range_top ~= 0 and m.design_range_top or nil,
- m.design_range_bottom ~= 0 and m.design_range_bottom or nil,
- }
- return t
-end
-
-local function load_font(filename, fontnames, newfontnames, texmf)
- local newmappings = newfontnames.mappings
- local newstatus = newfontnames.status
- local mappings = fontnames.mappings
- local status = fontnames.status
- local basefile = texmf and basename(filename) or filename
- if filename then
- if names.blacklist[filename] or
- names.blacklist[basename(filename)] then
- if trace_search then
- logs.report("ignoring font", "%s", filename)
- end
- return
- end
- local timestamp, db_timestamp
- db_timestamp = status[basefile] and status[basefile].timestamp
- timestamp = lfs.attributes(filename, "modification")
-
- local index_status = newstatus[basefile] or (not texmf and newstatus[basename(filename)])
- if index_status and index_status.timestamp == timestamp then
- -- already indexed this run
- return
- end
-
- newstatus[basefile] = newstatus[basefile] or { }
- newstatus[basefile].timestamp = timestamp
- newstatus[basefile].index = newstatus[basefile].index or { }
-
- if db_timestamp == timestamp and not newstatus[basefile].index[1] then
- for _,v in next, status[basefile].index do
- local index = #newstatus[basefile].index
- newmappings[#newmappings+1] = mappings[v]
- newstatus[basefile].index[index+1] = #newmappings
- end
- if trace_loading then
- logs.report("font already indexed", "%s", basefile)
- end
- return
- end
- local info = fontloader.info(filename)
- if info then
- if type(info) == "table" and #info > 1 then
- for i in next, info do
- local fullinfo = font_fullinfo(filename, i-1, texmf)
- if not fullinfo then
- return
- end
- local index = newstatus[basefile].index[i]
- if newstatus[basefile].index[i] then
- index = newstatus[basefile].index[i]
- else
- index = #newmappings+1
- end
- newmappings[index] = fullinfo
- newstatus[basefile].index[i] = index
- end
- else
- local fullinfo = font_fullinfo(filename, false, texmf)
- if not fullinfo then
- return
- end
- local index
- if newstatus[basefile].index[1] then
- index = newstatus[basefile].index[1]
- else
- index = #newmappings+1
- end
- newmappings[index] = fullinfo
- newstatus[basefile].index[1] = index
- end
- else
- if trace_loading then
- logs.report("failed to load", "%s", basefile)
- end
- end
- end
-end
-
-local function path_normalize(path)
- --[[
- path normalization:
- - a\b\c -> a/b/c
- - a/../b -> b
- - /cygdrive/a/b -> a:/b
- - reading symlinks under non-Win32
- - using kpse.readable_file on Win32
- ]]
- if os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then
- path = path:gsub('\\', '/')
- path = path:lower()
- path = path:gsub('^/cygdrive/(%a)/', '%1:/')
- end
- if os.type ~= "windows" and os.type ~= "msdos" then
- local dest = lfs.readlink(path)
- if dest then
- if kpse.readable_file(dest) then
- path = dest
- elseif kpse.readable_file(file.join(file.dirname(path), dest)) then
- path = file.join(file.dirname(path), dest)
- else
- -- broken symlink?
- end
- end
- end
- path = file.collapse_path(path)
- return path
-end
-
-fonts.path_normalize = path_normalize
-
-names.blacklist = { }
-
-local function read_blacklist()
- local files = {
- kpse.lookup("otfl-blacklist.cnf", {all=true, format="tex"})
- }
- local blacklist = names.blacklist
- local whitelist = { }
-
- if files and type(files) == "table" then
- for _,v in next, files do
- for line in io.lines(v) do
- line = line:strip() -- to get rid of lines like " % foo"
- if line:find("^%%") or line:is_empty() then
- -- comment or empty line
- else
- line = line:split("%")[1]
- line = line:strip()
- if string.sub(line,1,1) == "-" then
- whitelist[string.sub(line,2,-1)] = true
- else
- if trace_search then
- logs.report("blacklisted file", "%s", line)
- end
- blacklist[line] = true
- end
- end
- end
- end
- end
- for _,fontname in next, whitelist do
- blacklist[fontname] = nil
- end
-end
-
-local font_extensions = { "otf", "ttf", "ttc", "dfont" }
-local font_extensions_set = {}
-for key, value in next, font_extensions do
- font_extensions_set[value] = true
-end
-
-local installed_fonts_scanned = false
-
-local function scan_installed_fonts(fontnames, newfontnames)
- -- Try to query and add font list from operating system.
- -- This uses the lualatex-platform module.
- logs.info("Scanning fonts known to operating system...")
- local fonts = get_installed_fonts()
- if fonts and #fonts > 0 then
- installed_fonts_scanned = true
- if trace_search then
- logs.report("operating system fonts found", "%d", #fonts)
- end
- for key, value in next, fonts do
- local file = value.path
- if file then
- local ext = extname(file)
- if ext and font_extensions_set[ext] then
- file = path_normalize(file)
- if trace_loading then
- logs.report("loading font", "%s", file)
- end
- load_font(file, fontnames, newfontnames, false)
- end
- end
- end
- else
- if trace_search then
- logs.report("Could not retrieve list of installed fonts")
- end
- end
-end
-
-local function scan_dir(dirname, fontnames, newfontnames, texmf)
- --[[
- This function scans a directory and populates the list of fonts
- with all the fonts it finds.
- - dirname is the name of the directory to scan
- - names is the font database to fill
- - texmf is a boolean saying if we are scanning a texmf directory
- ]]
- local list, found = { }, { }
- local nbfound = 0
- if trace_search then
- logs.report("scanning", "%s", dirname)
- end
- for _,i in next, font_extensions do
- for _,ext in next, { i, upper(i) } do
- found = glob(format("%s/**.%s$", dirname, ext))
- -- note that glob fails silently on broken symlinks, which happens
- -- sometimes in TeX Live.
- if trace_search then
- logs.report("fonts found", "%s '%s' fonts found", #found, ext)
- end
- nbfound = nbfound + #found
- table.append(list, found)
- end
- end
- if trace_search then
- logs.report("fonts found", "%d fonts found in '%s'", nbfound, dirname)
- end
-
- for _,file in next, list do
- file = path_normalize(file)
- if trace_loading then
- logs.report("loading font", "%s", file)
- end
- load_font(file, fontnames, newfontnames, texmf)
- end
-end
-
-local function scan_texmf_fonts(fontnames, newfontnames)
- --[[
- This function scans all fonts in the texmf tree, through kpathsea
- variables OPENTYPEFONTS and TTFONTS of texmf.cnf
- ]]
- if expandpath("$OSFONTDIR"):is_empty() then
- logs.info("Scanning TEXMF fonts...")
- else
- logs.info("Scanning TEXMF and OS fonts...")
- end
- local fontdirs = expandpath("$OPENTYPEFONTS"):gsub("^\.", "")
- fontdirs = fontdirs .. expandpath("$TTFONTS"):gsub("^\.", "")
- if not fontdirs:is_empty() then
- for _,d in next, splitpath(fontdirs) do
- scan_dir(d, fontnames, newfontnames, true)
- end
- end
-end
-
---[[
- For the OS fonts, there are several options:
- - if OSFONTDIR is set (which is the case under windows by default but
- not on the other OSs), it scans it at the same time as the texmf tree,
- in the scan_texmf_fonts.
- - if not:
- - under Windows and Mac OSX, we take a look at some hardcoded directories
- - under Unix, we read /etc/fonts/fonts.conf and read the directories in it
-
- This means that if you have fonts in fancy directories, you need to set them
- in OSFONTDIR.
-]]
-
-local function read_fonts_conf(path, results)
- --[[
- This function parses /etc/fonts/fonts.conf and returns all the dir it finds.
- The code is minimal, please report any error it may generate.
- ]]
- local f = io.open(path)
- if not f then
- if trace_search then
- logs.report("cannot open file", "%s", path)
- end
- return results
- end
- local incomments = false
- for line in f:lines() do
- while line and line ~= "" do
- -- spaghetti code... hmmm...
- if incomments then
- local tmp = find(line, '-->')
- if tmp then
- incomments = false
- line = sub(line, tmp+3)
- else
- line = nil
- end
- else
- local tmp = find(line, '<!--')
- local newline = line
- if tmp then
- -- for the analysis, we take everything that is before the
- -- comment sign
- newline = sub(line, 1, tmp-1)
- -- and we loop again with the comment
- incomments = true
- line = sub(line, tmp+4)
- else
- -- if there is no comment start, the block after that will
- -- end the analysis, we exit the while loop
- line = nil
- end
- for dir in gmatch(newline, '<dir>([^<]+)</dir>') do
- -- now we need to replace ~ by kpse.expand_path('~')
- if sub(dir, 1, 1) == '~' then
- dir = file.join(kpse.expand_path('~'), sub(dir, 2))
- end
- -- we exclude paths with texmf in them, as they should be
- -- found anyway
- if not find(dir, 'texmf') then
- results[#results+1] = dir
- end
- end
- for include in gmatch(newline, '<include[^<]*>([^<]+)</include>') do
- -- include here can be four things: a directory or a file,
- -- in absolute or relative path.
- if sub(include, 1, 1) == '~' then
- include = file.join(kpse.expand_path('~'),sub(include, 2))
- -- First if the path is relative, we make it absolute:
- elseif not lfs.isfile(include) and not lfs.isdir(include) then
- include = file.join(file.dirname(path), include)
- end
- if lfs.isfile(include) then
- -- maybe we should prevent loops here?
- -- we exclude path with texmf in them, as they should
- -- be found otherwise
- read_fonts_conf(include, results)
- elseif lfs.isdir(include) then
- for _,f in next, glob(file.join(include, "*.conf")) do
- read_fonts_conf(f, results)
- end
- end
- end
- end
- end
- end
- f:close()
- return results
-end
-
--- for testing purpose
-names.read_fonts_conf = read_fonts_conf
-
-local function get_os_dirs()
- if os.name == 'macosx' then
- return {
- file.join(kpse.expand_path('~'), "Library/Fonts"),
- "/Library/Fonts",
- "/System/Library/Fonts",
- "/Network/Library/Fonts",
- }
- elseif os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then
- local windir = os.getenv("WINDIR")
- return { file.join(windir, 'Fonts') }
- else
- for _,p in next, {"/usr/local/etc/fonts/fonts.conf", "/etc/fonts/fonts.conf"} do
- if lfs.isfile(p) then
- return read_fonts_conf("/etc/fonts/fonts.conf", {})
- end
- end
- end
- return {}
-end
-
-local function scan_os_fonts(fontnames, newfontnames)
- --[[
- This function scans the OS fonts through
- - fontcache for Unix (reads the fonts.conf file and scans the directories)
- - a static set of directories for Windows and MacOSX
- ]]
- logs.info("Scanning OS fonts...")
- if trace_search then
- logs.info("Searching in static system directories...")
- end
- for _,d in next, get_os_dirs() do
- scan_dir(d, fontnames, newfontnames, false)
- end
-end
-
-local function update_names(fontnames, force)
- --[[
- The main function, scans everything
- - fontnames is the final table to return
- - force is whether we rebuild it from scratch or not
- ]]
- logs.info("Updating the font names database")
-
- if force then
- fontnames = fontnames_init()
- else
- if not fontnames then
- fontnames = names.load()
- end
- if fontnames.version ~= names.version then
- fontnames = fontnames_init()
- if trace_search then
- logs.report("No font names database or old one found; "
- .."generating new one")
- end
- end
- end
- local newfontnames = fontnames_init()
- read_blacklist()
- installed_font_scanned = false
- scan_installed_fonts(fontnames, newfontnames)
- scan_texmf_fonts(fontnames, newfontnames)
- if not installed_fonts_scanned and expandpath("$OSFONTDIR"):is_empty() then
- scan_os_fonts(fontnames, newfontnames)
- end
- return newfontnames
-end
-
-local function save_names(fontnames)
- local path = names.path.dir
- if not lfs.isdir(path) then
- dir.mkdirs(path)
- end
- path = file.join(path, names.path.basename)
- if file.iswritable(path) then
- local luaname, lucname = make_name(path)
- table.tofile(luaname, fontnames, true)
- caches.compile(fontnames,luaname,lucname)
- logs.info("Font names database saved")
- return path
- else
- logs.info("Failed to save names database")
- return nil
- end
-end
-
-local function scan_external_dir(dir)
- local old_names, new_names
- if loaded then
- old_names = names.data
- else
- old_names = names.load()
- loaded = true
- end
- new_names = table.copy(old_names)
- scan_dir(dir, old_names, new_names)
- names.data = new_names
-end
-
-names.scan = scan_external_dir
-names.load = load_names
-names.update = update_names
-names.save = save_names
-
--- dummy
-function fonts.names.getfilename(askedname,suffix) -- only supported in mkiv
- return ""
-end