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.lua375
1 files changed, 375 insertions, 0 deletions
diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua
new file mode 100644
index 0000000..aeba0f1
--- /dev/null
+++ b/otfl-font-nms.lua
@@ -0,0 +1,375 @@
+-- This lua script is made to generate the font database for LuaTeX, in order
+-- for it to be able to load a font according to its name, like XeTeX does.
+--
+-- It is part of the luaotfload bundle, see luaotfload's README for legal
+-- notice.
+
+-- some usual initializations
+luaotfload = luaotfload or { }
+luaotfload.fonts = { }
+
+luaotfload.fonts.module = {
+ name = "luaotfload.fonts",
+ version = 1.001,
+ date = "2010/01/12",
+ description = "luaotfload font database.",
+ author = "Khaled Hosny and Elie Roux",
+ copyright = "Luaotfload Development Team",
+ license = "CC0"
+}
+
+kpse.set_program_name("luatex")
+
+local luaextra_file = kpse.find_file("luaextra.lua")
+if not luaextra_file then
+ texio.write_nl("Error: cannot find 'luaextra.lua', exiting.")
+ os.exit(1)
+end
+dofile(luaextra_file)
+
+local splitpath, expandpath, glob, basename = file.split_path, kpse.expand_path, dir.glob, file.basename
+local upper, format, rep = string.upper, string.format, string.rep
+
+function kpse.do_file(name)
+ return dofile(kpse.find_file(name))
+end
+
+-- the file name of the font database
+luaotfload.fonts.basename = "otfl-names.lua"
+
+-- the path to add to TEXMFVAR or TEXMFSYSVAR to get the final directory in
+-- normal cases
+luaotfload.fonts.subtexmfvardir = "/tex/"
+
+-- the directory in which the database will be saved, can be overwritten
+luaotfload.fonts.directory = kpse.expand_var("$TEXMFVAR") .. luaotfload.fonts.subtexmfvardir
+
+-- the version of the database, to be checked by the lookup function of
+-- luaotfload
+luaotfload.fonts.version = 2.004
+
+-- Log facilities:
+-- - level 0 is quiet
+-- - level 1 is the progress bar
+-- - level 2 prints the searched directories
+-- - level 3 prints all the loaded fonts
+-- - level 4 prints all informations when searching directories (debug only)
+luaotfload.fonts.log_level = 1
+
+local lastislog = 0
+
+function luaotfload.fonts.log(lvl, fmt, ...)
+ if lvl <= luaotfload.fonts.log_level then
+ lastislog = 1
+ texio.write_nl(format("luaotfload | %s", format(fmt,...)))
+ end
+end
+
+local log = luaotfload.fonts.log
+
+-- The progress bar
+local function progress(current, total)
+ if luaotfload.fonts.log_level == 1 then
+-- local width = os.getenv("COLUMNS") -2 --doesn't work
+ local width = 78
+ local percent = current/total
+ local gauge = format("[%s]", string.rpadd(" ", width, " "))
+ if percent > 0 then
+ local done = string.rpadd("=", (width * percent) - 1, "=") .. ">"
+ gauge = format("[%s]", string.rpadd(done, width, " ") )
+ end
+ if lastislog == 1 then
+ texio.write_nl("")
+ lastislog = 0
+ end
+ io.stderr:write("\r"..gauge)
+ io.stderr:flush()
+ end
+end
+
+function fontloader.fullinfo(...)
+ local t = { }
+ local f = fontloader.open(...)
+ local m = f and fontloader.to_table(f)
+ fontloader.close(f)
+ -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size
+ if m.fontstyle_name then
+ for _,v in pairs(m.fontstyle_name) do
+ if v.lang == 1033 then
+ t.fontstyle_name = v.name
+ end
+ end
+ end
+ if m.names then
+ for _,v in pairs(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, -- 18, 4
+ family = v.names.preffamilyname or v.names.family, -- 17, 1
+ subfamily = t.fontstyle_name or v.names.prefmodifiers or v.names.subfamily, -- opt. style, 16, 2
+ psname = v.names.postscriptname --or t.fontname
+ }
+ end
+ end
+ end
+ t.fontname = m.fontname
+ t.fullname = m.fullname
+ t.familyname = m.familyname
+ t.filename = m.origname
+ 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, names, texmf)
+ log(3, "Loading font: %s", filename)
+ local mappings = names.mappings or { }
+ local families = names.families or { }
+ local checksums = names.checksums or { }
+ if filename then
+ local checksum = file.checksum(filename)
+ if checksums[checksum] and checksums[checksum] == filename then
+ log(3, "Font already indexed: %s", filename)
+ return
+ end
+ checksums[checksum] = filename
+ local info = fontloader.info(filename)
+ if info then
+ if type(info) == "table" and #info > 1 then
+ for index,_ in ipairs(info) do
+ local fullinfo = fontloader.fullinfo(filename, index-1)
+ if texmf then
+ fullinfo.filename = basename(filename)
+ end
+ mappings[#mappings+1] = fullinfo
+ if fullinfo.names.family then
+ if not families[fullinfo.names.family] then
+ families[fullinfo.names.family] = { }
+ end
+ table.insert(families[fullinfo.names.family], #mappings)
+ else
+ log(3, "Warning: font with broken names table: %s, ignored", filename)
+ end
+ end
+ else
+ local fullinfo = fontloader.fullinfo(filename)
+ if texmf then
+ fullinfo.filename = basename(filename)
+ end
+ mappings[#mappings+1] = fullinfo
+ if fullinfo.names.family then
+ if not families[fullinfo.names.family] then
+ families[fullinfo.names.family] = { }
+ end
+ table.insert(families[fullinfo.names.family], #mappings)
+ else
+ log(3, "Warning: font with broken names table: %s, ignored", filename)
+ end
+ end
+ else
+ log(1, "Failed to load %s", filename)
+ end
+ names.mappings = names.mappings or mappings
+ names.families = names.families or families
+ names.checksums = names.checksums or checksums
+ end
+end
+
+-- We need to detect the OS (especially cygwin) to convert paths.
+local system = LUAROCKS_UNAME_S or io.popen("uname -s"):read("*l")
+if system then
+ if system:match("^CYGWIN") then
+ system = 'cygwin'
+ elseif system:match("^Windows") then
+ system = 'windows'
+ else
+ system = 'unix'
+ end
+else
+ system = 'unix' -- ?
+end
+log(2, "Detecting system: %s", system)
+
+-- path normalization:
+-- - a\b\c -> a/b/c
+-- - a/../b -> b
+-- - /cygdrive/a/b -> a:/b
+local function path_normalize(path)
+ if system ~= 'unix' then
+ path = path:gsub('\\', '/')
+ path = path:lower()
+ end
+ path = file.collapse_path(path)
+ if system == "cygwin" then
+ path = path:gsub('^/cygdrive/(%a)/', '%1:/')
+ end
+ return path
+end
+
+-- 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
+-- - recursive is whether we scan all directories recursively (always false
+-- in this script)
+-- - texmf is a boolean saying if we are scanning a texmf directory (always
+-- true in this script)
+-- - scanned_fonts contains the list of alread scanned fonts, in order for them
+-- not to be scanned twice. The function populates this list with the
+-- fonts it scans.
+local function scan_dir(dirname, names, recursive, texmf, scanned_fonts)
+ local list, found = { }, { }
+ local nbfound = 0
+ for _,ext in ipairs { "otf", "ttf", "ttc", "dfont" } do
+ if recursive then pat = "/**." else pat = "/*." end
+ log(4, "Scanning '%s' for '%s' fonts", dirname, ext)
+ found = glob(dirname .. pat .. ext)
+ -- note that glob fails silently on broken symlinks, which happens
+ -- sometimes in TeX Live.
+ log(4, "%s fonts found", #found)
+ nbfound = nbfound + #found
+ table.append(list, found)
+ log(4, "Scanning '%s' for '%s' fonts", dirname, upper(ext))
+ found = glob(dirname .. pat .. upper(ext))
+ table.append(list, found)
+ nbfound = nbfound + #found
+ end
+ log(2, "%d fonts found in '%s'", nbfound, dirname)
+ for _,fnt in ipairs(list) do
+ fnt = path_normalize(fnt)
+ if not scanned_fonts[fnt] then
+ load_font(fnt, names, texmf)
+ scanned_fonts[fnt] = true
+ end
+ end
+end
+
+-- The function that scans all fonts in the texmf tree, through kpathsea
+-- variables OPENTYPEFONTS and TTFONTS of texmf.cnf
+local function scan_texmf_tree(names)
+ if expandpath("$OSFONTDIR"):is_empty() then
+ log(1, "Scanning TEXMF fonts:")
+ else
+ log(1, "Scanning TEXMF and OS fonts:")
+ end
+ local scanned_fonts = {}
+ local fontdirs = expandpath("$OPENTYPEFONTS")
+ fontdirs = fontdirs .. string.gsub(expandpath("$TTFONTS"), "^\.", "")
+ if not fontdirs:is_empty() then
+ local explored_dirs = {}
+ fontdirs = splitpath(fontdirs)
+ -- hack, don't scan current dir
+ table.remove(fontdirs, 1)
+ count = 0
+ for _,d in ipairs(fontdirs) do
+ if not explored_dirs[d] then
+ count = count + 1
+ progress(count, #fontdirs)
+ scan_dir(d, names, false, true, scanned_fonts)
+ explored_dirs[d] = true
+ end
+ end
+ end
+ return scanned_fonts
+end
+
+-- this function takes raw data returned by fc-list, parses it, normalizes the
+-- paths and makes a list out of it.
+local function read_fcdata(data)
+ local list = { }
+ for line in data:lines() do
+ line = line:gsub(": ", "")
+ local ext = string.lower(string.match(line,"^.+%.([^/\\]-)$"))
+ if ext == "otf" or ext == "ttf" or ext == "ttc" or ext == "dfont" then
+ list[#list+1] = path_normalize(line:gsub(": ", ""))
+ end
+ end
+ return list
+end
+
+-- This function scans the OS fonts through fontcache (fc-list), it executes
+-- only if OSFONTDIR is empty (which is the case under most Unix by default).
+-- If OSFONTDIR is non-empty, this means that the system fonts it contains have
+-- already been scanned, and thus we don't scan them again.
+local function scan_os_fonts(names, scanned_fonts)
+ if expandpath("$OSFONTDIR"):is_empty() then
+ log(1, "Scanning system fonts:")
+ log(2, "Executing 'fc-list : file'")
+ local data = io.popen("fc-list : file", 'r')
+ log(2, "Parsing the result...")
+ local list = read_fcdata(data)
+ data:close()
+ log(2, "%d fonts found", #list)
+ log(2, "Scanning...", #list)
+ count = 0
+ for _,fnt in ipairs(list) do
+ count = count + 1
+ progress(count, #list)
+ if not scanned_fonts[fnt] then
+ load_font(fnt, names, false)
+ scanned_fonts[fnt] = true
+ end
+ end
+ end
+end
+
+local function fontnames_init()
+ return {
+ mappings = { },
+ families = { },
+ checksums = { },
+ version = luaotfload.fonts.version,
+ }
+end
+
+-- The main function, scans everything and writes the file.
+local function reload(force)
+ texio.write("luaotfload | Generating font names database.")
+ local fnames
+ if force then
+ fnames = fontnames_init()
+ else
+ fnames = kpse.do_file(luaotfload.fonts.basename)
+ if fnames and fnames.version and fnames.version == luaotfload.fonts.version then
+ else
+ log(2, "Old font names database version, generating new one")
+ fnames = fontnames_init()
+ end
+ end
+ local savepath = luaotfload.fonts.directory
+ savepath = path_normalize(savepath)
+ if not lfs.isdir(savepath) then
+ log(1, "Creating directory %s", savepath)
+ lfs.mkdir(savepath)
+ if not lfs.isdir(savepath) then
+ texio.write_nl(string.format("Error: cannot create directory '%s', exiting.\n", savepath))
+ os.exit(1)
+ end
+ end
+ savepath = savepath .. '/' .. luaotfload.fonts.basename
+ local fh = io.open(savepath, 'a+')
+ if not fh then
+ texio.write_nl(string.format("Error: cannot write file '%s', exiting.\n", savepath))
+ os.exit(1)
+ end
+ fh:close()
+ -- we save the scanned fonts in a variable in order for scan_os_fonts
+ -- not to rescan them
+ local scanned_fonts = scan_texmf_tree(fnames)
+ scan_os_fonts (fnames, scanned_fonts)
+ log(1, "%s fonts in %s families saved in the database",
+ #fnames.mappings, #table.keys(fnames.families))
+ io.savedata(savepath, table.serialize(fnames, true))
+ log(1, "Saved font names database in %s\n", savepath)
+end
+
+luaotfload.fonts.scan = scan_dir
+luaotfload.fonts.reload = reload