From 298dffaece66c307f9dcbfb23bba855385aa7132 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 17:19:53 +0100 Subject: [main] fix path resolver Close #162 --- luaotfload.dtx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index adbdf26..08f56af 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -2078,13 +2078,14 @@ request_resolvers.path = function (specification) name) file_resolver (specification) else - local suffix = filesuffix (name) - if formats[suffix] then - specification.forced = suffix - specification.name = file.removesuffix(name) - else - specification.name = name - end + local suffix = filesuffix (name) + if formats[suffix] then + specification.forced = suffix + specification.name = file.removesuffix(name) + specification.forcedname = name + else + specification.name = name + end end end -- cgit v1.2.3 From 118a01f6103d2d80e6c18bf9f7640ecf23c04cd4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:01:48 +0100 Subject: [*] annihilate legacy code --- luaotfload-legacy-attributes.lua | 27 - luaotfload-legacy-database.lua | 724 ---- luaotfload-legacy-merged.lua | 8157 -------------------------------------- luaotfload-legacy-tool.lua | 105 - luaotfload-legacy.lua | 402 -- 5 files changed, 9415 deletions(-) delete mode 100644 luaotfload-legacy-attributes.lua delete mode 100644 luaotfload-legacy-database.lua delete mode 100644 luaotfload-legacy-merged.lua delete mode 100755 luaotfload-legacy-tool.lua delete mode 100644 luaotfload-legacy.lua diff --git a/luaotfload-legacy-attributes.lua b/luaotfload-legacy-attributes.lua deleted file mode 100644 index c6130b4..0000000 --- a/luaotfload-legacy-attributes.lua +++ /dev/null @@ -1,27 +0,0 @@ ------------------------------------------------------------------------ --- FILE: otfl-luat-att.lua --- USAGE: with old luaotfload --- DESCRIPTION: setting attributes abide luatexbase rules --- REQUIREMENTS: some old luatex --- AUTHOR: Philipp Gesang (Phg), --- CREATED: 2013-05-10 20:37:19+0200 ------------------------------------------------------------------------ --- - -if not modules then modules = { } end modules ['otfl-luat-att'] = { - version = math.pi/42, - comment = "companion to luaotfload.lua", - author = "Philipp Gesang", - copyright = "Luaotfload Development Team", - license = "GNU GPL v2" -} - -function attributes.private(name) - local attr = "otfl@" .. name - local number = luatexbase.attributes[attr] - if not number then - number = luatexbase.new_attribute(attr) - end - return number -end - diff --git a/luaotfload-legacy-database.lua b/luaotfload-legacy-database.lua deleted file mode 100644 index b31fe88..0000000 --- a/luaotfload-legacy-database.lua +++ /dev/null @@ -1,724 +0,0 @@ -if not modules then modules = { } end modules ['font-nms'] = { - version = "old", - 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 = "old" -- not the same as in context -names.data = nil -names.path = { - basename = "otfl-names.lua", --- different from current - localdir = file.join(kpse.expand_var("$TEXMFVAR"), names_dir), - systemdir = file.join(kpse.expand_var("$TEXMFSYSVAR"), names_dir), -} - - -local splitpath, expandpath = file.split_path, kpse.expand_path -local glob, basename = dir.glob, file.basename -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 load_names() - local localpath = file.join(names.path.localdir, names.path.basename) - local systempath = file.join(names.path.systemdir, names.path.basename) - local kpsefound = kpse.find_file(names.path.basename) - local foundname - local data - if kpsefound and file.isreadable(kpsefound) then - data = dofile(kpsefound) - foundname = kpsefound - elseif file.isreadable(localpath) then - data = dofile(localpath) - foundname = localpath - elseif file.isreadable(systempath) then - data = dofile(systempath) - foundname = systempath - end - if data then - logs.info("Font names database loaded: " .. 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 - return data -end - -local synonyms = { - regular = { "normal", "roman", "plain", "book", "medium" }, - -- boldregular was for old versions of Linux Libertine, is it still useful? - -- semibold is in new versions of Linux Libertine, but there is also a bold, - -- not sure it's useful here... - bold = { "demi", "demibold", "semibold", "boldregular" }, - italic = { "regularitalic", "normalitalic", "oblique", "slanted" }, - bolditalic = { "boldoblique", "boldslanted", "demiitalic", "demioblique", "demislanted", "demibolditalic", "semibolditalic" }, -} - -local loaded = false -local reloaded = false - -function names.resolve(specification) - 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, subfamily, fullname, psname - local optsize, dsnsize, maxsize, minsize - - if face.names then - family = sanitize(face.names.family) - subfamily = sanitize(face.names.subfamily) - fullname = sanitize(face.names.fullname) - psname = sanitize(face.names.psname) - end - local fontname = sanitize(face.fontname) - local pfullname = sanitize(face.fullname) - 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 - elseif 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 - 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] - elseif lfs.isfile(found[1].found_at) then - logs.report("load font", - "font family='%s', subfamily='%s' found: %s", - name, style, found[1].found_at) - return found[1].found_at, 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] - elseif lfs.isfile(closest.found_at) then - logs.report("load font", - "font family='%s', subfamily='%s' found: %s", - name, style, closest.found_at) - return closest.found_at, 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 - 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(fmt, ...) - lastislog = 1 - texio.write_nl(format("luaotfload | %s", format(fmt,...))) - 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 found_at = filename - 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, - } - -- rather, waste space on paths - t.found_at = found_at - 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 table.contains(names.blacklist, filename) or - table.contains(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 not index then - 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 = newstatus[basefile].index[1] - if not index then - 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" 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 - - 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 trace_search then - logs.report("blacklisted file: %s", line) - end - blacklist[#blacklist+1] = line - end - end - end - end -end - -local font_extensions = { "otf", "ttf", "ttc", "dfont" } - -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("%s '%s' fonts found", #found, ext) - end - nbfound = nbfound + #found - table.append(list, found) - end - end - if trace_search then - logs.report("%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. - - in addition: - - 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 if they cannot be found by fontconfig. -]] - -local function read_fonts_conf(path, results, passed_paths) - --[[ - 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) - table.insert(passed_paths, path) - if not f then - logs.info("Warning: unable to read "..path.. ", skipping...") - 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, ' current -require("luaotfload-legacy-database") ---> old -require("alt_getopt") ---> ? - -local name = "mkluatexfontdb" -local version = "1.31 (legacy)" - -local names = fonts.names - -local function help_msg() - texio.write(string.format([[ -Usage: %s [OPTION]... - -================================================================================ - please update your luatex binary - this version is unsupported and likely to break things -================================================================================ - -Rebuild the LuaTeX font database. - -Valid options: - -f --force force re-indexing all fonts - -q --quiet don't output anything - -v --verbose=LEVEL be more verbose (print the searched directories) - -vv print the loaded fonts - -vvv print all steps of directory searching - -V --version print version and exit - -h --help print this message - -The output database file is named otfl-names.lua and is placed under: - - %s - -contact: https://github.com/lualatex/luaotfload - -]], name, names.path.localdir)) -end - -local function version_msg() - texio.write(string.format( - "%s version %s, database version %s.\n", name, version, names.version)) -end - ---[[ -Command-line processing. -Here we fill cmdargs with the good values, and then analyze it. ---]] - -local long_opts = { - force = "f", - quiet = "q", - help = "h", - verbose = 1 , - version = "V", -} - -local short_opts = "fqpvVh" - -local force_reload = nil - -local function process_cmdline() - local opts, optind, optarg = alt_getopt.get_ordered_opts (arg, short_opts, long_opts) - local log_level = 1 - for i,v in ipairs(opts) do - if v == "q" then - log_level = 0 - elseif v == "v" then - if log_level > 0 then - log_level = log_level + 1 - else - log_level = 2 - end - elseif v == "V" then - version_msg() - os.exit(0) - elseif v == "h" then - help_msg() - os.exit(0) - elseif v == "f" then - force_reload = 1 - end - end - names.set_log_level(log_level) -end - -local function generate(force) - local fontnames, saved - fontnames = names.update(fontnames, force) - logs.report("%s fonts in the database", #fontnames.mappings) - saved = names.save(fontnames) -end - -process_cmdline() -generate(force_reload) diff --git a/luaotfload-legacy.lua b/luaotfload-legacy.lua deleted file mode 100644 index 8bb1790..0000000 --- a/luaotfload-legacy.lua +++ /dev/null @@ -1,402 +0,0 @@ -module("luaotfload", package.seeall) - -luaotfload.module = { - name = "luaotfload-legacy", - version = 1.31, - date = "2013/04/25", - description = "Unsupported Luaotfload", - author = "Elie Roux & Hans Hagen", - copyright = "Elie Roux", - license = "GPL v2" -} - -local error, warning, info, log = luatexbase.provides_module(luaotfload.module) - ---[[doc-- - - This used to be a necessary initalization in order not to rebuild an - existing font. Maybe 600 should be replaced by |\pdfpkresolution| - or |texconfig.pk_dpi| (and it should be replaced dynamically), but - we don't have access (yet) to the |texconfig| table, so we let it be - 600. Anyway, it does still work fine even if |\pdfpkresolution| is - changed. - ---doc]]-- - -kpse.init_prog("", 600, "/") - ---[[doc-- - - The minimal required \luatex version. - We are tolerant folks. - ---doc]]-- - -local luatex_version = 60 -if tex.luatexversion < luatex_version then - warning("LuaTeX v%.2f is old, v%.2f is required, v0.76 recommended.", - tex.luatexversion/100, - luatex_version /100) -end - ---[[doc-- - - \subsection{Module loading} - We load the outdated \context files with this function. It - automatically adds the |otfl-| prefix to it, so that we call it with - the actual \context name. - ---doc]]-- - -function luaotfload.loadmodule(tofind) - local found = kpse.find_file(tofind,"tex") - if found then - log("loading file %s.", found) - dofile(found) - else - error("file %s not found.", tofind) - end -end -local loadmodule = luaotfload.loadmodule - ---[[doc-- - - Keep away from these lines! - ---doc]]-- -loadmodule"luaotfload-legacy-merged.lua" - -if not fonts then - loadmodule("otfl-luat-dum.lua") -- not used in context at all - loadmodule("otfl-luat-ovr.lua") -- override some luat-dum functions - loadmodule("otfl-data-con.lua") -- maybe some day we don't need this one - loadmodule("otfl-font-ini.lua") - loadmodule("otfl-node-dum.lua") - loadmodule("otfl-node-inj.lua") ---[[doc-- - By default \context takes some private attributes for internal use. To - avoide attribute clashes with other packages, we override the function - that allocates new attributes, making it a wraper around - |luatexbase.new_attribute()|. We also prefix attributes with |otfl@| to - avoid possiple name clashes. ---doc]]-- - loadmodule("luaotfload-legacy-attributes.lua") -- patch attributes ---[[doc-- - Font handling modules. ---doc]]-- - loadmodule("otfl-font-tfm.lua") - loadmodule("otfl-font-cid.lua") - loadmodule("otfl-font-ott.lua") - loadmodule("otfl-font-map.lua") - loadmodule("otfl-font-otf.lua") - loadmodule("otfl-font-otd.lua") - loadmodule("otfl-font-oti.lua") - loadmodule("otfl-font-otb.lua") - loadmodule("otfl-font-otn.lua") - loadmodule("otfl-font-ota.lua") - loadmodule("otfl-font-otc.lua") - loadmodule("otfl-font-def.lua") ---[[doc-- - \textsf{old luaotfload} specific modules. ---doc]]-- - loadmodule("otfl-font-xtx.lua") - loadmodule("otfl-font-dum.lua") - loadmodule("otfl-font-clr.lua") -end -loadmodule"luaotfload-legacy-database.lua" --- unmerged coz needed in db script - ---[[doc-- - - This is a patch for |otfl-font-def.lua|, that defines a reader for ofm - fonts, this is necessary if we set the forced field of the specification - to |ofm|. - ---doc]]-- - -if fonts and fonts.tfm and fonts.tfm.readers then - fonts.tfm.readers.ofm = fonts.tfm.readers.tfm -end - ---[[doc-- - - \subsection{Post-processing TFM table} - Here we do some final touches to the loaded TFM table before passing it - to the \tex end. - First we create a callback for patching fonts on the fly, to be used by - other packages. - ---doc]]-- - -luatexbase.create_callback("luaotfload.patch_font", "simple", function() end) - ---[[doc-- - - then define a function where font manipulation will take place. - ---doc]]-- - -local function def_font(...) - local fontdata = fonts.define.read(...) - if type(fontdata) == "table" and fontdata.shared then ---[[doc-- - - Then we populate |MathConstants| table, which is required for - OpenType math. - - Note: actually it isn’t, but you’re asking for it by using outdated - code. - ---doc]]-- - local otfdata = fontdata.shared.otfdata - if otfdata.metadata.math then - local mc = { } - for k,v in next, otfdata.metadata.math do - if k:find("Percent") then - -- keep percent values as is - mc[k] = v - else - mc[k] = v / fontdata.units * fontdata.size - end - end - -- for \overwithdelims - mc.FractionDelimiterSize = 1.01 * fontdata.size - mc.FractionDelimiterDisplayStyleSize = 2.39 * fontdata.size - - fontdata.MathConstants = mc - end ---[[doc-- - - Execute any registered font patching callbacks. - ---doc]]-- - luatexbase.call_callback("luaotfload.patch_font", fontdata) - end - return fontdata -end - ---[[doc-- -\subsection{\context override} - - We have a unified function for both file and name resolver. This - line is commented as it makes database reload too often. This means - that in some cases, a font in the database will not be found if - it's not in the texmf tree. A similar thing will reappear in next - version. - ---doc]]-- - ---fonts.define.resolvers.file = fonts.define.resolvers.name - ---[[doc-- - - Overriding some defaults set in \context code. - ---doc]]-- - -fonts.mode = "node" - ---[[doc-- - - The following features are useful in math (e.g. in XITS Math font), - but \textsf{luaotfload} does not recognize them in |base| mode. - ---doc]]-- - -local register_base_sub = fonts.otf.features.register_base_substitution -local gsubs = { - "ss01", "ss02", "ss03", "ss04", "ss05", - "ss06", "ss07", "ss08", "ss09", "ss10", - "ss11", "ss12", "ss13", "ss14", "ss15", - "ss16", "ss17", "ss18", "ss19", "ss20", -} - -for _,v in next, gsubs do - register_base_sub(v) -end - ---[[doc-- - - Finally we register the callbacks - ---doc]]-- - -luatexbase.add_to_callback("pre_linebreak_filter", - nodes.simple_font_handler, - "luaotfload.pre_linebreak_filter") -luatexbase.add_to_callback("hpack_filter", - nodes.simple_font_handler, - "luaotfload.hpack_filter") -luatexbase.reset_callback("define_font") -luatexbase.add_to_callback("define_font", - def_font, - "luaotfload.define_font", 1) -luatexbase.add_to_callback("find_vf_file", - fonts.vf.find, - "luaotfload.find_vf_file") ---[[doc-- - - XXX: see https://github.com/wspr/unicode-math/issues/185 - \luatex does not provide interface to accessing - |(Script)ScriptPercentScaleDown| math constants, so we - emulate \xetex behaviour by setting |\fontdimen10| and - |\fontdimen11|. - - Note: actually, it does now, but not unless you update. - ---doc]]-- - -local function set_sscale_diments(fontdata) - local mc = fontdata.MathConstants - if mc then - if mc["ScriptPercentScaleDown"] then - fontdata.parameters[10] = mc.ScriptPercentScaleDown - else -- resort to plain TeX default - fontdata.parameters[10] = 70 - end - if mc["ScriptScriptPercentScaleDown"] then - fontdata.parameters[11] = mc.ScriptScriptPercentScaleDown - else -- resort to plain TeX default - fontdata.parameters[11] = 50 - end - end -end - -luatexbase.add_to_callback("luaotfload.patch_font", set_sscale_diments, "unicodemath.set_sscale_diments") - ---[[doc-- - Version 2.3c of fontspec dropped a couple features that are now - provided in the luaotfload auxiliary libraries. To avoid breaking - Mik\TEX (again), which is sorta the entire point of distributing the - legacy codebase, we temporarily restore those functions here. - - Note that apart from cosmetic changes these are still the same as in - pre-TL2013 fontspec, relying on pairs() and other inefficient methods. ---doc]]-- - -luaotfload.aux = luaotfload.aux or { } -local aux = luaotfload.aux - -local stringlower = string.lower -local fontid = font.id - -local identifiers = fonts.identifiers - -local check_script = function (id, script) - local s = stringlower(script) - if id and id > 0 then - local tfmdata = identifiers[id] - local otfdata = tfmdata.shared and tfmdata.shared.otfdata - if otfdata then - local features = otfdata.luatex.features - for i, _ in pairs(features) do - for j, _ in pairs(features[i]) do - if features[i][j][s] then - fontspec.log("script '%s' exists in font '%s'", - script, tfmdata.fullname) - return true - end - end - end - end - end -end - -local check_language = function (id, script, language) - local s = stringlower(script) - local l = stringlower(language) - if id and id > 0 then - local tfmdata = identifiers[id] - local otfdata = tfmdata.shared and tfmdata.shared.otfdata - if otfdata then - local features = otfdata.luatex.features - for i, _ in pairs(features) do - for j, _ in pairs(features[i]) do - if features[i][j][s] and features[i][j][s][l] then - fontspec.log("language '%s' for script '%s' exists in font '%s'", - language, script, tfmdata.fullname) - return true - end - end - end - end - end -end - -local check_feature = function (id, script, language, feature) - local s = stringlower(script) - local l = stringlower(language) - local f = stringlower(feature:gsub("^[+-]", ""):gsub("=.*$", "")) - if id and id > 0 then - local tfmdata = identifiers[id] - local otfdata = tfmdata.shared and tfmdata.shared.otfdata - if otfdata then - local features = otfdata.luatex.features - for i, _ in pairs(features) do - if features[i][f] and features[i][f][s] then - if features[i][f][s][l] == true then - fontspec.log("feature '%s' for language '%s' and script '%s' exists in font '%s'", - feature, language, script, tfmdata.fullname) - return true - end - end - end - end - end -end - -local get_math_dimension = function(fnt, str) - if type(fnt) == "string" then - fnt = fontid(fnt) - end - local tfmdata = identifiers[fnt] - if tfmdata then - local mathdata = tfmdata.MathConstants - if mathdata then - return mathdata[str] - end - end -end - -aux.provides_script = check_script -aux.provides_language = check_language -aux.provides_feature = check_feature -aux.get_math_dimension = get_math_dimension - -local set_capheight = function (tfmdata) - local capheight - local shared = tfmdata.shared - if shared then - local metadata = shared.otfdata.metadata - local units_per_em = metadata.units_per_em or tfmdata.units - local os2_capheight = shared.otfdata.pfminfo.os2_capheight - local size = tfmdata.size - - if os2_capheight > 0 then - capheight = os2_capheight / units_per_em * size - else - local X8 = string.byte"X" - if tfmdata.characters[X8] then - capheight = tfmdata.characters[X8].height - else - capheight = metadata.ascent / units_per_em * size - end - end - else - local X8 = string.byte"X" - if tfmdata.characters[X8] then - capheight = tfmdata.characters[X8].height - end - end - if capheight then - tfmdata.parameters[8] = capheight - end -end -luatexbase.add_to_callback("luaotfload.patch_font", - set_capheight, - "luaotfload.set_capheight") - ---[[doc-- -End of auxiliary functionality that was moved from fontspec.lua. ---doc]]-- - --- vim:ts=2:sw=2:expandtab:ft=lua -- cgit v1.2.3 From cf11e5a0d9155095649460aa207dac80ce75ae80 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:06:29 +0100 Subject: [main] extinguish references to legacy code + bump version and dates --- luaotfload.dtx | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 08f56af..320b9d9 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -% Copyright (C) 2009-2013 +% Copyright (C) 2009-2014 % by Elie Roux % and Khaled Hosny % and Philipp Gesang @@ -40,7 +40,7 @@ \input docstrip.tex \Msg{************************************************************************} \Msg{* Installation} -\Msg{* Package: luaotfload v2.4 OpenType layout system} +\Msg{* Package: luaotfload v2.5 OpenType layout system} \Msg{************************************************************************} \keepsilent @@ -51,7 +51,7 @@ \preamble This is a generated file. -Copyright (C) 2009-2013 +Copyright (C) 2009-2014 by Elie Roux and Khaled Hosny and Philipp Gesang @@ -111,7 +111,7 @@ and the derived files %<*driver> \NeedsTeXFormat{LaTeX2e} \ProvidesFile{luaotfload.drv}% - [2013/12/28 v2.4 OpenType layout system]% +[2014/**/** v2.5 OpenType layout system]% \documentclass{ltxdoc} \usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace} \usepackage[x11names]{xcolor} @@ -231,7 +231,7 @@ and the derived files % \GetFileInfo{luaotfload.drv} % % \title{The \identifier{luaotfload} package} -% \date{2013/12/28 v2.4} +% \date{2014/**/** v2.5} % \author{Elie Roux · Khaled Hosny · Philipp Gesang\\ % Home: \url{https://github.com/lualatex/luaotfload}\\ % Support: \email{lualatex-dev@tug.org}} @@ -855,7 +855,7 @@ and the derived files % \identifier{luaotfload} adds a number of features that are not defined % in the original \OpenType specification, most of them % aiming at emulating the behavior familiar from other \TEX engines. -% Currently (2013) there are three of them: +% Currently (2014) there are three of them: % % \begin{description} % @@ -1608,8 +1608,8 @@ end luaotfload.module = { name = "luaotfload", - version = 2.40003, - date = "2013/12/28", + version = 2.50000, + date = "2014/**/**", description = "OpenType layout system.", author = "Elie Roux & Hans Hagen", copyright = "Elie Roux", @@ -2233,16 +2233,10 @@ luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec \else \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luaotfload}% - [2013/12/28 v2.4 OpenType layout system] + [2014/**/** v2.5 OpenType layout system] \RequirePackage{luatexbase} \fi -\ifnum\luatexversion<76 - %% here some deprecation warning would be in order - \RequireLuaModule{lualibs} - \RequireLuaModule{luaotfload-legacy} -\else - \RequireLuaModule{luaotfload} -\fi +\RequireLuaModule{luaotfload} \endinput % \end{macrocode} % \iffalse -- cgit v1.2.3 From b86cc36d326998dfb0d8bf1e062cfbf53a0c2b3a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:10:08 +0100 Subject: [tool] exterminate references to legacy code --- luaotfload-tool.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index fd84fa4..9c3569a 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -33,8 +33,7 @@ kpse.set_program_name "luatex" --[[doc-- We test for Lua 5.1 by means of capability detection to see if - we’re running an outdated Luatex. If so, we hand over control to - the legacy db runner. + we’re running an outdated Luatex. If so, we bail. \url{http://lua-users.org/wiki/LuaVersionCompatibility} @@ -63,8 +62,10 @@ if _G.getfenv ~= nil then -- 5.1 or LJ runtime = { "jit", jit.version } else runtime = { "stock", _VERSION } - local oldscript = kpsefind_file "luaotfload-legacy-tool.lua" - return require (oldscript) + print "FATAL ERROR" + print "Luaotfload requires a Luatex version >=0.76." + print "Please update your TeX distribution!" + os.exit (-1) end else -- 5.2 runtime = { "stock", _VERSION } -- cgit v1.2.3 From cd9eb96cbb2fe57061aa7efdec3e4bc1513aa04e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:16:37 +0100 Subject: [tool] destroy --alias option and legacy mkluatexfontdb emulation --- luaotfload-tool.lua | 43 +++++-------------------------------------- 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 9c3569a..894cf63 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -127,12 +127,7 @@ do -- we don’t have file.basename and the likes yet, so inline parser ftw local extension = dot * (1 - slash - dot)^1 local p_basename = path^-1 * C(thename) * extension^-1 * P(-1) - local self = lpegmatch(p_basename, stringlower(arg[0])) - if self == "luaotfload-tool" then - luaotfloadconfig.self = "luaotfload-tool" - else - luaotfloadconfig.self = "mkluatexfontdb" - end + luaotfloadconfig.self = "luaotfload-tool" end config.lualibs = config.lualibs or { } @@ -211,9 +206,6 @@ This tool is part of the luaotfload package. Valid options are: "environment", "index", "permissions", or "repository" - --alias= force behavior of "luaotfload-tool" or legacy - "mkluatexfontdb" - ------------------------------------------------------------------------------- DATABASE @@ -258,26 +250,9 @@ The font cache will be written to ]], mkluatexfontdb = [[ - -Usage: %s [OPTION]... - -Rebuild or update the Luaotfload font names database. - -Valid options: - -f --force force re-indexing all fonts - -q --quiet don't output anything - -v --verbose=LEVEL be more verbose (print the searched directories) - -vv print the loaded fonts - -vvv print all steps of directory searching - -V --version print version and exit - -h --help print this message - --alias= force behavior of "luaotfload-tool" or legacy - "mkluatexfontdb" - -The font database will be saved to - %s - %s - +FATAL ERROR +As of Luaotfload v2.5, legacy behavior is not supported anymore. Please +update your scripts and/or habits! Kthxbye. ]], short = [[ Usage: luaotfload-tool [--help] [--version] [--verbose=] @@ -1099,7 +1074,6 @@ local process_cmdline = function ( ) -- unit -> jobspec } local long_options = { - alias = 1, cache = 1, ["no-compress"] = "c", diagnose = 1, @@ -1186,8 +1160,6 @@ local process_cmdline = function ( ) -- unit -> jobspec elseif v == "I" then result.show_info = true result.full_info = true - elseif v == "alias" then - luaotfloadconfig.self = optarg[n] elseif v == "l" then action_pending["flush"] = true elseif v == "list" then @@ -1233,12 +1205,7 @@ local process_cmdline = function ( ) -- unit -> jobspec end end - if luaotfloadconfig.self == "mkluatexfontdb" then --- TODO drop legacy ballast after 2.4 - result.help_version = "mkluatexfontdb" - action_pending["generate"] = true - result.log_level = math.max(1, result.log_level) - logs.set_logout("stdout", finalizers) - elseif nopts == 0 then + if nopts == 0 then action_pending["help"] = true result.help_version = "short" end -- cgit v1.2.3 From d882e29709b07212bb4d85a0af0bb3d208927c81 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:19:06 +0100 Subject: [doc] silence claims about the existence of mkluatexfontdb# --- luaotfload-tool.lua | 4 +++- luaotfload-tool.rst | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 894cf63..517a88f 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -13,6 +13,8 @@ local version = "2.4" --- . --[[doc-- +luaotfload-tool(1) + This file was originally written (as \fileent{mkluatexfontdb.lua}) by Elie Roux and Khaled Hosny and, as a derived work of ConTeXt, is provided under the terms of the GPL v2.0 license as printed in full @@ -1055,7 +1057,7 @@ end --[[-- Command-line processing. -mkluatexfontdb.lua relies on the script alt_getopt to process argv and +luaotfload-tool relies on the script alt_getopt to process argv and analyzes its output. TODO with extended lualibs we have the functionality from the diff --git a/luaotfload-tool.rst b/luaotfload-tool.rst index 957ec85..04c34e9 100644 --- a/luaotfload-tool.rst +++ b/luaotfload-tool.rst @@ -49,10 +49,6 @@ the *Luaotfload* package. There are two general modes: **update** and + **update**: update the database or rebuild it entirely; + **query**: resolve a font name or display close matches. -Note that if the script is named ``mkluatexfontdb`` it will behave like -earlier versions (<=1.3) and always update the database first. Also, -the verbosity level will be set to 2. - OPTIONS ======================================================================= -- cgit v1.2.3 From e794cfd74ae38a332122cf2b53df5b2af5d3b1fb Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:20:43 +0100 Subject: [doc] update NEWS --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index b8f380d..922d4ff 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ Change History -------------- +2014/**/**, luaotfload v2.5 + * Remove legacy code. + * Remove compatibility with the old mkluatexfontdb script. + 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) * Better path and directory handling, especially on Windows -- cgit v1.2.3 From 9e2424f29eb2e54968026f4c847bf57c64f59ed6 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:27:11 +0100 Subject: [*] bump version --- luaotfload-auxiliary.lua | 6 +++--- luaotfload-colors.lua | 4 ++-- luaotfload-database.lua | 4 ++-- luaotfload-diagnostics.lua | 4 ++-- luaotfload-features.lua | 2 +- luaotfload-letterspace.lua | 2 +- luaotfload-loaders.lua | 2 +- luaotfload-override.lua | 6 +++--- luaotfload-tool.lua | 10 +++++----- luaotfload-tool.rst | 4 ++-- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/luaotfload-auxiliary.lua b/luaotfload-auxiliary.lua index d3de731..821a436 100644 --- a/luaotfload-auxiliary.lua +++ b/luaotfload-auxiliary.lua @@ -2,10 +2,10 @@ ----------------------------------------------------------------------- -- FILE: luaotfload-auxiliary.lua -- DESCRIPTION: part of luaotfload --- REQUIREMENTS: luaotfload 2.4 +-- REQUIREMENTS: luaotfload 2.5 -- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang --- VERSION: 2.4 --- CREATED: 2013-05-01 14:40:50+0200 +-- VERSION: 2.5 +-- MODIFIED: 2014-01-02 21:24:25+0100 ----------------------------------------------------------------------- -- diff --git a/luaotfload-colors.lua b/luaotfload-colors.lua index b8ecb87..86e493f 100644 --- a/luaotfload-colors.lua +++ b/luaotfload-colors.lua @@ -1,9 +1,9 @@ if not modules then modules = { } end modules ['luaotfload-colors'] = { - version = "2.4", + version = "2.5", comment = "companion to luaotfload.lua (font color)", author = "Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "Luaotfload Development Team", - license = "GNU GPL v2" + license = "GNU GPL v2.0" } --[[doc-- diff --git a/luaotfload-database.lua b/luaotfload-database.lua index b47fc8b..b22afd7 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -1,9 +1,9 @@ if not modules then modules = { } end modules ['luaotfload-database'] = { - version = "2.4", + version = "2.5", comment = "companion to luaotfload.lua", author = "Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "Luaotfload Development Team", - license = "GNU GPL v2" + license = "GNU GPL v2.0" } --[[doc-- diff --git a/luaotfload-diagnostics.lua b/luaotfload-diagnostics.lua index 68ed18c..1f29e3d 100644 --- a/luaotfload-diagnostics.lua +++ b/luaotfload-diagnostics.lua @@ -4,8 +4,8 @@ -- DESCRIPTION: functionality accessible by the --diagnose option -- REQUIREMENTS: luaotfload-tool.lua -- AUTHOR: Philipp Gesang (Phg), --- VERSION: 2.4 --- CREATED: 2013-07-28 10:01:18+0200 +-- VERSION: 2.5 +-- MODIFIED: 2014-01-02 21:23:06+0100 ----------------------------------------------------------------------- -- local names = fonts.names diff --git a/luaotfload-features.lua b/luaotfload-features.lua index 4b2f206..dc6b8a4 100644 --- a/luaotfload-features.lua +++ b/luaotfload-features.lua @@ -1,5 +1,5 @@ if not modules then modules = { } end modules ["features"] = { - version = "2.4", + version = "2.5", comment = "companion to luaotfload.lua", author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", diff --git a/luaotfload-letterspace.lua b/luaotfload-letterspace.lua index 7c5a967..d17aedf 100644 --- a/luaotfload-letterspace.lua +++ b/luaotfload-letterspace.lua @@ -1,5 +1,5 @@ if not modules then modules = { } end modules ['letterspace'] = { - version = "2.4", + version = "2.5", comment = "companion to luaotfload.lua", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL; adapted by Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", diff --git a/luaotfload-loaders.lua b/luaotfload-loaders.lua index 523dc24..fa00633 100644 --- a/luaotfload-loaders.lua +++ b/luaotfload-loaders.lua @@ -1,5 +1,5 @@ if not modules then modules = { } end modules ["loaders"] = { - version = "2.4", + version = "2.5", comment = "companion to luaotfload.lua", author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", diff --git a/luaotfload-override.lua b/luaotfload-override.lua index b1773fa..a692cb4 100644 --- a/luaotfload-override.lua +++ b/luaotfload-override.lua @@ -1,9 +1,9 @@ if not modules then modules = { } end modules ['luat-ovr'] = { - version = "2.4", - comment = "companion to luatex-*.tex", + version = "2.5", + comment = "companion to Luaotfload", author = "Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "Luaotfload Development Team", - license = "GNU GPL v2" + license = "GNU GPL v2.0" } --[[doc-- diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 517a88f..ba3fcb5 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -2,14 +2,14 @@ ----------------------------------------------------------------------- -- FILE: luaotfload-tool.lua -- DESCRIPTION: database functionality --- REQUIREMENTS: luaotfload 2.2 +-- REQUIREMENTS: luaotfload 2.5 -- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang --- VERSION: 2.4 --- LICENSE: GPL v2 --- MODIFIED: 2013-07-28 13:12:04+0200 +-- VERSION: 2.5 +-- LICENSE: GPL v2.0 +-- MODIFIED: 2014-01-02 21:21:10+0100 ----------------------------------------------------------------------- -local version = "2.4" --- . +local version = "2.5" --- . --[[doc-- diff --git a/luaotfload-tool.rst b/luaotfload-tool.rst index 04c34e9..be53ded 100644 --- a/luaotfload-tool.rst +++ b/luaotfload-tool.rst @@ -6,9 +6,9 @@ generate and query the Luaotfload font names database ----------------------------------------------------------------------- -:Date: 2013-07-31 +:Date: 2014-01-02 :Copyright: GPL v2.0 -:Version: 2.4 +:Version: 2.5 :Manual section: 1 :Manual group: text processing -- cgit v1.2.3 From 23b4da05696ec0c3a4c48083209ae37facd09acc Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:28:37 +0100 Subject: [*] remove tests directory Testing has been done using the test repo exclusively for quite some time now: https://bitbucket.org/phg/lua-la-tex-tests. Also, the ``mktests`` script partially automates testing of the font management. This changeset is just codifying the facts. If any of the tests deserves preservation please file an issue for inclusion into the test repo. Thanks! --- tests/alternate_sub.tex | 15 --- tests/anum.tex | 17 ---- tests/caseinsensitive.tex | 6 -- tests/color.tex | 10 -- tests/fallback.tex | 6 -- tests/featurefiles.tex | 5 - tests/font_patch.tex | 28 ------ tests/fontconfig_conf_reading.tex | 8 -- tests/fontencoding.tex | 4 - tests/fonts.conf.test | 25 ----- tests/fontspec_lookup.ltx | 41 --------- tests/frac.tex | 4 - tests/fullname.tex | 15 --- tests/itlc.tex | 5 - tests/lookups.tex | 20 ---- tests/marks.tex | 10 -- tests/math.tex | 56 ------------ tests/microtypography.tex | 36 -------- tests/opbd.fea | 187 -------------------------------------- tests/opbd.tex | 35 ------- tests/opticalsize.tex | 13 --- tests/pln-aux-1.tex | 55 ----------- tests/pln-aux-2.tex | 102 --------------------- tests/pln-aux-3.tex | 39 -------- tests/pln-aux-4.tex | 41 --------- tests/pln-request-4-slashed.tex | 12 --- tests/pln-request-5-cached.tex | 18 ---- tests/pln-subfont-1.tex | 12 --- tests/pln-tfm.tex | 10 -- tests/sanitize_color.tex | 8 -- tests/systemfonts.tex | 50 ---------- tests/texligatures.tex | 7 -- tests/tfmofm.ltx | 6 -- tests/tkrn.fea | 8 -- tests/weirdfonts.tex | 15 --- tests/zero_width_marks_lig.tex | 16 ---- 36 files changed, 945 deletions(-) delete mode 100644 tests/alternate_sub.tex delete mode 100644 tests/anum.tex delete mode 100644 tests/caseinsensitive.tex delete mode 100644 tests/color.tex delete mode 100644 tests/fallback.tex delete mode 100644 tests/featurefiles.tex delete mode 100644 tests/font_patch.tex delete mode 100644 tests/fontconfig_conf_reading.tex delete mode 100644 tests/fontencoding.tex delete mode 100644 tests/fonts.conf.test delete mode 100644 tests/fontspec_lookup.ltx delete mode 100644 tests/frac.tex delete mode 100644 tests/fullname.tex delete mode 100644 tests/itlc.tex delete mode 100644 tests/lookups.tex delete mode 100644 tests/marks.tex delete mode 100644 tests/math.tex delete mode 100644 tests/microtypography.tex delete mode 100644 tests/opbd.fea delete mode 100644 tests/opbd.tex delete mode 100644 tests/opticalsize.tex delete mode 100644 tests/pln-aux-1.tex delete mode 100644 tests/pln-aux-2.tex delete mode 100644 tests/pln-aux-3.tex delete mode 100644 tests/pln-aux-4.tex delete mode 100644 tests/pln-request-4-slashed.tex delete mode 100644 tests/pln-request-5-cached.tex delete mode 100644 tests/pln-subfont-1.tex delete mode 100644 tests/pln-tfm.tex delete mode 100644 tests/sanitize_color.tex delete mode 100644 tests/systemfonts.tex delete mode 100644 tests/texligatures.tex delete mode 100644 tests/tfmofm.ltx delete mode 100644 tests/tkrn.fea delete mode 100644 tests/weirdfonts.tex delete mode 100644 tests/zero_width_marks_lig.tex diff --git a/tests/alternate_sub.tex b/tests/alternate_sub.tex deleted file mode 100644 index 862c665..0000000 --- a/tests/alternate_sub.tex +++ /dev/null @@ -1,15 +0,0 @@ -\input luaotfload.sty -\font\0=name:Scheherazade:mode=node;script=arab;salt=0 at 10pt -\font\1=name:Scheherazade:mode=node;script=arab;salt=1 at 10pt -\font\2=name:Scheherazade:mode=node;script=arab;salt=2 at 10pt -\0 \char"06DD -\1 \char"06DD -\2 \char"06DD - -\font\0=name:Scheherazade:mode=base;script=arab;salt=0 at 10pt -\font\1=name:Scheherazade:mode=base;script=arab;salt=1 at 10pt -\font\2=name:Scheherazade:mode=base;script=arab;salt=2 at 10pt -\0 \char"06DD -\1 \char"06DD -\2 \char"06DD -\bye diff --git a/tests/anum.tex b/tests/anum.tex deleted file mode 100644 index 773955f..0000000 --- a/tests/anum.tex +++ /dev/null @@ -1,17 +0,0 @@ -\input luaotfload.sty - -\font\testd={name:amiri:script=arab;language=dflt;+anum} -\font\testa={name:amiri:script=arab;language=ara;+anum} -\font\testp={name:amiri:script=arab;language=far;+anum} -\font\tests={name:amiri:script=arab;language=snd;+anum} -\font\testu={name:amiri:script=arab;language=urd;+anum} -\font\testx={name:amiri:script=arab;language=xxx;+anum} - -\def\test{\luatextextdir TRT ضرب 0123456789} -\testd \test\par -\testa \test\par -\testp \test\par -\tests \test\par -\testu \test\par -\testx \test\par -\bye diff --git a/tests/caseinsensitive.tex b/tests/caseinsensitive.tex deleted file mode 100644 index 0e9fb5f..0000000 --- a/tests/caseinsensitive.tex +++ /dev/null @@ -1,6 +0,0 @@ -\input luaotfload.sty - -\font\termesr ={tex gyre termes:+liga} at 10pt - -\termesr fi fl ffi ffl ff\par -\bye diff --git a/tests/color.tex b/tests/color.tex deleted file mode 100644 index 188889c..0000000 --- a/tests/color.tex +++ /dev/null @@ -1,10 +0,0 @@ -\input luaotfload.sty - -\font\testa=file:lmroman10-regular.otf:color=FF0000BB;+trep at 10pt -\font\testb=file:lmroman10-regular.otf:color=FFFF0099;+trep at 10pt -\font\testc=file:lmroman10-regular.otf:color=559922;+trep at 12pt - -\testa FF0000BB \par -\testb FFFF0099 \par -\testc 559922 \par -\bye diff --git a/tests/fallback.tex b/tests/fallback.tex deleted file mode 100644 index 71baea9..0000000 --- a/tests/fallback.tex +++ /dev/null @@ -1,6 +0,0 @@ -\input luaotfload.sty -\font\testa={XITS Math} -\font\testb={XITS Math/B} -\testa text\par -\testb text\par -\bye diff --git a/tests/featurefiles.tex b/tests/featurefiles.tex deleted file mode 100644 index c1a3044..0000000 --- a/tests/featurefiles.tex +++ /dev/null @@ -1,5 +0,0 @@ -\input luaotfload.sty -\font\pagella=texgyrepagella-regular:mode=node;featurefile=tkrn.fea;+tkrn - -\pagella TEX -\bye diff --git a/tests/font_patch.tex b/tests/font_patch.tex deleted file mode 100644 index d3bba07..0000000 --- a/tests/font_patch.tex +++ /dev/null @@ -1,28 +0,0 @@ -\input{luaotfload.sty} -\directlua { - local function patch(fontdata) - local mc = fontdata.MathConstants - local em = fontdata.parameters.units - local sz = fontdata.parameters.size - if fontdata.psname == "CambriaMath" and mc then - mc.DisplayOperatorMinHeight = 2800 / em * sz - end - end - %% part of luaotfload-auxiliary - %luatexbase.add_to_callback( - %"luaotfload.patch_font", - %patch, - %"luaotfload.aux.patch_cambria_domh") -} - -\font\4={name:Cambria Math:mode=base;script=math} at 10pt -\font\5={name:Cambria Math:mode=base;script=math;ssty=1} at 7pt -\font\6={name:Cambria Math:mode=node;script=math;ssty=2} at 5pt -\textfont4=\4 \scriptfont4=\5 \scriptscriptfont4=\6 - -$$ -\Umathchar"1"4`∫ -\Umathchar"1"4`∑ -$$ -\bye - diff --git a/tests/fontconfig_conf_reading.tex b/tests/fontconfig_conf_reading.tex deleted file mode 100644 index 66ab377..0000000 --- a/tests/fontconfig_conf_reading.tex +++ /dev/null @@ -1,8 +0,0 @@ -\directlua{ - config = { lualibs = { load_extended = false } } - require"lualibs" - require"luaotfload-database" - local results = fonts.names.read_fonts_conf{"fonts.conf.test"} - inspect(results) -} -\bye diff --git a/tests/fontencoding.tex b/tests/fontencoding.tex deleted file mode 100644 index bbbbac6..0000000 --- a/tests/fontencoding.tex +++ /dev/null @@ -1,4 +0,0 @@ -\input luaotfload.sty -\font\tenrm=ec-lmr10 -\tenrm -\bye diff --git a/tests/fonts.conf.test b/tests/fonts.conf.test deleted file mode 100644 index 3c3e132..0000000 --- a/tests/fonts.conf.test +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - fontconfig/fonts.conf - test 1 ok - test 2 oktest 3 ok - test 4 oktest 5 ok - - /etc/fonts/conf.d - /etc/fonts/fonts.conf - /etc/fonts/conf.d/69-unifont.conf - diff --git a/tests/fontspec_lookup.ltx b/tests/fontspec_lookup.ltx deleted file mode 100644 index 6645427..0000000 --- a/tests/fontspec_lookup.ltx +++ /dev/null @@ -1,41 +0,0 @@ -\documentclass[a5paper,12pt]{scrartcl} -\usepackage{fontspec} -%% -------------------------------------------------------------------- -%% weirdness ahead -%% -------------------------------------------------------------------- -\setmainfont - [Numbers=Lining, - BoldFont={TeX Gyre Pagella Bold}, - BoldItalicFont={TeX Gyre Termes BoldItalic}] - {EB Garamond} -%% -------------------------------------------------------------------- - -%% -------------------------------------------------------------------- -%% excerpt from samples/knuth.tex -%% -------------------------------------------------------------------- -\def\knuth{% - Thus, I came to the conclusion that the designer of a new - system must not only be the implementer and first - large--scale user; the designer should also write the first - user manual. - - The separation of any of these four components would have - hurt \TeX\ significantly. If I had not participated fully in - all these activities, literally hundreds of improvements - would never have been made, because I would never have - thought of them or perceived why they were important. - -} - -%% -------------------------------------------------------------------- -%% main -%% -------------------------------------------------------------------- -\begin{document} - - \section{regular} {\rmfamily\upshape\knuth} - \section{bold face} {\rmfamily\bfseries\knuth} - \section{italic} {\rmfamily\itshape\knuth} - \section{slanted} {\rmfamily\slshape\knuth} - \section{bold italic} {\rmfamily\bfseries\itshape\knuth} - -\end{document} diff --git a/tests/frac.tex b/tests/frac.tex deleted file mode 100644 index c6a4868..0000000 --- a/tests/frac.tex +++ /dev/null @@ -1,4 +0,0 @@ -\input luaotfload.sty -\font\testa={name:Linux Libertine O:script=latn;+frac} at 10pt -\testa 1/8 -\bye diff --git a/tests/fullname.tex b/tests/fullname.tex deleted file mode 100644 index 78cf4d0..0000000 --- a/tests/fullname.tex +++ /dev/null @@ -1,15 +0,0 @@ -\input luaotfload.sty - -\font\testa={LM Roman Slanted 10 Regular} at 10pt -\font\testb={LM Roman 9 Italic} at 10pt -\font\testc={TeX Gyre Termes Bold} at 25pt -% Also testing with absolute filename, please change the path according to your -% system -\font\testd=file:/usr/share/texmf/fonts/opentype/public/lm/lmmono10-italic.otf - -\testa abcd ABCD\par -\testb abcd ABCD\par -\testc abcd ABCD\par -\testd abcd ABCD\par - -\bye diff --git a/tests/itlc.tex b/tests/itlc.tex deleted file mode 100644 index bff0c39..0000000 --- a/tests/itlc.tex +++ /dev/null @@ -1,5 +0,0 @@ -\input luaotfload.sty -\font\testa={Latin Modern Roman:script=latn} at 10pt -\font\testb={Latin Modern Roman/I:script=latn} at 10pt -\testa ({\testb f\/}) -\bye diff --git a/tests/lookups.tex b/tests/lookups.tex deleted file mode 100644 index 8b03d8e..0000000 --- a/tests/lookups.tex +++ /dev/null @@ -1,20 +0,0 @@ -\input luaotfload.sty -%% lookup font by name (involving database) -\font\first=name:iwonaregular at 42pt -%% lookup font by file name (kpse) -\font\second=file:antpoltltsemiexpd-bolditalic.otf at 42pt -%% lookup font by name, with style in slash notation -\font\third={name:Antykwa torunska/I} at 42pt -%% unspecified lookup; in definers.read: -%% - first it falls back to “file” -%% - empty “method” field triggers fallback to “name” -%% - names.resolve -> kpse.lookup -> hit! -\font\fourth=iwona at 42pt - -{\first foo \endgraf} -{\second bar \endgraf} -{\third baz \endgraf} -{\fourth xyzzy \endgraf} - -\bye - diff --git a/tests/marks.tex b/tests/marks.tex deleted file mode 100644 index 9dcf460..0000000 --- a/tests/marks.tex +++ /dev/null @@ -1,10 +0,0 @@ -\input luaotfload.sty -\font\test={file:GenBasR.ttf:script=latn} -%font\test={name:AntykwaTorunska:script=latn} -\test ä\quad Ä - -\test a\char"0308 %% -> combining dihaeresis -\quad A\char"0308 %% -> combining dihaeresis -\quad j\char"0323 %% -> combining dot below - -\bye diff --git a/tests/math.tex b/tests/math.tex deleted file mode 100644 index a2615f1..0000000 --- a/tests/math.tex +++ /dev/null @@ -1,56 +0,0 @@ -% start out with plain.tex, -% having TFM-based CM fonts preloaded in \fam 0...3 - -% load OT math font in \fam 4 -\input luaotfload.sty -\font\4={name:XITS Math:mode=base;script=math} at 10pt -\font\5={name:XITS Math:mode=base;script=math;ssty=1} at 7pt -\font\6={name:XITS Math:mode=node;script=math;ssty=2} at 5pt -\textfont4=\4 \scriptfont4=\5 \scriptscriptfont4=\6 - -\Umathcode`a="7"4"1D44E -\Umathcode`b="7"4"1D44F -\Umathcode`x="7"4"1D465 -\Umathcode`y="7"4"1D466 -\Umathcode`A="7"4"1D434 -\Umathcode`B="7"4"1D435 -\Umathcode`C="7"4"1D436 -\Umathcode`P="7"4"1D443 -\Umathcode`3="7"4`3 -\Umathcode`+="2"4`+ -\Umathcode`=="3"4`= -\Umathcode`(="4"4`( -\Umathcode`)="5"4`) - -\Udelcode`(="4`( -\Udelcode`)="4`) - -$$\Uradical "4 "221A {x}$$ -$$\Uroot "4 "221A {3}{x^3+y^3}$$ - -$$ - \Udelimiterover "4 "23DE {a+b} -+ \Udelimiterunder "4 "23DF {a+b} = C -$$ - -$$ - \Umathaccent "0 "4 "23DE {a+b} -$$ - -$$ -A \mathrel{\Uoverdelimiter "4 "2192 {a+b}} -B \mathrel{\Uunderdelimiter "4 "2192 {a+b}} -$$ - -$$ -{a \overwithdelims() b} -$$ - - -\centerline{$ a \overwithdelims() b $} - -$$ -P(a) -$$ - -\bye diff --git a/tests/microtypography.tex b/tests/microtypography.tex deleted file mode 100644 index 99deb5f..0000000 --- a/tests/microtypography.tex +++ /dev/null @@ -1,36 +0,0 @@ -\input luaotfload.sty - -\pdfprotrudechars2 \pdfadjustspacing2 - -\font\testa=file:texgyretermes-regular:script=latn at 12pt -\font\testb=file:texgyretermes-regular:script=latn;protrusion=default at 12pt - -\testa We thrive in information thick worlds because of our -marvelous and everyday capacity to select, edit, -single out, structure, highlight, group, pair, merge, -harmonize, synthesize, focus, organize, condense, -reduce, boil down, choose, categorize, catalog, classify, -list, abstract, scan, look into, idealize, isolate, -discriminate, distinguish, screen, pigeonhole, pick over, -sort, integrate, blend, inspect, filter, lump, skip, -smooth, chunk, average, approximate, cluster, aggregate, -outline, summarize, itemize, review, dip into, -flip through, browse, glance into, leaf through, skim, -refine, enumerate, glean, synopsize, winnow the wheat -from the chaff and separate the sheep from the goats.\par - -\testb We thrive in information thick worlds because of our -marvelous and everyday capacity to select, edit, -single out, structure, highlight, group, pair, merge, -harmonize, synthesize, focus, organize, condense, -reduce, boil down, choose, categorize, catalog, classify, -list, abstract, scan, look into, idealize, isolate, -discriminate, distinguish, screen, pigeonhole, pick over, -sort, integrate, blend, inspect, filter, lump, skip, -smooth, chunk, average, approximate, cluster, aggregate, -outline, summarize, itemize, review, dip into, -flip through, browse, glance into, leaf through, skim, -refine, enumerate, glean, synopsize, winnow the wheat -from the chaff and separate the sheep from the goats.\par - -\bye diff --git a/tests/opbd.fea b/tests/opbd.fea deleted file mode 100644 index 54f687a..0000000 --- a/tests/opbd.fea +++ /dev/null @@ -1,187 +0,0 @@ -languagesystem DFLT dlft; -languagesystem grek dflt; -languagesystem latn dflt; -languagesystem latn AZE; -languagesystem latn CRT; -languagesystem latn MOL; -languagesystem latn NLD; -languagesystem latn PLK; -languagesystem latn ROM; -languagesystem latn TRK; - -feature rtbd { - lookupflag 0; - pos \exclam <100 0 0 0>; - pos \percent <100 0 0 0>; - pos \ampersand <100 0 0 0>; - pos \parenright <300 0 0 0>; - pos \asterisk <200 0 0 0>; - pos \plus <250 0 0 0>; - pos \comma <500 0 0 0>; - pos \hyphen <500 0 0 0>; - pos \period <700 0 0 0>; - pos \slash <300 0 0 0>; - pos \one <100 0 0 0>; - pos \seven <50 0 0 0>; - pos \colon <500 0 0 0>; - pos \semicolon <500 0 0 0>; - pos \question <200 0 0 0>; - pos \at <50 0 0 0>; - pos \A <50 0 0 0>; - pos \K <50 0 0 0>; - pos \L <50 0 0 0>; - pos \T <50 0 0 0>; - pos \V <50 0 0 0>; - pos \W <50 0 0 0>; - pos \X <50 0 0 0>; - pos \Y <50 0 0 0>; - pos \k <50 0 0 0>; - pos \p <50 0 0 0>; - pos \r <50 0 0 0>; - pos \v <50 0 0 0>; - pos \w <50 0 0 0>; - pos \x <50 0 0 0>; - pos \y <70 0 0 0>; - pos \asciitilde <250 0 0 0>; - pos \Agrave <50 0 0 0>; - pos \Aacute <50 0 0 0>; - pos \Acircumflex <50 0 0 0>; - pos \Atilde <50 0 0 0>; - pos \Adieresis <50 0 0 0>; - pos \Aring <50 0 0 0>; - pos \Amacron <50 0 0 0>; - pos \Abreve <50 0 0 0>; - pos \Aogonek <50 0 0 0>; - pos \Kcommaaccent <50 0 0 0>; - pos \kcommaaccent <50 0 0 0>; - pos \Lacute <50 0 0 0>; - pos \Lcommaaccent <50 0 0 0>; - pos \Lcaron <50 0 0 0>; - pos \Ldot <50 0 0 0>; - pos \Lslash <50 0 0 0>; - pos \racute <50 0 0 0>; - pos \rcommaaccent <50 0 0 0>; - pos \rcaron <50 0 0 0>; - pos \wcircumflex <50 0 0 0>; - pos \ycircumflex <70 0 0 0>; - pos \Acaron <50 0 0 0>; - pos \Aringacute <50 0 0 0>; - pos \Adblgrave <50 0 0 0>; - pos \rdblgrave <50 0 0 0>; - pos \uni021A <50 0 0 0>; - pos \Alpha <50 0 0 0>; - pos \Kappa <50 0 0 0>; - pos \Lambda <50 0 0 0>; - pos \Tau <50 0 0 0>; - pos \Ldotbelow <50 0 0 0>; - pos \Ldotbelowmacron <50 0 0 0>; - pos \rdotaccent <50 0 0 0>; - pos \rdotbelow <50 0 0 0>; - pos \rdotbelowmacron <50 0 0 0>; - pos \Tdotbelow <50 0 0 0>; - pos \Tlinebelow <50 0 0 0>; - pos \wgrave <50 0 0 0>; - pos \wacute <50 0 0 0>; - pos \wdieresis <50 0 0 0>; - pos \Ahookabove <50 0 0 0>; - pos \Acircumflexacute <50 0 0 0>; - pos \Acircumflexgrave <50 0 0 0>; - pos \Acircumflexhookabove <50 0 0 0>; - pos \Acircumflextilde <50 0 0 0>; - pos \Acircumflexdotbelow <50 0 0 0>; - pos \Abreveacute <50 0 0 0>; - pos \Abrevegrave <50 0 0 0>; - pos \Abrevehookabove <50 0 0 0>; - pos \Abrevetilde <50 0 0 0>; - pos \Abrevedotbelow <50 0 0 0>; - pos \ygrave <70 0 0 0>; - pos \ydotbelow <70 0 0 0>; - pos \yhookabove <70 0 0 0>; - pos \ytilde <70 0 0 0>; - pos \endash <300 0 0 0>; - pos \emdash <200 0 0 0>; - pos \quoteleft <700 0 0 0>; - pos \quoteright <700 0 0 0>; - pos \quotedblleft <400 0 0 0>; - pos \quotedblright <400 0 0 0>; - pos \Aogonekacute <50 0 0 0>; - pos \L_uni0303 <50 0 0 0>; - pos \T_uni0303 <50 0 0 0>; - pos \T_uni0308 <50 0 0 0>; -} rtbd; - -feature lfbd { - lookupflag 0; - pos \percent <-100 0 -100 0>; - pos \ampersand <-50 0 -50 0>; - pos \parenleft <-100 0 -100 0>; - pos \asterisk <-200 0 -200 0>; - pos \plus <-250 0 -250 0>; - pos \hyphen <-400 0 -400 0>; - pos \slash <-200 0 -200 0>; - pos \one <-100 0 -100 0>; - pos \at <-50 0 -50 0>; - pos \A <-50 0 -50 0>; - pos \J <-50 0 -50 0>; - pos \T <-50 0 -50 0>; - pos \V <-50 0 -50 0>; - pos \W <-50 0 -50 0>; - pos \X <-50 0 -50 0>; - pos \Y <-50 0 -50 0>; - pos \p <-50 0 -50 0>; - pos \q <-50 0 -50 0>; - pos \v <-50 0 -50 0>; - pos \w <-50 0 -50 0>; - pos \x <-50 0 -50 0>; - pos \y <-50 0 -50 0>; - pos \asciitilde <-200 0 -200 0>; - pos \Agrave <-50 0 -50 0>; - pos \Aacute <-50 0 -50 0>; - pos \Acircumflex <-50 0 -50 0>; - pos \Atilde <-50 0 -50 0>; - pos \Adieresis <-50 0 -50 0>; - pos \Aring <-50 0 -50 0>; - pos \Amacron <-50 0 -50 0>; - pos \Abreve <-50 0 -50 0>; - pos \Aogonek <-50 0 -50 0>; - pos \Jcircumflex <-50 0 -50 0>; - pos \wcircumflex <-50 0 -50 0>; - pos \ycircumflex <-50 0 -50 0>; - pos \Acaron <-50 0 -50 0>; - pos \Aringacute <-50 0 -50 0>; - pos \Adblgrave <-50 0 -50 0>; - pos \uni021A <-50 0 -50 0>; - pos \Alpha <-50 0 -50 0>; - pos \Lambda <-50 0 -50 0>; - pos \Tau <-50 0 -50 0>; - pos \Tdotbelow <-50 0 -50 0>; - pos \Tlinebelow <-50 0 -50 0>; - pos \wgrave <-50 0 -50 0>; - pos \wacute <-50 0 -50 0>; - pos \wdieresis <-50 0 -50 0>; - pos \Ahookabove <-50 0 -50 0>; - pos \Acircumflexacute <-50 0 -50 0>; - pos \Acircumflexgrave <-50 0 -50 0>; - pos \Acircumflexhookabove <-50 0 -50 0>; - pos \Acircumflextilde <-50 0 -50 0>; - pos \Acircumflexdotbelow <-50 0 -50 0>; - pos \Abreveacute <-50 0 -50 0>; - pos \Abrevegrave <-50 0 -50 0>; - pos \Abrevehookabove <-50 0 -50 0>; - pos \Abrevetilde <-50 0 -50 0>; - pos \Abrevedotbelow <-50 0 -50 0>; - pos \ygrave <-50 0 -50 0>; - pos \ydotbelow <-50 0 -50 0>; - pos \yhookabove <-50 0 -50 0>; - pos \ytilde <-50 0 -50 0>; - pos \endash <-300 0 -300 0>; - pos \emdash <-200 0 -200 0>; - pos \quoteleft <-500 0 -500 0>; - pos \quoteright <-500 0 -500 0>; - pos \quotedblleft <-300 0 -300 0>; - pos \quotedblright <-300 0 -300 0>; - pos \Aogonekacute <-50 0 -50 0>; - pos \J_uni030C.cap <-50 0 -50 0>; - pos \T_uni0303 <-50 0 -50 0>; - pos \T_uni0308 <-50 0 -50 0>; -} lfbd; diff --git a/tests/opbd.tex b/tests/opbd.tex deleted file mode 100644 index 1a838cd..0000000 --- a/tests/opbd.tex +++ /dev/null @@ -1,35 +0,0 @@ -\input luaotfload.sty - -\pdfprotrudechars2 \pdfadjustspacing2 - -\font\testa=file:texgyrepagella-regular:script=latn at 12pt -\font\testb=file:texgyrepagella-regular:mode=node;script=latn;protrusion=yes;featurefile=opbd.fea;+opbd at 12pt - -\testa We thrive in information thick worlds because of our -marvelous and everyday capacity to select, edit, -single out, structure, highlight, group, pair, merge, -harmonize, synthesize, focus, organize, condense, -reduce, boil down, choose, categorize, catalog, classify, -list, abstract, scan, look into, idealize, isolate, -discriminate, distinguish, screen, pigeonhole, pick over, -sort, integrate, blend, inspect, filter, lump, skip, -smooth, chunk, average, approximate, cluster, aggregate, -outline, summarize, itemize, review, dip into, -flip through, browse, glance into, leaf through, skim, -refine, enumerate, glean, synopsize, winnow the wheat -from the chaff and separate the sheep from the goats.\par - -\testb We thrive in information thick worlds because of our -marvelous and everyday capacity to select, edit, -single out, structure, highlight, group, pair, merge, -harmonize, synthesize, focus, organize, condense, -reduce, boil down, choose, categorize, catalog, classify, -list, abstract, scan, look into, idealize, isolate, -discriminate, distinguish, screen, pigeonhole, pick over, -sort, integrate, blend, inspect, filter, lump, skip, -smooth, chunk, average, approximate, cluster, aggregate, -outline, summarize, itemize, review, dip into, -flip through, browse, glance into, leaf through, skim, -refine, enumerate, glean, synopsize, winnow the wheat -from the chaff and separate the sheep from the goats.\par -\bye diff --git a/tests/opticalsize.tex b/tests/opticalsize.tex deleted file mode 100644 index 53b1bb8..0000000 --- a/tests/opticalsize.tex +++ /dev/null @@ -1,13 +0,0 @@ -\input luaotfload.sty - -\font\testa={Latin Modern Roman/S=12} at 10pt -\font\testb={Latin Modern Roman} at 5pt -\font\testc={Latin Modern Roman} at 10pt -\font\testd={Latin Modern Roman/S=15} at 10pt -\font\teste={Latin Modern Roman/S=3} at 10pt -\testa abcd ABCD \par -\testb abcd ABCD \par -\testc abcd ABCD \par -\testd abcd ABCD \par -\teste abcd ABCD \par -\bye diff --git a/tests/pln-aux-1.tex b/tests/pln-aux-1.tex deleted file mode 100644 index a228be0..0000000 --- a/tests/pln-aux-1.tex +++ /dev/null @@ -1,55 +0,0 @@ -\input luaotfload.sty - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% usage for glyph tests -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\baselineskip=17.28pt - -\font\iwonaregular=name:iwona at 14.4pt -\font\lmromanten=file:lmroman10-regular.otf at 14.4pt -\font\cmuregular=file:cmunrm.otf at 14.4pt - -%% wrap tests in macros (could move to style file) -\def\doifglyphelse#1#2#3{% - \directlua{ - local codepoint = tonumber('\string#1') - if not codepoint then codepoint = unicode.utf8.byte('\string#1') end - if luaotfload.aux.font_has_glyph(font.current(), codepoint) then - tex.sprint('\string#2') - else - tex.sprint('\string#3') - end - }% -} - -\def\doifglyph#1#2{\doifglyphelse{#1}{#2}{}} - -%% no otf font loaded yet, so both fail: -first: -\doifglyphelse{a}{true}{false} -\doifglyph {a}{yep} - -%% load lm and try repeat: -\lmromanten -second: -\doifglyphelse{a}{true}{false} -\doifglyph {a}{yep} - -%% let’s test some more free fonts -\def\checkglyphset{% - \doifglyphelse ö{ö}{nope} - \doifglyphelse п{п}{nope} - \doifglyphelse α{α}{nope} - \doifglyphelse Æ{Æ}{nope} - \doifglyphelse ą{ą}{nope} - \doifglyphelse ř{ř}{nope} - \doifglyphelse ˝{˝}{nope} - \doifglyphelse ѩ{ѩ}{nope} - \endgraf -} - -\iwonaregular \checkglyphset -\lmromanten \checkglyphset -\cmuregular \checkglyphset - -\bye diff --git a/tests/pln-aux-2.tex b/tests/pln-aux-2.tex deleted file mode 100644 index 62192a5..0000000 --- a/tests/pln-aux-2.tex +++ /dev/null @@ -1,102 +0,0 @@ -\input luaotfload.sty -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% script, features, and language -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\font\minionregular=file:MinionPro_Regular.otf at 9pt -\font\biolinum=file:LinBiolinum_R.otf at 9pt -\font\cmuregular=file:cmunrm.otf at 9pt - -%% (1) luaotfload.aux.provides_script(font_id, script) -%% #1 defined font; #2 OT script tag -\def\providesscript[#1][#2]{% - \bgroup#1% - let’s see whether \detokenize{#1} has script #2: - \directlua{ - local aux = luaotfload.aux - local succ = aux.provides_script(font.current(), [[#2]]) - if succ then tex.sprint"true" else tex.sprint"false" end - }% - \egroup - \endgraf% -} - -\providesscript [\minionregular][latn]%% Latin -\providesscript [\biolinum][latn] -\providesscript [\cmuregular][latn] -\providesscript [\minionregular][cyrl]%% Cyrillic -\providesscript [\biolinum][cyrl] -\providesscript [\cmuregular][cyrl] -\providesscript [\minionregular][tibt]%% Tibetan -\providesscript [\biolinum][tibt] -\providesscript [\cmuregular][tibt] - -\hrule % -------------------------------------------------------------- - -%% (2) luaotfload.aux.provides_language(font_id, script, language) -%% #1 defined font; #2 OT script tag; #3 OT language tag -\def\provideslanguage[#1][#2][#3]{% - \bgroup#1% - let’s see whether \detokenize{#1} supports language #3 for script #2: - \directlua{ - local aux = luaotfload.aux - local succ = aux.provides_language(font.current(), [[#2]], [[#3]]) - if succ then tex.sprint"true" else tex.sprint"false" end - }% - \egroup - \endgraf% -} - -\provideslanguage [\minionregular][latn][nld]%% Latin/Dutch -\provideslanguage [\biolinum][latn][nld] -\provideslanguage [\cmuregular][latn][nld] -\provideslanguage [\minionregular][latn][deu]%% Latin/German -\provideslanguage [\biolinum][latn][deu] -\provideslanguage [\cmuregular][latn][deu] -\provideslanguage [\minionregular][cyrl][rus]%% Cyrillic/Russian -\provideslanguage [\biolinum][cyrl][rus] -\provideslanguage [\cmuregular][cyrl][rus] -\provideslanguage [\minionregular][cyrl][klm]%% Cyrillic/Kalmyk -\provideslanguage [\biolinum][cyrl][klm] -\provideslanguage [\cmuregular][cyrl][klm] -\provideslanguage [\minionregular][cyrl][srb]%% Cyrillic/Serbian -\provideslanguage [\biolinum][cyrl][srb] -\provideslanguage [\cmuregular][cyrl][srb] -\provideslanguage [\minionregular][tibt][tib]%% Tibetan -\provideslanguage [\biolinum][tibt][tib] -\provideslanguage [\cmuregular][tibt][tib] - -\hrule % -------------------------------------------------------------- - -%% (3) luaotfload.aux.provides_feature( -%% font_id, script, language, feature) -%% #1 defined font; #2 OT script tag; -%% #3 OT language tag; #4 OT feature -\def\providesfeature[#1][#2][#3][#4]{%this is getting ridiculous - \bgroup#1% - let’s see whether \detokenize{#1} supports feature #4 for the - combination of script #2 with language #3: - \directlua{ - local aux = luaotfload.aux - local succ = aux.provides_feature( - font.current(), [[#2]], [[#3]], [[#4]]) - if succ then tex.sprint"true" else tex.sprint"false" end - }% - \egroup - \endgraf% -} - -\providesfeature [\minionregular][latn][nld][liga]%% Latin/Dutch -\providesfeature [\biolinum][latn][nld][liga] -\providesfeature [\cmuregular][latn][nld][liga] -\providesfeature [\minionregular][latn][deu][liga]%% Latin/German -\providesfeature [\biolinum][latn][deu][liga] -\providesfeature [\cmuregular][latn][deu][liga] -\providesfeature [\minionregular][cyrl][srb][liga]%% Cyrillic/Serbian -\providesfeature [\biolinum][cyrl][srb][liga] -\providesfeature [\cmuregular][cyrl][srb][liga] -\providesfeature [\minionregular][tibt][tib][liga]%% Tibetan -\providesfeature [\biolinum][tibt][tib][liga] -\providesfeature [\cmuregular][tibt][tib][liga] - -\bye diff --git a/tests/pln-aux-3.tex b/tests/pln-aux-3.tex deleted file mode 100644 index 12a80cf..0000000 --- a/tests/pln-aux-3.tex +++ /dev/null @@ -1,39 +0,0 @@ -\input luaotfload.sty - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% math dimension getter -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\font\xitsmath=file:xits-math.otf -\font\cambriamath=file:cambria.ttc(1) - -\font\main=file:Iwona-Regular.otf at 12pt\main - -\directlua{ - local aux = luaotfload.aux - local test_a = function (fontname, dimension) - tex.sprint( - "(", fontname, " (", dimension, " ", - aux.get_math_dimension(fontname, dimension), - [[))\endgraf ]]) - end - - local test_b = function (fontname, dimension) - aux.sprint_math_dimension(fontname, dimension) - tex.print[[\endgraf ]] - end - - test_a("xitsmath", "AxisHeight") - test_a("xitsmath", "RadicalVerticalGap") - test_a("cambriamath", "StackTopShiftUp") - test_a("cambriamath", "FractionNumeratorGapMin") - - test_b("xitsmath", "AxisHeight") - test_b("xitsmath", "RadicalVerticalGap") - test_b("cambriamath", "StackTopShiftUp") - test_b("cambriamath", "FractionNumeratorGapMin") -} - -foo bar baz - -\bye diff --git a/tests/pln-aux-4.tex b/tests/pln-aux-4.tex deleted file mode 100644 index 80ffc0b..0000000 --- a/tests/pln-aux-4.tex +++ /dev/null @@ -1,41 +0,0 @@ -\input luaotfload.sty - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% unicode character mappings -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\font\ptserifregular = file:PTF55F.ttf \ptserifregular - -%% here we map the function luaotfload.aux.name_of_slot -%% on a short text, printing a list of letters, their -%% code points and names (as specified in the Adobe -%% Glyph List). - -\directlua{ - local aux = luaotfload.aux - local cbk = function (str) - if string.match(str, "^EOF") then - luatexbase.remove_from_callback("process_input_buffer", "weird") - return [[the end!]] - end - local res = { } - for chr in string.utfcharacters(str) do - local val = unicode.utf8.byte(chr) - local line = chr .. " <> " .. tostring(val) - line = line .. " <> " .. (aux.name_of_slot(val) or "") - res[\string#res+1] = line - end - return table.concat(res, [[\endgraf]]) - end - - luatexbase.add_to_callback("process_input_buffer", cbk, "weird") -} - -Я узнал что у меня -Есть огромная семья -И тропинка и лесок -В поле каждый колосок - -EOF - -\bye diff --git a/tests/pln-request-4-slashed.tex b/tests/pln-request-4-slashed.tex deleted file mode 100644 index 5e7d99e..0000000 --- a/tests/pln-request-4-slashed.tex +++ /dev/null @@ -1,12 +0,0 @@ -\ifdefined\directlua\input luaotfload.sty\fi -\font\iwona =iwona at 20pt -\font\iwonabold =iwona/b at 20pt -\font\iwonaitalic =iwona/i at 20pt -\font\iwonabolditalic =iwona/bi at 20pt - -\def\test{foo bar baz \endgraf} -{\iwona \test} -{\iwonabold \test} -{\iwonaitalic \test} -{\iwonabolditalic \test} -\bye diff --git a/tests/pln-request-5-cached.tex b/tests/pln-request-5-cached.tex deleted file mode 100644 index 8ba4a5e..0000000 --- a/tests/pln-request-5-cached.tex +++ /dev/null @@ -1,18 +0,0 @@ -\ifdefined\directlua - \directlua{config = config or { luaotfload = { } } - config.luaotfload.resolver = "cached" - config.luaotfload.loglevel = 5 } - \input luaotfload.sty -\fi - -\font\iwona =name:iwona at 20pt -\font\iwonabold =name:iwona/b at 20pt -\font\iwonaitalic =name:iwona/i at 20pt -\font\iwonabolditalic =name:iwona/bi at 20pt - -\def\test{foo bar baz \endgraf} -{\iwona \test} -{\iwonabold \test} -{\iwonaitalic \test} -{\iwonabolditalic \test} -\bye diff --git a/tests/pln-subfont-1.tex b/tests/pln-subfont-1.tex deleted file mode 100644 index fb8e1e7..0000000 --- a/tests/pln-subfont-1.tex +++ /dev/null @@ -1,12 +0,0 @@ -\ifdefined\directlua\input luaotfload.sty\fi -%% This requires the Cambria fonts from MS. -\directlua{ - inspect(fontloader.info"cambria.ttc") -} -%% Here we load both subfonts in the collection -%% with the not-quite documented subfont syntax. -\font\subfontone="file:cambria.ttc(0)" at 42pt -\font\subfonttwo="file:cambria.ttc(1)" at 42pt -\subfontone foo bar baz \endgraf -\subfonttwo foo bar baz \endgraf -\bye diff --git a/tests/pln-tfm.tex b/tests/pln-tfm.tex deleted file mode 100644 index 16ae41a..0000000 --- a/tests/pln-tfm.tex +++ /dev/null @@ -1,10 +0,0 @@ -\ifdefined\directlua\input luaotfload.sty \fi -%% TFM’s can be loaded with a file: request ... -\font\antykwatorunska="file:rm-anttr" -%% or with an anonymous request, like in þe olde TeX: -\font\antykwatorunskabcap=ec-anttbcap -\font\lmromanten={file:ec-lmr10} at 10pt -\antykwatorunska foo bar -\antykwatorunskabcap baz xyzzy -\lmromanten Donde, está, la biblioteca. Me llamo T-Bone La araña discoteca. -\bye diff --git a/tests/sanitize_color.tex b/tests/sanitize_color.tex deleted file mode 100644 index fe95d37..0000000 --- a/tests/sanitize_color.tex +++ /dev/null @@ -1,8 +0,0 @@ -\input luaotfload.sty -\font\testa={name:Latin Modern Roman:color=0000ff99f} at 10pt -\font\testb={name:Latin Modern Roman:color=0000ff9} at 10pt -\font\testc={name:Latin Modern Roman:color=0000f} at 10pt -\testa test\par -\testb test\par -\testc test\par -\bye diff --git a/tests/systemfonts.tex b/tests/systemfonts.tex deleted file mode 100644 index af08509..0000000 --- a/tests/systemfonts.tex +++ /dev/null @@ -1,50 +0,0 @@ -\input luaotfload.sty - -\font\termesr ={TeX Gyre Termes:+liga} at 10pt -\font\termesb ={TeX Gyre Termes/B:+liga} at 10pt -\font\termesi ={TeX Gyre Termes/I:+liga} at 10pt -\font\termesbi ={TeX Gyre Termes/BI:+liga} at 10pt -\font\termesib ={TeX Gyre Termes/IB:+liga} at 10pt -\font\termesicu={TeX Gyre Termes/ICU/B:+liga} at 10pt -\font\termesaat={TeX Gyre Termes/AAT/IB:+liga} at 10pt -\font\termess ={TeX Gyre Termes/ICU/S=10:+liga} at 10pt -\font\dsans ={DejaVu Sans:+liga} at 10pt -\font\dsansi ={DejaVu Sans/I:+liga} at 10pt -\font\dsansb ={DejaVu Sans/B:+liga} at 10pt -\font\dsansib ={DejaVu Sans/IB:+liga} at 10pt -\font\dsansextr={DejaVu Sans/Extra Light:+liga} at 10pt -\font\dsansecnd={DejaVu Sans/Condensed:+liga} at 10pt -\font\lmr ={Latin Modern Roman:+liga} at 10pt -\font\lmf ={Latin Modern Roman/S=5:+liga} at 10pt -\font\lmb ={Latin Modern Roman/B:+liga} at 10pt -\font\lmi ={Latin Modern Roman/I:+liga} at 10pt -\font\lmbi ={Latin Modern Roman/BI:+liga} at 10pt -\font\lms ={Latin Modern Roman Slanted:+liga} at 10pt -\font\lmltn ={Latin Modern Roman:script=latn} at 10pt -%% get this font here: -%% http://sourceforge.net/projects/arabeyes/files/kacst_fonts/kacst_one_5.0.tar.bz2/download -\font\arab ={KacstOne:mode=node;script=arab} at 10pt - -\termesr fi fl ffi ffl ff\par -\termesb fi fl ffi ffl ff\par -\termesi fi fl ffi ffl ff\par -\termesbi fi fl ffi ffl ff\par -\termesib fi fl ffi ffl ff\par -\termesicu fi fl ffi ffl ff\par -\termesaat fi fl ffi ffl ff\par -\termess fi fl ffi ffl ff\par -\dsans fi fl ffi ffl ff\par -\dsansi fi fl ffi ffl ff\par -\dsansb fi fl ffi ffl ff\par -\dsansib fi fl ffi ffl ff\par -\dsansextr fi fl ffi ffl ff\par -\dsansecnd fi fl ffi ffl ff\par -\lmr fi fl ffi ffl ff\par -\lmf fi fl ffi ffl ff\par -\lmb fi fl ffi ffl ff\par -\lmi fi fl ffi ffl ff\par -\lmbi fi fl ffi ffl ff\par -\lms fi fl ffi ffl ff\par -\lmltn fi fl ffi ffl ff\par -\leavevmode\arab\luatextextdir TRT بِسْمِ الله الرَّحْمنِ الرحيم\par -\bye diff --git a/tests/texligatures.tex b/tests/texligatures.tex deleted file mode 100644 index 8317ee1..0000000 --- a/tests/texligatures.tex +++ /dev/null @@ -1,7 +0,0 @@ -\input luaotfload.sty - -\font\testa={file:lmroman10-regular:mode=base;script=latn;+tlig} at 10pt -\font\testb={file:lmroman10-regular:mode=node;script=latn;+tlig} at 10pt -\testa ``fi `fl' ffi--ffl---ff'' !` ?` "\par -\testb ``fi `fl' ffi--ffl---ff'' !` ?` "\par -\bye diff --git a/tests/tfmofm.ltx b/tests/tfmofm.ltx deleted file mode 100644 index 0f9f904..0000000 --- a/tests/tfmofm.ltx +++ /dev/null @@ -1,6 +0,0 @@ -\documentclass{article} -\usepackage{luaotfload} - -\begin{document} -Hello \(1+1=\sqrt{4}\) -\end{document} diff --git a/tests/tkrn.fea b/tests/tkrn.fea deleted file mode 100644 index c83927d..0000000 --- a/tests/tkrn.fea +++ /dev/null @@ -1,8 +0,0 @@ -languagesystem DFLT dflt; -languagesystem latn dflt; - -feature tkrn { - lookupflag 0; - pos E X -125; - pos T <0 0 -166 0> E <0 -235 0 0>; -} tkrn; diff --git a/tests/weirdfonts.tex b/tests/weirdfonts.tex deleted file mode 100644 index 9cbf8ac..0000000 --- a/tests/weirdfonts.tex +++ /dev/null @@ -1,15 +0,0 @@ -%% non-standard fonts deserve an extra test file -\documentclass{scrartcl} -\usepackage{fontspec} -%% ···································································· -%% libertine monospace -%% ------------------- -%% real-world example from: http://tex.stackexchange.com/q/110566 -%% causing database lookups to fail; addressed in luaotfload since -%% https://github.com/phi-gamma/luaotfload/commit/4d0d2c19ab36d4918a72041a087fbcb451ac8c52 -\setmonofont{Linux Libertine Mono O} -%% ···································································· - -\begin{document} - foo {\ttfamily bar} baz -\end{document} diff --git a/tests/zero_width_marks_lig.tex b/tests/zero_width_marks_lig.tex deleted file mode 100644 index 2c6dba9..0000000 --- a/tests/zero_width_marks_lig.tex +++ /dev/null @@ -1,16 +0,0 @@ -\input luaotfload.sty - -% https://bugs.freedesktop.org/attachment.cgi?id=72363 -\font\testa=file:TestLig.ttf:script=tibt;+ccmp+abvs+blws+kern at 10pt - -\testa གཚོའི་ཁིའུ་ཨཱཿཀ - -% good result for the first part: -% https://bugs.freedesktop.org/attachment.cgi?id=72365 -% for the second part, the two circles shoud appear clearlybefore the last -% letter, not mixed with it - -% see http://lists.freedesktop.org/archives/harfbuzz/2013-April/003101.html -% for some technical details. - -\bye -- cgit v1.2.3 From e8016ee1a59d7d5921bc4918a65ce6a37a4b07e7 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:32:41 +0100 Subject: [*] excise test-related make rules --- Makefile | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 2d618e7..aec2ac0 100644 --- a/Makefile +++ b/Makefile @@ -34,12 +34,6 @@ UNPACKED = luaotfload.sty luaotfload.lua GENERATED = $(GRAPHED) $(UNPACKED) $(COMPILED) $(RESOURCES) $(MAN) SOURCE = $(DTX) $(MANSOURCE) $(OTFL) README Makefile NEWS $(RESOURCESCRIPTS) -# test files -TESTDIR = tests -TESTSTATUS = $(wildcard $(TESTDIR)/*.tex $(TESTDIR)/*.ltx) -TESTSTATUS_SYS = $(TESTDIR)/systemfonts.tex $(TESTDIR)/fontconfig_conf_reading.tex -TESTSTATUS_TL = $(filter-out $(TESTSTATUS_SYS), $(TESTSTATUS)) - # Files grouped by installation location SCRIPTSTATUS = $(SCRIPT) $(OLDSCRIPT) $(RESOURCESCRIPTS) RUNSTATUS = $(UNPACKED) $(filter-out $(SCRIPTSTATUS),$(OTFL)) @@ -146,21 +140,6 @@ install: $(ALL_STATUS) @echo "Installing in '$(TEXMFROOT)'." $(run-install) -check: $(RUNSTATUS) $(TESTSTATUS_TL) - @rm -rf var - @for f in $(TESTSTATUS_TL); do \ - echo "check: luatex $$f"; \ - luatex --interaction=batchmode $$f \ - > /dev/null || exit $$?; \ - done - -check-all: $(TESTSTATUS_SYS) check - @cd $(TESTDIR); for f in $(TESTSTATUS_SYS); do \ - echo "check: luatex $$f"; \ - $(TESTENV) luatex --interaction=batchmode ../$$f \ - > /dev/null || exit $$?; \ - done - manifest: @echo "Source files:" @for f in $(SOURCE); do echo $$f; done @@ -169,9 +148,9 @@ manifest: @for f in $(GENERATED); do echo $$f; done clean: - @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out $(TESTDIR)/*.log + @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out mrproper: clean - @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE) $(TESTDIR)/*.pdf + @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE) @$(RM) -r -- $(DISTDIR) -- cgit v1.2.3 From 56ac9fbd9adf570501e358334adef356c572905b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 2 Jan 2014 21:34:00 +0100 Subject: [doc] update news --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 922d4ff..9211e31 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ Change History 2014/**/**, luaotfload v2.5 * Remove legacy code. * Remove compatibility with the old mkluatexfontdb script. + * Remove test directory. Use https://bitbucket.org/phg/lua-la-tex-tests + instead. 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) -- cgit v1.2.3 From cfd807af6c4f27fc8bc95744cd1c4da8a07559e1 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 3 Jan 2014 18:14:20 +0100 Subject: [db] handle missing metadata table Fixes issue #164, hopefully. --- luaotfload-database.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index b22afd7..4be54d6 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -574,7 +574,16 @@ load_names = function (dry_run) report ("info", 3, "db", "Loading took %0.f ms.", 1000 * (osgettimeofday () - starttime)) - local db_version, nms_version = data.meta.version, names.version + local db_version, nms_version + if data.meta then + db_version = data.meta.version + else + --- Compatibility branch; the version info used to be + --- stored in the table root which is why updating from + --- an earlier index version broke. + db_version = data.version or -42 --- invalid + end + nms_version = names.version if db_version ~= nms_version then report ("both", 0, "db", [[Version mismatch; expected %4.3f, got %4.3f.]], -- cgit v1.2.3 From 5d21d8a105a5634e16a9c45ba5cbf6749a7b47db Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 3 Jan 2014 18:17:16 +0100 Subject: [db] fix incorrect local --- luaotfload-database.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 4be54d6..cc32bb8 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -83,7 +83,7 @@ local tableconcat = table.concat local tablesort = table.sort local utf8gsub = unicode.utf8.gsub local utf8lower = unicode.utf8.lower -local utf8length = unicode.utf8.length +local utf8len = unicode.utf8.len local zlibcompress = zlib.compress --- these come from Lualibs/Context @@ -2127,7 +2127,7 @@ end local truncate_string = function (str, restrict) local tw = luaotfloadconfig.termwidth local wd = tw - restrict - local len = utf8length (str) + local len = utf8len (str) if wd - len < 0 then --- combined length exceeds terminal, str = ".." .. stringsub(str, len - wd + 2) -- cgit v1.2.3 From 8cf014615f6dd78b59d03a189e91140c48770373 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 3 Jan 2014 20:03:08 +0100 Subject: [db] use (sanitized) names.fullname field when resolving fonts by name --- luaotfload-database.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index cc32bb8..72b043b 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -1070,9 +1070,10 @@ local resolve_fontname = function (specification, name, style) local face = mappings [i] local prefmodifiers = face.prefmodifiers local subfamily = face.subfamily - if face.fontname == name - or face.fullname == name - or face.psname == name + if face.fontname == name + or face.splainname == name + or face.fullname == name + or face.psname == name then return face.basename, face.subfont elseif face.familyname == name then @@ -1608,6 +1609,7 @@ t1_fullinfo = function (filename, _subfont, location, basename, format) fontname = sanitized.fontname, familyname = sanitized.familyname, plainname = fullname, + splainname = sanitized.fullname, psname = sanitized.fontname, version = metadata.version, size = false, @@ -2775,6 +2777,7 @@ local pull_values = function (entry) entry.psname = english.psname entry.fontname = info.fontname entry.fullname = english.fullname or info.fullname + entry.splainname = metadata.fullname entry.prefmodifiers = english.prefmodifiers local metafamily = metadata.familyname local familyname = english.preffamily or english.family -- cgit v1.2.3 From 7e1a7178de67590f0a484d06abfb933062c0ac87 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 3 Jan 2014 20:17:47 +0100 Subject: [tests] include two kinds of tests for Minion Pro --- mktests | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mktests b/mktests index 688e5f8..baa710c 100755 --- a/mktests +++ b/mktests @@ -86,6 +86,11 @@ local infer_regular_style = { { "Garamond Premier Pro", "GaramondPremrPro.otf" }, { "CMU Serif", "cmunrm.otf" }, { "CMU Sans Serif", "cmunss.otf" }, + { "Minion Pro", "MinionPro_Regular.otf" }, + --- Below test will succeed only if we match for the + --- splainname (= sanitized tfmdata.fullname) field + --- explicitly. + { "Minion Pro Italic", "MinionPro_It.otf" }, } local choose_optical_size = { @@ -120,6 +125,25 @@ local choose_style = { { { name = "CMU Sans Serif", style = "italic" }, "cmunsi.otf" }, -- no “italic” but “oblique” { { name = "CMU Sans Serif", style = "bold" }, "cmunsx.otf" }, { { name = "CMU Sans Serif", style = "bolditalic" }, "cmunso.otf" }, + --[[-- + Minion Pro Italic is exceptionally weird regarding identifiers in + that the postscript fontname and both info.fontname and + info.fullname are given as “minionproit”. Now its english fullname + (field 18) is “minionproital”. Only the value “fullname” in the root of + the tfmdata structure (not the one returned by fontloader.info()!) + accurately yields “Minion Pro Italic”. + + To complete the picture, the file naming isn’t very consistent either: + we find the suffixes “Regular” and “Bold”, but “It” and “BoldIt”. What + the hell were the designers smoking? + + Also, the full Minion Pro set comes with different optical sizes which + for monetary reasons cannot considered here. + --]]-- + { { name = "Minion Pro", style = "regular" }, "MinionPro_Regular.otf" }, + { { name = "Minion Pro", style = "italic" }, "MinionPro_It.otf" }, + { { name = "Minion Pro", style = "bold" }, "MinionPro_Bold.otf" }, + { { name = "Minion Pro", style = "bolditalic" }, "MinionPro_BoldIt.otf" }, } --- this needs a database built with --formats=+pfa,pfb,afm -- cgit v1.2.3 From a69e72718162d0bf1b701e31e67de2f2b8d2aebd Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 3 Jan 2014 22:33:19 +0100 Subject: [main] set correct default loglevel when running as tex (seriously, dtx is an abomination) --- luaotfload.dtx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 320b9d9..f450d61 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1595,7 +1595,7 @@ config.luaotfload = config.luaotfload or { } config.luaotfload.resolver = config.luaotfload.resolver or "cached" config.luaotfload.definer = config.luaotfload.definer or "patch" config.luaotfload.compatibility = config.luaotfload.compatibility or false -config.luaotfload.loglevel = config.luaotfload.loglevel or 1 +config.luaotfload.loglevel = config.luaotfload.loglevel or 2 config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter" config.luaotfload.prioritize = config.luaotfload.prioritize or "sys" config.luaotfload.names_dir = config.luaotfload.names_dir or "names" -- cgit v1.2.3 From 5d207c36c8bdf259f5c8b4e7d1a7894774f0edcf Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 3 Jan 2014 22:43:05 +0100 Subject: [log] fix newline handling of the last status message when imitating texio.*() Issue reported by /u/eg9 on https://github.com/lualatex/luaotfload/issues/164#issuecomment-31551430 --- luaotfload-override.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/luaotfload-override.lua b/luaotfload-override.lua index a692cb4..7cb9c5d 100644 --- a/luaotfload-override.lua +++ b/luaotfload-override.lua @@ -166,6 +166,8 @@ if texjob == true then statusln = function (str) if first_status == false then iowrite (kill_line) + else + iowrite "\n" end iowrite (str) end @@ -326,7 +328,9 @@ end local status_stop = function (...) if first_status == false then status_writer(...) - writeln "" + if texjob == false then + writeln "" + end end end -- cgit v1.2.3 From 1d16a614853476a8cc00f06a4f018e515b175b8d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 6 Jan 2014 21:00:17 +0100 Subject: [fontloader] sync with Context as of 2014-01-06 --- luaotfload-fontloader.lua | 140 +++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 59 deletions(-) diff --git a/luaotfload-fontloader.lua b/luaotfload-fontloader.lua index 1773241..2563739 100644 --- a/luaotfload-fontloader.lua +++ b/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 12/24/13 17:52:44 +-- merge date : 01/06/14 18:10:50 do -- begin closure to overcome local limits and interference @@ -6405,7 +6405,7 @@ local type,next,tonumber,tostring=type,next,tonumber,tostring local abs=math.abs local insert=table.insert local lpegmatch=lpeg.match -local reversed,concat,remove=table.reversed,table.concat,table.remove +local reversed,concat,remove,sortedkeys=table.reversed,table.concat,table.remove,table.sortedkeys local ioflush=io.flush local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive local formatters=string.formatters @@ -7045,7 +7045,6 @@ actions["prepare glyphs"]=function(data,filename,raw) } local altuni=glyph.altuni if altuni then - local d for i=1,#altuni do local a=altuni[i] local u=a.unicode @@ -7058,15 +7057,8 @@ actions["prepare glyphs"]=function(data,filename,raw) vv={ [u]=unicode } variants[v]=vv end - elseif d then - d[#d+1]=u - else - d={ u } end end - if d then - duplicates[unicode]=d - end end else report_otf("potential problem: glyph %U is used but empty",index) @@ -7084,47 +7076,49 @@ actions["check encoding"]=function(data,filename,raw) local duplicates=resources.duplicates local mapdata=raw.map or {} local unicodetoindex=mapdata and mapdata.map or {} + local indextounicode=mapdata and mapdata.backmap or {} local encname=lower(data.enc_name or mapdata.enc_name or "") local criterium=0xFFFF if find(encname,"unicode") then if trace_loading then report_otf("checking embedded unicode map %a",encname) end - for unicode,index in next,unicodetoindex do - if unicode<=criterium and not descriptions[unicode] then - local parent=indices[index] - if not parent then - report_otf("weird, unicode %U points to nowhere with index %H",unicode,index) - else - local parentdescription=descriptions[parent] - if parentdescription then - local altuni=parentdescription.altuni - if not altuni then - altuni={ { unicode=unicode } } - parentdescription.altuni=altuni - duplicates[parent]={ unicode } - else - local done=false - for i=1,#altuni do - if altuni[i].unicode==unicode then - done=true - break - end - end - if not done then - insert(altuni,{ unicode=unicode }) - insert(duplicates[parent],unicode) + local hash={} + for index,unicode in next,indices do + hash[index]=descriptions[unicode] + end + local reported={} + for unicode,index in next,unicodetoindex do + if not descriptions[unicode] then + local d=hash[index] + if d then + if d.unicode~=unicode then + local c=d.copies + if c then + c[unicode]=true + else + d.copies={ [unicode]=true } end end - if trace_loading then - report_otf("weird, unicode %U points to nowhere with index %H",unicode,index) - end - else - report_otf("weird, unicode %U points to %U with index %H",unicode,index) + elseif not reported[i] then + report_otf("missing index %i",index) + reported[i]=true end end end - end + for index,data in next,hash do + data.copies=sortedkeys(data.copies) + end + for index,unicode in next,indices do + local description=hash[index] + local copies=description.copies + if copies then + duplicates[unicode]=copies + description.copies=nil + else + report_otf("copies but no unicode parent %U",unicode) + end + end elseif properties.cidinfo then report_otf("warning: no unicode map, used cidmap %a",properties.cidinfo.usedname) else @@ -7132,6 +7126,7 @@ actions["check encoding"]=function(data,filename,raw) end if mapdata then mapdata.map={} + mapdata.backmap={} end end actions["add duplicates"]=function(data,filename,raw) @@ -7142,28 +7137,37 @@ actions["add duplicates"]=function(data,filename,raw) local indices=resources.indices local duplicates=resources.duplicates for unicode,d in next,duplicates do - for i=1,#d do - local u=d[i] - if not descriptions[u] then - local description=descriptions[unicode] - local duplicate=table.copy(description) - duplicate.comment=format("copy of U+%05X",unicode) - descriptions[u]=duplicate - local n=0 - for _,description in next,descriptions do - if kerns then - local kerns=description.kerns - for _,k in next,kerns do - local ku=k[unicode] - if ku then - k[u]=ku - n=n+1 + local nofduplicates=#d + if nofduplicates>4 then + if trace_loading then + report_otf("ignoring excessive duplicates of %U (n=%s)",unicode,nofduplicates) + end + else + for i=1,nofduplicates do + local u=d[i] + if not descriptions[u] then + local description=descriptions[unicode] + local n=0 + for _,description in next,descriptions do + if kerns then + local kerns=description.kerns + for _,k in next,kerns do + local ku=k[unicode] + if ku then + k[u]=ku + n=n+1 + end end end end - end - if trace_loading then - report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n) + if u>0 then + local duplicate=table.copy(description) + duplicate.comment=format("copy of U+%05X",unicode) + descriptions[u]=duplicate + if trace_loading then + report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n) + end + end end end end @@ -8184,6 +8188,24 @@ local function otftotfm(specification) local features=specification.features.normal local rawdata=otf.load(filename,sub,features and features.featurefile) if rawdata and next(rawdata) then + local descriptions=rawdata.descriptions + local duplicates=rawdata.resources.duplicates + if duplicates then + local nofduplicates,nofduplicated=0,0 + for parent,list in next,duplicates do + for i=1,#list do + local unicode=list[i] + if not descriptions[unicode] then + descriptions[unicode]=descriptions[parent] + nofduplicated=nofduplicated+1 + end + end + nofduplicates=nofduplicates+#list + end + if trace_otf and nofduplicated~=nofduplicates then + report_otf("%i extra duplicates copied out of %i",nofduplicated,nofduplicates) + end + end rawdata.lookuphash={} tfmdata=copytotfm(rawdata,cache_id) if tfmdata and next(tfmdata) then -- cgit v1.2.3 From 9ca7ecddf5c225abaf9719e3608b93ec7a37ac82 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 6 Jan 2014 22:45:00 +0100 Subject: [aux] extinguish compatibility code --- luaotfload-auxiliary.lua | 122 ++++++++--------------------------------------- 1 file changed, 19 insertions(+), 103 deletions(-) diff --git a/luaotfload-auxiliary.lua b/luaotfload-auxiliary.lua index 821a436..d3184c9 100644 --- a/luaotfload-auxiliary.lua +++ b/luaotfload-auxiliary.lua @@ -18,25 +18,25 @@ luaotfload.aux = luaotfload.aux or { } config = config or { } config.luaotfload = config.luaotfload or { } -local aux = luaotfload.aux -local log = luaotfload.log -local warning = luaotfload.log -local fonthashes = fonts.hashes -local identifiers = fonthashes.identifiers - -local fontid = font.id -local texsprint = tex.sprint - -local dofile = dofile -local getmetatable = getmetatable -local setmetatable = setmetatable -local utf8 = unicode.utf8 -local stringlower = string.lower -local stringformat = string.format -local stringgsub = string.gsub -local stringbyte = string.byte -local stringfind = string.find -local tablecopy = table.copy +local aux = luaotfload.aux +local log = luaotfload.log +local warning = luaotfload.log +local fonthashes = fonts.hashes +local identifiers = fonthashes.identifiers + +local fontid = font.id +local texsprint = tex.sprint + +local dofile = dofile +local getmetatable = getmetatable +local setmetatable = setmetatable +local utf8 = unicode.utf8 +local stringlower = string.lower +local stringformat = string.format +local stringgsub = string.gsub +local stringbyte = string.byte +local stringfind = string.find +local tablecopy = table.copy ----------------------------------------------------------------------- --- font patches @@ -76,90 +76,6 @@ end aux.stop_rewrite_fontname = stop_rewrite_fontname ---- as of 2.3 the compatibility hacks for TL 2013 are made optional - -if config.luaotfload.compatibility == true then - ---[[doc-- - -The font object (tfmdata) structure has changed since version 1.x, so -in case other packages haven’t been updated we put fallbacks in place -where they’d expect them. Specifically we have in mind: - - · fontspec - · unicode-math - · microtype (most likely fixed till TL2013) - ---doc]]-- - ---- fontobj -> fontobj -local add_fontdata_fallbacks = function (fontdata) - if type(fontdata) == "table" then - local fontparameters = fontdata.parameters - local metadata - if not fontdata.shared then --- that would be a tfm - --- we can’t really catch everything that - --- goes wrong; for some reason, fontspec.lua - --- just assumes it always gets an otf object, - --- so its capheight callback, which does not - --- bother to do any checks, will access - --- fontdata.shared no matter what ... - fontdata.units = fontdata.units_per_em - - else --- otf - metadata = fontdata.shared.rawdata.metadata - fontdata.name = metadata.origname or fontdata.name - fontdata.units = fontdata.units_per_em - fontdata.size = fontdata.size or fontparameters.size - local resources = fontdata.resources - --- for legacy fontspec.lua and unicode-math.lua - fontdata.shared.otfdata = { - pfminfo = { os2_capheight = metadata.pfminfo.os2_capheight }, - metadata = { ascent = metadata.ascent }, - } - --- for microtype and fontspec - --local fake_features = { } - local fake_features = table.copy(resources.features) - setmetatable(fake_features, { __index = function (tab, idx) - warning("some package (probably fontspec) is outdated") - warning( - "attempt to index " .. - "tfmdata.shared.otfdata.luatex.features (%s)", - idx) - --os.exit(1) - return tab[idx] - end, - }) - fontdata.shared.otfdata.luatex = { - unicodes = resources.unicodes, - features = fake_features, - } - end - end - return fontdata -end - -luatexbase.add_to_callback( - "luaotfload.patch_font", - add_fontdata_fallbacks, - "luaotfload.fontdata_fallbacks") - ---[[doc-- - -Additionally, the font registry is expected at fonts.identifiers -(fontspec) or fonts.ids (microtype), but in the meantime it has been -migrated to fonts.hashes.identifiers. We’ll make luaotfload satisfy -those assumptions. (Maybe it’d be more appropriate to use -font.getfont() since Hans made it a harmless wrapper [1].) - -[1] http://www.ntg.nl/pipermail/ntg-context/2013/072166.html - ---doc]]-- - -fonts.identifiers = fonts.hashes.identifiers -fonts.ids = fonts.hashes.identifiers - -end --[[doc-- This sets two dimensions apparently relied upon by the unicode-math -- cgit v1.2.3 From e45888dcc949aa1a2c3861a497c54073eba725f6 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 6 Jan 2014 22:47:10 +0100 Subject: [main] remove references to config.luaotfload.compatibility --- luaotfload.dtx | 1 - 1 file changed, 1 deletion(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index f450d61..3f533ba 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1594,7 +1594,6 @@ config.luaotfload = config.luaotfload or { } ------.luaotfload.resolver = config.luaotfload.resolver or "normal" config.luaotfload.resolver = config.luaotfload.resolver or "cached" config.luaotfload.definer = config.luaotfload.definer or "patch" -config.luaotfload.compatibility = config.luaotfload.compatibility or false config.luaotfload.loglevel = config.luaotfload.loglevel or 2 config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter" config.luaotfload.prioritize = config.luaotfload.prioritize or "sys" -- cgit v1.2.3 From c23a171d7e9b8cf8b26018fcfd253312d1c9234e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 6 Jan 2014 22:47:59 +0100 Subject: [aux] remove reference to unnecessary globals --- luaotfload-auxiliary.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/luaotfload-auxiliary.lua b/luaotfload-auxiliary.lua index d3184c9..334ac47 100644 --- a/luaotfload-auxiliary.lua +++ b/luaotfload-auxiliary.lua @@ -15,9 +15,6 @@ luaotfload = luaotfload or {} luaotfload.aux = luaotfload.aux or { } -config = config or { } -config.luaotfload = config.luaotfload or { } - local aux = luaotfload.aux local log = luaotfload.log local warning = luaotfload.log -- cgit v1.2.3 From 9e42ad3bf6f2a73bc98f8e05277d34a3b0ea8c1a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 7 Jan 2014 19:00:32 +0100 Subject: [fontloader] sync with Context as of 2014-01-07 --- luaotfload-fontloader.lua | 772 ++++++++++++++++++++++------------------------ 1 file changed, 363 insertions(+), 409 deletions(-) diff --git a/luaotfload-fontloader.lua b/luaotfload-fontloader.lua index 2563739..a3a2de5 100644 --- a/luaotfload-fontloader.lua +++ b/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 01/06/14 18:10:50 +-- merge date : 01/07/14 17:03:56 do -- begin closure to overcome local limits and interference @@ -6427,7 +6427,7 @@ local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf otf.glists={ "gsub","gpos" } -otf.version=2.749 +otf.version=2.750 otf.cache=containers.define("fonts","otf",otf.version,true) local fontdata=fonts.hashes.identifiers local chardata=characters and characters.data @@ -8903,26 +8903,12 @@ nodes.injections=nodes.injections or {} local injections=nodes.injections local nodecodes=nodes.nodecodes local glyph_code=nodecodes.glyph -local disc_code=nodecodes.disc local kern_code=nodecodes.kern -local nuts=nodes.nuts -local nodepool=nuts.pool +local nodepool=nodes.pool local newkern=nodepool.kern -local tonode=nuts.tonode -local tonut=nuts.tonut -local getfield=nuts.getfield -local getnext=nuts.getnext -local getprev=nuts.getprev -local getid=nuts.getid -local getattr=nuts.getattr -local getfont=nuts.getfont -local getsubtype=nuts.getsubtype -local getchar=nuts.getchar -local setfield=nuts.setfield -local setattr=nuts.setattr -local traverse_id=nuts.traverse_id -local insert_node_before=nuts.insert_before -local insert_node_after=nuts.insert_after +local traverse_id=node.traverse_id +local insert_node_before=node.insert_before +local insert_node_after=node.insert_after local a_kernpair=attributes.private('kernpair') local a_ligacomp=attributes.private('ligacomp') local a_markbase=attributes.private('markbase') @@ -8941,21 +8927,21 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2]) local ws,wn=tfmstart.width,tfmnext.width local bound=#cursives+1 - setattr(start,a_cursbase,bound) - setattr(nxt,a_curscurs,bound) + start[a_cursbase]=bound + nxt[a_curscurs]=bound cursives[bound]={ rlmode,dx,dy,ws,wn } return dx,dy,bound end function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4] if x~=0 or w~=0 or y~=0 or h~=0 then - local bound=getattr(current,a_kernpair) + local bound=current[a_kernpair] if bound then local kb=kerns[bound] kb[2],kb[3],kb[4],kb[5]=(kb[2] or 0)+x,(kb[3] or 0)+y,(kb[4] or 0)+w,(kb[5] or 0)+h else bound=#kerns+1 - setattr(current,a_kernpair,bound) + current[a_kernpair]=bound kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width } end return x,y,w,h,bound @@ -8966,7 +8952,7 @@ function injections.setkern(current,factor,rlmode,x,tfmchr) local dx=factor*x if dx~=0 then local bound=#kerns+1 - setattr(current,a_kernpair,bound) + current[a_kernpair]=bound kerns[bound]={ rlmode,dx } return dx,bound else @@ -8975,25 +8961,25 @@ function injections.setkern(current,factor,rlmode,x,tfmchr) end function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) - local bound=getattr(base,a_markbase) + local bound=base[a_markbase] local index=1 if bound then local mb=marks[bound] if mb then index=#mb+1 mb[index]={ dx,dy,rlmode } - setattr(start,a_markmark,bound) - setattr(start,a_markdone,index) + start[a_markmark]=bound + start[a_markdone]=index return dx,dy,bound else - report_injections("possible problem, %U is base mark without data (id %a)",getchar(base),bound) + report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound) end end index=index or 1 bound=#marks+1 - setattr(base,a_markbase,bound) - setattr(start,a_markmark,bound) - setattr(start,a_markdone,index) + base[a_markbase]=bound + start[a_markmark]=bound + start[a_markdone]=index marks[bound]={ [index]={ dx,dy,rlmode,baseismark } } return dx,dy,bound end @@ -9003,15 +8989,15 @@ end local function trace(head) report_injections("begin run") for n in traverse_id(glyph_code,head) do - if getsubtype(n)<256 then - local kp=getattr(n,a_kernpair) - local mb=getattr(n,a_markbase) - local mm=getattr(n,a_markmark) - local md=getattr(n,a_markdone) - local cb=getattr(n,a_cursbase) - local cc=getattr(n,a_curscurs) - local char=getchar(n) - report_injections("font %s, char %U, glyph %c",getfont(n),char,char) + if n.subtype<256 then + local kp=n[a_kernpair] + local mb=n[a_markbase] + local mm=n[a_markmark] + local md=n[a_markdone] + local cb=n[a_cursbase] + local cc=n[a_curscurs] + local char=n.char + report_injections("font %s, char %U, glyph %c",n.font,char,char) if kp then local k=kerns[kp] if k[3] then @@ -9052,23 +9038,21 @@ local function show_result(head) local current=head local skipping=false while current do - local id=getid(current) + local id=current.id if id==glyph_code then - report_injections("char: %C, width %p, xoffset %p, yoffset %p", - getchar(current),getfield(current,"width"),getfield(current,"xoffset"),getfield(current,"yoffset")) + report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset) skipping=false elseif id==kern_code then - report_injections("kern: %p",getfield(current,"kern")) + report_injections("kern: %p",current.kern) skipping=false elseif not skipping then report_injections() skipping=true end - current=getnext(current) + current=current.next end end function injections.handler(head,where,keep) - head=tonut(head) local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns) if has_marks or has_cursives then if trace_injections then @@ -9078,18 +9062,17 @@ function injections.handler(head,where,keep) if has_kerns then local nf,tm=nil,nil for n in traverse_id(glyph_code,head) do - if getsubtype(n)<256 then + if n.subtype<256 then nofvalid=nofvalid+1 valid[nofvalid]=n - local f=getfont(n) - if f~=nf then - nf=f - tm=fontdata[nf].resources.marks + if n.font~=nf then + nf=n.font + tm=fontdata[nf].resources.marks end if tm then - mk[n]=tm[getchar(n)] + mk[n]=tm[n.char] end - local k=getattr(n,a_kernpair) + local k=n[a_kernpair] if k then local kk=kerns[k] if kk then @@ -9109,16 +9092,15 @@ function injections.handler(head,where,keep) else local nf,tm=nil,nil for n in traverse_id(glyph_code,head) do - if getsubtype(n)<256 then + if n.subtype<256 then nofvalid=nofvalid+1 valid[nofvalid]=n - local f=getfont(n) - if f~=nf then - nf=f - tm=fontdata[nf].resources.marks + if n.font~=nf then + nf=n.font + tm=fontdata[nf].resources.marks end if tm then - mk[n]=tm[getchar(n)] + mk[n]=tm[n.char] end end end @@ -9127,7 +9109,7 @@ function injections.handler(head,where,keep) local cx={} if has_kerns and next(ky) then for n,k in next,ky do - setfield(n,"yoffset",k) + n.yoffset=k end end if has_cursives then @@ -9136,9 +9118,9 @@ function injections.handler(head,where,keep) for i=1,nofvalid do local n=valid[i] if not mk[n] then - local n_cursbase=getattr(n,a_cursbase) + local n_cursbase=n[a_cursbase] if p_cursbase then - local n_curscurs=getattr(n,a_curscurs) + local n_curscurs=n[a_curscurs] if p_cursbase==n_curscurs then local c=cursives[n_curscurs] if c then @@ -9161,20 +9143,20 @@ function injections.handler(head,where,keep) end end elseif maxt>0 then - local ny=getfield(n,"yoffset") + local ny=n.yoffset for i=maxt,1,-1 do ny=ny+d[i] local ti=t[i] - setfield(ti,"yoffset",getfield(ti,"yoffset")+ny) + ti.yoffset=ti.yoffset+ny end maxt=0 end if not n_cursbase and maxt>0 then - local ny=getfield(n,"yoffset") + local ny=n.yoffset for i=maxt,1,-1 do ny=ny+d[i] local ti=t[i] - setfield(ti,"yoffset",ny) + ti.yoffset=ny end maxt=0 end @@ -9182,11 +9164,11 @@ function injections.handler(head,where,keep) end end if maxt>0 then - local ny=getfield(n,"yoffset") + local ny=n.yoffset for i=maxt,1,-1 do ny=ny+d[i] local ti=t[i] - setfield(ti,"yoffset",ny) + ti.yoffset=ny end maxt=0 end @@ -9197,66 +9179,57 @@ function injections.handler(head,where,keep) if has_marks then for i=1,nofvalid do local p=valid[i] - local p_markbase=getattr(p,a_markbase) + local p_markbase=p[a_markbase] if p_markbase then local mrks=marks[p_markbase] local nofmarks=#mrks - for n in traverse_id(glyph_code,getnext(p)) do - local n_markmark=getattr(n,a_markmark) + for n in traverse_id(glyph_code,p.next) do + local n_markmark=n[a_markmark] if p_markbase==n_markmark then - local index=getattr(n,a_markdone) or 1 + local index=n[a_markdone] or 1 local d=mrks[index] if d then local rlmode=d[3] local k=wx[p] - local px=getfield(p,"xoffset") - local ox=0 if k then local x=k[2] local w=k[4] if w then if rlmode and rlmode>=0 then - ox=px-getfield(p,"width")+d[1]-(w-x) + n.xoffset=p.xoffset-p.width+d[1]-(w-x) else - ox=px-d[1]-x + n.xoffset=p.xoffset-d[1]-x end else if rlmode and rlmode>=0 then - ox=px-getfield(p,"width")+d[1] + n.xoffset=p.xoffset-p.width+d[1] else - ox=px-d[1]-x + n.xoffset=p.xoffset-d[1]-x end end else - local wp=getfield(p,"width") - local wn=getfield(n,"width") if rlmode and rlmode>=0 then - ox=px-wp+d[1] + n.xoffset=p.xoffset-p.width+d[1] else - ox=px-d[1] + n.xoffset=p.xoffset-d[1] end - if wn~=0 then - insert_node_before(head,n,newkern(-wn/2)) - insert_node_after(head,n,newkern(-wn/2)) + local w=n.width + if w~=0 then + insert_node_before(head,n,newkern(-w/2)) + insert_node_after(head,n,newkern(-w/2)) end end - setfield(n,"xoffset",ox) - local py=getfield(p,"yoffset") - local oy=0 if mk[p] then - oy=py+d[2] + n.yoffset=p.yoffset+d[2] else - oy=getfield(n,"yoffset")+py+d[2] + n.yoffset=n.yoffset+p.yoffset+d[2] end - setfield(n,"yoffset",oy) if nofmarks==1 then break else nofmarks=nofmarks-1 end end - elseif not n_markmark then - break else end end @@ -9308,7 +9281,6 @@ function injections.handler(head,where,keep) if not keep then kerns={} end -head=tonode(head) return head,true elseif not keep then kerns,cursives,marks={},{},{} @@ -9318,14 +9290,14 @@ head=tonode(head) trace(head) end for n in traverse_id(glyph_code,head) do - if getsubtype(n)<256 then - local k=getattr(n,a_kernpair) + if n.subtype<256 then + local k=n[a_kernpair] if k then local kk=kerns[k] if kk then local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4] if y and y~=0 then - setfield(n,"yoffset",y) + n.yoffset=y end if w then local wx=w-x @@ -9356,10 +9328,10 @@ head=tonode(head) if not keep then kerns={} end - return tonode(head),true + return head,true else end - return tonode(head),false + return head,false end end -- closure @@ -9774,25 +9746,12 @@ registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") registertracker("otf.actions","otf.replacements,otf.positions") registertracker("otf.injections","nodes.injections") registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") -local nuts=nodes.nuts -local tonode=nuts.tonode -local tonut=nuts.tonut -local getfield=nuts.getfield -local getnext=nuts.getnext -local getprev=nuts.getprev -local getid=nuts.getid -local getattr=nuts.getattr -local getfont=nuts.getfont -local getsubtype=nuts.getsubtype -local getchar=nuts.getchar -local setfield=nuts.setfield -local setattr=nuts.setattr -local insert_node_after=nuts.insert_after -local delete_node=nuts.delete -local copy_node=nuts.copy -local find_node_tail=nuts.tail -local flush_node_list=nuts.flush_list -local end_of_math=nuts.end_of_math +local insert_node_after=node.insert_after +local delete_node=nodes.delete +local copy_node=node.copy +local find_node_tail=node.tail or node.slide +local flush_node_list=node.flush_list +local end_of_math=node.end_of_math local setmetatableindex=table.setmetatableindex local zwnj=0x200C local zwj=0x200D @@ -9903,83 +9862,83 @@ local function pref(kind,lookupname) return formatters["feature %a, lookup %a"](kind,lookupname) end local function copy_glyph(g) - local components=getfield(g,"components") + local components=g.components if components then - setfield(g,"components",nil) + g.components=nil local n=copy_node(g) - setfield(g,"components",components) + g.components=components return n else return copy_node(g) end end local function markstoligature(kind,lookupname,head,start,stop,char) - if start==stop and getchar(start)==char then + if start==stop and start.char==char then return head,start else - local prev=getprev(start) - local next=getnext(stop) - setfield(start,"prev",nil) - setfield(stop,"next",nil) + local prev=start.prev + local next=stop.next + start.prev=nil + stop.next=nil local base=copy_glyph(start) if head==start then head=base end - setfield(base,"char",char) - setfield(base,"subtype",ligature_code) - setfield(base,"components",start) + base.char=char + base.subtype=ligature_code + base.components=start if prev then - setfield(prev,"next",base) + prev.next=base end if next then - setfield(next,"prev",base) + next.prev=base end - setfield(base,"next",next) - setfield(base,"prev",prev) + base.next=next + base.prev=prev return head,base end end local function getcomponentindex(start) - if getid(start)~=glyph_code then + if start.id~=glyph_code then return 0 - elseif getsubtype(start)==ligature_code then + elseif start.subtype==ligature_code then local i=0 - local components=getfield(start,"components") + local components=start.components while components do i=i+getcomponentindex(components) - components=getnext(components) + components=components.next end return i - elseif not marks[getchar(start)] then + elseif not marks[start.char] then return 1 else return 0 end end local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) - if start==stop and getchar(start)==char then - setfield(start,"char",char) + if start==stop and start.char==char then + start.char=char return head,start end - local prev=getprev(start) - local next=getnext(stop) - setfield(start,"prev",nil) - setfield(stop,"next",nil) + local prev=start.prev + local next=stop.next + start.prev=nil + stop.next=nil local base=copy_glyph(start) if start==head then head=base end - setfield(base,"char",char) - setfield(base,"subtype",ligature_code) - setfield(base,"components",start) + base.char=char + base.subtype=ligature_code + base.components=start if prev then - setfield(prev,"next",base) + prev.next=base end if next then - setfield(next,"prev",base) + next.prev=base end - setfield(base,"next",next) - setfield(base,"prev",prev) + base.next=next + base.prev=prev if not discfound then local deletemarks=markflag~="mark" local components=start @@ -9988,42 +9947,42 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun local head=base local current=base while start do - local char=getchar(start) + local char=start.char if not marks[char] then baseindex=baseindex+componentindex componentindex=getcomponentindex(start) elseif not deletemarks then - setattr(start,a_ligacomp,baseindex+(getattr(start,a_ligacomp) or componentindex)) + start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex) if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp)) + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) end head,current=insert_node_after(head,current,copy_node(start)) elseif trace_marks then logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char)) end - start=getnext(start) + start=start.next end - local start=getnext(current) - while start and getid(start)==glyph_code do - local char=getchar(start) + local start=current.next + while start and start.id==glyph_code do + local char=start.char if marks[char] then - setattr(start,a_ligacomp,baseindex+(getattr(start,a_ligacomp) or componentindex)) + start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex) if trace_marks then - logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp)) + logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) end else break end - start=getnext(start) + start=start.next end end return head,base end function handlers.gsub_single(head,start,kind,lookupname,replacement) if trace_singles then - logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement)) + logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) end - setfield(start,"char",replacement) + start.char=replacement return head,start,true end local function get_alternative_glyph(start,alternatives,value,trace_alternatives) @@ -10049,7 +10008,7 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range") end elseif value==0 then - return getchar(start),trace_alternatives and formatters["invalid value %a, %s"](value,"no change") + return start.char,trace_alternatives and formatters["invalid value %a, %s"](value,"no change") elseif value<1 then return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1) else @@ -10060,25 +10019,25 @@ end local function multiple_glyphs(head,start,multiple,ignoremarks) local nofmultiples=#multiple if nofmultiples>0 then - setfield(start,"char",multiple[1]) + start.char=multiple[1] if nofmultiples>1 then - local sn=getnext(start) + local sn=start.next for k=2,nofmultiples do local n=copy_node(start) - setfield(n,"char",multiple[k]) - setfield(n,"next",sn) - setfield(n,"prev",start) + n.char=multiple[k] + n.next=sn + n.prev=start if sn then - setfield(sn,"prev",n) + sn.prev=n end - setfield(start,"next",n) + start.next=n start=n end end return head,start,true else if trace_multiples then - logprocess("no multiple for %s",gref(getchar(start))) + logprocess("no multiple for %s",gref(start.char)) end return head,start,false end @@ -10088,34 +10047,34 @@ function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence local choice,comment=get_alternative_glyph(start,alternative,value,trace_alternatives) if choice then if trace_alternatives then - logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(getchar(start)),choice,gref(choice),comment) + logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment) end - setfield(start,"char",choice) + start.char=choice else if trace_alternatives then - logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(getchar(start)),comment) + logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment) end end return head,start,true end function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence) if trace_multiples then - logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(getchar(start)),gref(multiple)) + logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) end return multiple_glyphs(head,start,multiple,sequence.flags[1]) end function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) - local s,stop,discfound=getnext(start),nil,false - local startchar=getchar(start) + local s,stop,discfound=start.next,nil,false + local startchar=start.char if marks[startchar] then while s do - local id=getid(s) - if id==glyph_code and getfont(s)==currentfont and getsubtype(s)<256 then - local lg=ligature[getchar(s)] + local id=s.id + if id==glyph_code and s.font==currentfont and s.subtype<256 then + local lg=ligature[s.char] if lg then stop=s ligature=lg - s=getnext(s) + s=s.next else break end @@ -10127,9 +10086,9 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) local lig=ligature.ligature if lig then if trace_ligatures then - local stopchar=getchar(stop) + local stopchar=stop.char head,start=markstoligature(kind,lookupname,head,start,stop,lig) - logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start))) + logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else head,start=markstoligature(kind,lookupname,head,start,stop,lig) end @@ -10140,18 +10099,18 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) else local skipmark=sequence.flags[1] while s do - local id=getid(s) - if id==glyph_code and getsubtype(s)<256 then - if getfont(s)==currentfont then - local char=getchar(s) + local id=s.id + if id==glyph_code and s.subtype<256 then + if s.font==currentfont then + local char=s.char if skipmark and marks[char] then - s=getnext(s) + s=s.next else local lg=ligature[char] if lg then stop=s ligature=lg - s=getnext(s) + s=s.next else break end @@ -10161,7 +10120,7 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) end elseif id==disc_code then discfound=true - s=getnext(s) + s=s.next else break end @@ -10170,35 +10129,36 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) if lig then if stop then if trace_ligatures then - local stopchar=getchar(stop) + local stopchar=stop.char head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) - logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start))) + logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) end + return head,start,true else - setfield(start,"char",lig) + start.char=lig if trace_ligatures then logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig)) end + return head,start,true end - return head,start,true else end end return head,start,false end function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) - local markchar=getchar(start) + local markchar=start.char if marks[markchar] then - local base=getprev(start) - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - local basechar=getchar(base) + local base=start.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char if marks[basechar] then while true do - base=getprev(base) - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - basechar=getchar(base) + base=base.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + basechar=base.char if not marks[basechar] then break end @@ -10247,16 +10207,16 @@ function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence return head,start,false end function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence) - local markchar=getchar(start) + local markchar=start.char if marks[markchar] then - local base=getprev(start) - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - local basechar=getchar(base) + local base=start.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char if marks[basechar] then while true do - base=getprev(base) - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - basechar=getchar(base) + base=base.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + basechar=base.char if not marks[basechar] then break end @@ -10268,7 +10228,7 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ end end end - local index=getattr(start,a_ligacomp) + local index=start[a_ligacomp] local baseanchors=descriptions[basechar] if baseanchors then baseanchors=baseanchors.anchors @@ -10313,22 +10273,22 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ return head,start,false end function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) - local markchar=getchar(start) + local markchar=start.char if marks[markchar] then - local base=getprev(start) - local slc=getattr(start,a_ligacomp) + local base=start.prev + local slc=start[a_ligacomp] if slc then while base do - local blc=getattr(base,a_ligacomp) + local blc=base[a_ligacomp] if blc and blc~=slc then - base=getprev(base) + base=base.prev else break end end end - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - local basechar=getchar(base) + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char local baseanchors=descriptions[basechar] if baseanchors then baseanchors=baseanchors.anchors @@ -10366,20 +10326,20 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence return head,start,false end function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) - local alreadydone=cursonce and getattr(start,a_cursbase) + local alreadydone=cursonce and start[a_cursbase] if not alreadydone then local done=false - local startchar=getchar(start) + local startchar=start.char if marks[startchar] then if trace_cursive then logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) end else - local nxt=getnext(start) - while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do - local nextchar=getchar(nxt) + local nxt=start.next + while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do + local nextchar=nxt.char if marks[nextchar] then - nxt=getnext(nxt) + nxt=nxt.next else local entryanchors=descriptions[nextchar] if entryanchors then @@ -10413,13 +10373,13 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) return head,start,done else if trace_cursive and trace_details then - logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone) + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) end return head,start,false end end function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) - local startchar=getchar(start) + local startchar=start.char local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) if trace_kerns then logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) @@ -10427,33 +10387,33 @@ function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) return head,start,false end function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) - local snext=getnext(start) + local snext=start.next if not snext then return head,start,false else local prev,done=start,false local factor=tfmdata.parameters.factor local lookuptype=lookuptypes[lookupname] - while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do - local nextchar=getchar(snext) + while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do + local nextchar=snext.char local krn=kerns[nextchar] if not krn and marks[nextchar] then prev=snext - snext=getnext(snext) + snext=snext.next else if not krn then elseif type(krn)=="table" then if lookuptype=="pair" then local a,b=krn[2],krn[3] if a and #a>0 then - local startchar=getchar(start) + local startchar=start.char local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) if trace_kerns then logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) end end if b and #b>0 then - local startchar=getchar(start) + local startchar=start.char local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) if trace_kerns then logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) @@ -10466,7 +10426,7 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) elseif krn~=0 then local k=setkern(snext,factor,rlmode,krn) if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) + logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) end done=true end @@ -10501,13 +10461,13 @@ function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,looku return head,start,false end function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) - local char=getchar(start) + local char=start.char local replacement=replacements[char] if replacement then if trace_singles then logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) end - setfield(start,"char",replacement) + start.char=replacement return head,start,true else return head,start,false @@ -10520,8 +10480,8 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) end while current do - if getid(current)==glyph_code then - local currentchar=getchar(current) + if current.id==glyph_code then + local currentchar=current.char local lookupname=subtables[1] local replacement=lookuphash[lookupname] if not replacement then @@ -10538,21 +10498,21 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo if trace_singles then logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) end - setfield(current,"char",replacement) + current.char=replacement end end return head,start,true elseif current==stop then break else - current=getnext(current) + current=current.next end end return head,start,false end chainmores.gsub_single=chainprocs.gsub_single function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local startchar=getchar(start) + local startchar=start.char local subtables=currentlookup.subtables local lookupname=subtables[1] local replacements=lookuphash[lookupname] @@ -10581,8 +10541,8 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext local subtables=currentlookup.subtables local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue while current do - if getid(current)==glyph_code then - local currentchar=getchar(current) + if current.id==glyph_code then + local currentchar=current.char local lookupname=subtables[1] local alternatives=lookuphash[lookupname] if not alternatives then @@ -10597,7 +10557,7 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext if trace_alternatives then logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment) end - setfield(start,"char",choice) + start.char=choice else if trace_alternatives then logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) @@ -10611,14 +10571,14 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext elseif current==stop then break else - current=getnext(current) + current=current.next end end return head,start,false end chainmores.gsub_alternate=chainprocs.gsub_alternate function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) - local startchar=getchar(start) + local startchar=start.char local subtables=currentlookup.subtables local lookupname=subtables[1] local ligatures=lookuphash[lookupname] @@ -10633,20 +10593,20 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) end else - local s=getnext(start) + local s=start.next local discfound=false local last=stop local nofreplacements=0 local skipmark=currentlookup.flags[1] while s do - local id=getid(s) + local id=s.id if id==disc_code then - s=getnext(s) + s=s.next discfound=true else - local schar=getchar(s) + local schar=s.char if skipmark and marks[schar] then - s=getnext(s) + s=s.next else local lg=ligatures[schar] if lg then @@ -10654,7 +10614,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, if s==stop then break else - s=getnext(s) + s=s.next end else break @@ -10671,7 +10631,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, if start==stop then logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) else - logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)),gref(l2)) + logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) end end head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) @@ -10680,7 +10640,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, if start==stop then logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) else - logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop))) + logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) end end end @@ -10689,7 +10649,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, end chainmores.gsub_ligature=chainprocs.gsub_ligature function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar=getchar(start) + local markchar=start.char if marks[markchar] then local subtables=currentlookup.subtables local lookupname=subtables[1] @@ -10698,14 +10658,14 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext markanchors=markanchors[markchar] end if markanchors then - local base=getprev(start) - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - local basechar=getchar(base) + local base=start.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char if marks[basechar] then while true do - base=getprev(base) - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - basechar=getchar(base) + base=base.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + basechar=base.char if not marks[basechar] then break end @@ -10752,7 +10712,7 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext return head,start,false end function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar=getchar(start) + local markchar=start.char if marks[markchar] then local subtables=currentlookup.subtables local lookupname=subtables[1] @@ -10761,14 +10721,14 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon markanchors=markanchors[markchar] end if markanchors then - local base=getprev(start) - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - local basechar=getchar(base) + local base=start.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char if marks[basechar] then while true do - base=getprev(base) - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - basechar=getchar(base) + base=base.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + basechar=base.char if not marks[basechar] then break end @@ -10780,7 +10740,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon end end end - local index=getattr(start,a_ligacomp) + local index=start[a_ligacomp] local baseanchors=descriptions[basechar].anchors if baseanchors then local baseanchors=baseanchors['baselig'] @@ -10819,7 +10779,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon return head,start,false end function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar=getchar(start) + local markchar=start.char if marks[markchar] then local subtables=currentlookup.subtables local lookupname=subtables[1] @@ -10828,20 +10788,20 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext markanchors=markanchors[markchar] end if markanchors then - local base=getprev(start) - local slc=getattr(start,a_ligacomp) + local base=start.prev + local slc=start[a_ligacomp] if slc then while base do - local blc=getattr(base,a_ligacomp) + local blc=base[a_ligacomp] if blc and blc~=slc then - base=getprev(base) + base=base.prev else break end end end - if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then - local basechar=getchar(base) + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char local baseanchors=descriptions[basechar].anchors if baseanchors then baseanchors=baseanchors['basemark'] @@ -10877,9 +10837,9 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext return head,start,false end function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local alreadydone=cursonce and getattr(start,a_cursbase) + local alreadydone=cursonce and start[a_cursbase] if not alreadydone then - local startchar=getchar(start) + local startchar=start.char local subtables=currentlookup.subtables local lookupname=subtables[1] local exitanchors=lookuphash[lookupname] @@ -10893,11 +10853,11 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) end else - local nxt=getnext(start) - while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do - local nextchar=getchar(nxt) + local nxt=start.next + while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do + local nextchar=nxt.char if marks[nextchar] then - nxt=getnext(nxt) + nxt=nxt.next else local entryanchors=descriptions[nextchar] if entryanchors then @@ -10931,7 +10891,7 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l return head,start,done else if trace_cursive and trace_details then - logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone) + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) end return head,start,false end @@ -10939,7 +10899,7 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l return head,start,false end function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) - local startchar=getchar(start) + local startchar=start.char local subtables=currentlookup.subtables local lookupname=subtables[1] local kerns=lookuphash[lookupname] @@ -10956,9 +10916,9 @@ function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lo end chainmores.gpos_single=chainprocs.gpos_single function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) - local snext=getnext(start) + local snext=start.next if snext then - local startchar=getchar(start) + local startchar=start.char local subtables=currentlookup.subtables local lookupname=subtables[1] local kerns=lookuphash[lookupname] @@ -10968,26 +10928,26 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look local lookuptype=lookuptypes[lookupname] local prev,done=start,false local factor=tfmdata.parameters.factor - while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do - local nextchar=getchar(snext) + while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do + local nextchar=snext.char local krn=kerns[nextchar] if not krn and marks[nextchar] then prev=snext - snext=getnext(snext) + snext=snext.next else if not krn then elseif type(krn)=="table" then if lookuptype=="pair" then local a,b=krn[2],krn[3] if a and #a>0 then - local startchar=getchar(start) + local startchar=start.char local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) if trace_kerns then logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) end end if b and #b>0 then - local startchar=getchar(start) + local startchar=start.char local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) if trace_kerns then logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) @@ -10999,7 +10959,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look if a and a~=0 then local k=setkern(snext,factor,rlmode,a) if trace_kerns then - logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) + logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) end end if b and b~=0 then @@ -11010,7 +10970,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look elseif krn~=0 then local k=setkern(snext,factor,rlmode,krn) if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) + logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) end done=true end @@ -11031,10 +10991,6 @@ local function show_skip(kind,chainname,char,ck,class) logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) end end -local quit_on_no_replacement=true -directives.register("otf.chain.quitonnoreplacement",function(value) - quit_on_no_replacement=value -end) local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) local flags=sequence.flags local done=false @@ -11052,7 +11008,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq local seq=ck[3] local s=#seq if s==1 then - match=getid(current)==glyph_code and getfont(current)==currentfont and getsubtype(current)<256 and seq[1][getchar(current)] + match=current.id==glyph_code and current.font==currentfont and current.subtype<256 and seq[1][current.char] else local f,l=ck[4],ck[5] if f==1 and f==l then @@ -11060,13 +11016,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq if f==l then else local n=f+1 - last=getnext(last) + last=last.next while n<=l do if last then - local id=getid(last) + local id=last.id if id==glyph_code then - if getfont(last)==currentfont and getsubtype(last)<256 then - local char=getchar(last) + if last.font==currentfont and last.subtype<256 then + local char=last.char local ccd=descriptions[char] if ccd then local class=ccd.class @@ -11075,10 +11031,10 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq if trace_skips then show_skip(kind,chainname,char,ck,class) end - last=getnext(last) + last=last.next elseif seq[n][char] then if n1 then - local prev=getprev(start) + local prev=start.prev if prev then local n=f-1 while n>=1 do if prev then - local id=getid(prev) + local id=prev.id if id==glyph_code then - if getfont(prev)==currentfont and getsubtype(prev)<256 then - local char=getchar(prev) + if prev.font==currentfont and prev.subtype<256 then + local char=prev.char local ccd=descriptions[char] if ccd then local class=ccd.class @@ -11145,7 +11101,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq match=false break end - prev=getprev(prev) + prev=prev.prev elseif seq[n][32] then n=n -1 else @@ -11165,15 +11121,15 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq end end if match and s>l then - local current=last and getnext(last) + local current=last and last.next if current then local n=l+1 while n<=s do if current then - local id=getid(current) + local id=current.id if id==glyph_code then - if getfont(current)==currentfont and getsubtype(current)<256 then - local char=getchar(current) + if current.font==currentfont and current.subtype<256 then + local char=current.char local ccd=descriptions[char] if ccd then local class=ccd.class @@ -11203,7 +11159,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq match=false break end - current=getnext(current) + current=current.next elseif seq[n][32] then n=n+1 else @@ -11226,7 +11182,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq if match then if trace_contexts then local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5] - local char=getchar(start) + local char=start.char if ck[9] then logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a", cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) @@ -11260,12 +11216,12 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq repeat if skipped then while true do - local char=getchar(start) + local char=start.char local ccd=descriptions[char] if ccd then local class=ccd.class if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - start=getnext(start) + start=start.next else break end @@ -11295,7 +11251,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq end end if start then - start=getnext(start) + start=start.next else end until i>nofchainlookups @@ -11305,7 +11261,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq if replacements then head,start,done=chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) else - done=quit_on_no_replacement + done=true if trace_contexts then logprocess("%s: skipping match",cref(kind,chainname)) end @@ -11422,7 +11378,6 @@ local function featuresprocessor(head,font,attr) if not lookuphash then return head,false end - head=tonut(head) if trace_steps then checkstep(head) end @@ -11455,10 +11410,10 @@ local function featuresprocessor(head,font,attr) local handler=handlers[typ] local start=find_node_tail(head) while start do - local id=getid(start) + local id=start.id if id==glyph_code then - if getfont(start)==font and getsubtype(start)<256 then - local a=getattr(start,0) + if start.font==font and start.subtype<256 then + local a=start[0] if a then a=a==attr else @@ -11469,7 +11424,7 @@ local function featuresprocessor(head,font,attr) local lookupname=subtables[i] local lookupcache=lookuphash[lookupname] if lookupcache then - local lookupmatch=lookupcache[getchar(start)] + local lookupmatch=lookupcache[start.char] if lookupmatch then head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) if success then @@ -11480,15 +11435,15 @@ local function featuresprocessor(head,font,attr) report_missing_cache(typ,lookupname) end end - if start then start=getprev(start) end + if start then start=start.prev end else - start=getprev(start) + start=start.prev end else - start=getprev(start) + start=start.prev end else - start=getprev(start) + start=start.prev end end else @@ -11506,16 +11461,16 @@ local function featuresprocessor(head,font,attr) local head=start local done=false while start do - local id=getid(start) - if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then - local a=getattr(start,0) + local id=start.id + if id==glyph_code and start.font==font and start.subtype<256 then + local a=start[0] if a then - a=(a==attr) and (not attribute or getattr(start,a_state)==attribute) + a=(a==attr) and (not attribute or start[a_state]==attribute) else - a=not attribute or getattr(start,a_state)==attribute + a=not attribute or start[a_state]==attribute end if a then - local lookupmatch=lookupcache[getchar(start)] + local lookupmatch=lookupcache[start.char] if lookupmatch then local ok head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) @@ -11523,12 +11478,12 @@ local function featuresprocessor(head,font,attr) done=true end end - if start then start=getnext(start) end + if start then start=start.next end else - start=getnext(start) + start=start.next end else - start=getnext(start) + start=start.next end end if done then @@ -11537,18 +11492,18 @@ local function featuresprocessor(head,font,attr) end end local function kerndisc(disc) - local prev=getprev(disc) - local next=getnext(disc) + local prev=disc.prev + local next=disc.next if prev and next then - setfield(prev,"next",next) - local a=getattr(prev,0) + prev.next=next + local a=prev[0] if a then - a=(a==attr) and (not attribute or getattr(prev,a_state)==attribute) + a=(a==attr) and (not attribute or prev[a_state]==attribute) else - a=not attribute or getattr(prev,a_state)==attribute + a=not attribute or prev[a_state]==attribute end if a then - local lookupmatch=lookupcache[getchar(prev)] + local lookupmatch=lookupcache[prev.char] if lookupmatch then local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) if ok then @@ -11557,22 +11512,22 @@ local function featuresprocessor(head,font,attr) end end end - setfield(prev,"next",disc) + prev.next=disc end return next end while start do - local id=getid(start) + local id=start.id if id==glyph_code then - if getfont(start)==font and getsubtype(start)<256 then - local a=getattr(start,0) + if start.font==font and start.subtype<256 then + local a=start[0] if a then - a=(a==attr) and (not attribute or getattr(start,a_state)==attribute) + a=(a==attr) and (not attribute or start[a_state]==attribute) else - a=not attribute or getattr(start,a_state)==attribute + a=not attribute or start[a_state]==attribute end if a then - local lookupmatch=lookupcache[getchar(start)] + local lookupmatch=lookupcache[start.char] if lookupmatch then local ok head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) @@ -11580,38 +11535,38 @@ local function featuresprocessor(head,font,attr) success=true end end - if start then start=getnext(start) end + if start then start=start.next end else - start=getnext(start) + start=start.next end else - start=getnext(start) + start=start.next end elseif id==disc_code then - if getsubtype(start)==discretionary_code then - local pre=getfield(start,"pre") + if start.subtype==discretionary_code then + local pre=start.pre if pre then local new=subrun(pre) - if new then setfield(start,"pre",new) end + if new then start.pre=new end end - local post=getfield(start,"post") + local post=start.post if post then local new=subrun(post) - if new then setfield(start,"post",new) end + if new then start.post=new end end - local replace=getfield(start,"replace") + local replace=start.replace if replace then local new=subrun(replace) - if new then setfield(start,"replace",new) end + if new then start.replace=new end end elseif typ=="gpos_single" or typ=="gpos_pair" then kerndisc(start) end - start=getnext(start) + start=start.next elseif id==whatsit_code then - local subtype=getsubtype(start) + local subtype=start.subtype if subtype==dir_code then - local dir=getfield(start,"dir") + local dir=start.dir if dir=="+TRT" or dir=="+TLT" then topstack=topstack+1 dirstack[topstack]=dir @@ -11630,7 +11585,7 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) end elseif subtype==localpar_code then - local dir=getfield(start,"dir") + local dir=start.dir if dir=="TRT" then rlparmode=-1 elseif dir=="TLT" then @@ -11643,11 +11598,11 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) end end - start=getnext(start) + start=start.next elseif id==math_code then - start=getnext(end_of_math(start)) + start=end_of_math(start).next else - start=getnext(start) + start=start.next end end end @@ -11656,20 +11611,20 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then local head=start local done=false while start do - local id=getid(start) - if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then - local a=getattr(start,0) + local id=start.id + if id==glyph_code and start.id==font and start.subtype<256 then + local a=start[0] if a then - a=(a==attr) and (not attribute or getattr(start,a_state)==attribute) + a=(a==attr) and (not attribute or start[a_state]==attribute) else - a=not attribute or getattr(start,a_state)==attribute + a=not attribute or start[a_state]==attribute end if a then for i=1,ns do local lookupname=subtables[i] local lookupcache=lookuphash[lookupname] if lookupcache then - local lookupmatch=lookupcache[getchar(start)] + local lookupmatch=lookupcache[start.char] if lookupmatch then local ok head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) @@ -11684,12 +11639,12 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then report_missing_cache(typ,lookupname) end end - if start then start=getnext(start) end + if start then start=start.next end else - start=getnext(start) + start=start.next end else - start=getnext(start) + start=start.next end end if done then @@ -11698,22 +11653,22 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then end end local function kerndisc(disc) - local prev=getprev(disc) - local next=getnext(disc) + local prev=disc.prev + local next=disc.next if prev and next then - setfield(prev,"next",next) - local a=getattr(prev,0) + prev.next=next + local a=prev[0] if a then - a=(a==attr) and (not attribute or getattr(prev,a_state)==attribute) + a=(a==attr) and (not attribute or prev[a_state]==attribute) else - a=not attribute or getattr(prev,a_state)==attribute + a=not attribute or prev[a_state]==attribute end if a then for i=1,ns do local lookupname=subtables[i] local lookupcache=lookuphash[lookupname] if lookupcache then - local lookupmatch=lookupcache[getchar(prev)] + local lookupmatch=lookupcache[prev.char] if lookupmatch then local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) if ok then @@ -11726,26 +11681,26 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then end end end - setfield(prev,"next",disc) + prev.next=disc end return next end while start do - local id=getid(start) + local id=start.id if id==glyph_code then - if getfont(start)==font and getsubtype(start)<256 then - local a=getattr(start,0) + if start.font==font and start.subtype<256 then + local a=start[0] if a then - a=(a==attr) and (not attribute or getattr(start,a_state)==attribute) + a=(a==attr) and (not attribute or start[a_state]==attribute) else - a=not attribute or getattr(start,a_state)==attribute + a=not attribute or start[a_state]==attribute end if a then for i=1,ns do local lookupname=subtables[i] local lookupcache=lookuphash[lookupname] if lookupcache then - local lookupmatch=lookupcache[getchar(start)] + local lookupmatch=lookupcache[start.char] if lookupmatch then local ok head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) @@ -11760,38 +11715,38 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then report_missing_cache(typ,lookupname) end end - if start then start=getnext(start) end + if start then start=start.next end else - start=getnext(start) + start=start.next end else - start=getnext(start) + start=start.next end elseif id==disc_code then - if getsubtype(start)==discretionary_code then - local pre=getfield(start,"pre") + if start.subtype==discretionary_code then + local pre=start.pre if pre then local new=subrun(pre) - if new then setfield(start,"pre",new) end + if new then start.pre=new end end - local post=getfield(start,"post") + local post=start.post if post then local new=subrun(post) - if new then setfield(start,"post",new) end + if new then start.post=new end end - local replace=getfield(start,"replace") + local replace=start.replace if replace then local new=subrun(replace) - if new then setfield(start,"replace",new) end + if new then start.replace=new end end elseif typ=="gpos_single" or typ=="gpos_pair" then kerndisc(start) end - start=getnext(start) + start=start.next elseif id==whatsit_code then - local subtype=getsubtype(start) + local subtype=start.subtype if subtype==dir_code then - local dir=getfield(start,"dir") + local dir=start.dir if dir=="+TRT" or dir=="+TLT" then topstack=topstack+1 dirstack[topstack]=dir @@ -11810,7 +11765,7 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) end elseif subtype==localpar_code then - local dir=getfield(start,"dir") + local dir=start.dir if dir=="TRT" then rlparmode=-1 elseif dir=="TLT" then @@ -11823,11 +11778,11 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) end end - start=getnext(start) + start=start.next elseif id==math_code then - start=getnext(end_of_math(start)) + start=end_of_math(start).next else - start=getnext(start) + start=start.next end end end @@ -11839,7 +11794,6 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then registerstep(head) end end - head=tonode(head) return head,done end local function generic(lookupdata,lookupname,unicode,lookuphash) -- cgit v1.2.3 From 8ec94be21f0a842b99272f0bb9dbe20cf4a2eeee Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 7 Jan 2014 19:05:28 +0100 Subject: [main] substitute new filenames for node-inj.lua and font-otn.lua --- luaotfload.dtx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 3f533ba..0d60d8a 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1878,10 +1878,11 @@ else--- the loading sequence is known to change, so this might have to loadmodule('font-oti.lua') loadmodule('font-otf.lua') loadmodule('font-otb.lua') - loadmodule('node-inj.lua') + loadmodule('luatex-fonts-inj.lua') --> since 2014-01-07, replaces node-inj.lua loadmodule('font-ota.lua') loadmodule('font-otn.lua') - loadmodule('font-otp.lua')--- since 2013-04-23 + loadmodule('luatex-fonts-otn.lua') --> since 2014-01-07, replaces font-otn.lua + loadmodule('font-otp.lua') --> since 2013-04-23 loadmodule('luatex-fonts-lua.lua') loadmodule('font-def.lua') loadmodule('luatex-fonts-def.lua') -- cgit v1.2.3 From 290ef88cf337115bcac4093b5ff99cdcbba3a338 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 7 Jan 2014 20:35:08 +0100 Subject: [doc] update file names in manual --- luaotfload.dtx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 0d60d8a..f77502b 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1199,6 +1199,8 @@ and the derived files % \incitem{luatex-fonts-tfm.lua} % \incitem{luatex-fonts-chr.lua} % \incitem{luatex-fonts-lua.lua} +% \incitem{luatex-fonts-inj.lua} +% \incitem{luatex-fonts-otn.lua} % \incitem{luatex-fonts-def.lua} % \incitem{luatex-fonts-ext.lua} % \incitem{luatex-fonts-cbk.lua} @@ -1214,8 +1216,7 @@ and the derived files % \incitem{font-con.lua} \incitem{font-cid.lua} % \incitem{font-map.lua} \incitem{font-oti.lua} % \incitem{font-otf.lua} \incitem{font-otb.lua} -% \incitem{node-inj.lua} \incitem{font-ota.lua} -% \incitem{font-otn.lua} \incitem{font-def.lua} +% \incitem{font-ota.lua} \incitem{font-def.lua} % \incitem{font-otp.lua} % \end{itemize} % \end{multicols} -- cgit v1.2.3 From 3d7b2bcbe2438d44d9612d58caf3d044c4f8fb02 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 7 Jan 2014 20:36:26 +0100 Subject: [doc] update file graph --- filegraph.dot | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/filegraph.dot b/filegraph.dot index 9eccd80..1228690 100644 --- a/filegraph.dot +++ b/filegraph.dot @@ -256,6 +256,7 @@ strict digraph luaotfload_files { //looks weird with circo ... luatex-font-tfm.lua luatex-font-afm.lua luatex-font-afk.lua luatex-fonts-tfm.lua luatex-fonts-chr.lua luatex-fonts-lua.lua + luatex-fonts-inj.lua luatex-fonts-otn.lua luatex-fonts-def.lua luatex-fonts-ext.lua luatex-fonts-cbk.lua @@ -275,8 +276,8 @@ strict digraph luaotfload_files { //looks weird with circo ... Font and Node Libraries from Context data-con.lua font-ini.lua font-con.lua font-cid.lua font-map.lua font-oti.lua - font-otf.lua font-otb.lua node-inj.lua - font-ota.lua font-otn.lua font-def.lua + font-otf.lua font-otb.lua font-ota.lua + font-def.lua >, ] -- cgit v1.2.3 From 319be72c5d5beb8d05e5a2e572d5860beb5f09f5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 7 Jan 2014 20:38:40 +0100 Subject: [main] obliterate reference to obsolete font-otn.lua --- luaotfload.dtx | 1 - 1 file changed, 1 deletion(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index f77502b..6f8ebc4 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1881,7 +1881,6 @@ else--- the loading sequence is known to change, so this might have to loadmodule('font-otb.lua') loadmodule('luatex-fonts-inj.lua') --> since 2014-01-07, replaces node-inj.lua loadmodule('font-ota.lua') - loadmodule('font-otn.lua') loadmodule('luatex-fonts-otn.lua') --> since 2014-01-07, replaces font-otn.lua loadmodule('font-otp.lua') --> since 2013-04-23 loadmodule('luatex-fonts-lua.lua') -- cgit v1.2.3 From ec9087800c135b68d08bfb54da170d675fdc4f7b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 7 Jan 2014 20:41:32 +0100 Subject: [fontloader] import luatex-fonts-inj.lua, luatex-fonts-otn.lua --- luaotfload-fonts-inj.lua | 526 +++++++++ luaotfload-fonts-otn.lua | 2848 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3374 insertions(+) create mode 100644 luaotfload-fonts-inj.lua create mode 100644 luaotfload-fonts-otn.lua diff --git a/luaotfload-fonts-inj.lua b/luaotfload-fonts-inj.lua new file mode 100644 index 0000000..ae48150 --- /dev/null +++ b/luaotfload-fonts-inj.lua @@ -0,0 +1,526 @@ +if not modules then modules = { } end modules ['node-inj'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +-- This is very experimental (this will change when we have luatex > .50 and +-- a few pending thingies are available. Also, Idris needs to make a few more +-- test fonts. Btw, future versions of luatex will have extended glyph properties +-- that can be of help. Some optimizations can go away when we have faster machines. + +-- todo: make a special one for context + +local next = next +local utfchar = utf.char + +local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) + +local report_injections = logs.reporter("nodes","injections") + +local attributes, nodes, node = attributes, nodes, node + +fonts = fonts +local fontdata = fonts.hashes.identifiers + +nodes.injections = nodes.injections or { } +local injections = nodes.injections + +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph +local kern_code = nodecodes.kern +local nodepool = nodes.pool +local newkern = nodepool.kern + +local traverse_id = node.traverse_id +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after + +local a_kernpair = attributes.private('kernpair') +local a_ligacomp = attributes.private('ligacomp') +local a_markbase = attributes.private('markbase') +local a_markmark = attributes.private('markmark') +local a_markdone = attributes.private('markdone') +local a_cursbase = attributes.private('cursbase') +local a_curscurs = attributes.private('curscurs') +local a_cursdone = attributes.private('cursdone') + +-- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as +-- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner +-- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure +-- that this code is not 100% okay but examples are needed to figure things out. + +function injections.installnewkern(nk) + newkern = nk or newkern +end + +local cursives = { } +local marks = { } +local kerns = { } + +-- Currently we do gpos/kern in a bit inofficial way but when we have the extra fields in +-- glyphnodes to manipulate ht/dp/wd explicitly I will provide an alternative; also, we +-- can share tables. + +-- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs +-- checking with husayni (volt and fontforge). + +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) + local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) + local ws, wn = tfmstart.width, tfmnext.width + local bound = #cursives + 1 + start[a_cursbase] = bound + nxt[a_curscurs] = bound + cursives[bound] = { rlmode, dx, dy, ws, wn } + return dx, dy, bound +end + +function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) + local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] + -- dy = y - h + if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then + local bound = current[a_kernpair] + if bound then + local kb = kerns[bound] + -- inefficient but singles have less, but weird anyway, needs checking + kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h + else + bound = #kerns + 1 + current[a_kernpair] = bound + kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width } + end + return x, y, w, h, bound + end + return x, y, w, h -- no bound +end + +function injections.setkern(current,factor,rlmode,x,tfmchr) + local dx = factor*x + if dx ~= 0 then + local bound = #kerns + 1 + current[a_kernpair] = bound + kerns[bound] = { rlmode, dx } + return dx, bound + else + return 0, 0 + end +end + +function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) -- ba=baseanchor, ma=markanchor + local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) -- the index argument is no longer used but when this + local bound = base[a_markbase] -- fails again we should pass it + local index = 1 + if bound then + local mb = marks[bound] + if mb then + -- if not index then index = #mb + 1 end + index = #mb + 1 + mb[index] = { dx, dy, rlmode } + start[a_markmark] = bound + start[a_markdone] = index + return dx, dy, bound + else + report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound) + end + end +-- index = index or 1 + index = index or 1 + bound = #marks + 1 + base[a_markbase] = bound + start[a_markmark] = bound + start[a_markdone] = index + marks[bound] = { [index] = { dx, dy, rlmode, baseismark } } + return dx, dy, bound +end + +local function dir(n) + return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" +end + +local function trace(head) + report_injections("begin run") + for n in traverse_id(glyph_code,head) do + if n.subtype < 256 then + local kp = n[a_kernpair] + local mb = n[a_markbase] + local mm = n[a_markmark] + local md = n[a_markdone] + local cb = n[a_cursbase] + local cc = n[a_curscurs] + local char = n.char + report_injections("font %s, char %U, glyph %c",n.font,char,char) + if kp then + local k = kerns[kp] + if k[3] then + report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5]) + else + report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2]) + end + end + if mb then + report_injections(" markbase: bound %a",mb) + end + if mm then + local m = marks[mm] + if mb then + local m = m[mb] + if m then + report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2]) + else + report_injections(" markmark: bound %a, missing index",mm) + end + else + m = m[1] + report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2]) + end + end + if cb then + report_injections(" cursbase: bound %a",cb) + end + if cc then + local c = cursives[cc] + report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3]) + end + end + end + report_injections("end run") +end + +-- todo: reuse tables (i.e. no collection), but will be extra fields anyway +-- todo: check for attribute + +-- We can have a fast test on a font being processed, so we can check faster for marks etc +-- but I'll make a context variant anyway. + +local function show_result(head) + local current = head + local skipping = false + while current do + local id = current.id + if id == glyph_code then + report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset) + skipping = false + elseif id == kern_code then + report_injections("kern: %p",current.kern) + skipping = false + elseif not skipping then + report_injections() + skipping = true + end + current = current.next + end +end + +function injections.handler(head,where,keep) + local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) + if has_marks or has_cursives then + if trace_injections then + trace(head) + end + -- in the future variant we will not copy items but refs to tables + local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0 + if has_kerns then -- move outside loop + local nf, tm = nil, nil + for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts + if n.subtype < 256 then + nofvalid = nofvalid + 1 + valid[nofvalid] = n + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].resources.marks + end + if tm then + mk[n] = tm[n.char] + end + local k = n[a_kernpair] + if k then + local kk = kerns[k] + if kk then + local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0 + local dy = y - h + if dy ~= 0 then + ky[n] = dy + end + if w ~= 0 or x ~= 0 then + wx[n] = kk + end + rl[n] = kk[1] -- could move in test + end + end + end + end + else + local nf, tm = nil, nil + for n in traverse_id(glyph_code,head) do + if n.subtype < 256 then + nofvalid = nofvalid + 1 + valid[nofvalid] = n + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].resources.marks + end + if tm then + mk[n] = tm[n.char] + end + end + end + end + if nofvalid > 0 then + -- we can assume done == true because we have cursives and marks + local cx = { } + if has_kerns and next(ky) then + for n, k in next, ky do + n.yoffset = k + end + end + -- todo: reuse t and use maxt + if has_cursives then + local p_cursbase, p = nil, nil + -- since we need valid[n+1] we can also use a "while true do" + local t, d, maxt = { }, { }, 0 + for i=1,nofvalid do -- valid == glyphs + local n = valid[i] + if not mk[n] then + local n_cursbase = n[a_cursbase] + if p_cursbase then + local n_curscurs = n[a_curscurs] + if p_cursbase == n_curscurs then + local c = cursives[n_curscurs] + if c then + local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] + if rlmode >= 0 then + dx = dx - ws + else + dx = dx + wn + end + if dx ~= 0 then + cx[n] = dx + rl[n] = rlmode + end + -- if rlmode and rlmode < 0 then + dy = -dy + -- end + maxt = maxt + 1 + t[maxt] = p + d[maxt] = dy + else + maxt = 0 + end + end + elseif maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + local ti = t[i] + ti.yoffset = ti.yoffset + ny + end + maxt = 0 + end + if not n_cursbase and maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + local ti = t[i] + ti.yoffset = ny + end + maxt = 0 + end + p_cursbase, p = n_cursbase, n + end + end + if maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + local ti = t[i] + ti.yoffset = ny + end + maxt = 0 + end + if not keep then + cursives = { } + end + end + if has_marks then + for i=1,nofvalid do + local p = valid[i] + local p_markbase = p[a_markbase] + if p_markbase then + local mrks = marks[p_markbase] + local nofmarks = #mrks + for n in traverse_id(glyph_code,p.next) do + local n_markmark = n[a_markmark] + if p_markbase == n_markmark then + local index = n[a_markdone] or 1 + local d = mrks[index] + if d then + local rlmode = d[3] + -- + local k = wx[p] + if k then + local x = k[2] + local w = k[4] + if w then + if rlmode and rlmode >= 0 then + -- kern(x) glyph(p) kern(w-x) mark(n) + n.xoffset = p.xoffset - p.width + d[1] - (w-x) + else + -- kern(w-x) glyph(p) kern(x) mark(n) + n.xoffset = p.xoffset - d[1] - x + end + else + if rlmode and rlmode >= 0 then + -- okay for husayni + n.xoffset = p.xoffset - p.width + d[1] + else + -- needs checking: is x ok here? + n.xoffset = p.xoffset - d[1] - x + end + end + else + if rlmode and rlmode >= 0 then + n.xoffset = p.xoffset - p.width + d[1] + else + n.xoffset = p.xoffset - d[1] + end + local w = n.width + if w ~= 0 then + insert_node_before(head,n,newkern(-w/2)) + insert_node_after(head,n,newkern(-w/2)) + end + end + -- -- + if mk[p] then + n.yoffset = p.yoffset + d[2] + else + n.yoffset = n.yoffset + p.yoffset + d[2] + end + -- + if nofmarks == 1 then + break + else + nofmarks = nofmarks - 1 + end + end + else + -- KE: there can be sequences in ligatures + end + end + end + end + if not keep then + marks = { } + end + end + -- todo : combine + if next(wx) then + for n, k in next, wx do + -- only w can be nil (kernclasses), can be sped up when w == nil + local x = k[2] + local w = k[4] + if w then + local rl = k[1] -- r2l = k[6] + local wx = w - x + if rl < 0 then -- KE: don't use r2l here + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) -- type 0/2 + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) -- type 0/2 + end + else + if x ~= 0 then + insert_node_before(head,n,newkern(x)) -- type 0/2 + end + if wx ~= 0 then + insert_node_after (head,n,newkern(wx)) -- type 0/2 + end + end + elseif x ~= 0 then + -- this needs checking for rl < 0 but it is unlikely that a r2l script + -- uses kernclasses between glyphs so we're probably safe (KE has a + -- problematic font where marks interfere with rl < 0 in the previous + -- case) + insert_node_before(head,n,newkern(x)) -- a real font kern, type 0 + end + end + end + if next(cx) then + for n, k in next, cx do + if k ~= 0 then + local rln = rl[n] + if rln and rln < 0 then + insert_node_before(head,n,newkern(-k)) -- type 0/2 + else + insert_node_before(head,n,newkern(k)) -- type 0/2 + end + end + end + end + if not keep then + kerns = { } + end + -- if trace_injections then + -- show_result(head) + -- end + return head, true + elseif not keep then + kerns, cursives, marks = { }, { }, { } + end + elseif has_kerns then + if trace_injections then + trace(head) + end + for n in traverse_id(glyph_code,head) do + if n.subtype < 256 then + local k = n[a_kernpair] + if k then + local kk = kerns[k] + if kk then + local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4] + if y and y ~= 0 then + n.yoffset = y -- todo: h ? + end + if w then + -- copied from above + -- local r2l = kk[6] + local wx = w - x + if rl < 0 then -- KE: don't use r2l here + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) + end + else + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + if wx ~= 0 then + insert_node_after(head,n,newkern(wx)) + end + end + else + -- simple (e.g. kernclass kerns) + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + end + end + end + end + end + if not keep then + kerns = { } + end + -- if trace_injections then + -- show_result(head) + -- end + return head, true + else + -- no tracing needed + end + return head, false +end diff --git a/luaotfload-fonts-otn.lua b/luaotfload-fonts-otn.lua new file mode 100644 index 0000000..c57be5f --- /dev/null +++ b/luaotfload-fonts-otn.lua @@ -0,0 +1,2848 @@ +if not modules then modules = { } end modules ['font-otn'] = { + 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", +} + +-- preprocessors = { "nodes" } + +-- this is still somewhat preliminary and it will get better in due time; +-- much functionality could only be implemented thanks to the husayni font +-- of Idris Samawi Hamid to who we dedicate this module. + +-- in retrospect it always looks easy but believe it or not, it took a lot +-- of work to get proper open type support done: buggy fonts, fuzzy specs, +-- special made testfonts, many skype sessions between taco, idris and me, +-- torture tests etc etc ... unfortunately the code does not show how much +-- time it took ... + +-- todo: +-- +-- kerning is probably not yet ok for latin around dics nodes (interesting challenge) +-- extension infrastructure (for usage out of context) +-- sorting features according to vendors/renderers +-- alternative loop quitters +-- check cursive and r2l +-- find out where ignore-mark-classes went +-- default features (per language, script) +-- handle positions (we need example fonts) +-- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) +-- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests) +-- remove some optimizations (when I have a faster machine) +-- +-- maybe redo the lot some way (more context specific) + +--[[ldx-- +

This module is a bit more split up that I'd like but since we also want to test +with plain it has to be so. This module is part of +and discussion about improvements and functionality mostly happens on the + mailing list.

+ +

The specification of OpenType is kind of vague. Apart from a lack of a proper +free specifications there's also the problem that Microsoft and Adobe +may have their own interpretation of how and in what order to apply features. +In general the Microsoft website has more detailed specifications and is a +better reference. There is also some information in the FontForge help files.

+ +

Because there is so much possible, fonts might contain bugs and/or be made to +work with certain rederers. These may evolve over time which may have the side +effect that suddenly fonts behave differently.

+ +

After a lot of experiments (mostly by Taco, me and Idris) we're now at yet another +implementation. Of course all errors are mine and of course the code can be +improved. There are quite some optimizations going on here and processing speed +is currently acceptable. Not all functions are implemented yet, often because I +lack the fonts for testing. Many scripts are not yet supported either, but I will +look into them as soon as users ask for it.

+ +

Because there are different interpretations possible, I will extend the code +with more (configureable) variants. I can also add hooks for users so that they can +write their own extensions.

+ +

Glyphs are indexed not by unicode but in their own way. This is because there is no +relationship with unicode at all, apart from the fact that a font might cover certain +ranges of characters. One character can have multiple shapes. However, at the + end we use unicode so and all extra glyphs are mapped into a private +space. This is needed because we need to access them and has to include +then in the output eventually.

+ +

The raw table as it coms from gets reorganized in to fit out needs. +In that table is packed (similar tables are shared) and cached on disk +so that successive runs can use the optimized table (after loading the table is +unpacked). The flattening code used later is a prelude to an even more compact table +format (and as such it keeps evolving).

+ +

This module is sparsely documented because it is a moving target. The table format +of the reader changes and we experiment a lot with different methods for supporting +features.

+ +

As with the code, we may decide to store more information in the + table.

+ +

Incrementing the version number will force a re-cache. We jump the number by one +when there's a fix in the library or code that +results in different tables.

+--ldx]]-- + +-- action handler chainproc chainmore comment +-- +-- gsub_single ok ok ok +-- gsub_multiple ok ok not implemented yet +-- gsub_alternate ok ok not implemented yet +-- gsub_ligature ok ok ok +-- gsub_context ok -- +-- gsub_contextchain ok -- +-- gsub_reversecontextchain ok -- +-- chainsub -- ok +-- reversesub -- ok +-- gpos_mark2base ok ok +-- gpos_mark2ligature ok ok +-- gpos_mark2mark ok ok +-- gpos_cursive ok untested +-- gpos_single ok ok +-- gpos_pair ok ok +-- gpos_context ok -- +-- gpos_contextchain ok -- +-- +-- todo: contextpos and contextsub and class stuff +-- +-- actions: +-- +-- handler : actions triggered by lookup +-- chainproc : actions triggered by contextual lookup +-- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij) +-- +-- remark: the 'not implemented yet' variants will be done when we have fonts that use them +-- remark: we need to check what to do with discretionaries + +-- We used to have independent hashes for lookups but as the tags are unique +-- we now use only one hash. If needed we can have multiple again but in that +-- case I will probably prefix (i.e. rename) the lookups in the cached font file. + +-- Todo: make plugin feature that operates on char/glyphnode arrays + +local concat, insert, remove = table.concat, table.insert, table.remove +local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip +local type, next, tonumber, tostring = type, next, tonumber, tostring +local lpegmatch = lpeg.match +local random = math.random +local formatters = string.formatters + +local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes + +local registertracker = trackers.register + +local fonts = fonts +local otf = fonts.handlers.otf + +local trace_lookups = false registertracker("otf.lookups", function(v) trace_lookups = v end) +local trace_singles = false registertracker("otf.singles", function(v) trace_singles = v end) +local trace_multiples = false registertracker("otf.multiples", function(v) trace_multiples = v end) +local trace_alternatives = false registertracker("otf.alternatives", function(v) trace_alternatives = v end) +local trace_ligatures = false registertracker("otf.ligatures", function(v) trace_ligatures = v end) +local trace_contexts = false registertracker("otf.contexts", function(v) trace_contexts = v end) +local trace_marks = false registertracker("otf.marks", function(v) trace_marks = v end) +local trace_kerns = false registertracker("otf.kerns", function(v) trace_kerns = v end) +local trace_cursive = false registertracker("otf.cursive", function(v) trace_cursive = v end) +local trace_preparing = false registertracker("otf.preparing", function(v) trace_preparing = v end) +local trace_bugs = false registertracker("otf.bugs", function(v) trace_bugs = v end) +local trace_details = false registertracker("otf.details", function(v) trace_details = v end) +local trace_applied = false registertracker("otf.applied", function(v) trace_applied = v end) +local trace_steps = false registertracker("otf.steps", function(v) trace_steps = v end) +local trace_skips = false registertracker("otf.skips", function(v) trace_skips = v end) +local trace_directions = false registertracker("otf.directions", function(v) trace_directions = v end) + +local report_direct = logs.reporter("fonts","otf direct") +local report_subchain = logs.reporter("fonts","otf subchain") +local report_chain = logs.reporter("fonts","otf chain") +local report_process = logs.reporter("fonts","otf process") +local report_prepare = logs.reporter("fonts","otf prepare") +local report_warning = logs.reporter("fonts","otf warning") + +registertracker("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) +registertracker("otf.normal_chain", function(v) otf.setcontextchain(v and "normal") end) + +registertracker("otf.replacements", "otf.singles,otf.multiples,otf.alternatives,otf.ligatures") +registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") +registertracker("otf.actions","otf.replacements,otf.positions") +registertracker("otf.injections","nodes.injections") + +registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") + +local insert_node_after = node.insert_after +local delete_node = nodes.delete +local copy_node = node.copy +local find_node_tail = node.tail or node.slide +local flush_node_list = node.flush_list +local end_of_math = node.end_of_math + +local setmetatableindex = table.setmetatableindex + +local zwnj = 0x200C +local zwj = 0x200D +local wildcard = "*" +local default = "dflt" + +local nodecodes = nodes.nodecodes +local whatcodes = nodes.whatcodes +local glyphcodes = nodes.glyphcodes +local disccodes = nodes.disccodes + +local glyph_code = nodecodes.glyph +local glue_code = nodecodes.glue +local disc_code = nodecodes.disc +local whatsit_code = nodecodes.whatsit +local math_code = nodecodes.math + +local dir_code = whatcodes.dir +local localpar_code = whatcodes.localpar + +local discretionary_code = disccodes.discretionary + +local ligature_code = glyphcodes.ligature + +local privateattribute = attributes.private + +-- Something is messed up: we have two mark / ligature indices, one at the injection +-- end and one here ... this is bases in KE's patches but there is something fishy +-- there as I'm pretty sure that for husayni we need some connection (as it's much +-- more complex than an average font) but I need proper examples of all cases, not +-- of only some. + +local a_state = privateattribute('state') +local a_markbase = privateattribute('markbase') +local a_markmark = privateattribute('markmark') +local a_markdone = privateattribute('markdone') -- assigned at the injection end +local a_cursbase = privateattribute('cursbase') +local a_curscurs = privateattribute('curscurs') +local a_cursdone = privateattribute('cursdone') +local a_kernpair = privateattribute('kernpair') +local a_ligacomp = privateattribute('ligacomp') -- assigned here (ideally it should be combined) + +local injections = nodes.injections +local setmark = injections.setmark +local setcursive = injections.setcursive +local setkern = injections.setkern +local setpair = injections.setpair + +local markonce = true +local cursonce = true +local kernonce = true + +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers + +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register + +local onetimemessage = fonts.loggers.onetimemessage or function() end + +otf.defaultnodealternate = "none" -- first last + +-- we share some vars here, after all, we have no nested lookups and less code + +local tfmdata = false +local characters = false +local descriptions = false +local resources = false +local marks = false +local currentfont = false +local lookuptable = false +local anchorlookups = false +local lookuptypes = false +local handlers = { } +local rlmode = 0 +local featurevalue = false + +-- head is always a whatsit so we can safely assume that head is not changed + +-- we use this for special testing and documentation + +local checkstep = (nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end +local registerstep = (nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end +local registermessage = (nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_direct(...) +end + +local function logwarning(...) + report_direct(...) +end + +local f_unicode = formatters["%U"] +local f_uniname = formatters["%U (%s)"] +local f_unilist = formatters["% t (% t)"] + +local function gref(n) -- currently the same as in font-otb + if type(n) == "number" then + local description = descriptions[n] + local name = description and description.name + if name then + return f_uniname(n,name) + else + return f_unicode(n) + end + elseif n then + local num, nam = { }, { } + for i=1,#n do + local ni = n[i] + if tonumber(ni) then -- later we will start at 2 + local di = descriptions[ni] + num[i] = f_unicode(ni) + nam[i] = di and di.name or "-" + end + end + return f_unilist(num,nam) + else + return "" + end +end + +local function cref(kind,chainname,chainlookupname,lookupname,index) -- not in the mood to alias f_ + if index then + return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookupname,index) + elseif lookupname then + return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookupname) + elseif chainlookupname then + return formatters["feature %a, chain %a, sub %a"](kind,chainname,chainlookupname) + elseif chainname then + return formatters["feature %a, chain %a"](kind,chainname) + else + return formatters["feature %a"](kind) + end +end + +local function pref(kind,lookupname) + return formatters["feature %a, lookup %a"](kind,lookupname) +end + +-- We can assume that languages that use marks are not hyphenated. We can also assume +-- that at most one discretionary is present. + +-- We do need components in funny kerning mode but maybe I can better reconstruct then +-- as we do have the font components info available; removing components makes the +-- previous code much simpler. Also, later on copying and freeing becomes easier. +-- However, for arabic we need to keep them around for the sake of mark placement +-- and indices. + +local function copy_glyph(g) -- next and prev are untouched ! + local components = g.components + if components then + g.components = nil + local n = copy_node(g) + g.components = components + return n + else + return copy_node(g) + end +end + +-- start is a mark and we need to keep that one + +local function markstoligature(kind,lookupname,head,start,stop,char) + if start == stop and start.char == char then + return head, start + else + local prev = start.prev + local next = stop.next + start.prev = nil + stop.next = nil + local base = copy_glyph(start) + if head == start then + head = base + end + base.char = char + base.subtype = ligature_code + base.components = start + if prev then + prev.next = base + end + if next then + next.prev = base + end + base.next = next + base.prev = prev + return head, base + end +end + +-- The next code is somewhat complicated by the fact that some fonts can have ligatures made +-- from ligatures that themselves have marks. This was identified by Kai in for instance +-- arabtype: KAF LAM SHADDA ALEF FATHA (0x0643 0x0644 0x0651 0x0627 0x064E). This becomes +-- KAF LAM-ALEF with a SHADDA on the first and a FATHA op de second component. In a next +-- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the +-- third component. + +local function getcomponentindex(start) + if start.id ~= glyph_code then + return 0 + elseif start.subtype == ligature_code then + local i = 0 + local components = start.components + while components do + i = i + getcomponentindex(components) + components = components.next + end + return i + elseif not marks[start.char] then + return 1 + else + return 0 + end +end + +-- eventually we will do positioning in an other way (needs addional w/h/d fields) + +local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head + if start == stop and start.char == char then + start.char = char + return head, start + end + local prev = start.prev + local next = stop.next + start.prev = nil + stop.next = nil + local base = copy_glyph(start) + if start == head then + head = base + end + base.char = char + base.subtype = ligature_code + base.components = start -- start can have components + if prev then + prev.next = base + end + if next then + next.prev = base + end + base.next = next + base.prev = prev + if not discfound then + local deletemarks = markflag ~= "mark" + local components = start + local baseindex = 0 + local componentindex = 0 + local head = base + local current = base + -- first we loop over the glyphs in start .. stop + while start do + local char = start.char + if not marks[char] then + baseindex = baseindex + componentindex + componentindex = getcomponentindex(start) + elseif not deletemarks then -- quite fishy + start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) + if trace_marks then + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) + end + head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components + elseif trace_marks then + logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char)) + end + start = start.next + end + -- we can have one accent as part of a lookup and another following + -- local start = components -- was wrong (component scanning was introduced when more complex ligs in devanagari was added) + local start = current.next + while start and start.id == glyph_code do + local char = start.char + if marks[char] then + start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) + if trace_marks then + logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) + end + else + break + end + start = start.next + end + end + return head, base +end + +function handlers.gsub_single(head,start,kind,lookupname,replacement) + if trace_singles then + logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) + end + start.char = replacement + return head, start, true +end + +local function get_alternative_glyph(start,alternatives,value,trace_alternatives) + local n = #alternatives + if value == "random" then + local r = random(1,n) + return alternatives[r], trace_alternatives and formatters["value %a, taking %a"](value,r) + elseif value == "first" then + return alternatives[1], trace_alternatives and formatters["value %a, taking %a"](value,1) + elseif value == "last" then + return alternatives[n], trace_alternatives and formatters["value %a, taking %a"](value,n) + else + value = tonumber(value) + if type(value) ~= "number" then + return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) + elseif value > n then + local defaultalt = otf.defaultnodealternate + if defaultalt == "first" then + return alternatives[n], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) + elseif defaultalt == "last" then + return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,n) + else + return false, trace_alternatives and formatters["invalid value %a, %s"](value,"out of range") + end + elseif value == 0 then + return start.char, trace_alternatives and formatters["invalid value %a, %s"](value,"no change") + elseif value < 1 then + return alternatives[1], trace_alternatives and formatters["invalid value %a, taking %a"](value,1) + else + return alternatives[value], trace_alternatives and formatters["value %a, taking %a"](value,value) + end + end +end + +local function multiple_glyphs(head,start,multiple,ignoremarks) + local nofmultiples = #multiple + if nofmultiples > 0 then + start.char = multiple[1] + if nofmultiples > 1 then + local sn = start.next + for k=2,nofmultiples do -- todo: use insert_node +-- untested: +-- +-- while ignoremarks and marks[sn.char] then +-- local sn = sn.next +-- end + local n = copy_node(start) -- ignore components + n.char = multiple[k] + n.next = sn + n.prev = start + if sn then + sn.prev = n + end + start.next = n + start = n + end + end + return head, start, true + else + if trace_multiples then + logprocess("no multiple for %s",gref(start.char)) + end + return head, start, false + end +end + +function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence) + local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue + local choice, comment = get_alternative_glyph(start,alternative,value,trace_alternatives) + if choice then + if trace_alternatives then + logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment) + end + start.char = choice + else + if trace_alternatives then + logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment) + end + end + return head, start, true +end + +function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence) + if trace_multiples then + logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) + end + return multiple_glyphs(head,start,multiple,sequence.flags[1]) +end + +function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) + local s, stop, discfound = start.next, nil, false + local startchar = start.char + if marks[startchar] then + while s do + local id = s.id + if id == glyph_code and s.font == currentfont and s.subtype<256 then + local lg = ligature[s.char] + if lg then + stop = s + ligature = lg + s = s.next + else + break + end + else + break + end + end + if stop then + local lig = ligature.ligature + if lig then + if trace_ligatures then + local stopchar = stop.char + head, start = markstoligature(kind,lookupname,head,start,stop,lig) + logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) + else + head, start = markstoligature(kind,lookupname,head,start,stop,lig) + end + return head, start, true + else + -- ok, goto next lookup + end + end + else + local skipmark = sequence.flags[1] + while s do + local id = s.id + if id == glyph_code and s.subtype<256 then + if s.font == currentfont then + local char = s.char + if skipmark and marks[char] then + s = s.next + else + local lg = ligature[char] + if lg then + stop = s + ligature = lg + s = s.next + else + break + end + end + else + break + end + elseif id == disc_code then + discfound = true + s = s.next + else + break + end + end + local lig = ligature.ligature + if lig then + if stop then + if trace_ligatures then + local stopchar = stop.char + head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) + logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) + else + head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) + end + return head, start, true + else + -- weird but happens (in some arabic font) + start.char = lig + if trace_ligatures then + logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig)) + end + return head, start, true + end + else + -- weird but happens + end + end + return head, start, false +end + +--[[ldx-- +

We get hits on a mark, but we're not sure if the it has to be applied so +we need to explicitly test for basechar, baselig and basemark entries.

+--ldx]]-- + +function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [start=mark] + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return head, start, false + end + end + end + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + end + if baseanchors then + local baseanchors = baseanchors['basechar'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return head, start, false +end + +function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence) + -- check chainpos variant + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [optional marks] [start=mark] + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return head, start, false + end + end + end + local index = start[a_ligacomp] + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + if baseanchors then + local baseanchors = baseanchors['baselig'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor, ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + ba = ba[index] + if ba then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index + if trace_marks then + logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", + pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) + end + return head, start, true + else + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index) + end + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return head, start, false +end + +function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [basemark] [start=mark] + local slc = start[a_ligacomp] + if slc then -- a rather messy loop ... needs checking with husayni + while base do + local blc = base[a_ligacomp] + if blc and blc ~= slc then + base = base.prev + else + break + end + end + end + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go + local basechar = base.char + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + if baseanchors then + baseanchors = baseanchors['basemark'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return head, start, false +end + +function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) -- to be checked + local alreadydone = cursonce and start[a_cursbase] + if not alreadydone then + local done = false + local startchar = start.char + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt = start.next + while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do + local nextchar = nxt.char + if marks[nextchar] then + -- should not happen (maybe warning) + nxt = nxt.next + else + local entryanchors = descriptions[nextchar] + if entryanchors then + entryanchors = entryanchors.anchors + if entryanchors then + entryanchors = entryanchors['centry'] + if entryanchors then + local al = anchorlookups[lookupname] + for anchor, entry in next, entryanchors do + if al[anchor] then + local exit = exitanchors[anchor] + if exit then + local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + end + done = true + break + end + end + end + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + end + break + end + end + end + return head, start, done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return head, start, false + end +end + +function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) + local startchar = start.char + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) + end + return head, start, false +end + +function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) + -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too + -- todo: kerns in components of ligatures + local snext = start.next + if not snext then + return head, start, false + else + local prev, done = start, false + local factor = tfmdata.parameters.factor + local lookuptype = lookuptypes[lookupname] + while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do + local nextchar = snext.char + local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = snext.next + else + if not krn then + -- skip + elseif type(krn) == "table" then + if lookuptype == "pair" then -- probably not needed + local a, b = krn[2], krn[3] + if a and #a > 0 then + local startchar = start.char + local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local startchar = start.char + local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else -- wrong ... position has different entries + report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) + -- local a, b = krn[2], krn[6] + -- if a and a ~= 0 then + -- local k = setkern(snext,factor,rlmode,a) + -- if trace_kerns then + -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + -- end + -- end + -- if b and b ~= 0 then + -- logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) + -- end + end + done = true + elseif krn ~= 0 then + local k = setkern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + end + done = true + end + break + end + end + return head, start, done + end +end + +--[[ldx-- +

I will implement multiple chain replacements once I run into a font that uses +it. It's not that complex to handle.

+--ldx]]-- + +local chainmores = { } +local chainprocs = { } + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_subchain(...) +end + +local logwarning = report_subchain + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_chain(...) +end + +local logwarning = report_chain + +-- We could share functions but that would lead to extra function calls with many +-- arguments, redundant tests and confusing messages. + +function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) + logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return head, start, false +end + +function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) + logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return head, start, false +end + +-- The reversesub is a special case, which is why we need to store the replacements +-- in a bit weird way. There is no lookup and the replacement comes from the lookup +-- itself. It is meant mostly for dealing with Urdu. + +function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) + local char = start.char + local replacement = replacements[char] + if replacement then + if trace_singles then + logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) + end + start.char = replacement + return head, start, true + else + return head, start, false + end +end + +--[[ldx-- +

This chain stuff is somewhat tricky since we can have a sequence of actions to be +applied: single, alternate, multiple or ligature where ligature can be an invalid +one in the sense that it will replace multiple by one but not neccessary one that +looks like the combination (i.e. it is the counterpart of multiple then). For +example, the following is valid:

+ + +xxxabcdexxx [single a->A][multiple b->BCD][ligature cde->E] xxxABCDExxx + + +

Therefore we we don't really do the replacement here already unless we have the +single lookup case. The efficiency of the replacements can be improved by deleting +as less as needed but that would also make the code even more messy.

+--ldx]]-- + +-- local function delete_till_stop(head,start,stop,ignoremarks) -- keeps start +-- local n = 1 +-- if start == stop then +-- -- done +-- elseif ignoremarks then +-- repeat -- start x x m x x stop => start m +-- local next = start.next +-- if not marks[next.char] then +-- local components = next.components +-- if components then -- probably not needed +-- flush_node_list(components) +-- end +-- head = delete_node(head,next) +-- end +-- n = n + 1 +-- until next == stop +-- else -- start x x x stop => start +-- repeat +-- local next = start.next +-- local components = next.components +-- if components then -- probably not needed +-- flush_node_list(components) +-- end +-- head = delete_node(head,next) +-- n = n + 1 +-- until next == stop +-- end +-- return head, n +-- end + +--[[ldx-- +

Here we replace start by a single variant, First we delete the rest of the +match.

+--ldx]]-- + +function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) + -- todo: marks ? + local current = start + local subtables = currentlookup.subtables + if #subtables > 1 then + logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) + end + while current do + if current.id == glyph_code then + local currentchar = current.char + local lookupname = subtables[1] -- only 1 + local replacement = lookuphash[lookupname] + if not replacement then + if trace_bugs then + logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + replacement = replacement[currentchar] + if not replacement or replacement == "" then + if trace_bugs then + logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) + end + else + if trace_singles then + logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) + end + current.char = replacement + end + end + return head, start, true + elseif current == stop then + break + else + current = current.next + end + end + return head, start, false +end + +chainmores.gsub_single = chainprocs.gsub_single + +--[[ldx-- +

Here we replace start by a sequence of new glyphs. First we delete the rest of +the match.

+--ldx]]-- + +function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + -- local head, n = delete_till_stop(head,start,stop) + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local replacements = lookuphash[lookupname] + if not replacements then + if trace_bugs then + logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) + end + else + replacements = replacements[startchar] + if not replacements or replacement == "" then + if trace_bugs then + logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) + end + else + if trace_multiples then + logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) + end + return multiple_glyphs(head,start,replacements,currentlookup.flags[1]) + end + end + return head, start, false +end + +chainmores.gsub_multiple = chainprocs.gsub_multiple + +--[[ldx-- +

Here we replace start by new glyph. First we delete the rest of the match.

+--ldx]]-- + +-- char_1 mark_1 -> char_x mark_1 (ignore marks) +-- char_1 mark_1 -> char_x + +-- to be checked: do we always have just one glyph? +-- we can also have alternates for marks +-- marks come last anyway +-- are there cases where we need to delete the mark + +function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local current = start + local subtables = currentlookup.subtables + local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue + while current do + if current.id == glyph_code then -- is this check needed? + local currentchar = current.char + local lookupname = subtables[1] + local alternatives = lookuphash[lookupname] + if not alternatives then + if trace_bugs then + logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname)) + end + else + alternatives = alternatives[currentchar] + if alternatives then + local choice, comment = get_alternative_glyph(current,alternatives,value,trace_alternatives) + if choice then + if trace_alternatives then + logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment) + end + start.char = choice + else + if trace_alternatives then + logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) + end + end + elseif trace_bugs then + logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment) + end + end + return head, start, true + elseif current == stop then + break + else + current = current.next + end + end + return head, start, false +end + +chainmores.gsub_alternate = chainprocs.gsub_alternate + +--[[ldx-- +

When we replace ligatures we use a helper that handles the marks. I might change +this function (move code inline and handle the marks by a separate function). We +assume rather stupid ligatures (no complex disc nodes).

+--ldx]]-- + +function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local ligatures = lookuphash[lookupname] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + ligatures = ligatures[startchar] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + end + else + local s = start.next + local discfound = false + local last = stop + local nofreplacements = 0 + local skipmark = currentlookup.flags[1] + while s do + local id = s.id + if id == disc_code then + s = s.next + discfound = true + else + local schar = s.char + if skipmark and marks[schar] then -- marks + s = s.next + else + local lg = ligatures[schar] + if lg then + ligatures, last, nofreplacements = lg, s, nofreplacements + 1 + if s == stop then + break + else + s = s.next + end + else + break + end + end + end + end + local l2 = ligatures.ligature + if l2 then + if chainindex then + stop = last + end + if trace_ligatures then + if start == stop then + logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) + else + logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) + end + end + head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) + return head, start, true, nofreplacements + elseif trace_bugs then + if start == stop then + logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + else + logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) + end + end + end + end + return head, start, false, 0 +end + +chainmores.gsub_ligature = chainprocs.gsub_ligature + +function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = lookuphash[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [start=mark] + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return head, start, false + end + end + end + local baseanchors = descriptions[basechar].anchors + if baseanchors then + local baseanchors = baseanchors['basechar'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return head, start, false +end + +function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = lookuphash[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [optional marks] [start=mark] + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) + end + return head, start, false + end + end + end + -- todo: like marks a ligatures hash + local index = start[a_ligacomp] + local baseanchors = descriptions[basechar].anchors + if baseanchors then + local baseanchors = baseanchors['baselig'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + ba = ba[index] + if ba then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", + cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) + end + return head, start, true + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return head, start, false +end + +function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + -- local alreadydone = markonce and start[a_markmark] + -- if not alreadydone then + -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = lookuphash[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [basemark] [start=mark] + local slc = start[a_ligacomp] + if slc then -- a rather messy loop ... needs checking with husayni + while base do + local blc = base[a_ligacomp] + if blc and blc ~= slc then + base = base.prev + else + break + end + end + end + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go + local basechar = base.char + local baseanchors = descriptions[basechar].anchors + if baseanchors then + baseanchors = baseanchors['basemark'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + -- elseif trace_marks and trace_details then + -- logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) + -- end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return head, start, false +end + +function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local alreadydone = cursonce and start[a_cursbase] + if not alreadydone then + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local exitanchors = lookuphash[lookupname] + if exitanchors then + exitanchors = exitanchors[startchar] + end + if exitanchors then + local done = false + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt = start.next + while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do + local nextchar = nxt.char + if marks[nextchar] then + -- should not happen (maybe warning) + nxt = nxt.next + else + local entryanchors = descriptions[nextchar] + if entryanchors then + entryanchors = entryanchors.anchors + if entryanchors then + entryanchors = entryanchors['centry'] + if entryanchors then + local al = anchorlookups[lookupname] + for anchor, entry in next, entryanchors do + if al[anchor] then + local exit = exitanchors[anchor] + if exit then + local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + end + done = true + break + end + end + end + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + end + break + end + end + end + return head, start, done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return head, start, false + end + end + return head, start, false +end + +function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + -- untested .. needs checking for the new model + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = lookuphash[lookupname] + if kerns then + kerns = kerns[startchar] -- needed ? + if kerns then + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) + end + end + end + return head, start, false +end + +chainmores.gpos_single = chainprocs.gpos_single -- okay? + +-- when machines become faster i will make a shared function + +function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + local snext = start.next + if snext then + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = lookuphash[lookupname] + if kerns then + kerns = kerns[startchar] + if kerns then + local lookuptype = lookuptypes[lookupname] + local prev, done = start, false + local factor = tfmdata.parameters.factor + while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do + local nextchar = snext.char + local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = snext.next + else + if not krn then + -- skip + elseif type(krn) == "table" then + if lookuptype == "pair" then + local a, b = krn[2], krn[3] + if a and #a > 0 then + local startchar = start.char + local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local startchar = start.char + local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else + report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) + local a, b = krn[2], krn[6] + if a and a ~= 0 then + local k = setkern(snext,factor,rlmode,a) + if trace_kerns then + logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + end + if b and b ~= 0 then + logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) + end + end + done = true + elseif krn ~= 0 then + local k = setkern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + done = true + end + break + end + end + return head, start, done + end + end + end + return head, start, false +end + +chainmores.gpos_pair = chainprocs.gpos_pair -- okay? + +-- what pointer to return, spec says stop +-- to be discussed ... is bidi changer a space? +-- elseif char == zwnj and sequence[n][32] then -- brrr + +-- somehow l or f is global +-- we don't need to pass the currentcontext, saves a bit +-- make a slow variant then can be activated but with more tracing + +local function show_skip(kind,chainname,char,ck,class) + if ck[9] then + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) + else + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) + end +end + +local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) + -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] + local flags = sequence.flags + local done = false + local skipmark = flags[1] + local skipligature = flags[2] + local skipbase = flags[3] + local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) + local markclass = sequence.markclass -- todo, first we need a proper test + local skipped = false + for k=1,#contexts do + local match = true + local current = start + local last = start + local ck = contexts[k] + local seq = ck[3] + local s = #seq + -- f..l = mid string + if s == 1 then + -- never happens + match = current.id == glyph_code and current.font == currentfont and current.subtype<256 and seq[1][current.char] + else + -- maybe we need a better space check (maybe check for glue or category or combination) + -- we cannot optimize for n=2 because there can be disc nodes + local f, l = ck[4], ck[5] + -- current match + if f == 1 and f == l then -- current only + -- already a hit + -- match = true + else -- before/current/after | before/current | current/after + -- no need to test first hit (to be optimized) + if f == l then -- new, else last out of sync (f is > 1) + -- match = true + else + local n = f + 1 + last = last.next + while n <= l do + if last then + local id = last.id + if id == glyph_code then + if last.font == currentfont and last.subtype<256 then + local char = last.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + last = last.next + elseif seq[n][char] then + if n < l then + last = last.next + end + n = n + 1 + else + match = false + break + end + else + match = false + break + end + else + match = false + break + end + elseif id == disc_code then + last = last.next + else + match = false + break + end + else + match = false + break + end + end + end + end + -- before + if match and f > 1 then + local prev = start.prev + if prev then + local n = f-1 + while n >= 1 do + if prev then + local id = prev.id + if id == glyph_code then + if prev.font == currentfont and prev.subtype<256 then -- normal char + local char = prev.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n = n -1 + else + match = false + break + end + else + match = false + break + end + else + match = false + break + end + elseif id == disc_code then + -- skip 'm + elseif seq[n][32] then + n = n -1 + else + match = false + break + end + prev = prev.prev + elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces + n = n -1 + else + match = false + break + end + end + elseif f == 2 then + match = seq[1][32] + else + for n=f-1,1 do + if not seq[n][32] then + match = false + break + end + end + end + end + -- after + if match and s > l then + local current = last and last.next + if current then + -- removed optimization for s-l == 1, we have to deal with marks anyway + local n = l + 1 + while n <= s do + if current then + local id = current.id + if id == glyph_code then + if current.font == currentfont and current.subtype<256 then -- normal char + local char = current.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n = n + 1 + else + match = false + break + end + else + match = false + break + end + else + match = false + break + end + elseif id == disc_code then + -- skip 'm + elseif seq[n][32] then -- brrr + n = n + 1 + else + match = false + break + end + current = current.next + elseif seq[n][32] then + n = n + 1 + else + match = false + break + end + end + elseif s-l == 1 then + match = seq[s][32] + else + for n=l+1,s do + if not seq[n][32] then + match = false + break + end + end + end + end + end + if match then + -- ck == currentcontext + if trace_contexts then + local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5] + local char = start.char + if ck[9] then + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a", + cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) + else + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a", + cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) + end + end + local chainlookups = ck[6] + if chainlookups then + local nofchainlookups = #chainlookups + -- we can speed this up if needed + if nofchainlookups == 1 then + local chainlookupname = chainlookups[1] + local chainlookup = lookuptable[chainlookupname] + if chainlookup then + local cp = chainprocs[chainlookup.type] + if cp then + local ok + head, start, ok = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + if ok then + done = true + end + else + logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + end + else -- shouldn't happen + logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname)) + end + else + local i = 1 + repeat + if skipped then + while true do + local char = start.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + start = start.next + else + break + end + else + break + end + end + end + local chainlookupname = chainlookups[i] + local chainlookup = lookuptable[chainlookupname] + if not chainlookup then + -- okay, n matches, < n replacements + i = i + 1 + else + local cp = chainmores[chainlookup.type] + if not cp then + -- actually an error + logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + i = i + 1 + else + local ok, n + head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) + -- messy since last can be changed ! + if ok then + done = true + -- skip next one(s) if ligature + i = i + (n or 1) + else + i = i + 1 + end + end + end + if start then + start = start.next + else + -- weird + end + until i > nofchainlookups + end + else + local replacements = ck[7] + if replacements then + head, start, done = chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) -- sequence + else + done = true -- can be meant to be skipped + if trace_contexts then + logprocess("%s: skipping match",cref(kind,chainname)) + end + end + end + end + end + return head, start, done +end + +-- Because we want to keep this elsewhere (an because speed is less an issue) we +-- pass the font id so that the verbose variant can access the relevant helper tables. + +local verbose_handle_contextchain = function(font,...) + logwarning("no verbose handler installed, reverting to 'normal'") + otf.setcontextchain() + return normal_handle_contextchain(...) +end + +otf.chainhandlers = { + normal = normal_handle_contextchain, + verbose = verbose_handle_contextchain, +} + +function otf.setcontextchain(method) + if not method or method == "normal" or not otf.chainhandlers[method] then + if handlers.contextchain then -- no need for a message while making the format + logwarning("installing normal contextchain handler") + end + handlers.contextchain = normal_handle_contextchain + else + logwarning("installing contextchain handler %a",method) + local handler = otf.chainhandlers[method] + handlers.contextchain = function(...) + return handler(currentfont,...) -- hm, get rid of ... + end + end + handlers.gsub_context = handlers.contextchain + handlers.gsub_contextchain = handlers.contextchain + handlers.gsub_reversecontextchain = handlers.contextchain + handlers.gpos_contextchain = handlers.contextchain + handlers.gpos_context = handlers.contextchain +end + +otf.setcontextchain() + +local missing = { } -- we only report once + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_process(...) +end + +local logwarning = report_process + +local function report_missing_cache(typ,lookup) + local f = missing[currentfont] if not f then f = { } missing[currentfont] = f end + local t = f[typ] if not t then t = { } f[typ] = t end + if not t[lookup] then + t[lookup] = true + logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname) + end +end + +local resolved = { } -- we only resolve a font,script,language pair once + +-- todo: pass all these 'locals' in a table + +local lookuphashes = { } + +setmetatableindex(lookuphashes, function(t,font) + local lookuphash = fontdata[font].resources.lookuphash + if not lookuphash or not next(lookuphash) then + lookuphash = false + end + t[font] = lookuphash + return lookuphash +end) + +-- fonts.hashes.lookups = lookuphashes + +local autofeatures = fonts.analyzers.features -- was: constants + +local function initialize(sequence,script,language,enabled) + local features = sequence.features + if features then + for kind, scripts in next, features do + local valid = enabled[kind] + if valid then + local languages = scripts[script] or scripts[wildcard] + if languages and (languages[language] or languages[wildcard]) then + return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence } + end + end + end + end + return false +end + +function otf.dataset(tfmdata,font) -- generic variant, overloaded in context + local shared = tfmdata.shared + local properties = tfmdata.properties + local language = properties.language or "dflt" + local script = properties.script or "dflt" + local enabled = shared.features + local res = resolved[font] + if not res then + res = { } + resolved[font] = res + end + local rs = res[script] + if not rs then + rs = { } + res[script] = rs + end + local rl = rs[language] + if not rl then + rl = { + -- indexed but we can also add specific data by key + } + rs[language] = rl + local sequences = tfmdata.resources.sequences +-- setmetatableindex(rl, function(t,k) +-- if type(k) == "number" then +-- local v = enabled and initialize(sequences[k],script,language,enabled) +-- t[k] = v +-- return v +-- end +-- end) +for s=1,#sequences do + local v = enabled and initialize(sequences[s],script,language,enabled) + if v then + rl[#rl+1] = v + end +end + end + return rl +end + +-- elseif id == glue_code then +-- if p[5] then -- chain +-- local pc = pp[32] +-- if pc then +-- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) +-- if ok then +-- done = true +-- end +-- if start then start = start.next end +-- else +-- start = start.next +-- end +-- else +-- start = start.next +-- end + +-- there will be a new direction parser (pre-parsed etc) + +-- less bytecode: 290 -> 254 +-- +-- attr = attr or false +-- +-- local a = getattr(start,0) +-- if (a == attr and (not attribute or getattr(start,a_state) == attribute)) or (not attribute or getattr(start,a_state) == attribute) then +-- -- the action +-- end + +local function featuresprocessor(head,font,attr) + + local lookuphash = lookuphashes[font] -- we can also check sequences here + + if not lookuphash then + return head, false + end + + if trace_steps then + checkstep(head) + end + + tfmdata = fontdata[font] + descriptions = tfmdata.descriptions + characters = tfmdata.characters + resources = tfmdata.resources + + marks = resources.marks + anchorlookups = resources.lookup_to_anchor + lookuptable = resources.lookups + lookuptypes = resources.lookuptypes + + currentfont = font + rlmode = 0 + + local sequences = resources.sequences + local done = false + local datasets = otf.dataset(tfmdata,font,attr) + + local dirstack = { } -- could move outside function + + -- We could work on sub start-stop ranges instead but I wonder if there is that + -- much speed gain (experiments showed that it made not much sense) and we need + -- to keep track of directions anyway. Also at some point I want to play with + -- font interactions and then we do need the full sweeps. + + -- Keeping track of the headnode is needed for devanagari (I generalized it a bit + -- so that multiple cases are also covered.) + + for s=1,#datasets do + local dataset = datasets[s] + featurevalue = dataset[1] -- todo: pass to function instead of using a global + + local sequence = dataset[5] -- sequences[s] -- also dataset[5] + local rlparmode = 0 + local topstack = 0 + local success = false + local attribute = dataset[2] + local chain = dataset[3] -- sequence.chain or 0 + local typ = sequence.type + local subtables = sequence.subtables + if chain < 0 then + -- this is a limited case, no special treatments like 'init' etc + local handler = handlers[typ] + -- we need to get rid of this slide! probably no longer needed in latest luatex + local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo + while start do + local id = start.id + if id == glyph_code then + if start.font == font and start.subtype<256 then + local a = start[0] + if a then + a = a == attr + else + a = true + end + if a then + for i=1,#subtables do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if success then + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.prev end + else + start = start.prev + end + else + start = start.prev + end + else + start = start.prev + end + end + else + local handler = handlers[typ] + local ns = #subtables + local start = head -- local ? + rlmode = 0 -- to be checked ? + if ns == 1 then -- happens often + local lookupname = subtables[1] + local lookupcache = lookuphash[lookupname] + if not lookupcache then -- also check for empty cache + report_missing_cache(typ,lookupname) + else + + local function subrun(start) + -- mostly for gsub, gpos would demand a more clever approach + local head = start + local done = false + while start do + local id = start.id + if id == glyph_code and start.font == font and start.subtype <256 then + local a = start[0] + if a then + a = (a == attr) and (not attribute or start[a_state] == attribute) + else + a = not attribute or start[a_state] == attribute + end + if a then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- sequence kan weg + local ok + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + done = true + end + end + if start then start = start.next end + else + start = start.next + end + else + start = start.next + end + end + if done then + success = true + return head + end + end + + local function kerndisc(disc) -- we can assume that prev and next are glyphs + local prev = disc.prev + local next = disc.next + if prev and next then + prev.next = next + -- next.prev = prev + local a = prev[0] + if a then + a = (a == attr) and (not attribute or prev[a_state] == attribute) + else + a = not attribute or prev[a_state] == attribute + end + if a then + local lookupmatch = lookupcache[prev.char] + if lookupmatch then + -- sequence kan weg + local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + done = true + success = true + end + end + end + prev.next = disc + -- next.prev = disc + end + return next + end + + while start do + local id = start.id + if id == glyph_code then + if start.font == font and start.subtype<256 then + local a = start[0] + if a then + a = (a == attr) and (not attribute or start[a_state] == attribute) + else + a = not attribute or start[a_state] == attribute + end + if a then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- sequence kan weg + local ok + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + success = true + end + end + if start then start = start.next end + else + start = start.next + end + else + start = start.next + end + elseif id == disc_code then + -- mostly for gsub + if start.subtype == discretionary_code then + local pre = start.pre + if pre then + local new = subrun(pre) + if new then start.pre = new end + end + local post = start.post + if post then + local new = subrun(post) + if new then start.post = new end + end + local replace = start.replace + if replace then + local new = subrun(replace) + if new then start.replace = new end + end +elseif typ == "gpos_single" or typ == "gpos_pair" then + kerndisc(start) + end + start = start.next + elseif id == whatsit_code then -- will be function + local subtype = start.subtype + if subtype == dir_code then + local dir = start.dir + if dir == "+TRT" or dir == "+TLT" then + topstack = topstack + 1 + dirstack[topstack] = dir + elseif dir == "-TRT" or dir == "-TLT" then + topstack = topstack - 1 + end + local newdir = dirstack[topstack] + if newdir == "+TRT" then + rlmode = -1 + elseif newdir == "+TLT" then + rlmode = 1 + else + rlmode = rlparmode + end + if trace_directions then + report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) + end + elseif subtype == localpar_code then + local dir = start.dir + if dir == "TRT" then + rlparmode = -1 + elseif dir == "TLT" then + rlparmode = 1 + else + rlparmode = 0 + end + -- one might wonder if the par dir should be looked at, so we might as well drop the next line + rlmode = rlparmode + if trace_directions then + report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) + end + end + start = start.next + elseif id == math_code then + start = end_of_math(start).next + else + start = start.next + end + end + end + else + + local function subrun(start) + -- mostly for gsub, gpos would demand a more clever approach + local head = start + local done = false + while start do + local id = start.id + if id == glyph_code and start.id == font and start.subtype <256 then + local a = start[0] + if a then + a = (a == attr) and (not attribute or start[a_state] == attribute) + else + a = not attribute or start[a_state] == attribute + end + if a then + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + done = true + break + elseif not start then + -- don't ask why ... shouldn't happen + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.next end + else + start = start.next + end + else + start = start.next + end + end + if done then + success = true + return head + end + end + + local function kerndisc(disc) -- we can assume that prev and next are glyphs + local prev = disc.prev + local next = disc.next + if prev and next then + prev.next = next + -- next.prev = prev + local a = prev[0] + if a then + a = (a == attr) and (not attribute or prev[a_state] == attribute) + else + a = not attribute or prev[a_state] == attribute + end + if a then + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[prev.char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + done = true + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + end + prev.next = disc + -- next.prev = disc + end + return next + end + + while start do + local id = start.id + if id == glyph_code then + if start.font == font and start.subtype<256 then + local a = start[0] + if a then + a = (a == attr) and (not attribute or start[a_state] == attribute) + else + a = not attribute or start[a_state] == attribute + end + if a then + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + success = true + break + elseif not start then + -- don't ask why ... shouldn't happen + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.next end + else + start = start.next + end + else + start = start.next + end + elseif id == disc_code then + -- mostly for gsub + if start.subtype == discretionary_code then + local pre = start.pre + if pre then + local new = subrun(pre) + if new then start.pre = new end + end + local post = start.post + if post then + local new = subrun(post) + if new then start.post = new end + end + local replace = start.replace + if replace then + local new = subrun(replace) + if new then start.replace = new end + end +elseif typ == "gpos_single" or typ == "gpos_pair" then + kerndisc(start) + end + start = start.next + elseif id == whatsit_code then + local subtype = start.subtype + if subtype == dir_code then + local dir = start.dir + if dir == "+TRT" or dir == "+TLT" then + topstack = topstack + 1 + dirstack[topstack] = dir + elseif dir == "-TRT" or dir == "-TLT" then + topstack = topstack - 1 + end + local newdir = dirstack[topstack] + if newdir == "+TRT" then + rlmode = -1 + elseif newdir == "+TLT" then + rlmode = 1 + else + rlmode = rlparmode + end + if trace_directions then + report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) + end + elseif subtype == localpar_code then + local dir = start.dir + if dir == "TRT" then + rlparmode = -1 + elseif dir == "TLT" then + rlparmode = 1 + else + rlparmode = 0 + end + rlmode = rlparmode + if trace_directions then + report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) + end + end + start = start.next + elseif id == math_code then + start = end_of_math(start).next + else + start = start.next + end + end + end + end + if success then + done = true + end + if trace_steps then -- ? + registerstep(head) + end + end + return head, done +end + +local function generic(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if target then + target[unicode] = lookupdata + else + lookuphash[lookupname] = { [unicode] = lookupdata } + end +end + +local action = { + + substitution = generic, + multiple = generic, + alternate = generic, + position = generic, + + ligature = function(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target + end + for i=1,#lookupdata do + local li = lookupdata[i] + local tu = target[li] + if not tu then + tu = { } + target[li] = tu + end + target = tu + end + target.ligature = unicode + end, + + pair = function(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target + end + local others = target[unicode] + local paired = lookupdata[1] + if others then + others[paired] = lookupdata + else + others = { [paired] = lookupdata } + target[unicode] = others + end + end, + +} + +local function prepare_lookups(tfmdata) + + local rawdata = tfmdata.shared.rawdata + local resources = rawdata.resources + local lookuphash = resources.lookuphash + local anchor_to_lookup = resources.anchor_to_lookup + local lookup_to_anchor = resources.lookup_to_anchor + local lookuptypes = resources.lookuptypes + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + + -- we cannot free the entries in the descriptions as sometimes we access + -- then directly (for instance anchors) ... selectively freeing does save + -- much memory as it's only a reference to a table and the slot in the + -- description hash is not freed anyway + + for unicode, character in next, characters do -- we cannot loop over descriptions ! + + local description = descriptions[unicode] + + if description then + + local lookups = description.slookups + if lookups then + for lookupname, lookupdata in next, lookups do + action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) + end + end + + local lookups = description.mlookups + if lookups then + for lookupname, lookuplist in next, lookups do + local lookuptype = lookuptypes[lookupname] + for l=1,#lookuplist do + local lookupdata = lookuplist[l] + action[lookuptype](lookupdata,lookupname,unicode,lookuphash) + end + end + end + + local list = description.kerns + if list then + for lookup, krn in next, list do -- ref to glyph, saves lookup + local target = lookuphash[lookup] + if target then + target[unicode] = krn + else + lookuphash[lookup] = { [unicode] = krn } + end + end + end + + local list = description.anchors + if list then + for typ, anchors in next, list do -- types + if typ == "mark" or typ == "cexit" then -- or entry? + for name, anchor in next, anchors do + local lookups = anchor_to_lookup[name] + if lookups then + for lookup, _ in next, lookups do + local target = lookuphash[lookup] + if target then + target[unicode] = anchors + else + lookuphash[lookup] = { [unicode] = anchors } + end + end + end + end + end + end + end + + end + + end + +end + +local function split(replacement,original) + local result = { } + for i=1,#replacement do + result[original[i]] = replacement[i] + end + return result +end + +local valid = { + coverage = { chainsub = true, chainpos = true, contextsub = true }, + reversecoverage = { reversesub = true }, + glyphs = { chainsub = true, chainpos = true }, +} + +local function prepare_contextchains(tfmdata) + local rawdata = tfmdata.shared.rawdata + local resources = rawdata.resources + local lookuphash = resources.lookuphash + local lookups = rawdata.lookups + if lookups then + for lookupname, lookupdata in next, rawdata.lookups do + local lookuptype = lookupdata.type + if lookuptype then + local rules = lookupdata.rules + if rules then + local format = lookupdata.format + local validformat = valid[format] + if not validformat then + report_prepare("unsupported format %a",format) + elseif not validformat[lookuptype] then + -- todo: dejavu-serif has one (but i need to see what use it has) + report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookupname) + else + local contexts = lookuphash[lookupname] + if not contexts then + contexts = { } + lookuphash[lookupname] = contexts + end + local t, nt = { }, 0 + for nofrules=1,#rules do + local rule = rules[nofrules] + local current = rule.current + local before = rule.before + local after = rule.after + local replacements = rule.replacements + local sequence = { } + local nofsequences = 0 + -- Eventually we can store start, stop and sequence in the cached file + -- but then less sharing takes place so best not do that without a lot + -- of profiling so let's forget about it. + if before then + for n=1,#before do + nofsequences = nofsequences + 1 + sequence[nofsequences] = before[n] + end + end + local start = nofsequences + 1 + for n=1,#current do + nofsequences = nofsequences + 1 + sequence[nofsequences] = current[n] + end + local stop = nofsequences + if after then + for n=1,#after do + nofsequences = nofsequences + 1 + sequence[nofsequences] = after[n] + end + end + if sequence[1] then + -- Replacements only happen with reverse lookups as they are single only. We + -- could pack them into current (replacement value instead of true) and then + -- use sequence[start] instead but it's somewhat ugly. + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t + end + end + end + end + end + else + -- no rules + end + else + report_prepare("missing lookuptype for lookupname %a",lookupname) + end + end + end +end + +-- we can consider lookuphash == false (initialized but empty) vs lookuphash == table + +local function featuresinitializer(tfmdata,value) + if true then -- value then + -- beware we need to use the topmost properties table + local rawdata = tfmdata.shared.rawdata + local properties = rawdata.properties + if not properties.initialized then + local starttime = trace_preparing and os.clock() + local resources = rawdata.resources + resources.lookuphash = resources.lookuphash or { } + prepare_contextchains(tfmdata) + prepare_lookups(tfmdata) + properties.initialized = true + if trace_preparing then + report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname) + end + end + end +end + +registerotffeature { + name = "features", + description = "features", + default = true, + initializers = { + position = 1, + node = featuresinitializer, + }, + processors = { + node = featuresprocessor, + } +} + +-- This can be used for extra handlers, but should be used with care! + +otf.handlers = handlers -- cgit v1.2.3 From ef4abccff44fa27fbdf5bb658164d77903f914ef Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 13 Jan 2014 16:02:50 +0100 Subject: [tool] adapt --info option for version 0.78 --- luaotfload-tool.lua | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index ba3fcb5..1f57b44 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -349,15 +349,36 @@ local print_heading = function (title, level) texiowrite_nl (s .. stringrep(adornchar, textwidth-utf.len(s))) end +local baseindent = " " + +--[[doc-- + + show_info_items -- Together with show_info_table prints the table returned by + fontloader.info(), recursing into nested tables if appropriate (as necessitated + by Luatex versions 0.78+ which include the pfminfo table in the result. + +--doc]]-- + +local show_info_table show_info_table = function (t, depth) + depth = depth or 0 + local indent = stringrep (baseindent, depth) + local keys = table.sortedkeys (t) + for n = 1, #keys do + local key = keys [n] + local val = t [key] + if type (val) == "table" then + texiowrite_nl (indent .. stringformat (info_fmt, key, "")) + show_info_table (val, depth + 1) + else + texiowrite_nl (indent .. stringformat (info_fmt, key, val)) + end + end +end + local show_info_items = function (fontinfo) - local items = table.sortedkeys(fontinfo) - print_heading(fontinfo.fullname, 1) + print_heading (fontinfo.fullname, 1) texiowrite_nl "" - for n = 1, #items do - local item = items[n] - texiowrite_nl(stringformat( - info_fmt, item, fontinfo[item])) - end + show_info_table (fontinfo) texiowrite_nl "" end -- cgit v1.2.3 From 66d3fe0ec287aee7ee5180ee5c6ec01129b791b0 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 14 Jan 2014 10:19:13 +0100 Subject: [parsers] add file stub of luaotfload-parsers.lua to repo --- luaotfload-parsers.lua | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 luaotfload-parsers.lua diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua new file mode 100644 index 0000000..5391a1d --- /dev/null +++ b/luaotfload-parsers.lua @@ -0,0 +1,21 @@ +#!/usr/bin/env texlua +----------------------------------------------------------------------- +-- FILE: luaotfload-parsers.lua +-- DESCRIPTION: various lpeg-based parsers used in Luaotfload +-- REQUIREMENTS: Luaotfload > 2.4 +-- AUTHOR: Philipp Gesang (Phg), +-- VERSION: same as Luaotfload +-- CREATED: 2014-01-14 10:15:20+0100 +----------------------------------------------------------------------- +-- + +if not modules then modules = { } end modules ['luaotfload-parsers'] = { + version = "2.5", + comment = "companion to luaotfload.lua", + author = "Philipp Gesang", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2.0" +} + + + -- cgit v1.2.3 From 7844191ec1a3654a98200c4fdb65d8db23196620 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 14 Jan 2014 10:22:12 +0100 Subject: [main] add parsers to initialization chain --- luaotfload.dtx | 1 + 1 file changed, 1 insertion(+) diff --git a/luaotfload.dtx b/luaotfload.dtx index 6f8ebc4..366cb87 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1941,6 +1941,7 @@ logs.set_loglevel(config.luaotfload.loglevel) % Now we load the modules written for \identifier{luaotfload}. % % \begin{macrocode} +loadmodule"parsers.lua" --- new in 2.5; fonts.conf and syntax loadmodule"loaders.lua" --- “font-pfb” new in 2.0, added 2011 loadmodule"database.lua" --- “font-nms” loadmodule"colors.lua" --- “font-clr” -- cgit v1.2.3 From dca5e9058a8761e308ac1a2af85bd241f840fd96 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 14 Jan 2014 10:25:13 +0100 Subject: [tool] add parsers to initialization chain --- luaotfload-tool.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 1f57b44..18e9f9e 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -168,6 +168,7 @@ texio.write, texio.write_nl = backup.write, backup.write_nl utilities = backup.utilities require"luaotfload-override.lua" --- this populates the logs.* namespace +require"luaotfload-parsers" --- fonts.conf and request syntax require"luaotfload-database" require"alt_getopt" -- cgit v1.2.3 From 6232768122e63018a69bfd6159514b60020b75bb Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 14 Jan 2014 12:35:46 +0100 Subject: [db,parsers] move read_fonts_conf to parsers --- luaotfload-database.lua | 236 +-------------------------------------- luaotfload-parsers.lua | 287 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 285 insertions(+), 238 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 72b043b..32197cc 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -44,6 +44,8 @@ local P, R, S, lpegmatch local C, Cc, Cf, Cg, Cs, Ct = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cs, lpeg.Ct +local read_fonts_conf = luaotfload.parsers.read_fonts_conf + --- Luatex builtins local load = load local next = next @@ -545,7 +547,6 @@ local t1_fullinfo local load_names local load_lookups local read_blacklist -local read_fonts_conf local reload_db local resolve_name local resolve_cached @@ -2270,237 +2271,6 @@ local scan_texmf_fonts = function (currentnames, targetnames, dry_run) return n_scanned, n_new 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 read_fonts_conf -do --- closure for read_fonts_conf() - - local alpha = R("az", "AZ") - local digit = R"09" - local tag_name = C(alpha^1) - local whitespace = S" \n\r\t\v" - local ws = whitespace^1 - local comment = P"" - - ---> header specifica - local xml_declaration = P"")^0 * P"?>" - local xml_doctype = P"")^0 * P">" - local header = xml_declaration^-1 - * (xml_doctype + comment + ws)^0 - - ---> enforce root node - local root_start = P"<" * ws^-1 * P"fontconfig" * ws^-1 * P">" - local root_stop = P"" - - local dquote, squote = P[["]], P"'" - local xml_namestartchar = S":_" + alpha --- ascii only, funk the rest - local xml_namechar = S":._" + alpha + digit - local xml_name = ws^-1 - * C(xml_namestartchar * xml_namechar^0) - local xml_attvalue = dquote * C((1 - S[[%&"]])^1) * dquote * ws^-1 - + squote * C((1 - S[[%&']])^1) * squote * ws^-1 - local xml_attr = Cg(xml_name * P"=" * xml_attvalue) - local xml_attr_list = Cf(Ct"" * xml_attr^1, rawset) - - --[[doc-- - scan_node creates a parser for a given xml tag. - --doc]]-- - --- string -> bool -> lpeg_t - local scan_node = function (tag) - --- Node attributes go into a table with the index “attributes” - --- (relevant for “prefix="xdg"” and the likes). - local p_tag = P(tag) - local with_attributes = P"<" * p_tag - * Cg(xml_attr_list, "attributes")^-1 - * ws^-1 - * P">" - local plain = P"<" * p_tag * ws^-1 * P">" - local node_start = plain + with_attributes - local node_stop = P"" - --- there is no nesting, the earth is flat ... - local node = node_start - * Cc(tag) * C(comment + (1 - node_stop)^1) - * node_stop - return Ct(node) -- returns {string, string [, attributes = { key = val }] } - end - - --[[doc-- - At the moment, the interesting tags are “dir” for - directory declarations, and “include” for including - further configuration files. - - spec: http://freedesktop.org/software/fontconfig/fontconfig-user.html - --doc]]-- - local include_node = scan_node"include" - local dir_node = scan_node"dir" - - local element = dir_node - + include_node - + comment --> ignore - + P(1-root_stop) --> skip byte - - local root = root_start * Ct(element^0) * root_stop - local p_cheapxml = header * root - - --lpeg.print(p_cheapxml) ---> 757 rules with v0.10 - - --[[doc-- - fonts_conf_scanner() handles configuration files. - It is called on an abolute path to a config file (e.g. - /home/luser/.config/fontconfig/fonts.conf) and returns a list - of the nodes it managed to extract from the file. - --doc]]-- - --- string -> path list - local fonts_conf_scanner = function (path) - local fh = ioopen(path, "r") - if not fh then - report("both", 3, "db", "Cannot open fontconfig file %s.", path) - return - end - local raw = fh:read"*all" - fh:close() - - local confdata = lpegmatch(p_cheapxml, raw) - if not confdata then - report("both", 3, "db", "Cannot scan fontconfig file %s.", path) - return - end - return confdata - end - - local p_conf = P".conf" * P(-1) - local p_filter = (1 - p_conf)^1 * p_conf - - local conf_filter = function (path) - if lpegmatch (p_filter, path) then - return true - end - return false - end - - --[[doc-- - read_fonts_conf_indeed() is called with six arguments; the - latter three are tables that represent the state and are - always returned. - The first three are - · the path to the file - · the expanded $HOME - · the expanded $XDG_CONFIG_DIR - --doc]]-- - --- string -> string -> string -> tab -> tab -> (tab * tab * tab) - local read_fonts_conf_indeed - read_fonts_conf_indeed = function (start, home, xdg_home, - acc, done, dirs_done) - - local paths = fonts_conf_scanner(start) - if not paths then --- nothing to do - return acc, done, dirs_done - end - - for i=1, #paths do - local pathobj = paths[i] - local kind, path = pathobj[1], pathobj[2] - local attributes = pathobj.attributes - if attributes and attributes.prefix == "xdg" then - --- this prepends the xdg root (usually ~/.config) - path = filejoin(xdg_home, path) - end - - if kind == "dir" then - if stringsub(path, 1, 1) == "~" then - path = filejoin(home, stringsub(path, 2)) - end - --- We exclude paths with texmf in them, as they should be - --- found anyway; also duplicates are ignored by checking - --- if they are elements of dirs_done. - --- - --- FIXME does this mean we cannot access paths from - --- distributions (e.g. Context minimals) installed - --- separately? - if not (stringfind(path, "texmf") or dirs_done[path]) then - acc[#acc+1] = path - dirs_done[path] = true - end - - elseif kind == "include" then - --- here the path can be four things: a directory or a file, - --- in absolute or relative path. - if stringsub(path, 1, 1) == "~" then - path = filejoin(home, stringsub(path, 2)) - elseif --- if the path is relative, we make it absolute - not ( lfsisfile(path) or lfsisdir(path) ) - then - path = filejoin(filedirname(start), path) - end - if lfsisfile(path) - and kpsereadable_file(path) - and not done[path] - then - --- we exclude path with texmf in them, as they should - --- be found otherwise - acc = read_fonts_conf_indeed( - path, home, xdg_home, - acc, done, dirs_done) - elseif lfsisdir(path) then --- arrow code ahead - local config_files = find_files (path, conf_filter) - for _, filename in next, config_files do - if not done[filename] then - acc = read_fonts_conf_indeed( - filename, home, xdg_home, - acc, done, dirs_done) - end - end - end --- match “kind” - end --- iterate paths - end - - --inspect(acc) - --inspect(done) - return acc, done, dirs_done - end --- read_fonts_conf_indeed() - - --[[doc-- - read_fonts_conf() sets up an accumulator and two sets - for tracking what’s been done. - - Also, the environment variables HOME and XDG_CONFIG_HOME -- - which are constants anyways -- are expanded so don’t have to - repeat that over and over again as with the old parser. - Now they’re just passed on to every call of - read_fonts_conf_indeed(). - - read_fonts_conf() is also the only reference visible outside - the closure. - --doc]]-- - --- list -> list - read_fonts_conf = function (path_list) - local home = kpseexpand_path"~" --- could be os.getenv"HOME" - local xdg_home = kpseexpand_path"$XDG_CONFIG_HOME" - if xdg_home == "" then xdg_home = filejoin(home, ".config") end - local acc = { } ---> list: paths collected - local done = { } ---> set: files inspected - local dirs_done = { } ---> set: dirs in list - for i=1, #path_list do --- we keep the state between files - acc, done, dirs_done = read_fonts_conf_indeed( - path_list[i], home, xdg_home, - acc, done, dirs_done) - end - return acc - end -end --- read_fonts_conf closure - --- TODO stuff those paths into some writable table --- unit -> string list local function get_os_dirs () @@ -2519,7 +2289,7 @@ local function get_os_dirs () "/usr/local/etc/fonts/fonts.conf", "/etc/fonts/fonts.conf", } - local os_dirs = read_fonts_conf(fonts_conves) + local os_dirs = read_fonts_conf(fonts_conves, find_files) return os_dirs end return {} diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua index 5391a1d..89e3bc9 100644 --- a/luaotfload-parsers.lua +++ b/luaotfload-parsers.lua @@ -10,12 +10,289 @@ -- if not modules then modules = { } end modules ['luaotfload-parsers'] = { - version = "2.5", - comment = "companion to luaotfload.lua", - author = "Philipp Gesang", - copyright = "Luaotfload Development Team", - license = "GNU GPL v2.0" + version = "2.5", + comment = "companion to luaotfload.lua", + author = "Philipp Gesang", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2.0" } +luaotfload = luaotfload or { } +luaotfload.parsers = luaotfload.parsers or { } +local parsers = luaotfload.parsers + +local lpeg = require "lpeg" +local P, R, S = lpeg.P, lpeg.R, lpeg.S +local lpegmatch = lpeg.match +local C, Cc, Cf = lpeg.C, lpeg.Cc, lpeg.Cf +local Cg, Cs, Ct = lpeg.Cg, lpeg.Cs, lpeg.Ct + +local kpse = kpse +local kpseexpand_path = kpse.expand_path +local kpsereadable_file = kpse.readable_file + +local file = file +local filejoin = file.join +local filedirname = file.dirname + +local io = io +local ioopen = io.open + +local logs = logs +local report = logs.report + +local string = string +local stringsub = string.sub +local stringfind = string.find + +local lfs = lfs +local lfsisfile = lfs.isfile +local lfsisdir = lfs.isdir + +--[[doc-- + + For fonts installed on the operating system, there are several + options to make Luaotfload index them: + + - 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 function scan_texmf_fonts(). + + - Otherwise + - under Windows and Mac OSX, we take a look at some hardcoded + directories, + - under Unix, it reads /etc/fonts/fonts.conf and processes the + directories specified there. + + This means that if you have fonts in fancy directories, you need to + set them in OSFONTDIR. + + Beware: OSFONTDIR is a kpathsea variable, so fonts found in these + paths, though technically system fonts, are registered in the + category “texmf”, not “system”. This may have consequences for the + lookup order when a font file (or a font with the same name + information) is located in both the system and the texmf tree. + +--doc]]-- + +local alpha = R("az", "AZ") +local digit = R"09" +local tag_name = C(alpha^1) +local whitespace = S" \n\r\t\v" +local ws = whitespace^1 +local comment = P"" + +---> header specifica +local xml_declaration = P"")^0 * P"?>" +local xml_doctype = P"")^0 * P">" +local header = xml_declaration^-1 + * (xml_doctype + comment + ws)^0 + +---> enforce root node +local root_start = P"<" * ws^-1 * P"fontconfig" * ws^-1 * P">" +local root_stop = P"" + +local dquote, squote = P[["]], P"'" +local xml_namestartchar = S":_" + alpha --- ascii only, funk the rest +local xml_namechar = S":._" + alpha + digit +local xml_name = ws^-1 + * C(xml_namestartchar * xml_namechar^0) +local xml_attvalue = dquote * C((1 - S[[%&"]])^1) * dquote * ws^-1 + + squote * C((1 - S[[%&']])^1) * squote * ws^-1 +local xml_attr = Cg(xml_name * P"=" * xml_attvalue) +local xml_attr_list = Cf(Ct"" * xml_attr^1, rawset) + +--[[doc-- + scan_node creates a parser for a given xml tag. +--doc]]-- +--- string -> bool -> lpeg_t +local scan_node = function (tag) + --- Node attributes go into a table with the index “attributes” + --- (relevant for “prefix="xdg"” and the likes). + local p_tag = P(tag) + local with_attributes = P"<" * p_tag + * Cg(xml_attr_list, "attributes")^-1 + * ws^-1 + * P">" + local plain = P"<" * p_tag * ws^-1 * P">" + local node_start = plain + with_attributes + local node_stop = P"" + --- there is no nesting, the earth is flat ... + local node = node_start + * Cc(tag) * C(comment + (1 - node_stop)^1) + * node_stop + return Ct(node) -- returns {string, string [, attributes = { key = val }] } +end + +--[[doc-- + At the moment, the interesting tags are “dir” for + directory declarations, and “include” for including + further configuration files. + + spec: http://freedesktop.org/software/fontconfig/fontconfig-user.html +--doc]]-- +local include_node = scan_node"include" +local dir_node = scan_node"dir" + +local element = dir_node + + include_node + + comment --> ignore + + P(1-root_stop) --> skip byte + +local root = root_start * Ct(element^0) * root_stop +local p_cheapxml = header * root + +--lpeg.print(p_cheapxml) ---> 757 rules with v0.10 + +--[[doc-- + fonts_conf_scanner() handles configuration files. + It is called on an abolute path to a config file (e.g. + /home/luser/.config/fontconfig/fonts.conf) and returns a list + of the nodes it managed to extract from the file. +--doc]]-- +--- string -> path list +local fonts_conf_scanner = function (path) + local fh = ioopen(path, "r") + if not fh then + report("both", 3, "db", "Cannot open fontconfig file %s.", path) + return + end + local raw = fh:read"*all" + fh:close() + + local confdata = lpegmatch(p_cheapxml, raw) + if not confdata then + report("both", 3, "db", "Cannot scan fontconfig file %s.", path) + return + end + return confdata +end + +local p_conf = P".conf" * P(-1) +local p_filter = (1 - p_conf)^1 * p_conf + +local conf_filter = function (path) + if lpegmatch (p_filter, path) then + return true + end + return false +end + +--[[doc-- + read_fonts_conf_indeed() is called with six arguments; the + latter three are tables that represent the state and are + always returned. + The first three are + · the path to the file + · the expanded $HOME + · the expanded $XDG_CONFIG_DIR +--doc]]-- +--- string -> string -> string -> tab -> tab -> (tab * tab * tab) +local read_fonts_conf_indeed +read_fonts_conf_indeed = function (start, home, xdg_home, + acc, done, dirs_done, + find_files) + + local paths = fonts_conf_scanner(start) + if not paths then --- nothing to do + return acc, done, dirs_done + end + + for i=1, #paths do + local pathobj = paths[i] + local kind, path = pathobj[1], pathobj[2] + local attributes = pathobj.attributes + if attributes and attributes.prefix == "xdg" then + --- this prepends the xdg root (usually ~/.config) + path = filejoin(xdg_home, path) + end + + if kind == "dir" then + if stringsub(path, 1, 1) == "~" then + path = filejoin(home, stringsub(path, 2)) + end + --- We exclude paths with texmf in them, as they should be + --- found anyway; also duplicates are ignored by checking + --- if they are elements of dirs_done. + --- + --- FIXME does this mean we cannot access paths from + --- distributions (e.g. Context minimals) installed + --- separately? + if not (stringfind(path, "texmf") or dirs_done[path]) then + acc[#acc+1] = path + dirs_done[path] = true + end + + elseif kind == "include" then + --- here the path can be four things: a directory or a file, + --- in absolute or relative path. + if stringsub(path, 1, 1) == "~" then + path = filejoin(home, stringsub(path, 2)) + elseif --- if the path is relative, we make it absolute + not ( lfsisfile(path) or lfsisdir(path) ) + then + path = filejoin(filedirname(start), path) + end + if lfsisfile(path) + and kpsereadable_file(path) + and not done[path] + then + --- we exclude path with texmf in them, as they should + --- be found otherwise + acc = read_fonts_conf_indeed( + path, home, xdg_home, + acc, done, dirs_done) + elseif lfsisdir(path) then --- arrow code ahead + local config_files = find_files (path, conf_filter) + for _, filename in next, config_files do + if not done[filename] then + acc = read_fonts_conf_indeed( + filename, home, xdg_home, + acc, done, dirs_done) + end + end + end --- match “kind” + end --- iterate paths + end + + --inspect(acc) + --inspect(done) + return acc, done, dirs_done + end --- read_fonts_conf_indeed() + +--[[doc-- + read_fonts_conf() sets up an accumulator and two sets + for tracking what’s been done. + + Also, the environment variables HOME and XDG_CONFIG_HOME -- + which are constants anyways -- are expanded so don’t have to + repeat that over and over again as with the old parser. + Now they’re just passed on to every call of + read_fonts_conf_indeed(). + + read_fonts_conf() is also the only reference visible outside + the closure. +--doc]]-- + +--- list -> (string -> function option -> string list) -> list + +local read_fonts_conf = function (path_list, find_files) + local home = kpseexpand_path"~" --- could be os.getenv"HOME" + local xdg_home = kpseexpand_path"$XDG_CONFIG_HOME" + if xdg_home == "" then xdg_home = filejoin(home, ".config") end + local acc = { } ---> list: paths collected + local done = { } ---> set: files inspected + local dirs_done = { } ---> set: dirs in list + for i=1, #path_list do --- we keep the state between files + acc, done, dirs_done = read_fonts_conf_indeed( + path_list[i], home, xdg_home, + acc, done, dirs_done, + find_files) + end + return acc +end + +luaotfload.parsers.read_fonts_conf = read_fonts_conf -- cgit v1.2.3 From cbf1a28451ad42a7f8c2a9256810868f378e4631 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 14 Jan 2014 13:21:20 +0100 Subject: [db,tool,diagnose,parsers] move miscellaneous patterns into luaotfload-parsers --- luaotfload-database.lua | 17 ++++------------- luaotfload-diagnostics.lua | 8 ++++---- luaotfload-parsers.lua | 20 ++++++++++++++++++++ luaotfload-tool.lua | 4 ++-- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 32197cc..762e853 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -44,7 +44,10 @@ local P, R, S, lpegmatch local C, Cc, Cf, Cg, Cs, Ct = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cs, lpeg.Ct -local read_fonts_conf = luaotfload.parsers.read_fonts_conf +local parsers = luaotfload.parsers +local read_fonts_conf = parsers.read_fonts_conf +local stripslashes = parsers.stripslashes +local splitcomma = parsers.splitcomma --- Luatex builtins local load = load @@ -183,18 +186,6 @@ if not luaotfloadconfig.termwidth then luaotfloadconfig.termwidth = tw end -names.patterns = { } -local patterns = names.patterns - -local trailingslashes = P"/"^1 * P(-1) -local stripslashes = C((1 - trailingslashes)^0) -patterns.stripslashes = stripslashes - -local comma = P"," -local noncomma = 1-comma -local splitcomma = Ct((C(noncomma^1) + comma)^1) -patterns.splitcomma = splitcomma - local format_precedence = { "otf", "ttc", "ttf", "dfont", "afm", "pfb", diff --git a/luaotfload-diagnostics.lua b/luaotfload-diagnostics.lua index 1f29e3d..1ae9a90 100644 --- a/luaotfload-diagnostics.lua +++ b/luaotfload-diagnostics.lua @@ -53,6 +53,10 @@ local out = function (...) logs.names_report (false, 0, "diagnose", ...) end +local parsers = luaotfload.parsers +local stripslashes = parsers.stripslashes +local splitcomma = parsers.splitcomma + local check_index = function (errcnt) out "================= font names ==================" @@ -163,8 +167,6 @@ local analyze_permissions = function (raw) return lpegmatch (p_permissions, raw) end -local stripslashes = names.patterns.stripslashes - local get_permissions = function (t, location) if stringsub (location, #location) == "/" then --- strip trailing slashes (lfs idiosyncrasy on Win) @@ -605,8 +607,6 @@ local anamneses = { "permissions" } -local splitcomma = names.patterns.splitcomma - local diagnose = function (job) local errcnt = 0 local asked = job.asked_diagnostics diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua index 89e3bc9..0789c42 100644 --- a/luaotfload-parsers.lua +++ b/luaotfload-parsers.lua @@ -49,6 +49,10 @@ local lfs = lfs local lfsisfile = lfs.isfile local lfsisdir = lfs.isdir +------------------------------------------------------------------------------- +--- FONTCONFIG +------------------------------------------------------------------------------- + --[[doc-- For fonts installed on the operating system, there are several @@ -296,3 +300,19 @@ end luaotfload.parsers.read_fonts_conf = read_fonts_conf + +------------------------------------------------------------------------------- +--- MISC PARSERS +------------------------------------------------------------------------------- + + +local trailingslashes = P"/"^1 * P(-1) +local stripslashes = C((1 - trailingslashes)^0) +parsers.stripslashes = stripslashes + +local comma = P"," +local noncomma = 1-comma +local splitcomma = Ct((C(noncomma^1) + comma)^1) +parsers.splitcomma = splitcomma + + diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 18e9f9e..32086e1 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -6,7 +6,7 @@ -- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang -- VERSION: 2.5 -- LICENSE: GPL v2.0 --- MODIFIED: 2014-01-02 21:21:10+0100 +-- MODIFIED: 2014-01-14 13:17:04+0100 ----------------------------------------------------------------------- local version = "2.5" --- . @@ -950,7 +950,7 @@ set_primary_field = function (fields, addme, acc, n) return acc end -local splitcomma = names.patterns.splitcomma +local splitcomma = luaotfload.parsers.splitcomma actions.list = function (job) local criterion = job.criterion -- cgit v1.2.3 From b7a935f81e6d80ff31f92bb75cdf175029524fea Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 14 Jan 2014 13:51:56 +0100 Subject: [db] strip redundand locals --- luaotfload-database.lua | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 762e853..3f321d2 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -36,13 +36,8 @@ if not modules then modules = { } end modules ['luaotfload-database'] = { --doc]]-- -local lpeg = require "lpeg" - -local P, R, S, lpegmatch - = lpeg.P, lpeg.R, lpeg.S, lpeg.match - -local C, Cc, Cf, Cg, Cs, Ct - = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cs, lpeg.Ct +local lpeg = require "lpeg" +local P, Cc, lpegmatch = lpeg.P, lpeg.Cc, lpeg.match local parsers = luaotfload.parsers local read_fonts_conf = parsers.read_fonts_conf @@ -65,7 +60,6 @@ local iolines = io.lines local ioopen = io.open local iopopen = io.popen local kpseexpand_path = kpse.expand_path -local kpseexpand_var = kpse.expand_var local kpsefind_file = kpse.find_file local kpselookup = kpse.lookup local kpsereadable_file = kpse.readable_file -- cgit v1.2.3 From 9417b2566c7e0ed40fbf216711c45d5e0be36fb7 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 14 Jan 2014 14:15:51 +0100 Subject: [features,parsers] move font request handler into luaotfload-parsers.lua --- luaotfload-features.lua | 260 ++--------------------------------------------- luaotfload-parsers.lua | 264 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 268 insertions(+), 256 deletions(-) diff --git a/luaotfload-features.lua b/luaotfload-features.lua index dc6b8a4..d786549 100644 --- a/luaotfload-features.lua +++ b/luaotfload-features.lua @@ -9,7 +9,12 @@ if not modules then modules = { } end modules ["features"] = { local type, next = type, next local tonumber = tonumber local tostring = tostring + +local lpeg = require "lpeg" local lpegmatch = lpeg.match +local P = lpeg.P +local R = lpeg.R +local C = lpeg.C ---[[ begin included font-ltx.lua ]] --- this appears to be based in part on luatex-fonts-def.lua @@ -822,259 +827,6 @@ local set_default_features = function (speclist) return speclist end ------------------------------------------------------------------------ ---- request syntax parser 2.2 ------------------------------------------------------------------------ ---- the luaotfload font request syntax (see manual) ---- has a canonical form: ---- ---- \font=:: ---- ---- where ---- is the control sequence that activates the font ---- is either “file” or “name”, determining the lookup ---- is either a file name (no path) or a font ---- name, depending on the lookup ---- is a list of switches or options, separated by ---- semicolons or commas; a switch is of the form “+” foo ---- or “-” foo, options are of the form lhs “=” rhs ---- ---- however, to ensure backward compatibility we also have ---- support for Xetex-style requests. ---- ---- for the Xetex emulation see: ---- · The XeTeX Reference Guide by Will Robertson, 2011 ---- · The XeTeX Companion by Michel Goosens, 2010 ---- · About XeTeX by Jonathan Kew, 2005 ---- ---- ---- caueat emptor. ---- the request is parsed into one of **four** different ---- lookup categories: the regular ones, file and name, ---- as well as the Xetex compatibility ones, path and anon. ---- (maybe a better choice of identifier would be “ambig”.) ---- ---- according to my reconstruction, the correct chaining ---- of the lookups for each category is as follows: ---- ---- | File -> ( db/filename lookup ) ---- ---- | Name -> ( db/name lookup, ---- db/filename lookup ) ---- ---- | Path -> ( db/filename lookup, ---- fullpath lookup ) ---- ---- | Anon -> ( kpse.find_file(), // <- for tfm, ofm ---- db/name lookup, ---- db/filename lookup, ---- fullpath lookup ) ---- ---- caching of successful lookups is essential. we now ---- as of v2.2 have an experimental lookup cache that is ---- stored in a separate file. it pertains only to name: ---- lookups, and is described in more detail in ---- luaotfload-database.lua. ---- ------------------------------------------------------------------------ - ---[[doc-- - - One further incompatibility between Xetex and Luatex-Fonts consists - in their option list syntax: apparently, Xetex requires key-value - options to be prefixed by a "+" (ascii “plus”) character. We - silently accept this as well, dropping the first byte if it is a - plus or minus character. - - Reference: https://github.com/lualatex/luaotfload/issues/79#issuecomment-18104483 - ---doc]]-- - -local handle_normal_option = function (key, val) - val = stringlower(val) - --- the former “toboolean()” handler - if val == "true" then - val = true - elseif val == "false" then - val = false - end - return key, val -end - ---[[doc-- - - Xetex style indexing begins at zero which we just increment before - passing it along to the font loader. Ymmv. - ---doc]]-- - -local handle_xetex_option = function (key, val) - val = stringlower(val) - local numeric = tonumber(val) --- decimal only; keeps colors intact - if numeric then --- ugh - if mathceil(numeric) == numeric then -- integer, possible index - val = tostring(numeric + 1) - end - elseif val == "true" then - val = true - elseif val == "false" then - val = false - end - return key, val -end - ---[[doc-- - - Instead of silently ignoring invalid options we emit a warning to - the log. - - Note that we have to return a pair to please rawset(). This creates - an entry on the resulting features hash which will later be removed - during set_default_features(). - ---doc]]-- - -local handle_invalid_option = function (opt) - report("log", 0, "load", "font option %q unknown.", opt) - return "", false -end - ---[[doc-- - - Dirty test if a file: request is actually a path: lookup; don’t - ask! Note this fails on Windows-style absolute paths. These will - *really* have to use the correct request. - ---doc]]-- - -local check_garbage = function (_,i, garbage) - if stringfind(garbage, "/") then - report("log", 0, "load", --- ffs use path! - "warning: path in file: lookups is deprecated; ") - report("log", 0, "load", "use bracket syntax instead!") - report("log", 0, "load", - "position: %d; full match: %q", - i, garbage) - return true - end - return false -end - -local lpegmatch = lpeg.match -local P, S, R = lpeg.P, lpeg.S, lpeg.R -local C, Cc, Cf, Cg, Cmt, Cs, Ct - = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cs, lpeg.Ct - ---- terminals and low-level classes ----------------------------------- ---- note we could use the predefined ones from lpeg.patterns -local dot = P"." -local colon = P":" -local featuresep = S",;" -local slash = P"/" -local equals = P"=" -local lbrk, rbrk = P"[", P"]" - -local spacing = S" \t\v" -local ws = spacing^0 - -local digit = R"09" -local alpha = R("az", "AZ") -local anum = alpha + digit -local decimal = digit^1 * (dot * digit^0)^-1 - ---- modifiers --------------------------------------------------------- ---[[doc-- - The slash notation: called “modifiers” (Kew) or “font options” - (Robertson, Goosens) - we only support the shorthands for italic / bold / bold italic - shapes, as well as setting optical size, the rest is ignored. ---doc]]-- -local style_modifier = (P"BI" + P"IB" + P"bi" + P"ib" + S"biBI") - / stringlower -local size_modifier = S"Ss" * P"=" --- optical size - * Cc"optsize" * C(decimal) -local other_modifier = P"AAT" + P"aat" --- apple stuff; unsupported - + P"ICU" + P"icu" --- not applicable - + P"GR" + P"gr" --- sil stuff; unsupported -local garbage_modifier = ((1 - colon - slash)^0 * Cc(false)) -local modifier = slash * (other_modifier --> ignore - + Cs(style_modifier) --> collect - + Ct(size_modifier) --> collect - + garbage_modifier) --> warn -local modifier_list = Cg(Ct(modifier^0), "modifiers") - ---- lookups ----------------------------------------------------------- -local fontname = C((1-S":(/")^1) --- like luatex-fonts -local unsupported = Cmt((1-S":(")^1, check_garbage) -local prefixed = P"name:" * ws * Cg(fontname, "name") ---- initially we intended file: to emulate the behavior of ---- luatex-fonts, i.e. no paths allowed. after all, we do have XeTeX ---- emulation with the path lookup and it interferes with db lookups. ---- turns out fontspec and other widely used packages rely on file: ---- with paths already, so we’ll add a less strict rule here. anyways, ---- we’ll emit a warning. - + P"file:" * ws * Cg(unsupported, "path") - + P"file:" * ws * Cg(fontname, "file") ---- EXPERIMENTAL: kpse lookup - + P"kpse:" * ws * Cg(fontname, "kpse") ---- EXPERIMENTAL: custom lookup - + P"my:" * ws * Cg(fontname, "my") -local unprefixed = Cg(fontname, "anon") -local path_lookup = lbrk * Cg(C((1-rbrk)^1), "path") * rbrk - ---- features ---------------------------------------------------------- -local field_char = anum + S"+-." --- sic! -local field = field_char^1 ---- assignments are “lhs=rhs” ---- or “+lhs=rhs” (Xetex-style) ---- switches are “+key” | “-key” -local normal_option = C(field) * ws * equals * ws * C(field) * ws -local xetex_option = P"+" * ws * normal_option -local ignore_option = (1 - equals - featuresep)^1 - * equals - * (1 - featuresep)^1 -local assignment = xetex_option / handle_xetex_option - + normal_option / handle_normal_option - + ignore_option / handle_invalid_option -local switch = P"+" * ws * C(field) * Cc(true) - + P"-" * ws * C(field) * Cc(false) - + C(field) * Cc(true) --- default -local feature_expr = ws * Cg(assignment + switch) * ws -local option = feature_expr -local feature_list = Cf(Ct"" - * option - * (featuresep * option^-1)^0 - , rawset) - * featuresep^-1 - ---- other ------------------------------------------------------------- ---- This rule is present in the original parser. It sets the “sub” ---- field of the specification which allows addressing a specific ---- font inside a TTC container. Neither in Luatex-Fonts nor in ---- Luaotfload is this documented, so we might as well silently drop ---- it. However, as backward compatibility is one of our prime goals we ---- just insert it here and leave it undocumented until someone cares ---- to ask. (Note: afair subfonts are numbered, but this rule matches a ---- string; I won’t mess with it though until someone reports a ---- problem.) ---- local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim ---- Note to self: subfonts apparently start at index 0. Tested with ---- Cambria.ttc that includes “Cambria Math” at 0 and “Cambria” at 1. ---- Other values cause luatex to segfault. -local subfont = P"(" * Cg((1 - S"()")^1, "sub") * P")" ---- top-level rules --------------------------------------------------- ---- \font\foo=: -local features = Cg(feature_list, "features") -local specification = (prefixed + unprefixed) - * subfont^-1 - * modifier_list^-1 -local font_request = Ct(path_lookup * (colon^-1 * features)^-1 - + specification * (colon * features)^-1) - --- lpeg.print(font_request) ---- new parser: 657 rules ---- old parser: 230 rules - local import_values = { --- That’s what the 1.x parser did, not quite as graciously, --- with an array of branch expressions. @@ -1138,7 +890,7 @@ end --- spec -> spec local handle_request = function (specification) - local request = lpegmatch(font_request, + local request = lpegmatch(luaotfload.parsers.font_request, specification.specification) if not request then --- happens when called with an absolute path diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua index 0789c42..a989722 100644 --- a/luaotfload-parsers.lua +++ b/luaotfload-parsers.lua @@ -1,12 +1,12 @@ #!/usr/bin/env texlua ------------------------------------------------------------------------ +------------------------------------------------------------------------------- -- FILE: luaotfload-parsers.lua -- DESCRIPTION: various lpeg-based parsers used in Luaotfload -- REQUIREMENTS: Luaotfload > 2.4 -- AUTHOR: Philipp Gesang (Phg), -- VERSION: same as Luaotfload -- CREATED: 2014-01-14 10:15:20+0100 ------------------------------------------------------------------------ +------------------------------------------------------------------------------- -- if not modules then modules = { } end modules ['luaotfload-parsers'] = { @@ -44,6 +44,7 @@ local report = logs.report local string = string local stringsub = string.sub local stringfind = string.find +local stringlower = string.lower local lfs = lfs local lfsisfile = lfs.isfile @@ -316,3 +317,262 @@ local splitcomma = Ct((C(noncomma^1) + comma)^1) parsers.splitcomma = splitcomma + +------------------------------------------------------------------------------- +--- FONT REQUEST +------------------------------------------------------------------------------- + + +--[[doc------------------------------------------------------------------------ + + The luaotfload font request syntax (see manual) + has a canonical form: + + \font=:: + + where + is the control sequence that activates the font + is either “file” or “name”, determining the lookup + is either a file name (no path) or a font + name, depending on the lookup + is a list of switches or options, separated by + semicolons or commas; a switch is of the form “+” foo + or “-” foo, options are of the form lhs “=” rhs + + however, to ensure backward compatibility we also have + support for Xetex-style requests. + + for the Xetex emulation see: + · The XeTeX Reference Guide by Will Robertson, 2011 + · The XeTeX Companion by Michel Goosens, 2010 + · About XeTeX by Jonathan Kew, 2005 + + + caueat emptor. + + the request is parsed into one of **four** different lookup + categories: the regular ones, file and name, as well as the + Xetex compatibility ones, path and anon. (maybe a better choice + of identifier would be “ambig”.) + + according to my reconstruction, the correct chaining of the + lookups for each category is as follows: + + | File -> ( db/filename lookup ) + + | Name -> ( db/name lookup, + db/filename lookup ) + + | Path -> ( db/filename lookup, + fullpath lookup ) + + | Anon -> ( kpse.find_file(), // <- for tfm, ofm + db/name lookup, + db/filename lookup, + fullpath lookup ) + + caching of successful lookups is essential. we now as of v2.2 + have a lookup cache that is stored in a separate file. it + pertains only to name: lookups, and is described in more detail + in luaotfload-database.lua. + +------------------------------------------------------------------------------- + + One further incompatibility between Xetex and Luatex-Fonts consists + in their option list syntax: apparently, Xetex requires key-value + options to be prefixed by a "+" (ascii “plus”) character. We + silently accept this as well, dropping the first byte if it is a + plus or minus character. + + Reference: https://github.com/lualatex/luaotfload/issues/79#issuecomment-18104483 + +--doc]]------------------------------------------------------------------------ + + +local handle_normal_option = function (key, val) + val = stringlower(val) + --- the former “toboolean()” handler + if val == "true" then + val = true + elseif val == "false" then + val = false + end + return key, val +end + +--[[doc-- + + Xetex style indexing begins at zero which we just increment before + passing it along to the font loader. Ymmv. + +--doc]]-- + +local handle_xetex_option = function (key, val) + val = stringlower(val) + local numeric = tonumber(val) --- decimal only; keeps colors intact + if numeric then --- ugh + if mathceil(numeric) == numeric then -- integer, possible index + val = tostring(numeric + 1) + end + elseif val == "true" then + val = true + elseif val == "false" then + val = false + end + return key, val +end + +--[[doc-- + + Instead of silently ignoring invalid options we emit a warning to + the log. + + Note that we have to return a pair to please rawset(). This creates + an entry on the resulting features hash which will later be removed + during set_default_features(). + +--doc]]-- + +local handle_invalid_option = function (opt) + report("log", 0, "load", "font option %q unknown.", opt) + return "", false +end + +--[[doc-- + + Dirty test if a file: request is actually a path: lookup; don’t + ask! Note this fails on Windows-style absolute paths. These will + *really* have to use the correct request. + +--doc]]-- + +local check_garbage = function (_,i, garbage) + if stringfind(garbage, "/") then + report("log", 0, "load", --- ffs use path! + "warning: path in file: lookups is deprecated; ") + report("log", 0, "load", "use bracket syntax instead!") + report("log", 0, "load", + "position: %d; full match: %q", + i, garbage) + return true + end + return false +end + +local lpegmatch = lpeg.match +local P, S, R = lpeg.P, lpeg.S, lpeg.R +local C, Cc, Cf, Cg, Cmt, Cs, Ct + = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cs, lpeg.Ct + +--- terminals and low-level classes ----------------------------------- +--- note we could use the predefined ones from lpeg.patterns +local dot = P"." +local colon = P":" +local featuresep = S",;" +local slash = P"/" +local equals = P"=" +local lbrk, rbrk = P"[", P"]" + +local spacing = S" \t\v" +local ws = spacing^0 + +local digit = R"09" +local alpha = R("az", "AZ") +local anum = alpha + digit +local decimal = digit^1 * (dot * digit^0)^-1 + +--- modifiers --------------------------------------------------------- +--[[doc-- + The slash notation: called “modifiers” (Kew) or “font options” + (Robertson, Goosens) + we only support the shorthands for italic / bold / bold italic + shapes, as well as setting optical size, the rest is ignored. +--doc]]-- +local style_modifier = (P"BI" + P"IB" + P"bi" + P"ib" + S"biBI") + / stringlower +local size_modifier = S"Ss" * P"=" --- optical size + * Cc"optsize" * C(decimal) +local other_modifier = P"AAT" + P"aat" --- apple stuff; unsupported + + P"ICU" + P"icu" --- not applicable + + P"GR" + P"gr" --- sil stuff; unsupported +local garbage_modifier = ((1 - colon - slash)^0 * Cc(false)) +local modifier = slash * (other_modifier --> ignore + + Cs(style_modifier) --> collect + + Ct(size_modifier) --> collect + + garbage_modifier) --> warn +local modifier_list = Cg(Ct(modifier^0), "modifiers") + +--- lookups ----------------------------------------------------------- +local fontname = C((1-S":(/")^1) --- like luatex-fonts +local unsupported = Cmt((1-S":(")^1, check_garbage) +local prefixed = P"name:" * ws * Cg(fontname, "name") +--- initially we intended file: to emulate the behavior of +--- luatex-fonts, i.e. no paths allowed. after all, we do have XeTeX +--- emulation with the path lookup and it interferes with db lookups. +--- turns out fontspec and other widely used packages rely on file: +--- with paths already, so we’ll add a less strict rule here. anyways, +--- we’ll emit a warning. + + P"file:" * ws * Cg(unsupported, "path") + + P"file:" * ws * Cg(fontname, "file") +--- EXPERIMENTAL: kpse lookup + + P"kpse:" * ws * Cg(fontname, "kpse") +--- EXPERIMENTAL: custom lookup + + P"my:" * ws * Cg(fontname, "my") +local unprefixed = Cg(fontname, "anon") +local path_lookup = lbrk * Cg(C((1-rbrk)^1), "path") * rbrk + +--- features ---------------------------------------------------------- +local field_char = anum + S"+-." --- sic! +local field = field_char^1 +--- assignments are “lhs=rhs” +--- or “+lhs=rhs” (Xetex-style) +--- switches are “+key” | “-key” +local normal_option = C(field) * ws * equals * ws * C(field) * ws +local xetex_option = P"+" * ws * normal_option +local ignore_option = (1 - equals - featuresep)^1 + * equals + * (1 - featuresep)^1 +local assignment = xetex_option / handle_xetex_option + + normal_option / handle_normal_option + + ignore_option / handle_invalid_option +local switch = P"+" * ws * C(field) * Cc(true) + + P"-" * ws * C(field) * Cc(false) + + C(field) * Cc(true) --- default +local feature_expr = ws * Cg(assignment + switch) * ws +local option = feature_expr +local feature_list = Cf(Ct"" + * option + * (featuresep * option^-1)^0 + , rawset) + * featuresep^-1 + +--- other ------------------------------------------------------------- +--- This rule is present in the original parser. It sets the “sub” +--- field of the specification which allows addressing a specific +--- font inside a TTC container. Neither in Luatex-Fonts nor in +--- Luaotfload is this documented, so we might as well silently drop +--- it. However, as backward compatibility is one of our prime goals we +--- just insert it here and leave it undocumented until someone cares +--- to ask. (Note: afair subfonts are numbered, but this rule matches a +--- string; I won’t mess with it though until someone reports a +--- problem.) +--- local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim +--- Note to self: subfonts apparently start at index 0. Tested with +--- Cambria.ttc that includes “Cambria Math” at 0 and “Cambria” at 1. +--- Other values cause luatex to segfault. +local subfont = P"(" * Cg((1 - S"()")^1, "sub") * P")" +--- top-level rules --------------------------------------------------- +--- \font\foo=: +local features = Cg(feature_list, "features") +local specification = (prefixed + unprefixed) + * subfont^-1 + * modifier_list^-1 +local font_request = Ct(path_lookup * (colon^-1 * features)^-1 + + specification * (colon * features)^-1) + +-- lpeg.print(font_request) +--- v2.5 parser: 1065 rules +--- v1.2 parser: 230 rules + +luaotfload.parsers.font_request = font_request + -- cgit v1.2.3 From 17fdb59546dda7c2294f2445c5e9a9402a3bfead Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 14 Jan 2014 14:25:55 +0100 Subject: [parsers] group definitions of terminals --- luaotfload-parsers.lua | 75 ++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua index a989722..e6db21f 100644 --- a/luaotfload-parsers.lua +++ b/luaotfload-parsers.lua @@ -25,7 +25,7 @@ local lpeg = require "lpeg" local P, R, S = lpeg.P, lpeg.R, lpeg.S local lpegmatch = lpeg.match local C, Cc, Cf = lpeg.C, lpeg.Cc, lpeg.Cf -local Cg, Cs, Ct = lpeg.Cg, lpeg.Cs, lpeg.Ct +local Cg, Cmt, Cs, Ct = lpeg.Cg, lpeg.Cmt, lpeg.Cs, lpeg.Ct local kpse = kpse local kpseexpand_path = kpse.expand_path @@ -50,6 +50,29 @@ local lfs = lfs local lfsisfile = lfs.isfile local lfsisdir = lfs.isdir +------------------------------------------------------------------------------- +--- COMMON PATTERNS +------------------------------------------------------------------------------- + +local dot = P"." +local colon = P":" +local comma = P"," +local noncomma = 1 - comma +local slash = P"/" +local equals = P"=" +local lbrk, rbrk = P"[", P"]" + +local spacing = S" \t\v" +local linebreak = S"\n\r" +local whitespace = spacing + linebreak +local ws = spacing^0 +local xmlws = whitespace^1 + +local digit = R"09" +local alpha = R("az", "AZ") +local anum = alpha + digit +local decimal = digit^1 * (dot * digit^0)^-1 + ------------------------------------------------------------------------------- --- FONTCONFIG ------------------------------------------------------------------------------- @@ -80,31 +103,27 @@ local lfsisdir = lfs.isdir --doc]]-- -local alpha = R("az", "AZ") -local digit = R"09" local tag_name = C(alpha^1) -local whitespace = S" \n\r\t\v" -local ws = whitespace^1 local comment = P"" ---> header specifica local xml_declaration = P"")^0 * P"?>" -local xml_doctype = P"")^0 * P">" local header = xml_declaration^-1 - * (xml_doctype + comment + ws)^0 + * (xml_doctype + comment + xmlws)^0 ---> enforce root node -local root_start = P"<" * ws^-1 * P"fontconfig" * ws^-1 * P">" -local root_stop = P"" +local root_start = P"<" * xmlws^-1 * P"fontconfig" * xmlws^-1 * P">" +local root_stop = P"" local dquote, squote = P[["]], P"'" local xml_namestartchar = S":_" + alpha --- ascii only, funk the rest local xml_namechar = S":._" + alpha + digit -local xml_name = ws^-1 +local xml_name = xmlws^-1 * C(xml_namestartchar * xml_namechar^0) -local xml_attvalue = dquote * C((1 - S[[%&"]])^1) * dquote * ws^-1 - + squote * C((1 - S[[%&']])^1) * squote * ws^-1 +local xml_attvalue = dquote * C((1 - S[[%&"]])^1) * dquote * xmlws^-1 + + squote * C((1 - S[[%&']])^1) * squote * xmlws^-1 local xml_attr = Cg(xml_name * P"=" * xml_attvalue) local xml_attr_list = Cf(Ct"" * xml_attr^1, rawset) @@ -118,11 +137,11 @@ local scan_node = function (tag) local p_tag = P(tag) local with_attributes = P"<" * p_tag * Cg(xml_attr_list, "attributes")^-1 - * ws^-1 + * xmlws^-1 * P">" - local plain = P"<" * p_tag * ws^-1 * P">" + local plain = P"<" * p_tag * xmlws^-1 * P">" local node_start = plain + with_attributes - local node_stop = P"" + local node_stop = P"" --- there is no nesting, the earth is flat ... local node = node_start * Cc(tag) * C(comment + (1 - node_stop)^1) @@ -307,12 +326,10 @@ luaotfload.parsers.read_fonts_conf = read_fonts_conf ------------------------------------------------------------------------------- -local trailingslashes = P"/"^1 * P(-1) +local trailingslashes = slash^1 * P(-1) local stripslashes = C((1 - trailingslashes)^0) parsers.stripslashes = stripslashes -local comma = P"," -local noncomma = 1-comma local splitcomma = Ct((C(noncomma^1) + comma)^1) parsers.splitcomma = splitcomma @@ -459,27 +476,7 @@ local check_garbage = function (_,i, garbage) return false end -local lpegmatch = lpeg.match -local P, S, R = lpeg.P, lpeg.S, lpeg.R -local C, Cc, Cf, Cg, Cmt, Cs, Ct - = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cs, lpeg.Ct - ---- terminals and low-level classes ----------------------------------- ---- note we could use the predefined ones from lpeg.patterns -local dot = P"." -local colon = P":" -local featuresep = S",;" -local slash = P"/" -local equals = P"=" -local lbrk, rbrk = P"[", P"]" - -local spacing = S" \t\v" -local ws = spacing^0 - -local digit = R"09" -local alpha = R("az", "AZ") -local anum = alpha + digit -local decimal = digit^1 * (dot * digit^0)^-1 +local featuresep = comma --- modifiers --------------------------------------------------------- --[[doc-- -- cgit v1.2.3 From 88c57cf65cb469a1328309a2f773d3d4bfc31715 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 14 Jan 2014 14:51:14 +0100 Subject: [status,doc,graph] ad references to luaotfload-parsers.lua --- filegraph.dot | 4 ++-- luaotfload.dtx | 1 + mkstatus | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/filegraph.dot b/filegraph.dot index 1228690..bce15cc 100644 --- a/filegraph.dot +++ b/filegraph.dot @@ -200,8 +200,8 @@ strict digraph luaotfload_files { //looks weird with circo ... - - + +
Luaotfload Libraries
luaotfload-auxiliary.lua luaotfload-features.lua
luaotfload-override.lua luaotfload-loaders.lua
luaotfload-database.lua luaotfload-color.lua
luaotfload-letterspace.lua
luaotfload-parsers.lua luaotfload-database.lua
luaotfload-color.lua luaotfload-letterspace.lua
>, ] diff --git a/luaotfload.dtx b/luaotfload.dtx index 366cb87..02ff0bb 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1264,6 +1264,7 @@ and the derived files % font reader as handler for % Postscript fonts % (\abbrev{pfa}, \abbrev{pfb}). +% \ouritem {luaotfload-parsers.lua} various \abbrev{lpeg}-based parsers. % \ouritem {luaotfload-database.lua} font names database. % \ouritem {luaotfload-colors.lua} color handling. % \ouritem {luaotfload-auxiliary.lua} access to internal functionality diff --git a/mkstatus b/mkstatus index 9940970..649d26b 100755 --- a/mkstatus +++ b/mkstatus @@ -53,6 +53,7 @@ local names = { "luaotfload.lua", "luaotfload-fontloader.lua", "luaotfload-override.lua", + "luaotfload-parsers.lua", "luaotfload-tool.lua", "mkcharacters", "mkglyphlist", -- cgit v1.2.3 From 915aca566df7dc6357aa0bab05d995602b6e8eb4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 16 Jan 2014 07:12:23 +0100 Subject: [*] remove luaotfload.lua from dtx --- luaotfload.dtx | 678 +-------------------------------------------------------- 1 file changed, 8 insertions(+), 670 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 02ff0bb..8d59a65 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -12,7 +12,7 @@ % % This work consists of the main source file luaotfload.dtx % and the derived files -% luaotfload.sty, luaotfload.lua +% luaotfload.sty % % Unpacking: % tex luaotfload.dtx @@ -63,7 +63,7 @@ This work is under the GPL v2.0 license. This work consists of the main source file luaotfload.dtx and the derived files - luaotfload.sty, luaotfload.lua + luaotfload.sty \endpreamble @@ -74,23 +74,6 @@ and the derived files \file{luaotfload.sty}{\from{luaotfload.dtx}{package}}% } -% The following hacks are to generate a lua file with lua comments starting with -% -- instead of %% - -\def\MetaPrefix{-- } - -\def\luapostamble{% - \MetaPrefix^^J% - \MetaPrefix\space End of File `\outFileName'.% -} - -\def\currentpostamble{\luapostamble}% - -\generate{% - \usedir{tex/luatex/luaotfload}% - \file{luaotfload.lua}{\from{luaotfload.dtx}{lua}}%% -} - \obeyspaces \Msg{************************************************************************} \Msg{*} @@ -1566,657 +1549,12 @@ and the derived files % % \section{\fileent{luaotfload.lua}} % -% This file initializes the system and loads the font loader. -% To minimize potential conflicts between other packages and the -% code imported from \CONTEXT, several precautions are in order. -% Some of the functionality that the font loader expects to be present, -% like raw access to callbacks, are assumed to have been disabled by -% \identifier{luatexbase} when this file is processed. -% In some cases it is possible to trick it by putting dummies into -% place and restoring the behavior from \identifier{luatexbase} after -% initilization. -% Other cases such as attribute allocation require that we hook the -% functionality from \identifier{luatexbase} into locations where they -% normally wouldn’t be. -% -% Anyways we can import the code base without modifications, which is -% due mostly to the extra effort by -% Hans Hagen to make \LUATEX-Fonts self-contained and encapsulate it, -% and especially due to his willingness to incorporate our suggestions. -% -% \iffalse -%<*lua> -% \fi -% \begin{macrocode} -luaotfload = luaotfload or {} -local luaotfload = luaotfload - -config = config or { } -config.luaotfload = config.luaotfload or { } -------.luaotfload.resolver = config.luaotfload.resolver or "normal" -config.luaotfload.resolver = config.luaotfload.resolver or "cached" -config.luaotfload.definer = config.luaotfload.definer or "patch" -config.luaotfload.loglevel = config.luaotfload.loglevel or 2 -config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter" -config.luaotfload.prioritize = config.luaotfload.prioritize or "sys" -config.luaotfload.names_dir = config.luaotfload.names_dir or "names" -config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts" -config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua" -config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" -if not config.luaotfload.strip then - config.luaotfload.strip = true -end - -luaotfload.module = { - name = "luaotfload", - version = 2.50000, - date = "2014/**/**", - description = "OpenType layout system.", - author = "Elie Roux & Hans Hagen", - copyright = "Elie Roux", - license = "GPL v2.0" -} - -local luatexbase = luatexbase - -local setmetatable = setmetatable -local type, next = type, next - -local kpsefind_file = kpse.find_file -local lfsisfile = lfs.isfile - -local add_to_callback, create_callback = - luatexbase.add_to_callback, luatexbase.create_callback -local reset_callback, call_callback = - luatexbase.reset_callback, luatexbase.call_callback - -local dummy_function = function () end - -local error, warning, info, log = - luatexbase.provides_module(luaotfload.module) - -luaotfload.error = error -luaotfload.warning = warning -luaotfload.info = info -luaotfload.log = log - -% \end{macrocode} -% We set the minimum version requirement for \LUATEX to v0.76, -% because the font loader requires recent features like direct -% attribute indexing and \luafunction{node.end_of_math()} that aren’t -% available in earlier versions.\footnote{% -% See Taco’s announcement of v0.76: -% \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042} -% and this commit by Hans that introduced those features. -% \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}. -% } -% -% \begin{macrocode} - -local luatex_version = 76 - -if tex.luatexversion < luatex_version then - warning("LuaTeX v%.2f is old, v%.2f is recommended.", - tex.luatexversion/100, - luatex_version /100) - --- we install a fallback for older versions as a safety - if not node.end_of_math then - local math_t = node.id"math" - local traverse_nodes = node.traverse_id - node.end_of_math = function (n) - for n in traverse_nodes(math_t, n.next) do - return n - end - end - end -end - -% \end{macrocode} -% \subsection{Module loading} -% We load the files imported from \CONTEXT with this function. -% It automatically prepends the prefix \fileent{luaotfload-} to its -% argument, so we can refer to the files with their actual \CONTEXT name. -% -% \begin{macrocode} - -local fl_prefix = "luaotfload" -- “luatex” for luatex-plain -local loadmodule = function (name) - require(fl_prefix .."-"..name) -end - -% \end{macrocode} -% Before \TeX Live 2013 version, \LUATEX had a bug that made ofm fonts fail -% when called with their extension. There was a side-effect making ofm -% totally unloadable when luaotfload was present. The following lines are -% a patch for this bug. The utility of these lines is questionable as they -% are not necessary since \TeX Live 2013. They should be removed in the next -% version. -% -% \begin{macrocode} -local Cs, P, lpegmatch = lpeg.Cs, lpeg.P, lpeg.match - -local p_dot, p_slash = P".", P"/" -local p_suffix = (p_dot * (1 - p_dot - p_slash)^1 * P(-1)) / "" -local p_removesuffix = Cs((p_suffix + 1)^1) - -local find_vf_file = function (name) - local fullname = kpsefind_file(name, "ovf") - if not fullname then - --fullname = kpsefind_file(file.removesuffix(name), "ovf") - fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf") - end - if fullname then - log("loading virtual font file %s.", fullname) - end - return fullname -end - -% \end{macrocode} -% \subsection{Preparing the Font Loader} -% We treat the fontloader as a black box so behavior is consistent -% between formats. -% We do no longer run the intermediate wrapper file -% \fileent{luaotfload-fonts.lua} which we used to import from -% \href{http://standalone.contextgarden.net/current/context/experimental/tex/generic/context/luatex/}{\LUATEX-Plain}. -% Rather, we load the fontloader code directly in the same fashion as -% \identifier{luatex-fonts}. -% How this is executed depends on the presence on the \emphasis{merged -% font loader code}. -% In \identifier{luaotfload} this is contained in the file -% \fileent{luaotfload-merged.lua}. -% If this file cannot be found, the original libraries from \CONTEXT of -% which the merged code was composed are loaded instead. -% The imported font loader will call \luafunction{callback.register} once -% while reading \fileent{font-def.lua}. -% This is unavoidable unless we modify the imported files, but harmless -% if we make it call a dummy instead. -% However, this problem might vanish if we decide to do the merging -% ourselves, like the \identifier{lualibs} package does. -% With this step we would obtain the freedom to load our own overrides in -% the process right where they are needed, at the cost of losing -% encapsulation. -% The decision on how to progress is currently on indefinite hold. -% -% \begin{macrocode} - -local starttime = os.gettimeofday() - -local trapped_register = callback.register -callback.register = dummy_function - -% \end{macrocode} -% By default, the fontloader requires a number of \emphasis{private -% attributes} for internal use. -% These must be kept consistent with the attribute handling methods as -% provided by \identifier{luatexbase}. -% Our strategy is to override the function that allocates new attributes -% before we initialize the font loader, making it a wrapper around -% \luafunction{luatexbase.new_attribute}.\footnote{% -% Many thanks, again, to Hans Hagen for making this part -% configurable! -% } -% The attribute identifiers are prefixed “\fileent{luaotfload@}” to -% avoid name clashes. -% -% \begin{macrocode} - -do - local new_attribute = luatexbase.new_attribute - local the_attributes = luatexbase.attributes - - attributes = attributes or { } - - attributes.private = function (name) - local attr = "luaotfload@" .. name --- used to be: “otfl@” - local number = the_attributes[attr] - if not number then - number = new_attribute(attr) - end - return number - end -end - -% \end{macrocode} -% These next lines replicate the behavior of \fileent{luatex-fonts.lua}. -% -% \begin{macrocode} - -local context_environment = { } - -local push_namespaces = function () - log("push namespace for font loader") - local normalglobal = { } - for k, v in next, _G do - normalglobal[k] = v - end - return normalglobal -end - -local pop_namespaces = function (normalglobal, isolate) - if normalglobal then - local _G = _G - local mode = "non-destructive" - if isolate then mode = "destructive" end - log("pop namespace from font loader -- " .. mode) - for k, v in next, _G do - if not normalglobal[k] then - context_environment[k] = v - if isolate then - _G[k] = nil - end - end - end - for k, v in next, normalglobal do - _G[k] = v - end - -- just to be sure: - setmetatable(context_environment,_G) - else - log("irrecoverable error during pop_namespace: no globals to restore") - os.exit() - end -end - -luaotfload.context_environment = context_environment -luaotfload.push_namespaces = push_namespaces -luaotfload.pop_namespaces = pop_namespaces - -local our_environment = push_namespaces() - -% \end{macrocode} -% The font loader requires that the attribute with index zero be zero. -% We happily oblige. -% (Cf. \fileent{luatex-fonts-nod.lua}.) -% -% \begin{macrocode} - -tex.attribute[0] = 0 - -% \end{macrocode} -% Now that things are sorted out we can finally load the fontloader. -% -% \begin{macrocode} - -loadmodule"fontloader.lua" ----loadmodule"font-odv.lua" --- <= Devanagari support from Context - -if fonts then - - if not fonts._merge_loaded_message_done_ then - log [["I am using the merged version of 'luaotfload.lua' here.]] - log [[ If you run into problems or experience unexpected]] - log [[ behaviour, and if you have ConTeXt installed you can try]] - log [[ to delete the file 'luaotfload-merged.lua' as I might]] - log [[ then use the possibly updated libraries. The merged]] - log [[ version is not supported as it is a frozen instance.]] - log [[ Problems can be reported to the ConTeXt mailing list."]] - end - fonts._merge_loaded_message_done_ = true - -else--- the loading sequence is known to change, so this might have to - --- be updated with future updates! - --- do not modify it though unless there is a change to the merged - --- package! - loadmodule("l-lua.lua") - loadmodule("l-lpeg.lua") - loadmodule("l-function.lua") - loadmodule("l-string.lua") - loadmodule("l-table.lua") - loadmodule("l-io.lua") - loadmodule("l-file.lua") - loadmodule("l-boolean.lua") - loadmodule("l-math.lua") - loadmodule("util-str.lua") - loadmodule('luatex-basics-gen.lua') - loadmodule('data-con.lua') - loadmodule('luatex-basics-nod.lua') - loadmodule('font-ini.lua') - loadmodule('font-con.lua') - loadmodule('luatex-fonts-enc.lua') - loadmodule('font-cid.lua') - loadmodule('font-map.lua') - loadmodule('luatex-fonts-syn.lua') - loadmodule('luatex-fonts-tfm.lua') - loadmodule('font-oti.lua') - loadmodule('font-otf.lua') - loadmodule('font-otb.lua') - loadmodule('luatex-fonts-inj.lua') --> since 2014-01-07, replaces node-inj.lua - loadmodule('font-ota.lua') - loadmodule('luatex-fonts-otn.lua') --> since 2014-01-07, replaces font-otn.lua - loadmodule('font-otp.lua') --> since 2013-04-23 - loadmodule('luatex-fonts-lua.lua') - loadmodule('font-def.lua') - loadmodule('luatex-fonts-def.lua') - loadmodule('luatex-fonts-ext.lua') - loadmodule('luatex-fonts-cbk.lua') -end --- non-merge fallback scope - -% \end{macrocode} -% Here we adjust the globals created during font loader initialization. -% If the second argument to \luafunction{pop_namespaces()} is \verb|true| -% this will restore the state of \luafunction{_G}, eliminating every -% global generated since the last call to \luafunction{push_namespaces()}. -% At the moment we see no reason to do this, and since the font loader is -% considered an essential part of \identifier{luatex} as well as a very -% well organized piece of code, we happily concede it the right to add to -% \luafunction{_G} if needed. -% -% \begin{macrocode} - -pop_namespaces(our_environment, false)-- true) - -log("fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) - -% \end{macrocode} -% \subsection{Callbacks} -% After the fontloader is ready we can restore the callback trap from -% \identifier{luatexbase}. -% -% \begin{macrocode} - -callback.register = trapped_register - -% \end{macrocode} -% We do our own callback handling with the means provided by luatexbase. -% Note: \luafunction{pre_linebreak_filter} and \luafunction{hpack_filter} -% are coupled in \CONTEXT in the concept of \emphasis{node processor}. -% -% \begin{macrocode} - -add_to_callback("pre_linebreak_filter", - nodes.simple_font_handler, - "luaotfload.node_processor", - 1) -add_to_callback("hpack_filter", - nodes.simple_font_handler, - "luaotfload.node_processor", - 1) -add_to_callback("find_vf_file", - find_vf_file, "luaotfload.find_vf_file") - -loadmodule"override.lua" --- “luat-ovr” - -logs.set_loglevel(config.luaotfload.loglevel) - -% \end{macrocode} -% Now we load the modules written for \identifier{luaotfload}. -% -% \begin{macrocode} -loadmodule"parsers.lua" --- new in 2.5; fonts.conf and syntax -loadmodule"loaders.lua" --- “font-pfb” new in 2.0, added 2011 -loadmodule"database.lua" --- “font-nms” -loadmodule"colors.lua" --- “font-clr” - -% \end{macrocode} -% Relying on the \verb|name:| resolver for everything has been the source -% of permanent trouble with the database. -% With the introduction of the new syntax parser we now have enough -% granularity to distinguish between the \XETEX emulation layer and the -% genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts. -% Another benefit is that we can now easily plug in or replace new lookup -% behaviors if necessary. -% The name resolver remains untouched, but it calls -% \luafunction{fonts.names.resolve()} internally anyways (see -% \fileent{luaotfload-database.lua}). -% -% \begin{macrocode} - -local filesuffix = file.suffix -local fileremovesuffix = file.removesuffix -local request_resolvers = fonts.definers.resolvers -local formats = fonts.formats -local names = fonts.names -formats.ofm = "type1" - -fonts.encodings.known = fonts.encodings.known or { } - -% \end{macrocode} -% \identifier{luaotfload} promises easy access to system fonts. -% Without additional precautions, this cannot be achieved by -% \identifier{kpathsea} alone, because it searches only the -% \fileent{texmf} directories by default. -% Although it is possible for \identifier{kpathsea} to include extra -% paths by adding them to the \verb|OSFONTDIR| environment variable, -% this is still short of the goal »\emphasis{it just works!}«. -% When building the font database \identifier{luaotfload} scans -% system font directories anyways, so we already have all the -% information for looking sytem fonts. -% With the release version 2.2 the file names are indexed in the database -% as well and we are ready to resolve \verb|file:| lookups this way. -% Thus we no longer need to call the \identifier{kpathsea} library in -% most cases when looking up font files, only when generating the database, -% and when verifying the existence of a file in the \fileent{texmf} tree. -% -% \begin{macrocode} - -local resolve_file = names.crude_file_lookup ---local resolve_file = names.crude_file_lookup_verbose -local resolve_name = names.resolve_name - -local file_resolver = function (specification) - local name = resolve_file (specification.name) - local suffix = filesuffix(name) - if formats[suffix] then - specification.forced = suffix - specification.forcedname = file.removesuffix(name) - else - specification.name = name - end -end - -request_resolvers.file = file_resolver - -% \end{macrocode} -% We classify as \verb|anon:| those requests that have neither a -% prefix nor brackets. According to Khaled\footnote{% -% \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}. -% } -% they are the \XETEX equivalent of a \verb|name:| request, so we will be -% treating them as such. -% -% \begin{macrocode} - ---request_resolvers.anon = request_resolvers.name - -% \end{macrocode} -% There is one drawback, though. -% This syntax is also used for requesting fonts in \identifier{Type1} -% (\abbrev{tfm}, \abbrev{ofm}) format. -% These are essentially \verb|file:| lookups and must be caught before -% the \verb|name:| resolver kicks in, lest they cause the database to -% update. -% Even if we were to require the \verb|file:| prefix for all -% \identifier{Type1} requests, tests have shown that certain fonts still -% include further fonts (e.~g. \fileent{omlgcb.ofm} will ask for -% \fileent{omsecob.tfm}) \emphasis{using the old syntax}. -% For this reason, we introduce an extra check with an early return. -% -% \begin{macrocode} - -local type1_formats = { "tfm", "ofm", } - -request_resolvers.anon = function (specification) - local name = specification.name - for i=1, #type1_formats do - local format = type1_formats[i] - if resolvers.findfile(name, format) then - specification.forcedname = file.addsuffix(name, format) - specification.forced = format - return - end - end - --- under some weird circumstances absolute paths get - --- passed to the definer; we have to catch them - --- before the name: resolver misinterprets them. - name = specification.specification - local exists, _ = lfsisfile(name) - if exists then --- garbage; we do this because we are nice, - --- not because it is correct - logs.names_report("log", 1, "load", "file %q exists", name) - logs.names_report("log", 1, "load", - "... overriding borked anon: lookup with path: lookup") - specification.name = name - request_resolvers.path(specification) - return - end - request_resolvers.name(specification) -end - -% \end{macrocode} -% Prior to version 2.2, \identifier{luaotfload} did not distinguish -% \verb|file:| and \verb|path:| lookups, causing complications with the -% resolver. -% Now we test if the requested name is an absolute path in the file -% system, otherwise we fall back to the \verb|file:| lookup. -% -% \begin{macrocode} - -request_resolvers.path = function (specification) - local name = specification.name - local exists, _ = lfsisfile(name) - if not exists then -- resort to file: lookup - logs.names_report("log", 1, "load", - "path lookup of %q unsuccessful, falling back to file:", - name) - file_resolver (specification) - else - local suffix = filesuffix (name) - if formats[suffix] then - specification.forced = suffix - specification.name = file.removesuffix(name) - specification.forcedname = name - else - specification.name = name - end - end -end - -% \end{macrocode} -% {\bfseries EXPERIMENTAL}: -% \identifier{kpse}-only resolver, for those who can do without system -% fonts. -% -% \begin{macrocode} - -request_resolvers.kpse = function (specification) - local name = specification.name - local suffix = filesuffix(name) - if suffix and formats[suffix] then - name = file.removesuffix(name) - if resolvers.findfile(name, suffix) then - specification.forced = suffix - specification.forcedname = name - return - end - end - for t, format in next, formats do --- brute force - if kpse.find_file (name, format) then - specification.forced = t - specification.name = name - return - end - end -end - -% \end{macrocode} -% The \verb|name:| resolver wraps the database function -% \luafunction{resolve_name}. -% -% \begin{macrocode} - ---- fonts.names.resolvers.name -- Customized version of the ---- generic name resolver. - -request_resolvers.name = function (specification) - local resolved, subfont = resolve_name (specification) - if resolved then - specification.resolved = resolved - specification.sub = subfont - specification.forced = filesuffix (resolved) - specification.forcedname = resolved - specification.name = fileremovesuffix (resolved) - else - file_resolver (specification) - end -end - -% \end{macrocode} -% Also {\bfseries EXPERIMENTAL}: -% custom file resolvers via callback. -% -% \begin{macrocode} -create_callback("luaotfload.resolve_font", "simple", dummy_function) - -request_resolvers.my = function (specification) - call_callback("luaotfload.resolve_font", specification) -end - -% \end{macrocode} -% We create a callback for patching fonts on the fly, to be used by other -% packages. -% It initially contains the empty function that we are going to override -% below. -% -% \begin{macrocode} - -create_callback("luaotfload.patch_font", "simple", dummy_function) - -% \end{macrocode} -% \subsection{\CONTEXT override} -% \label{define-font} -% We provide a simplified version of the original font definition -% callback. -% -% \begin{macrocode} - -local read_font_file = fonts.definers.read - ---- spec -> size -> id -> tmfdata -local patch_defined_font = function (specification, size, id) - local tfmdata = read_font_file(specification, size, id) - if type(tfmdata) == "table" and tfmdata.shared then - --- We need to test for the “shared” field here - --- or else the fontspec capheight callback will - --- operate on tfm fonts. - call_callback("luaotfload.patch_font", tfmdata, specification) - end - return tfmdata -end - -reset_callback "define_font" - -% \end{macrocode} -% Finally we register the callbacks. -% -% \begin{macrocode} - -local font_definer = config.luaotfload.definer - -if font_definer == "generic" then - add_to_callback("define_font", - fonts.definers.read, - "luaotfload.define_font", - 1) -elseif font_definer == "patch" then - add_to_callback("define_font", - patch_defined_font, - "luaotfload.define_font", - 1) -end - -loadmodule"features.lua" --- contains what was “font-ltx” and “font-otc” -loadmodule"letterspace.lua" --- extra character kerning -loadmodule"auxiliary.lua" --- additionaly high-level functionality (new) - -luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec - --- vim:tw=71:sw=4:ts=4:expandtab - -% \end{macrocode} -% -% \iffalse -% -% \fi +% As of version 2.5, the file \fileent{luaotfload.lua} is no longer +% generated from the \abbrev{dtx}. +% Instead, it is maintained separately as a plain \identifier{Lua} file +% in the Luaotfload \identifier{git} tree. +% The file documentation which used to be found in this section has +% been preserved in the comments. % % \section{\fileent{luaotfload.sty}} % -- cgit v1.2.3 From 028dec37109c4b0be47d2b12ad235c1a441259cd Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 16 Jan 2014 07:13:26 +0100 Subject: [main,gitignore] add self-contained luaotfload.lua --- .gitignore | 1 - luaotfload.lua | 700 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 700 insertions(+), 1 deletion(-) create mode 100644 luaotfload.lua diff --git a/.gitignore b/.gitignore index f231acf..e010a81 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ luaotfload-tool.1 luaotfload/* # Files generated by 'make world' and removed by 'make mrproper' -luaotfload.lua luaotfload.pdf luaotfload.sty luaotfload.tds.zip diff --git a/luaotfload.lua b/luaotfload.lua new file mode 100644 index 0000000..a10fc34 --- /dev/null +++ b/luaotfload.lua @@ -0,0 +1,700 @@ +----------------------------------------------------------------------- +-- FILE: luaotfload.lua +-- DESCRIPTION: Luatex fontloader initialization +-- REQUIREMENTS: luatex v.0.78 or later, the lualibs package +-- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang +-- VERSION: same as Luaotfload +-- MODIFIED: 2014-01-16 06:51:20+0100 +----------------------------------------------------------------------- +-- +--- Note: +--- This file was part of the original luaotfload.dtx and has been +--- converted to a pure Lua file during the transition from Luaotfload +--- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex) +--- markup. + +if not modules then modules = { } end modules ["luaotfload"] = { + version = "2.5", + comment = "fontloader initialization", + author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "GNU General Public License v. 2.0" +} + + +--[[doc-- + + This file initializes the system and loads the font loader. To + minimize potential conflicts between other packages and the code + imported from \CONTEXT, several precautions are in order. Some of + the functionality that the font loader expects to be present, like + raw access to callbacks, are assumed to have been disabled by + \identifier{luatexbase} when this file is processed. In some cases + it is possible to trick it by putting dummies into place and + restoring the behavior from \identifier{luatexbase} after + initilization. Other cases such as attribute allocation require + that we hook the functionality from \identifier{luatexbase} into + locations where they normally wouldn’t be. + + Anyways we can import the code base without modifications, which is + due mostly to the extra effort by Hans Hagen to make \LUATEX-Fonts + self-contained and encapsulate it, and especially due to his + willingness to incorporate our suggestions. + +--doc]]-- + +luaotfload = luaotfload or { } +local luaotfload = luaotfload + +config = config or { } +config.luaotfload = config.luaotfload or { } +------.luaotfload.resolver = config.luaotfload.resolver or "normal" +config.luaotfload.resolver = config.luaotfload.resolver or "cached" +config.luaotfload.definer = config.luaotfload.definer or "patch" +config.luaotfload.loglevel = config.luaotfload.loglevel or 2 +config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter" +config.luaotfload.prioritize = config.luaotfload.prioritize or "sys" +config.luaotfload.names_dir = config.luaotfload.names_dir or "names" +config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts" +config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua" +config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" + +if not config.luaotfload.strip then + config.luaotfload.strip = true +end + +luaotfload.module = { + name = "luaotfload", + version = 2.50000, + date = "2014/**/**", + description = "OpenType layout system.", + author = "Elie Roux & Hans Hagen", + copyright = "Elie Roux", + license = "GPL v2.0" +} + +local luatexbase = luatexbase + +local setmetatable = setmetatable +local type, next = type, next + +local kpsefind_file = kpse.find_file +local lfsisfile = lfs.isfile + +local add_to_callback, create_callback = + luatexbase.add_to_callback, luatexbase.create_callback +local reset_callback, call_callback = + luatexbase.reset_callback, luatexbase.call_callback + +local dummy_function = function () end + +local error, warning, info, log = + luatexbase.provides_module(luaotfload.module) + +luaotfload.error = error +luaotfload.warning = warning +luaotfload.info = info +luaotfload.log = log + +--[[doc-- + + We set the minimum version requirement for \LUATEX to v0.76, + because the font loader requires recent features like direct + attribute indexing and \luafunction{node.end_of_math()} that aren’t + available in earlier versions.\footnote{% + See Taco’s announcement of v0.76: + \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042} + and this commit by Hans that introduced those features. + \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}. + } + +--doc]]-- + +local luatex_version = 76 + +if tex.luatexversion < luatex_version then + warning("LuaTeX v%.2f is old, v%.2f is recommended.", + tex.luatexversion/100, + luatex_version /100) + --- we install a fallback for older versions as a safety + if not node.end_of_math then + local math_t = node.id"math" + local traverse_nodes = node.traverse_id + node.end_of_math = function (n) + for n in traverse_nodes(math_t, n.next) do + return n + end + end + end +end + +--[[doc-- + + \subsection{Module loading} + We load the files imported from \CONTEXT with this function. It + automatically prepends the prefix \fileent{luaotfload-} to its + argument, so we can refer to the files with their actual \CONTEXT + name. + +--doc]]-- + +local fl_prefix = "luaotfload" -- “luatex” for luatex-plain +local loadmodule = function (name) + require(fl_prefix .."-"..name) +end + +--[[doc-- + + Before \TeX Live 2013 version, \LUATEX had a bug that made ofm fonts + fail when called with their extension. There was a side-effect making + ofm totally unloadable when luaotfload was present. The following + lines are a patch for this bug. The utility of these lines is + questionable as they are not necessary since \TeX Live 2013. They + should be removed in the next version. + +--doc]]-- + + +local p_dot, p_slash = P".", P"/" +local p_suffix = (p_dot * (1 - p_dot - p_slash)^1 * P(-1)) / "" +local p_removesuffix = Cs((p_suffix + 1)^1) + +local find_vf_file = function (name) + local fullname = kpsefind_file(name, "ovf") + if not fullname then + --fullname = kpsefind_file(file.removesuffix(name), "ovf") + fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf") + end + if fullname then + log("loading virtual font file %s.", fullname) + end + return fullname +end + +--[[doc-- + + \subsection{Preparing the Font Loader} + We treat the fontloader as a black box so behavior is consistent + between formats. + We load the fontloader code directly in the same fashion as the + Plain format \identifier{luatex-fonts} that is part of Context. + How this is executed depends on the presence on the + \emphasis{merged font loader code}. + In \identifier{luaotfload} this is contained in the file + \fileent{luaotfload-merged.lua}. + If this file cannot be found, the original libraries from \CONTEXT + of which the merged code was composed are loaded instead. + Since these files are not shipped with Luaotfload, an installation + of Context is required. + (Since we pull the fontloader directly from the Context minimals, + the necessary Context version is likely to be more recent than that + of other TeX distributions like Texlive.) + The imported font loader will call \luafunction{callback.register} + once while reading \fileent{font-def.lua}. + This is unavoidable unless we modify the imported files, but + harmless if we make it call a dummy instead. + However, this problem might vanish if we decide to do the merging + ourselves, like the \identifier{lualibs} package does. + With this step we would obtain the freedom to load our own + overrides in the process right where they are needed, at the cost + of losing encapsulation. + The decision on how to progress is currently on indefinite hold. + +--doc]]-- + +local starttime = os.gettimeofday() + +local trapped_register = callback.register +callback.register = dummy_function + +--[[doc-- + + By default, the fontloader requires a number of \emphasis{private + attributes} for internal use. + These must be kept consistent with the attribute handling methods + as provided by \identifier{luatexbase}. + Our strategy is to override the function that allocates new + attributes before we initialize the font loader, making it a + wrapper around \luafunction{luatexbase.new_attribute}.\footnote{% + Many thanks, again, to Hans Hagen for making this part + configurable! + } + The attribute identifiers are prefixed “\fileent{luaotfload@}” to + avoid name clashes. + +--doc]]-- + +do + local new_attribute = luatexbase.new_attribute + local the_attributes = luatexbase.attributes + + attributes = attributes or { } + + attributes.private = function (name) + local attr = "luaotfload@" .. name --- used to be: “otfl@” + local number = the_attributes[attr] + if not number then + number = new_attribute(attr) + end + return number + end +end + +--[[doc-- + + These next lines replicate the behavior of + \fileent{luatex-fonts.lua}. + +--doc]]-- + +local context_environment = { } + +local push_namespaces = function () + log("push namespace for font loader") + local normalglobal = { } + for k, v in next, _G do + normalglobal[k] = v + end + return normalglobal +end + +local pop_namespaces = function (normalglobal, isolate) + if normalglobal then + local _G = _G + local mode = "non-destructive" + if isolate then mode = "destructive" end + log("pop namespace from font loader -- " .. mode) + for k, v in next, _G do + if not normalglobal[k] then + context_environment[k] = v + if isolate then + _G[k] = nil + end + end + end + for k, v in next, normalglobal do + _G[k] = v + end + -- just to be sure: + setmetatable(context_environment,_G) + else + log("irrecoverable error during pop_namespace: no globals to restore") + os.exit() + end +end + +luaotfload.context_environment = context_environment +luaotfload.push_namespaces = push_namespaces +luaotfload.pop_namespaces = pop_namespaces + +local our_environment = push_namespaces() + +--[[doc-- + + The font loader requires that the attribute with index zero be + zero. We happily oblige. + (Cf. \fileent{luatex-fonts-nod.lua}.) + +--doc]]-- + +tex.attribute[0] = 0 + +--[[doc-- + + Now that things are sorted out we can finally load the fontloader. + +--doc]]-- + +loadmodule "fontloader.lua" +---loadmodule"font-odv.lua" --- <= Devanagari support from Context + +if fonts then + + if not fonts._merge_loaded_message_done_ then + log [["I am using the merged version of 'luaotfload.lua' here.]] + log [[ If you run into problems or experience unexpected]] + log [[ behaviour, and if you have ConTeXt installed you can try]] + log [[ to delete the file 'luaotfload-merged.lua' as I might]] + log [[ then use the possibly updated libraries. The merged]] + log [[ version is not supported as it is a frozen instance.]] + log [[ Problems can be reported to the ConTeXt mailing list."]] + end + fonts._merge_loaded_message_done_ = true + +else--- the loading sequence is known to change, so this might have to + --- be updated with future updates! + --- do not modify it though unless there is a change to the merged + --- package! + loadmodule("l-lua.lua") + loadmodule("l-lpeg.lua") + loadmodule("l-function.lua") + loadmodule("l-string.lua") + loadmodule("l-table.lua") + loadmodule("l-io.lua") + loadmodule("l-file.lua") + loadmodule("l-boolean.lua") + loadmodule("l-math.lua") + loadmodule("util-str.lua") + loadmodule('luatex-basics-gen.lua') + loadmodule('data-con.lua') + loadmodule('luatex-basics-nod.lua') + loadmodule('font-ini.lua') + loadmodule('font-con.lua') + loadmodule('luatex-fonts-enc.lua') + loadmodule('font-cid.lua') + loadmodule('font-map.lua') + loadmodule('luatex-fonts-syn.lua') + loadmodule('luatex-fonts-tfm.lua') + loadmodule('font-oti.lua') + loadmodule('font-otf.lua') + loadmodule('font-otb.lua') + loadmodule('luatex-fonts-inj.lua') --> since 2014-01-07, replaces node-inj.lua + loadmodule('font-ota.lua') + loadmodule('luatex-fonts-otn.lua') --> since 2014-01-07, replaces font-otn.lua + loadmodule('font-otp.lua') --> since 2013-04-23 + loadmodule('luatex-fonts-lua.lua') + loadmodule('font-def.lua') + loadmodule('luatex-fonts-def.lua') + loadmodule('luatex-fonts-ext.lua') + loadmodule('luatex-fonts-cbk.lua') +end --- non-merge fallback scope + +--[[doc-- + + Here we adjust the globals created during font loader + initialization. If the second argument to + \luafunction{pop_namespaces()} is \verb|true| this will restore the + state of \luafunction{_G}, eliminating every global generated since + the last call to \luafunction{push_namespaces()}. At the moment we + see no reason to do this, and since the font loader is considered + an essential part of \identifier{luatex} as well as a very well + organized piece of code, we happily concede it the right to add to + \luafunction{_G} if needed. + +--doc]]-- + +pop_namespaces(our_environment, false)-- true) + +log("fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) + +--[[doc-- + + \subsection{Callbacks} + After the fontloader is ready we can restore the callback trap from + \identifier{luatexbase}. + +--doc]]-- + +callback.register = trapped_register + +--[[doc-- + + We do our own callback handling with the means provided by + luatexbase. + Note: \luafunction{pre_linebreak_filter} and + \luafunction{hpack_filter} are coupled in \CONTEXT in the concept + of \emphasis{node processor}. + +--doc]]-- + +add_to_callback("pre_linebreak_filter", + nodes.simple_font_handler, + "luaotfload.node_processor", + 1) +add_to_callback("hpack_filter", + nodes.simple_font_handler, + "luaotfload.node_processor", + 1) +add_to_callback("find_vf_file", + find_vf_file, "luaotfload.find_vf_file") + +loadmodule "override.lua" --- “luat-ovr” + +logs.set_loglevel(config.luaotfload.loglevel) + +--[[doc-- + + Now we load the modules written for \identifier{luaotfload}. + +--doc]]-- +loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax +loadmodule "loaders.lua" --- “font-pfb” new in 2.0, added 2011 +loadmodule "database.lua" --- “font-nms” +loadmodule "colors.lua" --- “font-clr” + +--[[doc-- + + Relying on the \verb|name:| resolver for everything has been the + source of permanent trouble with the database. + With the introduction of the new syntax parser we now have enough + granularity to distinguish between the \XETEX emulation layer and + the genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts. + Another benefit is that we can now easily plug in or replace new + lookup behaviors if necessary. + The name resolver remains untouched, but it calls + \luafunction{fonts.names.resolve()} internally anyways (see + \fileent{luaotfload-database.lua}). + +--doc]]-- + +local filesuffix = file.suffix +local fileremovesuffix = file.removesuffix +local request_resolvers = fonts.definers.resolvers +local formats = fonts.formats +local names = fonts.names +formats.ofm = "type1" + +fonts.encodings.known = fonts.encodings.known or { } + +--[[doc-- + + \identifier{luaotfload} promises easy access to system fonts. + Without additional precautions, this cannot be achieved by + \identifier{kpathsea} alone, because it searches only the + \fileent{texmf} directories by default. + Although it is possible for \identifier{kpathsea} to include extra + paths by adding them to the \verb|OSFONTDIR| environment variable, + this is still short of the goal »\emphasis{it just works!}«. + When building the font database \identifier{luaotfload} scans + system font directories anyways, so we already have all the + information for looking sytem fonts. + With the release version 2.2 the file names are indexed in the + database as well and we are ready to resolve \verb|file:| lookups + this way. + Thus we no longer need to call the \identifier{kpathsea} library in + most cases when looking up font files, only when generating the + database, and when verifying the existence of a file in the + \fileent{texmf} tree. + +--doc]]-- + +local resolve_file = names.crude_file_lookup +--local resolve_file = names.crude_file_lookup_verbose +local resolve_name = names.resolve_name + +local file_resolver = function (specification) + local name = resolve_file (specification.name) + local suffix = filesuffix(name) + if formats[suffix] then + specification.forced = suffix + specification.forcedname = file.removesuffix(name) + else + specification.name = name + end +end + +request_resolvers.file = file_resolver + +--[[doc-- + + We classify as \verb|anon:| those requests that have neither a + prefix nor brackets. According to Khaled\footnote{% + \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}. + } + they are the \XETEX equivalent of a \verb|name:| request, so we + will be treating them as such. + +--doc]]-- + +--request_resolvers.anon = request_resolvers.name + +--[[doc-- + + There is one drawback, though. + This syntax is also used for requesting fonts in \identifier{Type1} + (\abbrev{tfm}, \abbrev{ofm}) format. + These are essentially \verb|file:| lookups and must be caught + before the \verb|name:| resolver kicks in, lest they cause the + database to update. + Even if we were to require the \verb|file:| prefix for all + \identifier{Type1} requests, tests have shown that certain fonts + still include further fonts (e.~g. \fileent{omlgcb.ofm} will ask + for \fileent{omsecob.tfm}) \emphasis{using the old syntax}. + For this reason, we introduce an extra check with an early return. + +--doc]]-- + +local type1_formats = { "tfm", "ofm", } + +request_resolvers.anon = function (specification) + local name = specification.name + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(name, format) then + specification.forcedname = file.addsuffix(name, format) + specification.forced = format + return + end + end + --- under some weird circumstances absolute paths get + --- passed to the definer; we have to catch them + --- before the name: resolver misinterprets them. + name = specification.specification + local exists, _ = lfsisfile(name) + if exists then --- garbage; we do this because we are nice, + --- not because it is correct + logs.names_report("log", 1, "load", "file %q exists", name) + logs.names_report("log", 1, "load", + "... overriding borked anon: lookup with path: lookup") + specification.name = name + request_resolvers.path(specification) + return + end + request_resolvers.name(specification) +end + +--[[doc-- + + Prior to version 2.2, \identifier{luaotfload} did not distinguish + \verb|file:| and \verb|path:| lookups, causing complications with + the resolver. + Now we test if the requested name is an absolute path in the file + system, otherwise we fall back to the \verb|file:| lookup. + +--doc]]-- + +request_resolvers.path = function (specification) + local name = specification.name + local exists, _ = lfsisfile(name) + if not exists then -- resort to file: lookup + logs.names_report("log", 1, "load", + "path lookup of %q unsuccessful, falling back to file:", + name) + file_resolver (specification) + else + local suffix = filesuffix (name) + if formats[suffix] then + specification.forced = suffix + specification.name = file.removesuffix(name) + specification.forcedname = name + else + specification.name = name + end + end +end + +--[[doc-- + + {\bfseries EXPERIMENTAL}: + \identifier{kpse}-only resolver, for those who can do without + system fonts. + +--doc]]-- + +request_resolvers.kpse = function (specification) + local name = specification.name + local suffix = filesuffix(name) + if suffix and formats[suffix] then + name = file.removesuffix(name) + if resolvers.findfile(name, suffix) then + specification.forced = suffix + specification.forcedname = name + return + end + end + for t, format in next, formats do --- brute force + if kpse.find_file (name, format) then + specification.forced = t + specification.name = name + return + end + end +end + +--[[doc-- + + The \verb|name:| resolver wraps the database function + \luafunction{resolve_name}. + +--doc]]-- + +--- fonts.names.resolvers.name -- Customized version of the +--- generic name resolver. + +request_resolvers.name = function (specification) + local resolved, subfont = resolve_name (specification) + if resolved then + specification.resolved = resolved + specification.sub = subfont + specification.forced = filesuffix (resolved) + specification.forcedname = resolved + specification.name = fileremovesuffix (resolved) + else + file_resolver (specification) + end +end + +--[[doc-- + + Also {\bfseries EXPERIMENTAL}: custom file resolvers via callback. + +--doc]]-- +create_callback("luaotfload.resolve_font", "simple", dummy_function) + +request_resolvers.my = function (specification) + call_callback("luaotfload.resolve_font", specification) +end + +--[[doc-- + + We create a callback for patching fonts on the fly, to be used by + other packages. + It initially contains the empty function that we are going to + override below. + +--doc]]-- + +create_callback("luaotfload.patch_font", "simple", dummy_function) + +--[[doc-- + + \subsection{\CONTEXT override} + \label{define-font} + We provide a simplified version of the original font definition + callback. + +--doc]]-- + +local read_font_file = fonts.definers.read + +--- spec -> size -> id -> tmfdata +local patch_defined_font = function (specification, size, id) + local tfmdata = read_font_file(specification, size, id) + if type(tfmdata) == "table" and tfmdata.shared then + --- We need to test for the “shared” field here + --- or else the fontspec capheight callback will + --- operate on tfm fonts. + call_callback("luaotfload.patch_font", tfmdata, specification) + end + return tfmdata +end + +reset_callback "define_font" + +--[[doc-- + + Finally we register the callbacks. + +--doc]]-- + +local font_definer = config.luaotfload.definer + +if font_definer == "generic" then + add_to_callback("define_font", + fonts.definers.read, + "luaotfload.define_font", + 1) +elseif font_definer == "patch" then + add_to_callback("define_font", + patch_defined_font, + "luaotfload.define_font", + 1) +end + +loadmodule "features.lua" --- contains what was “font-ltx” and “font-otc” +loadmodule "letterspace.lua" --- extra character kerning +loadmodule "auxiliary.lua" --- additionaly high-level functionality (new) + +luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec + +-- vim:tw=79:sw=4:ts=4:et -- cgit v1.2.3 From ed5d9443bc2ab9f3a7cfff499c5a3adb34856aaa Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 16 Jan 2014 07:17:29 +0100 Subject: [main] move luaotfload.lua -> luaotfload-main.lua --- luaotfload-main.lua | 700 ++++++++++++++++++++++++++++++++++++++++++++++++++++ luaotfload.lua | 700 ---------------------------------------------------- 2 files changed, 700 insertions(+), 700 deletions(-) create mode 100644 luaotfload-main.lua delete mode 100644 luaotfload.lua diff --git a/luaotfload-main.lua b/luaotfload-main.lua new file mode 100644 index 0000000..90e35ef --- /dev/null +++ b/luaotfload-main.lua @@ -0,0 +1,700 @@ +----------------------------------------------------------------------- +-- FILE: luaotfload-main.lua +-- DESCRIPTION: Luatex fontloader initialization +-- REQUIREMENTS: luatex v.0.78 or later, the lualibs package +-- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang +-- VERSION: same as Luaotfload +-- MODIFIED: 2014-01-16 06:51:20+0100 +----------------------------------------------------------------------- +-- +--- Note: +--- This file was part of the original luaotfload.dtx and has been +--- converted to a pure Lua file during the transition from Luaotfload +--- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex) +--- markup. + +if not modules then modules = { } end modules ["luaotfload-main"] = { + version = "2.5", + comment = "fontloader initialization", + author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "GNU General Public License v. 2.0" +} + + +--[[doc-- + + This file initializes the system and loads the font loader. To + minimize potential conflicts between other packages and the code + imported from \CONTEXT, several precautions are in order. Some of + the functionality that the font loader expects to be present, like + raw access to callbacks, are assumed to have been disabled by + \identifier{luatexbase} when this file is processed. In some cases + it is possible to trick it by putting dummies into place and + restoring the behavior from \identifier{luatexbase} after + initilization. Other cases such as attribute allocation require + that we hook the functionality from \identifier{luatexbase} into + locations where they normally wouldn’t be. + + Anyways we can import the code base without modifications, which is + due mostly to the extra effort by Hans Hagen to make \LUATEX-Fonts + self-contained and encapsulate it, and especially due to his + willingness to incorporate our suggestions. + +--doc]]-- + +luaotfload = luaotfload or { } +local luaotfload = luaotfload + +config = config or { } +config.luaotfload = config.luaotfload or { } +------.luaotfload.resolver = config.luaotfload.resolver or "normal" +config.luaotfload.resolver = config.luaotfload.resolver or "cached" +config.luaotfload.definer = config.luaotfload.definer or "patch" +config.luaotfload.loglevel = config.luaotfload.loglevel or 2 +config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter" +config.luaotfload.prioritize = config.luaotfload.prioritize or "sys" +config.luaotfload.names_dir = config.luaotfload.names_dir or "names" +config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts" +config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua" +config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" + +if not config.luaotfload.strip then + config.luaotfload.strip = true +end + +luaotfload.module = { + name = "luaotfload", + version = 2.50000, + date = "2014/**/**", + description = "OpenType layout system.", + author = "Elie Roux & Hans Hagen", + copyright = "Elie Roux", + license = "GPL v2.0" +} + +local luatexbase = luatexbase + +local setmetatable = setmetatable +local type, next = type, next + +local kpsefind_file = kpse.find_file +local lfsisfile = lfs.isfile + +local add_to_callback, create_callback = + luatexbase.add_to_callback, luatexbase.create_callback +local reset_callback, call_callback = + luatexbase.reset_callback, luatexbase.call_callback + +local dummy_function = function () end + +local error, warning, info, log = + luatexbase.provides_module(luaotfload.module) + +luaotfload.error = error +luaotfload.warning = warning +luaotfload.info = info +luaotfload.log = log + +--[[doc-- + + We set the minimum version requirement for \LUATEX to v0.76, + because the font loader requires recent features like direct + attribute indexing and \luafunction{node.end_of_math()} that aren’t + available in earlier versions.\footnote{% + See Taco’s announcement of v0.76: + \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042} + and this commit by Hans that introduced those features. + \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}. + } + +--doc]]-- + +local luatex_version = 76 + +if tex.luatexversion < luatex_version then + warning("LuaTeX v%.2f is old, v%.2f is recommended.", + tex.luatexversion/100, + luatex_version /100) + --- we install a fallback for older versions as a safety + if not node.end_of_math then + local math_t = node.id"math" + local traverse_nodes = node.traverse_id + node.end_of_math = function (n) + for n in traverse_nodes(math_t, n.next) do + return n + end + end + end +end + +--[[doc-- + + \subsection{Module loading} + We load the files imported from \CONTEXT with this function. It + automatically prepends the prefix \fileent{luaotfload-} to its + argument, so we can refer to the files with their actual \CONTEXT + name. + +--doc]]-- + +local fl_prefix = "luaotfload" -- “luatex” for luatex-plain +local loadmodule = function (name) + require(fl_prefix .."-"..name) +end + +--[[doc-- + + Before \TeX Live 2013 version, \LUATEX had a bug that made ofm fonts + fail when called with their extension. There was a side-effect making + ofm totally unloadable when luaotfload was present. The following + lines are a patch for this bug. The utility of these lines is + questionable as they are not necessary since \TeX Live 2013. They + should be removed in the next version. + +--doc]]-- + + +local p_dot, p_slash = P".", P"/" +local p_suffix = (p_dot * (1 - p_dot - p_slash)^1 * P(-1)) / "" +local p_removesuffix = Cs((p_suffix + 1)^1) + +local find_vf_file = function (name) + local fullname = kpsefind_file(name, "ovf") + if not fullname then + --fullname = kpsefind_file(file.removesuffix(name), "ovf") + fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf") + end + if fullname then + log("loading virtual font file %s.", fullname) + end + return fullname +end + +--[[doc-- + + \subsection{Preparing the Font Loader} + We treat the fontloader as a black box so behavior is consistent + between formats. + We load the fontloader code directly in the same fashion as the + Plain format \identifier{luatex-fonts} that is part of Context. + How this is executed depends on the presence on the + \emphasis{merged font loader code}. + In \identifier{luaotfload} this is contained in the file + \fileent{luaotfload-merged.lua}. + If this file cannot be found, the original libraries from \CONTEXT + of which the merged code was composed are loaded instead. + Since these files are not shipped with Luaotfload, an installation + of Context is required. + (Since we pull the fontloader directly from the Context minimals, + the necessary Context version is likely to be more recent than that + of other TeX distributions like Texlive.) + The imported font loader will call \luafunction{callback.register} + once while reading \fileent{font-def.lua}. + This is unavoidable unless we modify the imported files, but + harmless if we make it call a dummy instead. + However, this problem might vanish if we decide to do the merging + ourselves, like the \identifier{lualibs} package does. + With this step we would obtain the freedom to load our own + overrides in the process right where they are needed, at the cost + of losing encapsulation. + The decision on how to progress is currently on indefinite hold. + +--doc]]-- + +local starttime = os.gettimeofday() + +local trapped_register = callback.register +callback.register = dummy_function + +--[[doc-- + + By default, the fontloader requires a number of \emphasis{private + attributes} for internal use. + These must be kept consistent with the attribute handling methods + as provided by \identifier{luatexbase}. + Our strategy is to override the function that allocates new + attributes before we initialize the font loader, making it a + wrapper around \luafunction{luatexbase.new_attribute}.\footnote{% + Many thanks, again, to Hans Hagen for making this part + configurable! + } + The attribute identifiers are prefixed “\fileent{luaotfload@}” to + avoid name clashes. + +--doc]]-- + +do + local new_attribute = luatexbase.new_attribute + local the_attributes = luatexbase.attributes + + attributes = attributes or { } + + attributes.private = function (name) + local attr = "luaotfload@" .. name --- used to be: “otfl@” + local number = the_attributes[attr] + if not number then + number = new_attribute(attr) + end + return number + end +end + +--[[doc-- + + These next lines replicate the behavior of + \fileent{luatex-fonts.lua}. + +--doc]]-- + +local context_environment = { } + +local push_namespaces = function () + log("push namespace for font loader") + local normalglobal = { } + for k, v in next, _G do + normalglobal[k] = v + end + return normalglobal +end + +local pop_namespaces = function (normalglobal, isolate) + if normalglobal then + local _G = _G + local mode = "non-destructive" + if isolate then mode = "destructive" end + log("pop namespace from font loader -- " .. mode) + for k, v in next, _G do + if not normalglobal[k] then + context_environment[k] = v + if isolate then + _G[k] = nil + end + end + end + for k, v in next, normalglobal do + _G[k] = v + end + -- just to be sure: + setmetatable(context_environment,_G) + else + log("irrecoverable error during pop_namespace: no globals to restore") + os.exit() + end +end + +luaotfload.context_environment = context_environment +luaotfload.push_namespaces = push_namespaces +luaotfload.pop_namespaces = pop_namespaces + +local our_environment = push_namespaces() + +--[[doc-- + + The font loader requires that the attribute with index zero be + zero. We happily oblige. + (Cf. \fileent{luatex-fonts-nod.lua}.) + +--doc]]-- + +tex.attribute[0] = 0 + +--[[doc-- + + Now that things are sorted out we can finally load the fontloader. + +--doc]]-- + +loadmodule "fontloader.lua" +---loadmodule"font-odv.lua" --- <= Devanagari support from Context + +if fonts then + + if not fonts._merge_loaded_message_done_ then + log [["I am using the merged fontloader here.]] + log [[ If you run into problems or experience unexpected]] + log [[ behaviour, and if you have ConTeXt installed you can try]] + log [[ to delete the file 'luaotfload-fontloader.lua' as I might]] + log [[ then use the possibly updated libraries. The merged]] + log [[ version is not supported as it is a frozen instance.]] + log [[ Problems can be reported to the ConTeXt mailing list."]] + end + fonts._merge_loaded_message_done_ = true + +else--- the loading sequence is known to change, so this might have to + --- be updated with future updates! + --- do not modify it though unless there is a change to the merged + --- package! + loadmodule("l-lua.lua") + loadmodule("l-lpeg.lua") + loadmodule("l-function.lua") + loadmodule("l-string.lua") + loadmodule("l-table.lua") + loadmodule("l-io.lua") + loadmodule("l-file.lua") + loadmodule("l-boolean.lua") + loadmodule("l-math.lua") + loadmodule("util-str.lua") + loadmodule('luatex-basics-gen.lua') + loadmodule('data-con.lua') + loadmodule('luatex-basics-nod.lua') + loadmodule('font-ini.lua') + loadmodule('font-con.lua') + loadmodule('luatex-fonts-enc.lua') + loadmodule('font-cid.lua') + loadmodule('font-map.lua') + loadmodule('luatex-fonts-syn.lua') + loadmodule('luatex-fonts-tfm.lua') + loadmodule('font-oti.lua') + loadmodule('font-otf.lua') + loadmodule('font-otb.lua') + loadmodule('luatex-fonts-inj.lua') --> since 2014-01-07, replaces node-inj.lua + loadmodule('font-ota.lua') + loadmodule('luatex-fonts-otn.lua') --> since 2014-01-07, replaces font-otn.lua + loadmodule('font-otp.lua') --> since 2013-04-23 + loadmodule('luatex-fonts-lua.lua') + loadmodule('font-def.lua') + loadmodule('luatex-fonts-def.lua') + loadmodule('luatex-fonts-ext.lua') + loadmodule('luatex-fonts-cbk.lua') +end --- non-merge fallback scope + +--[[doc-- + + Here we adjust the globals created during font loader + initialization. If the second argument to + \luafunction{pop_namespaces()} is \verb|true| this will restore the + state of \luafunction{_G}, eliminating every global generated since + the last call to \luafunction{push_namespaces()}. At the moment we + see no reason to do this, and since the font loader is considered + an essential part of \identifier{luatex} as well as a very well + organized piece of code, we happily concede it the right to add to + \luafunction{_G} if needed. + +--doc]]-- + +pop_namespaces(our_environment, false)-- true) + +log("fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) + +--[[doc-- + + \subsection{Callbacks} + After the fontloader is ready we can restore the callback trap from + \identifier{luatexbase}. + +--doc]]-- + +callback.register = trapped_register + +--[[doc-- + + We do our own callback handling with the means provided by + luatexbase. + Note: \luafunction{pre_linebreak_filter} and + \luafunction{hpack_filter} are coupled in \CONTEXT in the concept + of \emphasis{node processor}. + +--doc]]-- + +add_to_callback("pre_linebreak_filter", + nodes.simple_font_handler, + "luaotfload.node_processor", + 1) +add_to_callback("hpack_filter", + nodes.simple_font_handler, + "luaotfload.node_processor", + 1) +add_to_callback("find_vf_file", + find_vf_file, "luaotfload.find_vf_file") + +loadmodule "override.lua" --- “luat-ovr” + +logs.set_loglevel(config.luaotfload.loglevel) + +--[[doc-- + + Now we load the modules written for \identifier{luaotfload}. + +--doc]]-- +loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax +loadmodule "loaders.lua" --- “font-pfb” new in 2.0, added 2011 +loadmodule "database.lua" --- “font-nms” +loadmodule "colors.lua" --- “font-clr” + +--[[doc-- + + Relying on the \verb|name:| resolver for everything has been the + source of permanent trouble with the database. + With the introduction of the new syntax parser we now have enough + granularity to distinguish between the \XETEX emulation layer and + the genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts. + Another benefit is that we can now easily plug in or replace new + lookup behaviors if necessary. + The name resolver remains untouched, but it calls + \luafunction{fonts.names.resolve()} internally anyways (see + \fileent{luaotfload-database.lua}). + +--doc]]-- + +local filesuffix = file.suffix +local fileremovesuffix = file.removesuffix +local request_resolvers = fonts.definers.resolvers +local formats = fonts.formats +local names = fonts.names +formats.ofm = "type1" + +fonts.encodings.known = fonts.encodings.known or { } + +--[[doc-- + + \identifier{luaotfload} promises easy access to system fonts. + Without additional precautions, this cannot be achieved by + \identifier{kpathsea} alone, because it searches only the + \fileent{texmf} directories by default. + Although it is possible for \identifier{kpathsea} to include extra + paths by adding them to the \verb|OSFONTDIR| environment variable, + this is still short of the goal »\emphasis{it just works!}«. + When building the font database \identifier{luaotfload} scans + system font directories anyways, so we already have all the + information for looking sytem fonts. + With the release version 2.2 the file names are indexed in the + database as well and we are ready to resolve \verb|file:| lookups + this way. + Thus we no longer need to call the \identifier{kpathsea} library in + most cases when looking up font files, only when generating the + database, and when verifying the existence of a file in the + \fileent{texmf} tree. + +--doc]]-- + +local resolve_file = names.crude_file_lookup +--local resolve_file = names.crude_file_lookup_verbose +local resolve_name = names.resolve_name + +local file_resolver = function (specification) + local name = resolve_file (specification.name) + local suffix = filesuffix(name) + if formats[suffix] then + specification.forced = suffix + specification.forcedname = file.removesuffix(name) + else + specification.name = name + end +end + +request_resolvers.file = file_resolver + +--[[doc-- + + We classify as \verb|anon:| those requests that have neither a + prefix nor brackets. According to Khaled\footnote{% + \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}. + } + they are the \XETEX equivalent of a \verb|name:| request, so we + will be treating them as such. + +--doc]]-- + +--request_resolvers.anon = request_resolvers.name + +--[[doc-- + + There is one drawback, though. + This syntax is also used for requesting fonts in \identifier{Type1} + (\abbrev{tfm}, \abbrev{ofm}) format. + These are essentially \verb|file:| lookups and must be caught + before the \verb|name:| resolver kicks in, lest they cause the + database to update. + Even if we were to require the \verb|file:| prefix for all + \identifier{Type1} requests, tests have shown that certain fonts + still include further fonts (e.~g. \fileent{omlgcb.ofm} will ask + for \fileent{omsecob.tfm}) \emphasis{using the old syntax}. + For this reason, we introduce an extra check with an early return. + +--doc]]-- + +local type1_formats = { "tfm", "ofm", } + +request_resolvers.anon = function (specification) + local name = specification.name + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(name, format) then + specification.forcedname = file.addsuffix(name, format) + specification.forced = format + return + end + end + --- under some weird circumstances absolute paths get + --- passed to the definer; we have to catch them + --- before the name: resolver misinterprets them. + name = specification.specification + local exists, _ = lfsisfile(name) + if exists then --- garbage; we do this because we are nice, + --- not because it is correct + logs.names_report("log", 1, "load", "file %q exists", name) + logs.names_report("log", 1, "load", + "... overriding borked anon: lookup with path: lookup") + specification.name = name + request_resolvers.path(specification) + return + end + request_resolvers.name(specification) +end + +--[[doc-- + + Prior to version 2.2, \identifier{luaotfload} did not distinguish + \verb|file:| and \verb|path:| lookups, causing complications with + the resolver. + Now we test if the requested name is an absolute path in the file + system, otherwise we fall back to the \verb|file:| lookup. + +--doc]]-- + +request_resolvers.path = function (specification) + local name = specification.name + local exists, _ = lfsisfile(name) + if not exists then -- resort to file: lookup + logs.names_report("log", 1, "load", + "path lookup of %q unsuccessful, falling back to file:", + name) + file_resolver (specification) + else + local suffix = filesuffix (name) + if formats[suffix] then + specification.forced = suffix + specification.name = file.removesuffix(name) + specification.forcedname = name + else + specification.name = name + end + end +end + +--[[doc-- + + {\bfseries EXPERIMENTAL}: + \identifier{kpse}-only resolver, for those who can do without + system fonts. + +--doc]]-- + +request_resolvers.kpse = function (specification) + local name = specification.name + local suffix = filesuffix(name) + if suffix and formats[suffix] then + name = file.removesuffix(name) + if resolvers.findfile(name, suffix) then + specification.forced = suffix + specification.forcedname = name + return + end + end + for t, format in next, formats do --- brute force + if kpse.find_file (name, format) then + specification.forced = t + specification.name = name + return + end + end +end + +--[[doc-- + + The \verb|name:| resolver wraps the database function + \luafunction{resolve_name}. + +--doc]]-- + +--- fonts.names.resolvers.name -- Customized version of the +--- generic name resolver. + +request_resolvers.name = function (specification) + local resolved, subfont = resolve_name (specification) + if resolved then + specification.resolved = resolved + specification.sub = subfont + specification.forced = filesuffix (resolved) + specification.forcedname = resolved + specification.name = fileremovesuffix (resolved) + else + file_resolver (specification) + end +end + +--[[doc-- + + Also {\bfseries EXPERIMENTAL}: custom file resolvers via callback. + +--doc]]-- +create_callback("luaotfload.resolve_font", "simple", dummy_function) + +request_resolvers.my = function (specification) + call_callback("luaotfload.resolve_font", specification) +end + +--[[doc-- + + We create a callback for patching fonts on the fly, to be used by + other packages. + It initially contains the empty function that we are going to + override below. + +--doc]]-- + +create_callback("luaotfload.patch_font", "simple", dummy_function) + +--[[doc-- + + \subsection{\CONTEXT override} + \label{define-font} + We provide a simplified version of the original font definition + callback. + +--doc]]-- + +local read_font_file = fonts.definers.read + +--- spec -> size -> id -> tmfdata +local patch_defined_font = function (specification, size, id) + local tfmdata = read_font_file(specification, size, id) + if type(tfmdata) == "table" and tfmdata.shared then + --- We need to test for the “shared” field here + --- or else the fontspec capheight callback will + --- operate on tfm fonts. + call_callback("luaotfload.patch_font", tfmdata, specification) + end + return tfmdata +end + +reset_callback "define_font" + +--[[doc-- + + Finally we register the callbacks. + +--doc]]-- + +local font_definer = config.luaotfload.definer + +if font_definer == "generic" then + add_to_callback("define_font", + fonts.definers.read, + "luaotfload.define_font", + 1) +elseif font_definer == "patch" then + add_to_callback("define_font", + patch_defined_font, + "luaotfload.define_font", + 1) +end + +loadmodule "features.lua" --- contains what was “font-ltx” and “font-otc” +loadmodule "letterspace.lua" --- extra character kerning +loadmodule "auxiliary.lua" --- additionaly high-level functionality (new) + +luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec + +-- vim:tw=79:sw=4:ts=4:et diff --git a/luaotfload.lua b/luaotfload.lua deleted file mode 100644 index a10fc34..0000000 --- a/luaotfload.lua +++ /dev/null @@ -1,700 +0,0 @@ ------------------------------------------------------------------------ --- FILE: luaotfload.lua --- DESCRIPTION: Luatex fontloader initialization --- REQUIREMENTS: luatex v.0.78 or later, the lualibs package --- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang --- VERSION: same as Luaotfload --- MODIFIED: 2014-01-16 06:51:20+0100 ------------------------------------------------------------------------ --- ---- Note: ---- This file was part of the original luaotfload.dtx and has been ---- converted to a pure Lua file during the transition from Luaotfload ---- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex) ---- markup. - -if not modules then modules = { } end modules ["luaotfload"] = { - version = "2.5", - comment = "fontloader initialization", - author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "GNU General Public License v. 2.0" -} - - ---[[doc-- - - This file initializes the system and loads the font loader. To - minimize potential conflicts between other packages and the code - imported from \CONTEXT, several precautions are in order. Some of - the functionality that the font loader expects to be present, like - raw access to callbacks, are assumed to have been disabled by - \identifier{luatexbase} when this file is processed. In some cases - it is possible to trick it by putting dummies into place and - restoring the behavior from \identifier{luatexbase} after - initilization. Other cases such as attribute allocation require - that we hook the functionality from \identifier{luatexbase} into - locations where they normally wouldn’t be. - - Anyways we can import the code base without modifications, which is - due mostly to the extra effort by Hans Hagen to make \LUATEX-Fonts - self-contained and encapsulate it, and especially due to his - willingness to incorporate our suggestions. - ---doc]]-- - -luaotfload = luaotfload or { } -local luaotfload = luaotfload - -config = config or { } -config.luaotfload = config.luaotfload or { } -------.luaotfload.resolver = config.luaotfload.resolver or "normal" -config.luaotfload.resolver = config.luaotfload.resolver or "cached" -config.luaotfload.definer = config.luaotfload.definer or "patch" -config.luaotfload.loglevel = config.luaotfload.loglevel or 2 -config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter" -config.luaotfload.prioritize = config.luaotfload.prioritize or "sys" -config.luaotfload.names_dir = config.luaotfload.names_dir or "names" -config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts" -config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua" -config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" - -if not config.luaotfload.strip then - config.luaotfload.strip = true -end - -luaotfload.module = { - name = "luaotfload", - version = 2.50000, - date = "2014/**/**", - description = "OpenType layout system.", - author = "Elie Roux & Hans Hagen", - copyright = "Elie Roux", - license = "GPL v2.0" -} - -local luatexbase = luatexbase - -local setmetatable = setmetatable -local type, next = type, next - -local kpsefind_file = kpse.find_file -local lfsisfile = lfs.isfile - -local add_to_callback, create_callback = - luatexbase.add_to_callback, luatexbase.create_callback -local reset_callback, call_callback = - luatexbase.reset_callback, luatexbase.call_callback - -local dummy_function = function () end - -local error, warning, info, log = - luatexbase.provides_module(luaotfload.module) - -luaotfload.error = error -luaotfload.warning = warning -luaotfload.info = info -luaotfload.log = log - ---[[doc-- - - We set the minimum version requirement for \LUATEX to v0.76, - because the font loader requires recent features like direct - attribute indexing and \luafunction{node.end_of_math()} that aren’t - available in earlier versions.\footnote{% - See Taco’s announcement of v0.76: - \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042} - and this commit by Hans that introduced those features. - \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}. - } - ---doc]]-- - -local luatex_version = 76 - -if tex.luatexversion < luatex_version then - warning("LuaTeX v%.2f is old, v%.2f is recommended.", - tex.luatexversion/100, - luatex_version /100) - --- we install a fallback for older versions as a safety - if not node.end_of_math then - local math_t = node.id"math" - local traverse_nodes = node.traverse_id - node.end_of_math = function (n) - for n in traverse_nodes(math_t, n.next) do - return n - end - end - end -end - ---[[doc-- - - \subsection{Module loading} - We load the files imported from \CONTEXT with this function. It - automatically prepends the prefix \fileent{luaotfload-} to its - argument, so we can refer to the files with their actual \CONTEXT - name. - ---doc]]-- - -local fl_prefix = "luaotfload" -- “luatex” for luatex-plain -local loadmodule = function (name) - require(fl_prefix .."-"..name) -end - ---[[doc-- - - Before \TeX Live 2013 version, \LUATEX had a bug that made ofm fonts - fail when called with their extension. There was a side-effect making - ofm totally unloadable when luaotfload was present. The following - lines are a patch for this bug. The utility of these lines is - questionable as they are not necessary since \TeX Live 2013. They - should be removed in the next version. - ---doc]]-- - - -local p_dot, p_slash = P".", P"/" -local p_suffix = (p_dot * (1 - p_dot - p_slash)^1 * P(-1)) / "" -local p_removesuffix = Cs((p_suffix + 1)^1) - -local find_vf_file = function (name) - local fullname = kpsefind_file(name, "ovf") - if not fullname then - --fullname = kpsefind_file(file.removesuffix(name), "ovf") - fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf") - end - if fullname then - log("loading virtual font file %s.", fullname) - end - return fullname -end - ---[[doc-- - - \subsection{Preparing the Font Loader} - We treat the fontloader as a black box so behavior is consistent - between formats. - We load the fontloader code directly in the same fashion as the - Plain format \identifier{luatex-fonts} that is part of Context. - How this is executed depends on the presence on the - \emphasis{merged font loader code}. - In \identifier{luaotfload} this is contained in the file - \fileent{luaotfload-merged.lua}. - If this file cannot be found, the original libraries from \CONTEXT - of which the merged code was composed are loaded instead. - Since these files are not shipped with Luaotfload, an installation - of Context is required. - (Since we pull the fontloader directly from the Context minimals, - the necessary Context version is likely to be more recent than that - of other TeX distributions like Texlive.) - The imported font loader will call \luafunction{callback.register} - once while reading \fileent{font-def.lua}. - This is unavoidable unless we modify the imported files, but - harmless if we make it call a dummy instead. - However, this problem might vanish if we decide to do the merging - ourselves, like the \identifier{lualibs} package does. - With this step we would obtain the freedom to load our own - overrides in the process right where they are needed, at the cost - of losing encapsulation. - The decision on how to progress is currently on indefinite hold. - ---doc]]-- - -local starttime = os.gettimeofday() - -local trapped_register = callback.register -callback.register = dummy_function - ---[[doc-- - - By default, the fontloader requires a number of \emphasis{private - attributes} for internal use. - These must be kept consistent with the attribute handling methods - as provided by \identifier{luatexbase}. - Our strategy is to override the function that allocates new - attributes before we initialize the font loader, making it a - wrapper around \luafunction{luatexbase.new_attribute}.\footnote{% - Many thanks, again, to Hans Hagen for making this part - configurable! - } - The attribute identifiers are prefixed “\fileent{luaotfload@}” to - avoid name clashes. - ---doc]]-- - -do - local new_attribute = luatexbase.new_attribute - local the_attributes = luatexbase.attributes - - attributes = attributes or { } - - attributes.private = function (name) - local attr = "luaotfload@" .. name --- used to be: “otfl@” - local number = the_attributes[attr] - if not number then - number = new_attribute(attr) - end - return number - end -end - ---[[doc-- - - These next lines replicate the behavior of - \fileent{luatex-fonts.lua}. - ---doc]]-- - -local context_environment = { } - -local push_namespaces = function () - log("push namespace for font loader") - local normalglobal = { } - for k, v in next, _G do - normalglobal[k] = v - end - return normalglobal -end - -local pop_namespaces = function (normalglobal, isolate) - if normalglobal then - local _G = _G - local mode = "non-destructive" - if isolate then mode = "destructive" end - log("pop namespace from font loader -- " .. mode) - for k, v in next, _G do - if not normalglobal[k] then - context_environment[k] = v - if isolate then - _G[k] = nil - end - end - end - for k, v in next, normalglobal do - _G[k] = v - end - -- just to be sure: - setmetatable(context_environment,_G) - else - log("irrecoverable error during pop_namespace: no globals to restore") - os.exit() - end -end - -luaotfload.context_environment = context_environment -luaotfload.push_namespaces = push_namespaces -luaotfload.pop_namespaces = pop_namespaces - -local our_environment = push_namespaces() - ---[[doc-- - - The font loader requires that the attribute with index zero be - zero. We happily oblige. - (Cf. \fileent{luatex-fonts-nod.lua}.) - ---doc]]-- - -tex.attribute[0] = 0 - ---[[doc-- - - Now that things are sorted out we can finally load the fontloader. - ---doc]]-- - -loadmodule "fontloader.lua" ----loadmodule"font-odv.lua" --- <= Devanagari support from Context - -if fonts then - - if not fonts._merge_loaded_message_done_ then - log [["I am using the merged version of 'luaotfload.lua' here.]] - log [[ If you run into problems or experience unexpected]] - log [[ behaviour, and if you have ConTeXt installed you can try]] - log [[ to delete the file 'luaotfload-merged.lua' as I might]] - log [[ then use the possibly updated libraries. The merged]] - log [[ version is not supported as it is a frozen instance.]] - log [[ Problems can be reported to the ConTeXt mailing list."]] - end - fonts._merge_loaded_message_done_ = true - -else--- the loading sequence is known to change, so this might have to - --- be updated with future updates! - --- do not modify it though unless there is a change to the merged - --- package! - loadmodule("l-lua.lua") - loadmodule("l-lpeg.lua") - loadmodule("l-function.lua") - loadmodule("l-string.lua") - loadmodule("l-table.lua") - loadmodule("l-io.lua") - loadmodule("l-file.lua") - loadmodule("l-boolean.lua") - loadmodule("l-math.lua") - loadmodule("util-str.lua") - loadmodule('luatex-basics-gen.lua') - loadmodule('data-con.lua') - loadmodule('luatex-basics-nod.lua') - loadmodule('font-ini.lua') - loadmodule('font-con.lua') - loadmodule('luatex-fonts-enc.lua') - loadmodule('font-cid.lua') - loadmodule('font-map.lua') - loadmodule('luatex-fonts-syn.lua') - loadmodule('luatex-fonts-tfm.lua') - loadmodule('font-oti.lua') - loadmodule('font-otf.lua') - loadmodule('font-otb.lua') - loadmodule('luatex-fonts-inj.lua') --> since 2014-01-07, replaces node-inj.lua - loadmodule('font-ota.lua') - loadmodule('luatex-fonts-otn.lua') --> since 2014-01-07, replaces font-otn.lua - loadmodule('font-otp.lua') --> since 2013-04-23 - loadmodule('luatex-fonts-lua.lua') - loadmodule('font-def.lua') - loadmodule('luatex-fonts-def.lua') - loadmodule('luatex-fonts-ext.lua') - loadmodule('luatex-fonts-cbk.lua') -end --- non-merge fallback scope - ---[[doc-- - - Here we adjust the globals created during font loader - initialization. If the second argument to - \luafunction{pop_namespaces()} is \verb|true| this will restore the - state of \luafunction{_G}, eliminating every global generated since - the last call to \luafunction{push_namespaces()}. At the moment we - see no reason to do this, and since the font loader is considered - an essential part of \identifier{luatex} as well as a very well - organized piece of code, we happily concede it the right to add to - \luafunction{_G} if needed. - ---doc]]-- - -pop_namespaces(our_environment, false)-- true) - -log("fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) - ---[[doc-- - - \subsection{Callbacks} - After the fontloader is ready we can restore the callback trap from - \identifier{luatexbase}. - ---doc]]-- - -callback.register = trapped_register - ---[[doc-- - - We do our own callback handling with the means provided by - luatexbase. - Note: \luafunction{pre_linebreak_filter} and - \luafunction{hpack_filter} are coupled in \CONTEXT in the concept - of \emphasis{node processor}. - ---doc]]-- - -add_to_callback("pre_linebreak_filter", - nodes.simple_font_handler, - "luaotfload.node_processor", - 1) -add_to_callback("hpack_filter", - nodes.simple_font_handler, - "luaotfload.node_processor", - 1) -add_to_callback("find_vf_file", - find_vf_file, "luaotfload.find_vf_file") - -loadmodule "override.lua" --- “luat-ovr” - -logs.set_loglevel(config.luaotfload.loglevel) - ---[[doc-- - - Now we load the modules written for \identifier{luaotfload}. - ---doc]]-- -loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax -loadmodule "loaders.lua" --- “font-pfb” new in 2.0, added 2011 -loadmodule "database.lua" --- “font-nms” -loadmodule "colors.lua" --- “font-clr” - ---[[doc-- - - Relying on the \verb|name:| resolver for everything has been the - source of permanent trouble with the database. - With the introduction of the new syntax parser we now have enough - granularity to distinguish between the \XETEX emulation layer and - the genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts. - Another benefit is that we can now easily plug in or replace new - lookup behaviors if necessary. - The name resolver remains untouched, but it calls - \luafunction{fonts.names.resolve()} internally anyways (see - \fileent{luaotfload-database.lua}). - ---doc]]-- - -local filesuffix = file.suffix -local fileremovesuffix = file.removesuffix -local request_resolvers = fonts.definers.resolvers -local formats = fonts.formats -local names = fonts.names -formats.ofm = "type1" - -fonts.encodings.known = fonts.encodings.known or { } - ---[[doc-- - - \identifier{luaotfload} promises easy access to system fonts. - Without additional precautions, this cannot be achieved by - \identifier{kpathsea} alone, because it searches only the - \fileent{texmf} directories by default. - Although it is possible for \identifier{kpathsea} to include extra - paths by adding them to the \verb|OSFONTDIR| environment variable, - this is still short of the goal »\emphasis{it just works!}«. - When building the font database \identifier{luaotfload} scans - system font directories anyways, so we already have all the - information for looking sytem fonts. - With the release version 2.2 the file names are indexed in the - database as well and we are ready to resolve \verb|file:| lookups - this way. - Thus we no longer need to call the \identifier{kpathsea} library in - most cases when looking up font files, only when generating the - database, and when verifying the existence of a file in the - \fileent{texmf} tree. - ---doc]]-- - -local resolve_file = names.crude_file_lookup ---local resolve_file = names.crude_file_lookup_verbose -local resolve_name = names.resolve_name - -local file_resolver = function (specification) - local name = resolve_file (specification.name) - local suffix = filesuffix(name) - if formats[suffix] then - specification.forced = suffix - specification.forcedname = file.removesuffix(name) - else - specification.name = name - end -end - -request_resolvers.file = file_resolver - ---[[doc-- - - We classify as \verb|anon:| those requests that have neither a - prefix nor brackets. According to Khaled\footnote{% - \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}. - } - they are the \XETEX equivalent of a \verb|name:| request, so we - will be treating them as such. - ---doc]]-- - ---request_resolvers.anon = request_resolvers.name - ---[[doc-- - - There is one drawback, though. - This syntax is also used for requesting fonts in \identifier{Type1} - (\abbrev{tfm}, \abbrev{ofm}) format. - These are essentially \verb|file:| lookups and must be caught - before the \verb|name:| resolver kicks in, lest they cause the - database to update. - Even if we were to require the \verb|file:| prefix for all - \identifier{Type1} requests, tests have shown that certain fonts - still include further fonts (e.~g. \fileent{omlgcb.ofm} will ask - for \fileent{omsecob.tfm}) \emphasis{using the old syntax}. - For this reason, we introduce an extra check with an early return. - ---doc]]-- - -local type1_formats = { "tfm", "ofm", } - -request_resolvers.anon = function (specification) - local name = specification.name - for i=1, #type1_formats do - local format = type1_formats[i] - if resolvers.findfile(name, format) then - specification.forcedname = file.addsuffix(name, format) - specification.forced = format - return - end - end - --- under some weird circumstances absolute paths get - --- passed to the definer; we have to catch them - --- before the name: resolver misinterprets them. - name = specification.specification - local exists, _ = lfsisfile(name) - if exists then --- garbage; we do this because we are nice, - --- not because it is correct - logs.names_report("log", 1, "load", "file %q exists", name) - logs.names_report("log", 1, "load", - "... overriding borked anon: lookup with path: lookup") - specification.name = name - request_resolvers.path(specification) - return - end - request_resolvers.name(specification) -end - ---[[doc-- - - Prior to version 2.2, \identifier{luaotfload} did not distinguish - \verb|file:| and \verb|path:| lookups, causing complications with - the resolver. - Now we test if the requested name is an absolute path in the file - system, otherwise we fall back to the \verb|file:| lookup. - ---doc]]-- - -request_resolvers.path = function (specification) - local name = specification.name - local exists, _ = lfsisfile(name) - if not exists then -- resort to file: lookup - logs.names_report("log", 1, "load", - "path lookup of %q unsuccessful, falling back to file:", - name) - file_resolver (specification) - else - local suffix = filesuffix (name) - if formats[suffix] then - specification.forced = suffix - specification.name = file.removesuffix(name) - specification.forcedname = name - else - specification.name = name - end - end -end - ---[[doc-- - - {\bfseries EXPERIMENTAL}: - \identifier{kpse}-only resolver, for those who can do without - system fonts. - ---doc]]-- - -request_resolvers.kpse = function (specification) - local name = specification.name - local suffix = filesuffix(name) - if suffix and formats[suffix] then - name = file.removesuffix(name) - if resolvers.findfile(name, suffix) then - specification.forced = suffix - specification.forcedname = name - return - end - end - for t, format in next, formats do --- brute force - if kpse.find_file (name, format) then - specification.forced = t - specification.name = name - return - end - end -end - ---[[doc-- - - The \verb|name:| resolver wraps the database function - \luafunction{resolve_name}. - ---doc]]-- - ---- fonts.names.resolvers.name -- Customized version of the ---- generic name resolver. - -request_resolvers.name = function (specification) - local resolved, subfont = resolve_name (specification) - if resolved then - specification.resolved = resolved - specification.sub = subfont - specification.forced = filesuffix (resolved) - specification.forcedname = resolved - specification.name = fileremovesuffix (resolved) - else - file_resolver (specification) - end -end - ---[[doc-- - - Also {\bfseries EXPERIMENTAL}: custom file resolvers via callback. - ---doc]]-- -create_callback("luaotfload.resolve_font", "simple", dummy_function) - -request_resolvers.my = function (specification) - call_callback("luaotfload.resolve_font", specification) -end - ---[[doc-- - - We create a callback for patching fonts on the fly, to be used by - other packages. - It initially contains the empty function that we are going to - override below. - ---doc]]-- - -create_callback("luaotfload.patch_font", "simple", dummy_function) - ---[[doc-- - - \subsection{\CONTEXT override} - \label{define-font} - We provide a simplified version of the original font definition - callback. - ---doc]]-- - -local read_font_file = fonts.definers.read - ---- spec -> size -> id -> tmfdata -local patch_defined_font = function (specification, size, id) - local tfmdata = read_font_file(specification, size, id) - if type(tfmdata) == "table" and tfmdata.shared then - --- We need to test for the “shared” field here - --- or else the fontspec capheight callback will - --- operate on tfm fonts. - call_callback("luaotfload.patch_font", tfmdata, specification) - end - return tfmdata -end - -reset_callback "define_font" - ---[[doc-- - - Finally we register the callbacks. - ---doc]]-- - -local font_definer = config.luaotfload.definer - -if font_definer == "generic" then - add_to_callback("define_font", - fonts.definers.read, - "luaotfload.define_font", - 1) -elseif font_definer == "patch" then - add_to_callback("define_font", - patch_defined_font, - "luaotfload.define_font", - 1) -end - -loadmodule "features.lua" --- contains what was “font-ltx” and “font-otc” -loadmodule "letterspace.lua" --- extra character kerning -loadmodule "auxiliary.lua" --- additionaly high-level functionality (new) - -luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec - --- vim:tw=79:sw=4:ts=4:et -- cgit v1.2.3 From 2779ad3091fd498f14963336afd625cbfa23408f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 16 Jan 2014 07:26:21 +0100 Subject: [*] adapt references to luaotfload.lua --- luaotfload-colors.lua | 2 +- luaotfload-database.lua | 2 +- luaotfload-features.lua | 2 +- luaotfload-fontloader.lua | 2 +- luaotfload-letterspace.lua | 2 +- luaotfload-loaders.lua | 2 +- luaotfload-parsers.lua | 2 +- luaotfload.dtx | 8 ++++---- mkstatus | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/luaotfload-colors.lua b/luaotfload-colors.lua index 86e493f..d999df6 100644 --- a/luaotfload-colors.lua +++ b/luaotfload-colors.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['luaotfload-colors'] = { version = "2.5", - comment = "companion to luaotfload.lua (font color)", + comment = "companion to luaotfload-main.lua (font color)", author = "Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "Luaotfload Development Team", license = "GNU GPL v2.0" diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 3f321d2..98c5d3f 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['luaotfload-database'] = { version = "2.5", - comment = "companion to luaotfload.lua", + comment = "companion to luaotfload-main.lua", author = "Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "Luaotfload Development Team", license = "GNU GPL v2.0" diff --git a/luaotfload-features.lua b/luaotfload-features.lua index d786549..5172f4b 100644 --- a/luaotfload-features.lua +++ b/luaotfload-features.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ["features"] = { version = "2.5", - comment = "companion to luaotfload.lua", + comment = "companion to luaotfload-main.lua", author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" diff --git a/luaotfload-fontloader.lua b/luaotfload-fontloader.lua index a3a2de5..9f21df8 100644 --- a/luaotfload-fontloader.lua +++ b/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 01/07/14 17:03:56 +-- merge date : 01/14/14 16:21:00 do -- begin closure to overcome local limits and interference diff --git a/luaotfload-letterspace.lua b/luaotfload-letterspace.lua index d17aedf..1957f9a 100644 --- a/luaotfload-letterspace.lua +++ b/luaotfload-letterspace.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['letterspace'] = { version = "2.5", - comment = "companion to luaotfload.lua", + comment = "companion to luaotfload-main.lua", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL; adapted by Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" diff --git a/luaotfload-loaders.lua b/luaotfload-loaders.lua index fa00633..2aa8c7c 100644 --- a/luaotfload-loaders.lua +++ b/luaotfload-loaders.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ["loaders"] = { version = "2.5", - comment = "companion to luaotfload.lua", + comment = "companion to luaotfload-main.lua", author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua index e6db21f..42a43aa 100644 --- a/luaotfload-parsers.lua +++ b/luaotfload-parsers.lua @@ -11,7 +11,7 @@ if not modules then modules = { } end modules ['luaotfload-parsers'] = { version = "2.5", - comment = "companion to luaotfload.lua", + comment = "companion to luaotfload-main.lua", author = "Philipp Gesang", copyright = "Luaotfload Development Team", license = "GNU GPL v2.0" diff --git a/luaotfload.dtx b/luaotfload.dtx index 8d59a65..6274c03 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -80,7 +80,7 @@ and the derived files \Msg{* To finish the installation you have to move the following} \Msg{* files into a directory searched by TeX:} \Msg{*} -\Msg{* luaotfload.sty, luaotfload.lua} +\Msg{* luaotfload.sty} \Msg{*} \Msg{* Happy TeXing!} \Msg{*} @@ -1210,7 +1210,7 @@ and the derived files % instead. % Their names remain the same as in \CONTEXT (without the % \verb|otfl|-prefix) since we imported the relevant section of -% \fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload.lua}. +% \fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload-main.lua}. % Thus if you prefer running bleeding edge code from the % \CONTEXT beta, all you have to do is remove % \fileent{luaotfload-merged.lua} from the search path. @@ -1552,7 +1552,7 @@ and the derived files % As of version 2.5, the file \fileent{luaotfload.lua} is no longer % generated from the \abbrev{dtx}. % Instead, it is maintained separately as a plain \identifier{Lua} file -% in the Luaotfload \identifier{git} tree. +% \fileent{luaotfload-main.lua} in the Luaotfload \identifier{git} tree. % The file documentation which used to be found in this section has % been preserved in the comments. % @@ -1576,7 +1576,7 @@ and the derived files [2014/**/** v2.5 OpenType layout system] \RequirePackage{luatexbase} \fi -\RequireLuaModule{luaotfload} +\RequireLuaModule{luaotfload-main} \endinput % \end{macrocode} % \iffalse diff --git a/mkstatus b/mkstatus index 649d26b..28b80ff 100755 --- a/mkstatus +++ b/mkstatus @@ -50,7 +50,7 @@ local names = { "luaotfload-glyphlist.lua", "luaotfload-letterspace.lua", "luaotfload-loaders.lua", - "luaotfload.lua", + "luaotfload-main.lua", "luaotfload-fontloader.lua", "luaotfload-override.lua", "luaotfload-parsers.lua", -- cgit v1.2.3 From e0836925310832691cc63a6daa78e0eb3cc1b9c5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 16 Jan 2014 07:28:28 +0100 Subject: [doc] update news --- NEWS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS b/NEWS index 9211e31..2e2bf8f 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,11 @@ Change History * Remove compatibility with the old mkluatexfontdb script. * Remove test directory. Use https://bitbucket.org/phg/lua-la-tex-tests instead. + * Remove luaotfload.lua from luaotfload.dtx; it is now a separate file + luaotfload-main.lua. + * Move the heavier LPEG parsers from luaotfload-features (syntax) and + luaotfload-database (fontconfig) into the new file + luaotfload-parsers.lua. 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) -- cgit v1.2.3 From d9e354e125ce8c8667a9f62af3d56de14ffc4d05 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 16 Jan 2014 07:29:37 +0100 Subject: [doc] update graph --- filegraph.dot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filegraph.dot b/filegraph.dot index bce15cc..90e6e5c 100644 --- a/filegraph.dot +++ b/filegraph.dot @@ -122,7 +122,7 @@ strict digraph luaotfload_files { //looks weird with circo ... style = "filled,rounded", penwidth=2] - luaotfload [label = "luaotfload.lua", + luaotfload [label = "luaotfload-main.lua", shape = rect, width = "3.2cm", height = "1.2cm", -- cgit v1.2.3 From 2da71ff00e1533aa6ca1c317ec89e70655f0dba3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 16 Jan 2014 07:29:52 +0100 Subject: [*] updat Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index aec2ac0..8405e3a 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ RESOURCES = $(GLYPHS) $(CHARS) $(STATUS) GRAPHED = $(DOTPDF) MAN = $(MANPAGE) COMPILED = $(DOC) -UNPACKED = luaotfload.sty luaotfload.lua +UNPACKED = luaotfload.sty GENERATED = $(GRAPHED) $(UNPACKED) $(COMPILED) $(RESOURCES) $(MAN) SOURCE = $(DTX) $(MANSOURCE) $(OTFL) README Makefile NEWS $(RESOURCESCRIPTS) -- cgit v1.2.3 From 0b7f519dc715b9725d5b369ca32cbabbcb4fd126 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 16 Jan 2014 07:42:03 +0100 Subject: [main] restore line that got lost during the move --- luaotfload-main.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/luaotfload-main.lua b/luaotfload-main.lua index 90e35ef..27f0a99 100644 --- a/luaotfload-main.lua +++ b/luaotfload-main.lua @@ -154,6 +154,7 @@ end --doc]]-- +local Cs, P, lpegmatch = lpeg.Cs, lpeg.P, lpeg.match local p_dot, p_slash = P".", P"/" local p_suffix = (p_dot * (1 - p_dot - p_slash)^1 * P(-1)) / "" -- cgit v1.2.3 From 2a5b67b754089b6fd3299e6621885ebcc3fb627b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 16 Jan 2014 07:47:07 +0100 Subject: [*] use different placeholder date in .sty --- luaotfload.dtx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 6274c03..8f8632c 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1573,7 +1573,7 @@ and the derived files \else \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luaotfload}% - [2014/**/** v2.5 OpenType layout system] + [2014/42/42 v2.5 OpenType layout system] \RequirePackage{luatexbase} \fi \RequireLuaModule{luaotfload-main} -- cgit v1.2.3 From e343508964be6518698e3b9ed211b487d985bd92 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 13:21:04 +0100 Subject: [db] integrate the values for italic angle and weight into style choice --- luaotfload-database.lua | 74 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 98c5d3f..09106a5 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -1479,10 +1479,7 @@ local organize_styledata = function (fontname, return { -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size size = get_size_info (metadata), - weight = { - pfminfo.weight, -- integer (multiple of 100?) - sanitize_fontname (info.weight), -- style name - }, + weight = pfminfo.weight or 400, split = split_fontname (fontname), width = pfminfo.width, italicangle = metadata.italicangle, @@ -1601,8 +1598,7 @@ t1_fullinfo = function (filename, _subfont, location, basename, format) size = false, splitstyle = splitstyle, fontstyle_name = style ~= "" and style or weight, - weight = { metadata.pfminfo.weight, - weight }, + weight = metadata.pfminfo.weight or 400, italicangle = italicangle, } end @@ -2458,13 +2454,55 @@ local check_regular do local splitfontname = lpeg.splitat "-" - local choose_exact = function (field) - if italic_synonym [field] then + --[[doc-- + + Regarding the italic angle, only a small minority of fonts advertise + oblique shape despite having a zero angle. On my machine, these are + + + # /luaotfload-tool.lua --list=subfamily:italic --fields=italicangle,plainname | grep -e '\s0\s' | cut -f 2,3 + 0 Quattrocento Sans Italic + 0 Libre Baskerville Italic + 0 Cabin Italic + 0 PersianModern-Italic + 0 PersianModern-ItalicShadow + 0 PersianModern-ItalicOutline + 0 Alegreya SC Italic + 0 Alegreya Italic + 0 XB Niloofar Italic + 0 Bukyvede-Italic + + (Weirdly, some of those set a nonzero italic angle only for the + bold italic variant, while neglecting to do so for the oblique + shape with normal weight ...) + These outliers can be detected by checking the appropriate subfamily + etc. fields. + + --doc]]-- + + local choose_exact = function (field, weight, italicangle) + local i = false + local b = false + + if italicangle ~= 0 or italic_synonym [field] then + i = true + end + + if weight == 700 or field == "bold" then + b = true + end + + if field == "bolditalic" or field == "boldoblique" then + b = true + i = true + end + + if i and b then + return "bi" + elseif i then return "i" - elseif field == "bold" then + elseif b then return "b" - elseif field == "bolditalic" or field == "boldoblique" then - return "bi" end return false @@ -2473,16 +2511,18 @@ do pick_style = function (fontstyle_name, prefmodifiers, subfamily, - splitstyle) + splitstyle, + weight, + italicangle) local style if fontstyle_name then - style = choose_exact (fontstyle_name) + style = choose_exact (fontstyle_name, weight, italicangle) end if not style then if prefmodifiers then - style = choose_exact (prefmodifiers) + style = choose_exact (prefmodifiers, weight, italicangle) elseif subfamily then - style = choose_exact (subfamily) + style = choose_exact (subfamily, weight, italicangle) end end -- if not style and splitstyle then @@ -2628,7 +2668,9 @@ local collect_families = function (mappings) local modifier = pick_style (fontstyle_name, prefmodifiers, subfamily, - splitstyle) + splitstyle, + weight, + italicangle) if not modifier then --- regular, exact only modifier = check_regular (fontstyle_name, -- cgit v1.2.3 From 3a82aa62d66be113ef4d5f13e9723d70943b02e3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 13:23:39 +0100 Subject: [tool] remove obsolete source documentation --- luaotfload-tool.lua | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 32086e1..5b7e0a2 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -88,21 +88,6 @@ end require(loader_path) ---[[doc-- -Depending on how the script is called we change its behavior. -For backwards compatibility, moving or symlinking the script to a -file name starting with \fileent{mkluatexfontdb} will cause it to -trigger a database update on every run. -Running as \fileent{luaotfload-tool} -- the new name -- will do this upon -request only. - -There are two naming conventions followed here: firstly that of -utilities such as \fileent{mktexpk}, \fileent{mktexlsr} and the likes, -and secondly that of \fileent{fmtutil}. -After support for querying the database was added, the latter appeared -to be the more appropriate. ---doc]]-- - config = config or { } local config = config local luaotfloadconfig = config.luaotfload or { } -- cgit v1.2.3 From 0f8d086d5f04f945198c054bc80c2a4f19cc6a8e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 13:38:16 +0100 Subject: [db] do not rely on italic angle when determining the font style The italic angle will be non-zero for semibold and other variants which messes up the assignment of font families. --- luaotfload-database.lua | 46 +++++++--------------------------------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 09106a5..28109ec 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -1483,10 +1483,6 @@ local organize_styledata = function (fontname, split = split_fontname (fontname), width = pfminfo.width, italicangle = metadata.italicangle, --- italicangle = { --- metadata.italicangle, -- float --- info.italicangle, -- truncated to integer point size? --- }, --- this is for querying, see www.ntg.nl/maps/40/07.pdf for details units_per_em = metadata.units_per_em, version = metadata.version, @@ -2454,37 +2450,11 @@ local check_regular do local splitfontname = lpeg.splitat "-" - --[[doc-- - - Regarding the italic angle, only a small minority of fonts advertise - oblique shape despite having a zero angle. On my machine, these are - - - # /luaotfload-tool.lua --list=subfamily:italic --fields=italicangle,plainname | grep -e '\s0\s' | cut -f 2,3 - 0 Quattrocento Sans Italic - 0 Libre Baskerville Italic - 0 Cabin Italic - 0 PersianModern-Italic - 0 PersianModern-ItalicShadow - 0 PersianModern-ItalicOutline - 0 Alegreya SC Italic - 0 Alegreya Italic - 0 XB Niloofar Italic - 0 Bukyvede-Italic - - (Weirdly, some of those set a nonzero italic angle only for the - bold italic variant, while neglecting to do so for the oblique - shape with normal weight ...) - These outliers can be detected by checking the appropriate subfamily - etc. fields. - - --doc]]-- - - local choose_exact = function (field, weight, italicangle) + local choose_exact = function (field, weight) local i = false local b = false - if italicangle ~= 0 or italic_synonym [field] then + if italic_synonym [field] then i = true end @@ -2512,17 +2482,16 @@ do prefmodifiers, subfamily, splitstyle, - weight, - italicangle) + weight) local style if fontstyle_name then - style = choose_exact (fontstyle_name, weight, italicangle) + style = choose_exact (fontstyle_name, weight) end if not style then if prefmodifiers then - style = choose_exact (prefmodifiers, weight, italicangle) + style = choose_exact (prefmodifiers, weight) elseif subfamily then - style = choose_exact (subfamily, weight, italicangle) + style = choose_exact (subfamily, weight) end end -- if not style and splitstyle then @@ -2669,8 +2638,7 @@ local collect_families = function (mappings) prefmodifiers, subfamily, splitstyle, - weight, - italicangle) + weight) if not modifier then --- regular, exact only modifier = check_regular (fontstyle_name, -- cgit v1.2.3 From 814443264c28faa585b364196b164cf5a9eed4ba Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 13:47:46 +0100 Subject: [parsers] fix incomplete feature separator rule --- luaotfload-parsers.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua index 42a43aa..51d251c 100644 --- a/luaotfload-parsers.lua +++ b/luaotfload-parsers.lua @@ -56,6 +56,7 @@ local lfsisdir = lfs.isdir local dot = P"." local colon = P":" +local semicolon = P";" local comma = P"," local noncomma = 1 - comma local slash = P"/" @@ -476,7 +477,7 @@ local check_garbage = function (_,i, garbage) return false end -local featuresep = comma +local featuresep = comma + semicolon --- modifiers --------------------------------------------------------- --[[doc-- -- cgit v1.2.3 From 0380abcae75cccdc394c4da1fbfe838e876e53cc Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 13:52:55 +0100 Subject: [tests] include new parsers file --- mktests | 1 + 1 file changed, 1 insertion(+) diff --git a/mktests b/mktests index baa710c..817fbf6 100755 --- a/mktests +++ b/mktests @@ -31,6 +31,7 @@ kpse.set_program_name "luatex" require "lualibs" require "luaotfload-basics-gen.lua" require "luaotfload-override.lua" +require "luaotfload-parsers" require "luaotfload-database" local names = fonts.names -- cgit v1.2.3 From c56e6b5862ec6e27f25bb00ffeba4ee4aa3ccf61 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 14:16:17 +0100 Subject: [db] test numeric weight only under certain circumstances Else DejaVu Sans picks the condensed shape for bold because all fonts in the bundle claim to be part of the same font family, without differentiating by setting a meta family. --- luaotfload-database.lua | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 28109ec..c979acf 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -2455,23 +2455,19 @@ do local b = false if italic_synonym [field] then - i = true + return "i" end - if weight == 700 or field == "bold" then - b = true + if field == "bold" then + return "b" end if field == "bolditalic" or field == "boldoblique" then - b = true - i = true + return "bi" end - if i and b then - return "bi" - elseif i then - return "i" - elseif b then + if weight == 700 then + --- matches only if weight is specified return "b" end @@ -2485,11 +2481,11 @@ do weight) local style if fontstyle_name then - style = choose_exact (fontstyle_name, weight) + style = choose_exact (fontstyle_name --[[ , weight ]]) end if not style then if prefmodifiers then - style = choose_exact (prefmodifiers, weight) + style = choose_exact (prefmodifiers --[[ , weight ]]) elseif subfamily then style = choose_exact (subfamily, weight) end @@ -2506,7 +2502,9 @@ do check_regular = function (fontstyle_name, prefmodifiers, subfamily, - splitstyle) + splitstyle, + italicangle, + weight) if fontstyle_name then return regular_synonym [fontstyle_name] @@ -2516,6 +2514,8 @@ do return regular_synonym [subfamily] elseif splitstyle then return regular_synonym [splitstyle] + elseif italicangle == 0 and weight == 400 then + return true end return nil @@ -2644,7 +2644,9 @@ local collect_families = function (mappings) modifier = check_regular (fontstyle_name, prefmodifiers, subfamily, - splitstyle) + splitstyle, + italicangle, + weight) end if modifier then -- cgit v1.2.3 From a52e157de5a969b7d80af1c8621a7d7b031b572f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 14:19:09 +0100 Subject: [tests] adapt file names --- mktests | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mktests b/mktests index 817fbf6..3a55b8d 100755 --- a/mktests +++ b/mktests @@ -87,11 +87,11 @@ local infer_regular_style = { { "Garamond Premier Pro", "GaramondPremrPro.otf" }, { "CMU Serif", "cmunrm.otf" }, { "CMU Sans Serif", "cmunss.otf" }, - { "Minion Pro", "MinionPro_Regular.otf" }, + { "Minion Pro", "MinionPro-Regular.otf" }, --- Below test will succeed only if we match for the --- splainname (= sanitized tfmdata.fullname) field --- explicitly. - { "Minion Pro Italic", "MinionPro_It.otf" }, + { "Minion Pro Italic", "MinionPro-It.otf" }, } local choose_optical_size = { @@ -141,10 +141,10 @@ local choose_style = { Also, the full Minion Pro set comes with different optical sizes which for monetary reasons cannot considered here. --]]-- - { { name = "Minion Pro", style = "regular" }, "MinionPro_Regular.otf" }, - { { name = "Minion Pro", style = "italic" }, "MinionPro_It.otf" }, - { { name = "Minion Pro", style = "bold" }, "MinionPro_Bold.otf" }, - { { name = "Minion Pro", style = "bolditalic" }, "MinionPro_BoldIt.otf" }, + { { name = "Minion Pro", style = "regular" }, "MinionPro-Regular.otf" }, + { { name = "Minion Pro", style = "italic" }, "MinionPro-It.otf" }, + { { name = "Minion Pro", style = "bold" }, "MinionPro-Bold.otf" }, + { { name = "Minion Pro", style = "bolditalic" }, "MinionPro-BoldIt.otf" }, } --- this needs a database built with --formats=+pfa,pfb,afm -- cgit v1.2.3 From 2ec3fd45d2e7b0c1b304e47ef01532a848acc73e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 19:17:23 +0100 Subject: [db] fall back to alternative bold-ish weights if a family lacks entries for bold Closing issue #177 This adds an extra pass to the family group builder. The lookup though stays the same as before. --- luaotfload-database.lua | 201 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 154 insertions(+), 47 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index c979acf..dcf5c86 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -2450,10 +2450,8 @@ local check_regular do local splitfontname = lpeg.splitat "-" - local choose_exact = function (field, weight) - local i = false - local b = false - + local choose_exact = function (field) + --- only clean matches, without guessing if italic_synonym [field] then return "i" end @@ -2466,36 +2464,39 @@ do return "bi" end - if weight == 700 then - --- matches only if weight is specified - return "b" - end - return false end pick_style = function (fontstyle_name, prefmodifiers, subfamily, - splitstyle, - weight) + splitstyle) local style if fontstyle_name then - style = choose_exact (fontstyle_name --[[ , weight ]]) + style = choose_exact (fontstyle_name) end if not style then if prefmodifiers then - style = choose_exact (prefmodifiers --[[ , weight ]]) + style = choose_exact (prefmodifiers) elseif subfamily then - style = choose_exact (subfamily, weight) + style = choose_exact (subfamily) end end --- if not style and splitstyle then --- style = choose_exact (splitstyle) --- end return style end + pick_fallback_style = function (italicangle, weight) + --- more aggressive, but only to determine bold faces + if weight > 500 then --- bold spectrum matches + if italicangle == 0 then + return tostring (weight) + else + return tostring (weight) .. "i" + end + end + return false + end + --- we use only exact matches here since there are constructs --- like “regularitalic” (Cabin, Bodoni Old Fashion) @@ -2573,28 +2574,25 @@ local add_family = function (name, subtable, modifier, entry) subtable [name] = familytable end - --- the style table is treated as an unordered list - local styletable = familytable [modifier] - if not styletable then - styletable = { } - familytable [modifier] = styletable - end + local size = entry.size - if not entry.prefmodifiers then --- default size for this style/family combo - styletable.default = entry.index - end + familytable [#familytable + 1] = { + index = entry.index, + size = size and { size [1], size [2], size [3] }, + modifier = modifier, + weight = entry.weight, + } +end - local size = entry.size --- dsnsize * high * low - if size then - styletable [#styletable + 1] = { - size [1], - size [2], - size [3], - entry.index, - } - else - styletable.default = entry.index +local get_subtable = function (families, entry) + local location = entry.location + local format = entry.format + local subtable = families [location] [format] + if not subtable then + subtable = { } + families [location] [format] = subtable end + return subtable end local collect_families = function (mappings) @@ -2615,14 +2613,7 @@ local collect_families = function (mappings) pull_values (entry) end - local location = entry.location - local format = entry.format - - local subtable = families [location] [format] - if not subtable then - subtable = { } - families [location] [format] = subtable - end + local subtable = get_subtable (families, entry) local familyname = entry.familyname local metafamily = entry.metafamily @@ -2637,8 +2628,7 @@ local collect_families = function (mappings) local modifier = pick_style (fontstyle_name, prefmodifiers, subfamily, - splitstyle, - weight) + splitstyle) if not modifier then --- regular, exact only modifier = check_regular (fontstyle_name, @@ -2674,6 +2664,11 @@ local collect_families = function (mappings) -- if metafamily and metafamily ~= familyname then -- add_family (metafamily, subtable, modifier, entry) -- end + elseif weight > 500 then -- in bold spectrum + modifier = pick_fallback_style (italicangle, weight) + if modifier then + add_family (familyname, subtable, modifier, entry) + end end end @@ -2681,6 +2676,116 @@ local collect_families = function (mappings) return families end +--[[doc-- + + add_bold_spectrum -- For not-quite-bold faces, determine whether + they can fill in for a missing bold face slot in a matching family. + + Some families like Lucida do not contain real bold / bold italic + members. Instead, they have semibold variants at weight 600 which + we must add in a separate pass. + +--doc]]-- + +local bold_spectrum_low = 501 --- 500 is medium, 900 heavy/black +local bold_weight = 700 +local style_categories = { "r", "b", "i", "bi" } +local bold_categories = { "b", "bi" } + +local group_modifiers = function (mappings, families) + report ("info", 2, "db", "Analyzing bold weight fallbacks.") + for location, location_data in next, families do + for format, format_data in next, location_data do + for familyname, collected in next, format_data do + local styledata = { } --- will replace the “collected” table + --- First, fill in the ordinary style data that + --- fits neatly into the four relevant modifier + --- categories. + for _, modifier in next, style_categories do + local entries + for key, info in next, collected do + if info.modifier == modifier then + if not entries then + entries = { } + end + local index = info.index + local entry = mappings [index] + local size = entry.size + if size then + entries [#entries + 1] = { + size [1], + size [2], + size [3], + index, + } + else + entries.default = index + end + collected [key] = nil + end + styledata [modifier] = entries + end + end + + --- At this point the family set may still lack + --- entries for bold or bold italic. We will fill + --- those in using the modifier with the numeric + --- weight that is closest to bold (700). + if next (collected) then --- there are uncategorized entries + for _, modifier in next, bold_categories do + if not styledata [modifier] then + local closest + local minimum = 2^51 + for key, info in next, collected do + local info_modifier = tonumber (info.modifier) and "b" or "bi" + if modifier == info_modifier then + local index = info.index + local entry = mappings [index] + local weight = entry.weight + local diff = weight < 700 and 700 - weight or weight - 700 + if diff < minimum then + minimum = diff + closest = weight + end + end + end + if closest then + --- We know there is a substitute face for the modifier. + --- Now we scan the list again to extract the size data + --- in case the shape is available at multiple sizes. + local entries = { } + for key, info in next, collected do + local info_modifier = tonumber (info.modifier) and "b" or "bi" + if modifier == info_modifier then + local index = info.index + local entry = mappings [index] + local size = entry.size + if entry.weight == closest then + if size then + entries [#entries + 1] = { + size [1], + size [2], + size [3], + index, + } + else + entries.default = index + end + end + end + end + styledata [modifier] = entries + end + end + end + end + format_data [familyname] = styledata + end + end + end + return families +end + local cmp_sizes = function (a, b) return a [1] < b [1] end @@ -2969,7 +3074,9 @@ update_names = function (currentnames, force, dry_run) targetnames.files = generate_filedata (targetnames.mappings) --- pass 4: build family lookup table - targetnames.families = collect_families (targetnames.mappings) + targetnames.families = collect_families (targetnames.mappings) + targetnames.families = group_modifiers (targetnames.mappings, + targetnames.families) --- pass 5: order design size tables targetnames.families = order_design_sizes (targetnames.families) -- cgit v1.2.3 From 238bc8d91de3413c843fc5d3c8edefcd975d9701 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 20:43:05 +0100 Subject: [db] drop redundant information between passes --- luaotfload-database.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index dcf5c86..ea79e14 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -2578,9 +2578,7 @@ local add_family = function (name, subtable, modifier, entry) familytable [#familytable + 1] = { index = entry.index, - size = size and { size [1], size [2], size [3] }, modifier = modifier, - weight = entry.weight, } end -- cgit v1.2.3 From dab04e4cca0e5e9877ab69b1f46574e7554670a8 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 20:43:33 +0100 Subject: [db] adapt status messages --- luaotfload-database.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index ea79e14..eda336b 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -2595,7 +2595,7 @@ end local collect_families = function (mappings) - report ("info", 2, "db", "Analyzing families, sizes, and styles.") + report ("info", 2, "db", "Analyzing families.") local families = { ["local"] = { }, @@ -2691,7 +2691,7 @@ local style_categories = { "r", "b", "i", "bi" } local bold_categories = { "b", "bi" } local group_modifiers = function (mappings, families) - report ("info", 2, "db", "Analyzing bold weight fallbacks.") + report ("info", 2, "db", "Analyzing shapes, weights, and styles.") for location, location_data in next, families do for format, format_data in next, location_data do for familyname, collected in next, format_data do @@ -3073,10 +3073,12 @@ update_names = function (currentnames, force, dry_run) --- pass 4: build family lookup table targetnames.families = collect_families (targetnames.mappings) + + --- pass 5: arrange style and size info targetnames.families = group_modifiers (targetnames.mappings, targetnames.families) - --- pass 5: order design size tables + --- pass 6: order design size tables targetnames.families = order_design_sizes (targetnames.families) -- cgit v1.2.3 From ea11a26f5c1b0e86187ab39235092363a7f03d42 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 26 Jan 2014 20:56:48 +0100 Subject: [db] increment index version --- luaotfload-database.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index eda336b..cf9e672 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -131,7 +131,7 @@ luaotfloadconfig.compress = luaotfloadconfig.compress ~= false local names = fonts.names local name_index = nil --> upvalue for names.data local lookup_cache = nil --> for names.lookups -names.version = 2.4 +names.version = 2.5 names.data = nil --- contains the loaded database names.lookups = nil --- contains the lookup cache -- cgit v1.2.3 From fade97082915b67806869ae5e35e520f6c354471 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 2 Feb 2014 09:57:41 +0100 Subject: [fontloader] sync with Context as of 2014-02-02 --- luaotfload-fontloader.lua | 93 +++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/luaotfload-fontloader.lua b/luaotfload-fontloader.lua index 9f21df8..3986118 100644 --- a/luaotfload-fontloader.lua +++ b/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 01/14/14 16:21:00 +-- merge date : 02/01/14 14:22:42 do -- begin closure to overcome local limits and interference @@ -101,7 +101,9 @@ local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.forma local floor=math.floor local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print -setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +if setinspector then + setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +end lpeg.patterns=lpeg.patterns or {} local patterns=lpeg.patterns local anything=P(1) @@ -395,7 +397,7 @@ function lpeg.replacer(one,two,makefunction,isutf) return pattern end end -function lpeg.finder(lst,makefunction) +function lpeg.finder(lst,makefunction,isutf) local pattern if type(lst)=="table" then pattern=P(false) @@ -411,7 +413,11 @@ function lpeg.finder(lst,makefunction) else pattern=P(lst) end - pattern=(1-pattern)^0*pattern + if isutf then + pattern=((utf8char or 1)-pattern)^0*pattern + else + pattern=(1-pattern)^0*pattern + end if makefunction then return function(str) return lpegmatch(pattern,str) @@ -1636,7 +1642,9 @@ function table.print(t,...) serialize(print,t,...) end end -setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +if setinspector then + setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +end function table.sub(t,i,j) return { unpack(t,i,j) } end @@ -2508,8 +2516,12 @@ local unpack,concat=table.unpack,table.concat local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc local patterns,lpegmatch=lpeg.patterns,lpeg.match local utfchar,utfbyte=utf.char,utf.byte -local loadstripped=_LUAVERSION<5.2 and load or function(str) - return load(dump(load(str),true)) +local loadstripped=function(str,shortcuts) + if shortcuts then + return load(dump(load(str),true),nil,nil,shortcuts) + else + return load(dump(load(str),true)) + end end if not number then number={} end local stripper=patterns.stripzeros @@ -2659,31 +2671,34 @@ function number.sparseexponent(f,n) end return tostring(n) end -local preamble=[[ -local type = type -local tostring = tostring -local tonumber = tonumber -local format = string.format -local concat = table.concat -local signed = number.signed -local points = number.points -local basepoints = number.basepoints -local utfchar = utf.char -local utfbyte = utf.byte -local lpegmatch = lpeg.match -local nspaces = string.nspaces -local tracedchar = string.tracedchar -local autosingle = string.autosingle -local autodouble = string.autodouble -local sequenced = table.sequenced -local formattednumber = number.formatted -local sparseexponent = number.sparseexponent -]] local template=[[ %s %s return function(%s) return %s end ]] +local environment={ + global=global or _G, + lpeg=lpeg, + type=type, + tostring=tostring, + tonumber=tonumber, + format=string.format, + concat=table.concat, + signed=number.signed, + points=number.points, + basepoints=number.basepoints, + utfchar=utf.char, + utfbyte=utf.byte, + lpegmatch=lpeg.match, + nspaces=string.nspaces, + tracedchar=string.tracedchar, + autosingle=string.autosingle, + autodouble=string.autodouble, + sequenced=table.sequenced, + formattednumber=number.formatted, + sparseexponent=number.sparseexponent, +} +local preamble="" local arguments={ "a1" } setmetatable(arguments,{ __index=function(t,k) local v=t[k-1]..",a"..k @@ -3005,8 +3020,8 @@ local builder=Cs { "start", ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, } local direct=Cs ( - P("%")/""*Cc([[local format = string.format return function(str) return format("%]])*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*Cc([[",str) end]])*P(-1) - ) + P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]] +) local function make(t,str) local f local p @@ -3018,7 +3033,7 @@ local function make(t,str) p=lpegmatch(builder,str,1,"..",t._extensions_) if n>0 then p=format(template,preamble,t._preamble_,arguments[n],p) - f=loadstripped(p)() + f=loadstripped(p,t._environment_)() else f=function() return str end end @@ -3031,7 +3046,11 @@ local function use(t,fmt,...) end strings.formatters={} function strings.formatters.new() - local t={ _extensions_={},_preamble_="",_type_="formatter" } + local e={} + for k,v in next,environment do + e[k]=v + end + local t={ _extensions_={},_preamble_="",_environment_=e,_type_="formatter" } setmetatable(t,{ __index=make,__call=use }) return t end @@ -3041,8 +3060,12 @@ string.formatter=function(str,...) return formatters[str](...) end local function add(t,name,template,preamble) if type(t)=="table" and t._type_=="formatter" then t._extensions_[name]=template or "%s" - if preamble then + if type(preamble)=="string" then t._preamble_=preamble.."\n"..t._preamble_ + elseif type(preamble)=="table" then + for k,v in next,preamble do + t._environment_[k]=v + end end end end @@ -3051,9 +3074,9 @@ patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"" patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) -add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]]) -add(formatters,"tex",[[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]]) -add(formatters,"lua",[[lpegmatch(luaescape,%s)]],[[local luaescape = lpeg.patterns.luaescape]]) +add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) +add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) +add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) end -- closure -- cgit v1.2.3 From cd7f95462e286b9e575a96a004f8f931a56f446c Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 5 Feb 2014 19:01:26 +0100 Subject: [tool] add brief package info to output of --version --- luaotfload-tool.lua | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 5b7e0a2..ec0611d 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -175,12 +175,13 @@ local help_messages = { Usage: %s [OPTIONS...] -Operations on the Luaotfload font names database. + Luaotfload font management and diagnostic utility. + This program is part of the Luaotfload package. -This tool is part of the luaotfload package. Valid options are: + Valid options are: ------------------------------------------------------------------------------- - VERBOSITY AND LOGGING + VERBOSITY AND DIAGNOSTICS -q --quiet don't output anything -v --verbose=LEVEL be more verbose (print the searched directories) @@ -266,8 +267,16 @@ local help_msg = function (version) luaotfloadconfig.cache_dir))) end +local about = [[ +%s: + Luaotfload font management and diagnostic utility. + License: GNU GPL v2.0. + Report problems to +]] + local version_msg = function ( ) local out = function (...) texiowrite_nl (stringformat (...)) end + out (about, luaotfloadconfig.self) out ("%s version %q", luaotfloadconfig.self, version) out ("revision %q", luaotfloadstatus.notes.revision) out ("database version %q", names.version) -- cgit v1.2.3 From 1f460397406ef41ae87ea171273c412a6acc21cf Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 5 Feb 2014 19:06:21 +0100 Subject: [tool] bury dead code --- luaotfload-tool.lua | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index ec0611d..8bc590a 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -104,18 +104,7 @@ if not luaotfloadconfig.strip then luaotfloadconfig.strip = true end -do -- we don’t have file.basename and the likes yet, so inline parser ftw - local slash = P"/" - local dot = P"." - local noslash = 1 - slash - local slashes = slash^1 - local path = slashes^-1 * (noslash^1 * slashes)^1 - local thename = (1 - slash - dot)^1 - local extension = dot * (1 - slash - dot)^1 - local p_basename = path^-1 * C(thename) * extension^-1 * P(-1) - - luaotfloadconfig.self = "luaotfload-tool" -end +luaotfloadconfig.self = "luaotfload-tool" config.lualibs = config.lualibs or { } config.lualibs.verbose = false -- cgit v1.2.3 From c29e7f302f6ab05b6e0975daeaa60240b530885d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 5 Feb 2014 20:28:53 +0100 Subject: [db] compensate for broken OT names tables --- luaotfload-database.lua | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index cf9e672..023d5f8 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -1376,21 +1376,34 @@ local get_english_names = function (names, basename) english_names = raw_namedata.names end end - else - -- no names table, probably a broken font - report("log", 1, "db", - "Broken font %s rejected due to missing names table.", - basename) - return nil end - return english_names + if not english_names then + -- no (English) names table, probably a broken font + report("both", 3, "db", + "%s: missing or broken names table.", basename) + end + + return english_names or { } end local organize_namedata = function (metadata, english_names, basename, info) + local default_name = english_names.compatfull + or english_names.fullname + or english_names.postscriptname + or metadata.fullname + or metadata.fontname + or info.fullname --- TODO check if fontloader.info() is ready for prime + or info.fontname + local default_family = english_names.preffamily + or english_names.family + or metadata.familyname + or info.familyname +-- local default_modifier = english_names.prefmodifiers +-- or english_names.subfamily local fontnames = { --- see --- https://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html @@ -1406,14 +1419,15 @@ local organize_namedata = function (metadata, --- However, in some fonts (e.g. CMU) all three fields are --- identical. fullname = --[[ 18 ]] english_names.compatfull - or --[[ 4 ]] english_names.fullname, + or --[[ 4 ]] english_names.fullname + or default_name, --- we keep both the “preferred family” and the “family” --- values around since both are valid but can turn out --- quite differently, e.g. with Latin Modern: --- preffamily: “Latin Modern Sans”, --- family: “LM Sans 10” preffamily = --[[ 16 ]] english_names.preffamilyname, - family = --[[ 1 ]] english_names.family, + family = --[[ 1 ]] english_names.family or default_family, prefmodifiers = --[[ 17 ]] english_names.prefmodifiers, subfamily = --[[ 2 ]] english_names.subfamily, psname = --[[ 6 ]] english_names.postscriptname, @@ -1477,7 +1491,7 @@ local organize_styledata = function (fontname, local names = metadata.names return { - -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size + --- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size size = get_size_info (metadata), weight = pfminfo.weight or 400, split = split_fontname (fontname), @@ -2568,6 +2582,9 @@ local pull_values = function (entry) end local add_family = function (name, subtable, modifier, entry) + if not name then --- probably borked font + return + end local familytable = subtable [name] if not familytable then familytable = { } -- cgit v1.2.3 From 44db443661f4d9455e86448885183c0690d62c93 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 5 Feb 2014 21:41:58 +0100 Subject: [doc] update manpage regarding --version --- luaotfload-tool.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/luaotfload-tool.rst b/luaotfload-tool.rst index be53ded..2ac206f 100644 --- a/luaotfload-tool.rst +++ b/luaotfload-tool.rst @@ -193,7 +193,8 @@ miscellaneous directory (the name will be chosen automatically (**experimental!**). ---version, -V Show version number and exit. +--version, -V Show version numbers of components as well as + some basic information and exit. --help, -h Show help message and exit. --diagnose=CHECK Run the diagnostic procedure *CHECK*. Available -- cgit v1.2.3 From 522b290487cc665412b1206ce394bd437dff8f70 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 7 Feb 2014 07:48:57 +0100 Subject: [fontloader] sync with Context as of 2014-02-07 --- luaotfload-fontloader.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/luaotfload-fontloader.lua b/luaotfload-fontloader.lua index 3986118..8c31750 100644 --- a/luaotfload-fontloader.lua +++ b/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 02/01/14 14:22:42 +-- merge date : 02/07/14 00:57:35 do -- begin closure to overcome local limits and interference @@ -6450,7 +6450,7 @@ local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf otf.glists={ "gsub","gpos" } -otf.version=2.750 +otf.version=2.751 otf.cache=containers.define("fonts","otf",otf.version,true) local fontdata=fonts.hashes.identifiers local chardata=characters and characters.data @@ -6602,6 +6602,7 @@ local valid_fields=table.tohash { "upos", "use_typo_metrics", "uwidth", + "validation_state", "version", "vert_base", "weight", @@ -7914,6 +7915,11 @@ actions["check metadata"]=function(data,filename,raw) ttftables[i].data="deleted" end end + if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then + local name=file.nameonly(filename) + metadata.fontname="bad-fontname-"..name + metadata.fullname="bad-fullname-"..name + end end actions["cleanup tables"]=function(data,filename,raw) data.resources.indices=nil -- cgit v1.2.3 From 20e5ffb1360a102ac52e3a0a99513499051318b8 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 7 Feb 2014 08:09:03 +0100 Subject: [db] replace broken fontnames with dummies --- luaotfload-database.lua | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 023d5f8..77a7162 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -105,6 +105,7 @@ local stringis_empty = string.is_empty local stringsplit = string.split local stringstrip = string.strip local tableappend = table.append +local tablecontains = table.contains local tablecopy = table.copy local tablefastcopy = table.fastcopy local tabletofile = table.tofile @@ -1331,6 +1332,13 @@ end --- find_closest() regarding the omission of ``fontloader.close()``. + TODO -- check if fontloader.info() is ready for prime in 0.78+ + -- fields /tables needed: + -- names + -- postscriptname + -- validation_state + -- .. + --doc]]-- local load_font_file = function (filename, subfont) @@ -1365,8 +1373,24 @@ local get_size_info = function (metadata) return false end -local get_english_names = function (names, basename) +local get_english_names = function (metadata, basename) + local validation_state = metadata.validation_state + if validation_state + and tablecontains (validation_state, "bad_ps_fontname") + then + report("both", 3, "db", + "%s has invalid postscript font names, using dummies.", + basename) + --- Broken names table, e.g. avkv.ttf with UTF-16 strings; + --- we put some dummies in place like the fontloader + --- (font-otf.lua) does. + return { + fontname = "bad-fontname-" .. basename, + fullname = "bad-fullname-" .. basename, + } + end + local names = metadata.names local english_names if names then @@ -1396,7 +1420,7 @@ local organize_namedata = function (metadata, or english_names.postscriptname or metadata.fullname or metadata.fontname - or info.fullname --- TODO check if fontloader.info() is ready for prime + or info.fullname or info.fontname local default_family = english_names.preffamily or english_names.family @@ -1467,7 +1491,6 @@ local organize_namedata = function (metadata, fullname = metadata.fullname, familyname = metadata.familyname, } - end @@ -1523,7 +1546,7 @@ ot_fullinfo = function (filename, return nil end - local english_names = get_english_names (metadata.names, basename) + local english_names = get_english_names (metadata, basename) local namedata = organize_namedata (metadata, english_names, basename, -- cgit v1.2.3 From bb4ed0bee36aebbb12362a2237c154d2006a7ee3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 7 Feb 2014 22:36:32 +0100 Subject: [db] copy names fields from tfmdata and insert fallbacks for broken names --- luaotfload-database.lua | 114 ++++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 42 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 77a7162..d9bba80 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -1373,23 +1373,7 @@ local get_size_info = function (metadata) return false end -local get_english_names = function (metadata, basename) - local validation_state = metadata.validation_state - if validation_state - and tablecontains (validation_state, "bad_ps_fontname") - then - report("both", 3, "db", - "%s has invalid postscript font names, using dummies.", - basename) - --- Broken names table, e.g. avkv.ttf with UTF-16 strings; - --- we put some dummies in place like the fontloader - --- (font-otf.lua) does. - return { - fontname = "bad-fontname-" .. basename, - fullname = "bad-fullname-" .. basename, - } - end - +local get_english_names = function (metadata) local names = metadata.names local english_names @@ -1397,34 +1381,78 @@ local get_english_names = function (metadata, basename) --inspect(names) for _, raw_namedata in next, names do if raw_namedata.lang == "English (US)" then - english_names = raw_namedata.names + return raw_namedata.names end end end - if not english_names then - -- no (English) names table, probably a broken font + -- no (English) names table, probably a broken font + report("both", 3, "db", + "%s: missing or broken English names table.", basename) + return { fontname = metadata.fontname, + fullname = metadata.fullname, } +end + +--[[-- + In case of broken PS names we set some dummies. However, we cannot + directly modify the font data as returned by fontloader.open() because + it is a userdata object. + + For this reason we copy what is necessary whilst keeping the table + structure the same as in the tfmdata. +--]]-- +local get_raw_info = function (metadata, basename) + local fullname + local fontname + local psname + + local validation_state = metadata.validation_state + if validation_state + and tablecontains (validation_state, "bad_ps_fontname") + then + --- Broken names table, e.g. avkv.ttf with UTF-16 strings; + --- we put some dummies in place like the fontloader + --- (font-otf.lua) does. report("both", 3, "db", - "%s: missing or broken names table.", basename) + "%s has invalid postscript font names, using dummies.", + basename) + fontname = "bad-fontname-" .. basename + fullname = "bad-fullname-" .. basename + else + fontname = metadata.fontname + fullname = metadata.fullname end - return english_names or { } + return { + familyname = metadata.familyname, + fontname = fontname, + fontstyle_name = metadata.fontstyle_name, + fullname = fullname, + italicangle = metadata.italicangle, + names = metadata.names, + pfminfo = metadata.pfminfo, + units_per_em = metadata.units_per_em, + version = metadata.version, + design_size = metadata.design_size, + design_range_top = metadata.design_range_top, + design_range_bottom = metadata.design_range_bottom, + } end -local organize_namedata = function (metadata, +local organize_namedata = function (rawinfo, english_names, basename, info) local default_name = english_names.compatfull or english_names.fullname or english_names.postscriptname - or metadata.fullname - or metadata.fontname + or rawinfo.fullname + or rawinfo.fontname or info.fullname or info.fontname local default_family = english_names.preffamily or english_names.family - or metadata.familyname + or rawinfo.familyname or info.familyname -- local default_modifier = english_names.prefmodifiers -- or english_names.subfamily @@ -1458,9 +1486,9 @@ local organize_namedata = function (metadata, }, metadata = { - fullname = metadata.fullname, - fontname = metadata.fontname, - familyname = metadata.familyname, + fullname = rawinfo.fullname, + fontname = rawinfo.fontname, + familyname = rawinfo.familyname, }, info = { @@ -1471,14 +1499,14 @@ local organize_namedata = function (metadata, } -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size - if metadata.fontstyle_name then + if rawinfo.fontstyle_name then --- not present in all fonts, often differs from the preferred --- subfamily as well as subfamily fields, e.g. with --- LMSans10-BoldOblique: --- subfamily: “Bold Italic” --- prefmodifiers: “10 Bold Oblique” --- fontstyle_name: “Bold Oblique” - for _, name in next, metadata.fontstyle_name do + for _, name in next, rawinfo.fontstyle_name do if name.lang == 1033 then --- I hate magic numbers fontnames.fontstyle_name = name.name end @@ -1487,9 +1515,9 @@ local organize_namedata = function (metadata, return { sanitized = sanitize_fontnames (fontnames), - fontname = metadata.fontname, - fullname = metadata.fullname, - familyname = metadata.familyname, + fontname = rawinfo.fontname, + fullname = rawinfo.fullname, + familyname = rawinfo.familyname, } end @@ -1546,13 +1574,18 @@ ot_fullinfo = function (filename, return nil end - local english_names = get_english_names (metadata, basename) - local namedata = organize_namedata (metadata, + local rawinfo = get_raw_info (metadata, basename) + --- Closing the file manually is a tad faster and more memory + --- efficient than having it closed by the gc + fontloaderclose (metadata) + + local english_names = get_english_names (rawinfo) + local namedata = organize_namedata (rawinfo, english_names, basename, info) local style = organize_styledata (namedata.fontname, - metadata, + rawinfo, english_names, info) @@ -1564,11 +1597,8 @@ ot_fullinfo = function (filename, format = format, names = namedata, style = style, - version = metadata.version, + version = rawinfo.version, } - --- Closing the file manually is a tad faster and more memory - --- efficient than having it closed by the gc - fontloaderclose (metadata) return res end @@ -2577,7 +2607,7 @@ local pull_values = function (entry) --- pull name info ... entry.psname = english.psname - entry.fontname = info.fontname + entry.fontname = info.fontname or metadata.fontname entry.fullname = english.fullname or info.fullname entry.splainname = metadata.fullname entry.prefmodifiers = english.prefmodifiers -- cgit v1.2.3 From 4ec63fd11536844ed53c16006ac37a33623e61e8 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 8 Feb 2014 20:36:32 +0100 Subject: [log,override,main,tool] move logging functionality into separate file --- luaotfload-log.lua | 406 ++++++++++++++++++++++++++++++++++++++++++++++++ luaotfload-main.lua | 3 +- luaotfload-override.lua | 399 +++-------------------------------------------- luaotfload-tool.lua | 2 +- 4 files changed, 430 insertions(+), 380 deletions(-) create mode 100644 luaotfload-log.lua diff --git a/luaotfload-log.lua b/luaotfload-log.lua new file mode 100644 index 0000000..938050a --- /dev/null +++ b/luaotfload-log.lua @@ -0,0 +1,406 @@ +if not modules then modules = { } end modules ["luaotfload-log"] = { + version = "2.5", + comment = "companion to Luaotfload", + author = "Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2.0" +} + +--[[doc-- +The logging system is slow in general, as we always have the function +call overhead even if we aren’t going to output anything. On the other +hand, the more efficient approach followed by Context isn’t an option +because we lack a user interface to toggle per-subsystem tracing. +--doc]]-- + +local module_name = "luaotfload" --- prefix for messages + +local ioopen = io.open +local iowrite = io.write +local lfsisdir = lfs.isdir +local lfsisfile = lfs.isfile +local md5sumhexa = md5.sumhexa +local osdate = os.date +local ostime = os.time +local select = select +local stringformat = string.format +local stringsub = string.sub +local tableconcat = table.concat +local texiowrite_nl = texio.write_nl +local texiowrite = texio.write +local type = type + +local dummyfunction = function () end + +local texjob = false +if tex and (tex.jobname or tex.formatname) then + --- TeX + texjob = true +end + +local loglevel = 0 --- default +local logout = "log" + +--- int -> bool +local set_loglevel = function (n) + if type(n) == "number" then + loglevel = n + end + return true +end +logs.setloglevel = set_loglevel +logs.set_loglevel = set_loglevel +logs.set_log_level = set_loglevel --- accomodating lazy typists + +--- unit -> int +local get_loglevel = function ( ) + return loglevel +end +logs.getloglevel = get_loglevel +logs.get_loglevel = get_loglevel +logs.get_log_level = get_loglevel + +local writeln --- pointer to terminal/log writer +local statusln --- terminal writer that reuses the current line +local first_status = true --- indicate the begin of a status region + +local log_msg = [[ +logging output redirected to %s +to monitor the progress run "tail -f %s" in another terminal +]] + +local tmppath = os.getenv "TMPDIR" or "/tmp" + +local choose_logfile = function ( ) + if lfsisdir (tmppath) then + local fname + repeat --- ensure that file of that name doesn’t exist + --- TODO this could use os.uuid() instead of md5 + fname = tmppath .. "/luaotfload-log-" + .. stringsub (md5sumhexa (ostime ()), 1, 8) + until not lfsisfile (fname) + iowrite (stringformat (log_msg, fname, fname)) + return ioopen (fname, "w") + end + --- missing /tmp + return false +end + +local set_logout = function (s, finalizers) + if s == "stdout" then + logout = "redirect" + elseif s == "file" then --- inject custom logger + logout = "redirect" + local chan = choose_logfile () + chan:write (stringformat ("logging initiated at %s", + osdate ("%F %T", ostime ()))) + local writefile = function (...) + if select ("#", ...) == 2 then + chan:write (select (2, ...)) + else + chan:write (select (1, ...)) + end + end + local writefile_nl= function (...) + chan:write "\n" + if select ("#", ...) == 2 then + chan:write (select (2, ...)) + else + chan:write (select (1, ...)) + end + end + + local writeln_orig = writeln + + texiowrite = writefile + texiowrite_nl = writefile_nl + writeln = writefile_nl + statusln = dummyfunction + + finalizers[#finalizers+1] = function () + chan:write (stringformat ("\nlogging finished at %s\n", + osdate ("%F %T", ostime ()))) + chan:close () + texiowrite = texio.write + texiowrite_nl = texio.write_nl + writeln = writeln_orig + end + --else --- remains “log” + end + return finalizers +end + +logs.set_logout = set_logout + +local log = function (category, fmt, ...) + local res = { module_name, "|", category, ":" } + if fmt then + res [#res + 1] = stringformat (fmt, ...) + end + texiowrite_nl (logout, tableconcat(res, " ")) +end + +--- with faux db update with maximum verbosity: +--- +--- --------- -------- +--- buffering time (s) +--- --------- -------- +--- full 4.12 +--- line 4.20 +--- none 4.39 +--- --------- -------- +--- + +io.stdout:setvbuf "no" +io.stderr:setvbuf "no" + +local kill_line = "\r\x1b[K" + +if texjob == true then + --- We imitate the texio.* functions so the output is consistent. + writeln = function (str) + iowrite "\n" + iowrite(str) + end + statusln = function (str) + if first_status == false then + iowrite (kill_line) + else + iowrite "\n" + end + iowrite (str) + end +else + writeln = function (str) + iowrite(str) + iowrite "\n" + end + statusln = function (str) + if first_status == false then + iowrite (kill_line) + end + iowrite (str) + end +end + +stdout = function (writer, category, ...) + local res = { module_name, "|", category, ":" } + local nargs = select("#", ...) + if nargs == 0 then + --writeln tableconcat(res, " ") + --return + elseif nargs == 1 then + res[#res+1] = select(1, ...) -- around 30% faster than unpack() + else + res[#res+1] = stringformat(...) + end + writer (tableconcat(res, " ")) +end + +--- at default (zero), we aim to be quiet +local level_ids = { common = 1, loading = 2, search = 3 } + +--[[doc-- + + The names_report logger is used more or less all over luaotfload. + Its requirements are twofold: + + 1) Provide two logging channels, the terminal and the log file; + 2) Allow for control over verbosity levels. + + The first part is addressed by specifying the log *mode* as the + first argument that can be either “log”, meaning the log file, or + “both”: log file and stdout. Anything else is taken as referring to + stdout only. + + Verbosity levels, though not as fine-grained as e.g. Context’s + system of tracers, allow keeping the logging spam caused by + different subsystems manageable. By default, luaotfload will not + emit anything if things are running smoothly on level zero. Only + warning messages are relayed, while the other messages are skipped + over. (This is a little sub-optimal performance-wise since the + function calls to the logger are executed regardless.) The log + level during a Luatex run can be adjusted by setting the “loglevel” + field in config.luaotfload, or by calling logs.set_loglevel() as + defined above. + +--doc]]-- + +local names_report = function (mode, lvl, ...) + if type(lvl) == "string" then + lvl = level_ids[lvl] + end + if not lvl then lvl = 0 end + + if loglevel >= lvl then + if mode == "log" then + log (...) + elseif mode == "both" and logout ~= "redirect" then + log (...) + stdout (writeln, ...) + else + stdout (writeln, ...) + end + end +end + +logs.names_report = names_report + +--[[doc-- + + status_logger -- Overwrites the most recently printed line of the + terminal. Its purpose is to provide feedback without spamming + stdout with irrelevant messages, i.e. when building the database. + + Status logging must be initialized by calling status_start() and + properly reset via status_stop(). + + The arguments low and high indicate the loglevel threshold at which + linewise and full logging is triggered, respectively. E.g. + + names_status (1, 4, "term", "Hello, world!") + + will print nothing if the loglevel is less than one, reuse the + current line if the loglevel ranges from one to three inclusively, + and output the message on a separate line otherwise. + +--doc]]-- + +local status_logger = function (mode, ...) + if mode == "log" then + log (...) + else + if mode == "both" and logout ~= "redirect" then + log (...) + stdout (statusln, ...) + else + stdout (statusln, ...) + end + first_status = false + end +end + +--[[doc-- + + status_start -- Initialize status logging. This installs the status + logger if the loglevel is in the specified range, and the normal + logger otherwise. It also resets the first line state which + causing the next line printed using the status logger to not kill + the current line. + +--doc]]-- + +local status_writer +local status_low = 99 +local status_high = 99 + +local status_start = function (low, high) + first_status = true + status_low = low + status_high = high + + if os.type == "windows" --- Assume broken terminal. + or os.getenv "TERM" == "dumb" + then + status_writer = function (mode, ...) + names_report (mode, high, ...) + end + return + end + + if low <= loglevel and loglevel < high then + status_writer = status_logger + else + status_writer = function (mode, ...) + names_report (mode, high, ...) + end + end +end + +--[[doc-- + + status_stop -- Finalize a status region by outputting a newline and + printing a message. + +--doc]]-- + +local status_stop = function (...) + if first_status == false then + status_writer(...) + if texjob == false then + writeln "" + end + end +end + +logs.names_status = function (...) status_writer (...) end +logs.names_status_start = status_start +logs.names_status_stop = status_stop + +--[[doc-- + + The fontloader comes with the Context logging mechanisms + inaccessible. Instead, it provides dumb fallbacks based + on the functions in texio.write*() that can be overridden + by providing a function texio.reporter(). + + The fontloader output can be quite verbose, so we disable + it entirely by default. + +--doc]]-- + +local texioreporter = function (message) + names_report("log", 2, message) +end + +texio.reporter = texioreporter + +--[[doc-- + + Adobe Glyph List. + ------------------------------------------------------------------- + + Context provides a somewhat different font-age.lua from an unclear + origin. Unfortunately, the file name it reads from is hard-coded + in font-enc.lua, so we have to replace the entire table. + + This shouldn’t cause any complications. Due to its implementation + the glyph list will be loaded upon loading a OTF or TTF for the + first time during a TeX run. (If one sticks to TFM/OFM then it is + never read at all.) For this reason we can install a metatable that + looks up the file of our choosing and only falls back to the + Context one in case it cannot be found. + +--doc]]-- + +if fonts then --- need to be running TeX + if next(fonts.encodings.agl) then + --- unnecessary because the file shouldn’t be loaded at this time + --- but we’re just making sure + fonts.encodings.agl = nil + collectgarbage"collect" + end + + + fonts.encodings.agl = { } + + setmetatable(fonts.encodings.agl, { __index = function (t, k) + if k == "unicodes" then + local glyphlist = resolvers.findfile"luaotfload-glyphlist.lua" + if glyphlist then + names_report("log", 1, "load", "loading the Adobe glyph list") + else + glyphlist = resolvers.findfile"font-age.lua" + names_report("both", 0, "load", + "loading the extended glyph list from ConTeXt") + end + local unicodes = dofile(glyphlist) + fonts.encodings.agl = { unicodes = unicodes } + return unicodes + else + return nil + end + end }) +end + +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/luaotfload-main.lua b/luaotfload-main.lua index 27f0a99..3f356ed 100644 --- a/luaotfload-main.lua +++ b/luaotfload-main.lua @@ -409,7 +409,8 @@ add_to_callback("hpack_filter", add_to_callback("find_vf_file", find_vf_file, "luaotfload.find_vf_file") -loadmodule "override.lua" --- “luat-ovr” +loadmodule "log.lua" --- messages; used to be part of -override +loadmodule "override.lua" --- load glyphlist on demand logs.set_loglevel(config.luaotfload.loglevel) diff --git a/luaotfload-override.lua b/luaotfload-override.lua index 7cb9c5d..4363b74 100644 --- a/luaotfload-override.lua +++ b/luaotfload-override.lua @@ -1,4 +1,4 @@ -if not modules then modules = { } end modules ['luat-ovr'] = { +if not modules then modules = { } end modules ["luaotfload-override"] = { version = "2.5", comment = "companion to Luaotfload", author = "Khaled Hosny, Elie Roux, Philipp Gesang", @@ -6,355 +6,9 @@ if not modules then modules = { } end modules ['luat-ovr'] = { license = "GNU GPL v2.0" } ---[[doc-- -The logging system is slow in general, as we always have the function -call overhead even if we aren’t going to output anything. On the other -hand, the more efficient approach followed by Context isn’t an option -because we lack a user interface to toggle per-subsystem tracing. ---doc]]-- - -local module_name = "luaotfload" - -local ioopen = io.open -local iowrite = io.write -local lfsisdir = lfs.isdir -local lfsisfile = lfs.isfile -local md5sumhexa = md5.sumhexa -local osdate = os.date -local ostime = os.time -local select = select -local stringformat = string.format -local stringsub = string.sub -local tableconcat = table.concat -local texio_write_nl = texio.write_nl -local texiowrite_nl = texio.write_nl -local texio_write = texio.write -local texiowrite = texio.write -local type = type - -local dummyfunction = function () end - -local texjob = false -if tex and (tex.jobname or tex.formatname) then - --- TeX - texjob = true -end - -local loglevel = 0 --- default -local logout = "log" - ---- int -> bool -local set_loglevel = function (n) - if type(n) == "number" then - loglevel = n - end - return true -end -logs.setloglevel = set_loglevel -logs.set_loglevel = set_loglevel -logs.set_log_level = set_loglevel --- accomodating lazy typists - ---- unit -> int -local get_loglevel = function ( ) - return loglevel -end -logs.getloglevel = get_loglevel -logs.get_loglevel = get_loglevel -logs.get_log_level = get_loglevel - -local writeln --- pointer to terminal/log writer -local statusln --- terminal writer that reuses the current line -local first_status = true --- indicate the begin of a status region - -local log_msg = [[ -logging output redirected to %s -to monitor the progress run "tail -f %s" in another terminal -]] - -local tmppath = os.getenv "TMPDIR" or "/tmp" - -local choose_logfile = function ( ) - if lfsisdir (tmppath) then - local fname - repeat --- ensure that file of that name doesn’t exist - fname = tmppath .. "/luaotfload-log-" - .. stringsub (md5sumhexa (ostime ()), 1, 8) - until not lfsisfile (fname) - iowrite (stringformat (log_msg, fname, fname)) - return ioopen (fname, "w") - end - --- missing /tmp - return false -end - -local set_logout = function (s, finalizers) - if s == "stdout" then - logout = "redirect" - elseif s == "file" then --- inject custom logger - logout = "redirect" - local chan = choose_logfile () - chan:write (stringformat ("logging initiated at %s", - osdate ("%F %T", ostime ()))) - local writefile = function (...) - if select ("#", ...) == 2 then - chan:write (select (2, ...)) - else - chan:write (select (1, ...)) - end - end - local writefile_nl= function (...) - chan:write "\n" - if select ("#", ...) == 2 then - chan:write (select (2, ...)) - else - chan:write (select (1, ...)) - end - end - - local writeln_orig = writeln - - texiowrite = writefile - texiowrite_nl = writefile_nl - writeln = writefile_nl - statusln = dummyfunction - - finalizers[#finalizers+1] = function () - chan:write (stringformat ("\nlogging finished at %s\n", - osdate ("%F %T", ostime ()))) - chan:close () - texiowrite = texio.write - texiowrite_nl = texio.write_nl - writeln = writeln_orig - end - --else --- remains “log” - end - return finalizers -end - -logs.set_logout = set_logout - -local log = function (category, fmt, ...) - local res = { module_name, "|", category, ":" } - if fmt then - res [#res + 1] = stringformat (fmt, ...) - end - texiowrite_nl (logout, tableconcat(res, " ")) -end - ---- with faux db update with maximum verbosity: ---- ---- --------- -------- ---- buffering time (s) ---- --------- -------- ---- full 4.12 ---- line 4.20 ---- none 4.39 ---- --------- -------- ---- - -io.stdout:setvbuf "no" -io.stderr:setvbuf "no" - -local kill_line = "\r\x1b[K" - -if texjob == true then - --- We imitate the texio.* functions so the output is consistent. - writeln = function (str) - iowrite "\n" - iowrite(str) - end - statusln = function (str) - if first_status == false then - iowrite (kill_line) - else - iowrite "\n" - end - iowrite (str) - end -else - writeln = function (str) - iowrite(str) - iowrite "\n" - end - statusln = function (str) - if first_status == false then - iowrite (kill_line) - end - iowrite (str) - end -end - -stdout = function (writer, category, ...) - local res = { module_name, "|", category, ":" } - local nargs = select("#", ...) - if nargs == 0 then - --writeln tableconcat(res, " ") - --return - elseif nargs == 1 then - res[#res+1] = select(1, ...) -- around 30% faster than unpack() - else - res[#res+1] = stringformat(...) - end - writer (tableconcat(res, " ")) -end - ---- at default (zero), we aim to be quiet -local level_ids = { common = 1, loading = 2, search = 3 } - ---[[doc-- - - The names_report logger is used more or less all over luaotfload. - Its requirements are twofold: - - 1) Provide two logging channels, the terminal and the log file; - 2) Allow for control over verbosity levels. - - The first part is addressed by specifying the log *mode* as the - first argument that can be either “log”, meaning the log file, or - “both”: log file and stdout. Anything else is taken as referring to - stdout only. - - Verbosity levels, though not as fine-grained as e.g. Context’s - system of tracers, allow keeping the logging spam caused by - different subsystems manageable. By default, luaotfload will not - emit anything if things are running smoothly on level zero. Only - warning messages are relayed, while the other messages are skipped - over. (This is a little sub-optimal performance-wise since the - function calls to the logger are executed regardless.) The log - level during a Luatex run can be adjusted by setting the “loglevel” - field in config.luaotfload, or by calling logs.set_loglevel() as - defined above. - ---doc]]-- - -local names_report = function (mode, lvl, ...) - if type(lvl) == "string" then - lvl = level_ids[lvl] - end - if not lvl then lvl = 0 end - - if loglevel >= lvl then - if mode == "log" then - log (...) - elseif mode == "both" and logout ~= "redirect" then - log (...) - stdout (writeln, ...) - else - stdout (writeln, ...) - end - end -end - -logs.names_report = names_report - ---[[doc-- - - status_logger -- Overwrites the most recently printed line of the - terminal. Its purpose is to provide feedback without spamming - stdout with irrelevant messages, i.e. when building the database. - - Status logging must be initialized by calling status_start() and - properly reset via status_stop(). - - The arguments low and high indicate the loglevel threshold at which - linewise and full logging is triggered, respectively. E.g. - - names_status (1, 4, "term", "Hello, world!") - - will print nothing if the loglevel is less than one, reuse the - current line if the loglevel ranges from one to three inclusively, - and output the message on a separate line otherwise. - ---doc]]-- - -local status_logger = function (mode, ...) - if mode == "log" then - log (...) - else - if mode == "both" and logout ~= "redirect" then - log (...) - stdout (statusln, ...) - else - stdout (statusln, ...) - end - first_status = false - end -end - ---[[doc-- - - status_start -- Initialize status logging. This installs the status - logger if the loglevel is in the specified range, and the normal - logger otherwise. It also resets the first line state which - causing the next line printed using the status logger to not kill - the current line. - ---doc]]-- - -local status_writer -local status_low = 99 -local status_high = 99 - -local status_start = function (low, high) - first_status = true - status_low = low - status_high = high - - if os.type == "windows" --- Assume broken terminal. - or os.getenv "TERM" == "dumb" - then - status_writer = function (mode, ...) - names_report (mode, high, ...) - end - return - end - - if low <= loglevel and loglevel < high then - status_writer = status_logger - else - status_writer = function (mode, ...) - names_report (mode, high, ...) - end - end -end - ---[[doc-- - - status_stop -- Finalize a status region by outputting a newline and - printing a message. - ---doc]]-- - -local status_stop = function (...) - if first_status == false then - status_writer(...) - if texjob == false then - writeln "" - end - end -end - -logs.names_status = function (...) status_writer (...) end -logs.names_status_start = status_start -logs.names_status_stop = status_stop - ---[[doc-- - - The fontloader comes with the Context logging mechanisms - inaccessible. Instead, it provides dumb fallbacks based - on the functions in texio.write*() that can be overridden - by providing a function texio.reporter(). - - The fontloader output can be quite verbose, so we disable - it entirely by default. - ---doc]]-- - -local texioreporter = function (message) - names_report("log", 2, message) -end - -texio.reporter = texioreporter +local findfile = resolvers.findfile +local names_report = logs.names_report +local encodings = fonts.encodings --[[doc-- @@ -374,34 +28,23 @@ texio.reporter = texioreporter --doc]]-- -if fonts then --- need to be running TeX - if next(fonts.encodings.agl) then - --- unnecessary because the file shouldn’t be loaded at this time - --- but we’re just making sure - fonts.encodings.agl = nil - collectgarbage"collect" - end - - - fonts.encodings.agl = { } +encodings.agl = { } - setmetatable(fonts.encodings.agl, { __index = function (t, k) - if k == "unicodes" then - local glyphlist = resolvers.findfile"luaotfload-glyphlist.lua" - if glyphlist then - names_report("log", 1, "load", "loading the Adobe glyph list") - else - glyphlist = resolvers.findfile"font-age.lua" - names_report("both", 0, "load", - "loading the extended glyph list from ConTeXt") - end - local unicodes = dofile(glyphlist) - fonts.encodings.agl = { unicodes = unicodes } - return unicodes - else - return nil - end - end }) -end +setmetatable(fonts.encodings.agl, { __index = function (t, k) + if k ~= "unicodes" then + return nil + end + local glyphlist = findfile "luaotfload-glyphlist.lua" + if glyphlist then + names_report("log", 1, "load", "loading the Adobe glyph list") + else + glyphlist = findfile "font-age.lua" + names_report("both", 0, "load", + "loading the extended glyph list from ConTeXt") + end + local unicodes = dofile(glyphlist) + encodings.agl = { unicodes = unicodes } + return unicodes +end }) -- vim:tw=71:sw=4:ts=4:expandtab diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 8bc590a..6ca7d69 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -141,7 +141,7 @@ require"luaotfload-basics-gen.lua" texio.write, texio.write_nl = backup.write, backup.write_nl utilities = backup.utilities -require"luaotfload-override.lua" --- this populates the logs.* namespace +require"luaotfload-log.lua" --- this populates the logs.* namespace require"luaotfload-parsers" --- fonts.conf and request syntax require"luaotfload-database" require"alt_getopt" -- cgit v1.2.3 From 2c73e4165909bc0d82cc62a9031bd687a5e000b6 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 9 Feb 2014 13:57:49 +0100 Subject: [log] use UUID instead of MD5(time) when creating log files --- luaotfload-log.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/luaotfload-log.lua b/luaotfload-log.lua index 938050a..d057eb3 100644 --- a/luaotfload-log.lua +++ b/luaotfload-log.lua @@ -19,9 +19,9 @@ local ioopen = io.open local iowrite = io.write local lfsisdir = lfs.isdir local lfsisfile = lfs.isfile -local md5sumhexa = md5.sumhexa local osdate = os.date local ostime = os.time +local osuuid = os.uuid local select = select local stringformat = string.format local stringsub = string.sub @@ -75,9 +75,7 @@ local choose_logfile = function ( ) if lfsisdir (tmppath) then local fname repeat --- ensure that file of that name doesn’t exist - --- TODO this could use os.uuid() instead of md5 - fname = tmppath .. "/luaotfload-log-" - .. stringsub (md5sumhexa (ostime ()), 1, 8) + fname = tmppath .. "/luaotfload-log-" .. osuuid() until not lfsisfile (fname) iowrite (stringformat (log_msg, fname, fname)) return ioopen (fname, "w") -- cgit v1.2.3 From 73ab3c0ae57a4918b6149ae862fc3a24c7651190 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 9 Feb 2014 13:59:25 +0100 Subject: [doc] update manpage paragraph about --log --- luaotfload-tool.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaotfload-tool.rst b/luaotfload-tool.rst index 2ac206f..6863918 100644 --- a/luaotfload-tool.rst +++ b/luaotfload-tool.rst @@ -188,10 +188,10 @@ miscellaneous troubleshooting), where *CHANNEL* can be 1) ``stdout`` -> all output will be - dumped to the terminal; or + dumped to the terminal (default); or 2) ``file`` -> write to a file to the temporary directory (the name will be chosen - automatically (**experimental!**). + automatically. --version, -V Show version numbers of components as well as some basic information and exit. -- cgit v1.2.3 From 405ff5a3f24566f76a3d08d7ba217a137fcb5bdd Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 9 Feb 2014 14:04:26 +0100 Subject: [*] update references to luaotfload-override/luaotfload-log --- NEWS | 1 + filegraph.dot | 3 ++- mkstatus | 1 + mktests | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 2e2bf8f..6be91b4 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ Change History * Move the heavier LPEG parsers from luaotfload-features (syntax) and luaotfload-database (fontconfig) into the new file luaotfload-parsers.lua. + * Move logging routines from luaotfload-override in to luaotfload-log. 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) diff --git a/filegraph.dot b/filegraph.dot index 90e6e5c..47db9ea 100644 --- a/filegraph.dot +++ b/filegraph.dot @@ -200,8 +200,9 @@ strict digraph luaotfload_files { //looks weird with circo ... Luaotfload Libraries luaotfload-auxiliary.lua luaotfload-features.lua luaotfload-override.lua luaotfload-loaders.lua + luaotfload-log.lua luaotfload-letterspace.lua luaotfload-parsers.lua luaotfload-database.lua - luaotfload-color.lua luaotfload-letterspace.lua + luaotfload-color.lua >, ] diff --git a/mkstatus b/mkstatus index 28b80ff..6e6e375 100755 --- a/mkstatus +++ b/mkstatus @@ -50,6 +50,7 @@ local names = { "luaotfload-glyphlist.lua", "luaotfload-letterspace.lua", "luaotfload-loaders.lua", + "luaotfload-log.lua", "luaotfload-main.lua", "luaotfload-fontloader.lua", "luaotfload-override.lua", diff --git a/mktests b/mktests index 3a55b8d..0bf3f64 100755 --- a/mktests +++ b/mktests @@ -30,7 +30,7 @@ kpse.set_program_name "luatex" require "lualibs" require "luaotfload-basics-gen.lua" -require "luaotfload-override.lua" +require "luaotfload-log.lua" require "luaotfload-parsers" require "luaotfload-database" -- cgit v1.2.3 From e069690a7f747f7b17baed6974168192857eb485 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 9 Feb 2014 15:15:46 +0100 Subject: [log,*] move logs -> luaotfload.log Next step toward https://github.com/lualatex/luaotfload/issues/169 Signed-off-by: Philipp Gesang --- luaotfload-auxiliary.lua | 61 ++++++++++++++++++----------------- luaotfload-database.lua | 28 +++++++++------- luaotfload-diagnostics.lua | 3 +- luaotfload-features.lua | 3 +- luaotfload-letterspace.lua | 11 ++++--- luaotfload-log.lua | 34 ++++++++++---------- luaotfload-main.lua | 60 +++++++++++++++++++--------------- luaotfload-override.lua | 4 ++- luaotfload-parsers.lua | 4 +-- luaotfload-tool.lua | 80 ++++++++++++++++++++++++---------------------- 10 files changed, 157 insertions(+), 131 deletions(-) diff --git a/luaotfload-auxiliary.lua b/luaotfload-auxiliary.lua index 334ac47..9ab78e7 100644 --- a/luaotfload-auxiliary.lua +++ b/luaotfload-auxiliary.lua @@ -17,7 +17,7 @@ luaotfload.aux = luaotfload.aux or { } local aux = luaotfload.aux local log = luaotfload.log -local warning = luaotfload.log +local report = log.names_report local fonthashes = fonts.hashes local identifiers = fonthashes.identifiers @@ -54,8 +54,8 @@ local start_rewrite_fontname = function () rewrite_fontname, "luaotfload.rewrite_fontname") rewriting = true - logs.names_report ("log", 0, "aux", - "start rewriting tfmdata.name field") + report ("log", 0, "aux", + "start rewriting tfmdata.name field") end end @@ -66,8 +66,8 @@ local stop_rewrite_fontname = function () luatexbase.remove_fromt_callback ("luaotfload.patch_font", "luaotfload.rewrite_fontname") rewriting = false - logs.names_report ("log", 0, "aux", - "stop rewriting tfmdata.name field") + report ("log", 0, "aux", + "stop rewriting tfmdata.name field") end end @@ -366,7 +366,7 @@ do local load_chardef = function () - log ("Loading character metadata from %s.", chardef) + report ("both", 1, "aux", "Loading character metadata from %s.", chardef) chardata = dofile (kpse.find_file (chardef, "lua")) if chardata == nil then @@ -424,19 +424,19 @@ local provides_script = function (font_id, asked_script) --- where method: "gpos" | "gsub" for feature, data in next, featuredata do if data[asked_script] then - log(stringformat( - "font no %d (%s) defines feature %s for script %s", - font_id, fontname, feature, asked_script)) + report ("log", 1, "aux", + "font no %d (%s) defines feature %s for script %s", + font_id, fontname, feature, asked_script) return true end end end - log(stringformat( - "font no %d (%s) defines no feature for script %s", - font_id, fontname, asked_script)) + report ("log", 0, "aux", + "font no %d (%s) defines no feature for script %s", + font_id, fontname, asked_script) end end - log(stringformat("no font with id %d", font_id)) + report ("log", 0, "aux", "no font with id %d", font_id) return false end @@ -463,20 +463,22 @@ local provides_language = function (font_id, asked_script, asked_language) for feature, data in next, featuredata do local scriptdata = data[asked_script] if scriptdata and scriptdata[asked_language] then - log(stringformat("font no %d (%s) defines feature %s " - .. "for script %s with language %s", - font_id, fontname, feature, - asked_script, asked_language)) + report ("log", 1, "aux", + "font no %d (%s) defines feature %s " + .. "for script %s with language %s", + font_id, fontname, feature, + asked_script, asked_language) return true end end end - log(stringformat( - "font no %d (%s) defines no feature for script %s with language %s", - font_id, fontname, asked_script, asked_language)) + report ("log", 0, "aux", + "font no %d (%s) defines no feature " + .. "for script %s with language %s", + font_id, fontname, asked_script, asked_language) end end - log(stringformat("no font with id %d", font_id)) + report ("log", 0, "aux", "no font with id %d", font_id) return false end @@ -534,20 +536,21 @@ local provides_feature = function (font_id, asked_script, if feature then local scriptdata = feature[asked_script] if scriptdata and scriptdata[asked_language] then - log(stringformat("font no %d (%s) defines feature %s " - .. "for script %s with language %s", - font_id, fontname, asked_feature, - asked_script, asked_language)) + report ("log", 1, "aux", + "font no %d (%s) defines feature %s " + .. "for script %s with language %s", + font_id, fontname, asked_feature, + asked_script, asked_language) return true end end end - log(stringformat( - "font no %d (%s) does not define feature %s for script %s with language %s", - font_id, fontname, asked_feature, asked_script, asked_language)) + report ("log", 0, "aux", + "font no %d (%s) does not define feature %s for script %s with language %s", + font_id, fontname, asked_feature, asked_script, asked_language) end end - log(stringformat("no font with id %d", font_id)) + report ("log", 0, "aux", "no font with id %d", font_id) return false end diff --git a/luaotfload-database.lua b/luaotfload-database.lua index d9bba80..8f603db 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -44,6 +44,13 @@ local read_fonts_conf = parsers.read_fonts_conf local stripslashes = parsers.stripslashes local splitcomma = parsers.splitcomma +local log = luaotfload.log +local report = log.names_report +local report_status = log.names_status +local report_status_start = log.names_status_start +local report_status_stop = log.names_status_stop + + --- Luatex builtins local load = load local next = next @@ -151,11 +158,6 @@ local make_luanames = function (path) filereplacesuffix(path, "luc") end -local report = logs.names_report -local report_status = logs.names_status -local report_status_start = logs.names_status_start -local report_status_stop = logs.names_status_stop - --- The “termwidth” value is only considered when printing --- short status messages, e.g. when building the database --- online. @@ -231,7 +233,11 @@ if not runasscript then index.lua, index.luc = make_luanames (index_file) else --- running as script, inject some dummies caches = { } - logs = { report = function () end } + local dummy_function = function () end + log = { report = dummy_function, + report_status = dummy_function, + report_status_start = dummy_function, + report_status_stop = dummy_function, } end @@ -2284,7 +2290,7 @@ local scan_texmf_fonts = function (currentnames, targetnames, dry_run) report ("info", 1, "db", "Scanning TEXMF fonts...") else report ("info", 1, "db", "Scanning TEXMF and OS fonts...") - if logs.get_loglevel () > 3 then + if log.get_loglevel () > 3 then local osdirs = filesplitpath (osfontdir) report ("info", 0, "db", "$OSFONTDIR has %d entries:", #osdirs) @@ -2979,7 +2985,7 @@ local collect_statistics = function (mappings) local n_fullname = setsize (fullname) local n_family = setsize (family) - if logs.get_loglevel () > 1 then + if log.get_loglevel () > 1 then local pprint_top = function (hash, n, set) local freqs = { } @@ -3165,7 +3171,7 @@ update_names = function (currentnames, force, dry_run) if success then local success = save_lookups () if success then - logs.names_report ("info", 2, "cache", + report ("info", 2, "cache", "Lookup cache emptied.") return targetnames end @@ -3354,7 +3360,7 @@ end local purge_cache = function ( ) local writable_path = getwritablecachepath () local luanames, lucnames, rest = collect_cache(writable_path) - if logs.get_loglevel() > 1 then + if log.get_loglevel() > 1 then print_cache("writable path", writable_path, luanames, lucnames, rest) end local success = purge_from_cache("writable path", writable_path, luanames, false) @@ -3365,7 +3371,7 @@ end local erase_cache = function ( ) local writable_path = getwritablecachepath () local luanames, lucnames, rest, all = collect_cache(writable_path) - if logs.get_loglevel() > 1 then + if log.get_loglevel() > 1 then print_cache("writable path", writable_path, luanames, lucnames, rest) end local success = purge_from_cache("writable path", writable_path, all, true) diff --git a/luaotfload-diagnostics.lua b/luaotfload-diagnostics.lua index 1ae9a90..4ffaa06 100644 --- a/luaotfload-diagnostics.lua +++ b/luaotfload-diagnostics.lua @@ -49,8 +49,9 @@ local lpeg = require "lpeg" local C, Cg, Ct = lpeg.C, lpeg.Cg, lpeg.Ct local lpegmatch = lpeg.match +local names_report = luaotfload.log.names_report local out = function (...) - logs.names_report (false, 0, "diagnose", ...) + names_report (false, 0, "diagnose", ...) end local parsers = luaotfload.parsers diff --git a/luaotfload-features.lua b/luaotfload-features.lua index 5172f4b..73edcd2 100644 --- a/luaotfload-features.lua +++ b/luaotfload-features.lua @@ -48,7 +48,8 @@ function fonts.definers.getspecification(str) return "", str, "", ":", str end -local report = logs.names_report +local log = luaotfload.log +local report = log.names_report local stringfind = string.find local stringlower = string.lower diff --git a/luaotfload-letterspace.lua b/luaotfload-letterspace.lua index 1957f9a..a855bf3 100644 --- a/luaotfload-letterspace.lua +++ b/luaotfload-letterspace.lua @@ -6,6 +6,9 @@ if not modules then modules = { } end modules ['letterspace'] = { license = "see context related readme files" } +local log = luaotfload.log +local report = log.names_report + local getmetatable = getmetatable local require = require local setmetatable = setmetatable @@ -512,10 +515,10 @@ otffeatures.register { local initializecompatfontkerning = function (tfmdata, percentage) local factor = tonumber (percentage) if not factor then - logs.names_report ("both", 0, "letterspace", - "Invalid argument to letterspace: %s (type %q), " .. - "was expecting percentage as Lua number instead.", - percentage, type (percentage)) + names_report ("both", 0, "letterspace", + "Invalid argument to letterspace: %s (type %q), " .. + "was expecting percentage as Lua number instead.", + percentage, type (percentage)) return end return initializefontkerning (tfmdata, factor * 0.01) diff --git a/luaotfload-log.lua b/luaotfload-log.lua index d057eb3..080550a 100644 --- a/luaotfload-log.lua +++ b/luaotfload-log.lua @@ -15,6 +15,10 @@ because we lack a user interface to toggle per-subsystem tracing. local module_name = "luaotfload" --- prefix for messages +luaotfload = luaotfload or { } +luaotfload.log = luaotfload.log or { } +local log = luaotfload.log + local ioopen = io.open local iowrite = io.write local lfsisdir = lfs.isdir @@ -48,17 +52,13 @@ local set_loglevel = function (n) end return true end -logs.setloglevel = set_loglevel -logs.set_loglevel = set_loglevel -logs.set_log_level = set_loglevel --- accomodating lazy typists +log.set_loglevel = set_loglevel --- unit -> int local get_loglevel = function ( ) return loglevel end -logs.getloglevel = get_loglevel -logs.get_loglevel = get_loglevel -logs.get_log_level = get_loglevel +log.get_loglevel = get_loglevel local writeln --- pointer to terminal/log writer local statusln --- terminal writer that reuses the current line @@ -128,9 +128,9 @@ local set_logout = function (s, finalizers) return finalizers end -logs.set_logout = set_logout +log.set_logout = set_logout -local log = function (category, fmt, ...) +local basic_logger = function (category, fmt, ...) local res = { module_name, "|", category, ":" } if fmt then res [#res + 1] = stringformat (fmt, ...) @@ -219,7 +219,7 @@ local level_ids = { common = 1, loading = 2, search = 3 } over. (This is a little sub-optimal performance-wise since the function calls to the logger are executed regardless.) The log level during a Luatex run can be adjusted by setting the “loglevel” - field in config.luaotfload, or by calling logs.set_loglevel() as + field in config.luaotfload, or by calling log.set_loglevel() as defined above. --doc]]-- @@ -232,9 +232,9 @@ local names_report = function (mode, lvl, ...) if loglevel >= lvl then if mode == "log" then - log (...) + basic_logger (...) elseif mode == "both" and logout ~= "redirect" then - log (...) + basic_logger (...) stdout (writeln, ...) else stdout (writeln, ...) @@ -242,7 +242,7 @@ local names_report = function (mode, lvl, ...) end end -logs.names_report = names_report +log.names_report = names_report --[[doc-- @@ -266,10 +266,10 @@ logs.names_report = names_report local status_logger = function (mode, ...) if mode == "log" then - log (...) + basic_logger (...) else if mode == "both" and logout ~= "redirect" then - log (...) + basic_logger (...) stdout (statusln, ...) else stdout (statusln, ...) @@ -331,9 +331,9 @@ local status_stop = function (...) end end -logs.names_status = function (...) status_writer (...) end -logs.names_status_start = status_start -logs.names_status_stop = status_stop +log.names_status = function (...) status_writer (...) end +log.names_status_start = status_start +log.names_status_stop = status_stop --[[doc-- diff --git a/luaotfload-main.lua b/luaotfload-main.lua index 3f356ed..35c01f2 100644 --- a/luaotfload-main.lua +++ b/luaotfload-main.lua @@ -4,7 +4,7 @@ -- REQUIREMENTS: luatex v.0.78 or later, the lualibs package -- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang -- VERSION: same as Luaotfload --- MODIFIED: 2014-01-16 06:51:20+0100 +-- MODIFIED: 2014-02-09 14:42:22+0100 ----------------------------------------------------------------------- -- --- Note: @@ -45,6 +45,7 @@ if not modules then modules = { } end modules ["luaotfload-main"] = { luaotfload = luaotfload or { } local luaotfload = luaotfload +luaotfload.log = luaotfload.log or { } config = config or { } config.luaotfload = config.luaotfload or { } @@ -91,10 +92,12 @@ local dummy_function = function () end local error, warning, info, log = luatexbase.provides_module(luaotfload.module) -luaotfload.error = error -luaotfload.warning = warning -luaotfload.info = info -luaotfload.log = log +luaotfload.log.tex = { + error = error, + warning = warning, + info = info, + log = log, +} --[[doc-- @@ -143,6 +146,12 @@ local loadmodule = function (name) require(fl_prefix .."-"..name) end +loadmodule "log.lua" --- messages; used to be part of -override +local log = luaotfload.log +local report = log.names_report + +log.set_loglevel(config.luaotfload.loglevel) + --[[doc-- Before \TeX Live 2013 version, \LUATEX had a bug that made ofm fonts @@ -167,7 +176,8 @@ local find_vf_file = function (name) fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf") end if fullname then - log("loading virtual font file %s.", fullname) + report ("log", 0, "main", + "loading virtual font file %s.", fullname) end return fullname end @@ -251,7 +261,7 @@ end local context_environment = { } local push_namespaces = function () - log("push namespace for font loader") + report ("log", 1, "main", "push namespace for font loader") local normalglobal = { } for k, v in next, _G do normalglobal[k] = v @@ -264,7 +274,7 @@ local pop_namespaces = function (normalglobal, isolate) local _G = _G local mode = "non-destructive" if isolate then mode = "destructive" end - log("pop namespace from font loader -- " .. mode) + report ("log", 1, "main", "pop namespace from font loader -- " .. mode) for k, v in next, _G do if not normalglobal[k] then context_environment[k] = v @@ -279,7 +289,8 @@ local pop_namespaces = function (normalglobal, isolate) -- just to be sure: setmetatable(context_environment,_G) else - log("irrecoverable error during pop_namespace: no globals to restore") + report ("both", 0, "main", + "irrecoverable error during pop_namespace: no globals to restore") os.exit() end end @@ -312,13 +323,13 @@ loadmodule "fontloader.lua" if fonts then if not fonts._merge_loaded_message_done_ then - log [["I am using the merged fontloader here.]] - log [[ If you run into problems or experience unexpected]] - log [[ behaviour, and if you have ConTeXt installed you can try]] - log [[ to delete the file 'luaotfload-fontloader.lua' as I might]] - log [[ then use the possibly updated libraries. The merged]] - log [[ version is not supported as it is a frozen instance.]] - log [[ Problems can be reported to the ConTeXt mailing list."]] + report ("log", 0, "main", [["I am using the merged fontloader here.]]) + report ("log", 0, "main", [[ If you run into problems or experience unexpected]]) + report ("log", 0, "main", [[ behaviour, and if you have ConTeXt installed you can try]]) + report ("log", 0, "main", [[ to delete the file 'luaotfload-fontloader.lua' as I might]]) + report ("log", 0, "main", [[ then use the possibly updated libraries. The merged]]) + report ("log", 0, "main", [[ version is not supported as it is a frozen instance.]]) + report ("log", 0, "main", [[ Problems can be reported to the ConTeXt mailing list."]]) end fonts._merge_loaded_message_done_ = true @@ -376,7 +387,7 @@ end --- non-merge fallback scope pop_namespaces(our_environment, false)-- true) -log("fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) +report ("both", 0, "main", "fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) --[[doc-- @@ -409,11 +420,8 @@ add_to_callback("hpack_filter", add_to_callback("find_vf_file", find_vf_file, "luaotfload.find_vf_file") -loadmodule "log.lua" --- messages; used to be part of -override loadmodule "override.lua" --- load glyphlist on demand -logs.set_loglevel(config.luaotfload.loglevel) - --[[doc-- Now we load the modules written for \identifier{luaotfload}. @@ -535,9 +543,9 @@ request_resolvers.anon = function (specification) local exists, _ = lfsisfile(name) if exists then --- garbage; we do this because we are nice, --- not because it is correct - logs.names_report("log", 1, "load", "file %q exists", name) - logs.names_report("log", 1, "load", - "... overriding borked anon: lookup with path: lookup") + log.names_report("log", 1, "load", "file %q exists", name) + log.names_report("log", 1, "load", + "... overriding borked anon: lookup with path: lookup") specification.name = name request_resolvers.path(specification) return @@ -559,9 +567,9 @@ request_resolvers.path = function (specification) local name = specification.name local exists, _ = lfsisfile(name) if not exists then -- resort to file: lookup - logs.names_report("log", 1, "load", - "path lookup of %q unsuccessful, falling back to file:", - name) + log.names_report("log", 1, "load", + "path lookup of %q unsuccessful, falling back to file:", + name) file_resolver (specification) else local suffix = filesuffix (name) diff --git a/luaotfload-override.lua b/luaotfload-override.lua index 4363b74..e726f6f 100644 --- a/luaotfload-override.lua +++ b/luaotfload-override.lua @@ -7,9 +7,11 @@ if not modules then modules = { } end modules ["luaotfload-override"] = { } local findfile = resolvers.findfile -local names_report = logs.names_report local encodings = fonts.encodings +local log = luaotfload.log +local names_report = log.names_report + --[[doc-- Adobe Glyph List. diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua index 51d251c..8060d05 100644 --- a/luaotfload-parsers.lua +++ b/luaotfload-parsers.lua @@ -38,8 +38,8 @@ local filedirname = file.dirname local io = io local ioopen = io.open -local logs = logs -local report = logs.report +local log = luaotfload.log +local report = log.names_report local string = string local stringsub = string.sub diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index 6ca7d69..e791e3d 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -9,7 +9,10 @@ -- MODIFIED: 2014-01-14 13:17:04+0100 ----------------------------------------------------------------------- -local version = "2.5" --- . +luaotfload = luaotfload or { } +local version = "2.5" --- .- +luaotfload.version = version +luaotfload.self = "luaotfload-tool" --[[doc-- @@ -104,8 +107,6 @@ if not luaotfloadconfig.strip then luaotfloadconfig.strip = true end -luaotfloadconfig.self = "luaotfload-tool" - config.lualibs = config.lualibs or { } config.lualibs.verbose = false config.lualibs.prefer_merged = true @@ -141,23 +142,24 @@ require"luaotfload-basics-gen.lua" texio.write, texio.write_nl = backup.write, backup.write_nl utilities = backup.utilities -require"luaotfload-log.lua" --- this populates the logs.* namespace +require"luaotfload-log.lua" --- this populates the luaotfload.log.* namespace require"luaotfload-parsers" --- fonts.conf and request syntax require"luaotfload-database" require"alt_getopt" -local names = fonts.names - +local names = fonts.names local status_file = "luaotfload-status" local luaotfloadstatus = require (status_file) luaotfloadconfig.status = luaotfloadstatus - local sanitize_fontname = names.sanitize_fontname -local pathdata = names.path -local names_plain = pathdata.index.lua -local names_gzip = names_plain .. ".gz" -local names_bin = pathdata.index.luc +local log = luaotfload.log +local names_report = log.names_report -- TODO improve identifier + +local pathdata = names.path +local names_plain = pathdata.index.lua +local names_gzip = names_plain .. ".gz" +local names_bin = pathdata.index.luc local help_messages = { ["luaotfload-tool"] = [[ @@ -248,7 +250,7 @@ Enter 'luaotfload-tool --help' for a larger list of options. local help_msg = function (version) local template = help_messages[version] iowrite(stringformat(template, - luaotfloadconfig.self, + luaotfload.self, -- names_plain, names_gzip, names_bin, @@ -265,8 +267,8 @@ local about = [[ local version_msg = function ( ) local out = function (...) texiowrite_nl (stringformat (...)) end - out (about, luaotfloadconfig.self) - out ("%s version %q", luaotfloadconfig.self, version) + out (about, luaotfload.self) + out ("%s version %q", luaotfload.self, version) out ("revision %q", luaotfloadstatus.notes.revision) out ("database version %q", names.version) out ("Lua interpreter: %s; version %q", runtime[1], runtime[2]) @@ -681,7 +683,7 @@ local show_font_info = function (basename, askedname, detail, warnings) if nfonts > 0 then -- true type collection local subfont if askedname then - logs.names_report(true, 1, "resolve", + log.names_report(true, 1, "resolve", [[%s is part of the font collection %s]], askedname, basename) subfont = subfont_by_name(shortinfo, askedname) @@ -692,10 +694,10 @@ local show_font_info = function (basename, askedname, detail, warnings) show_full_info(fullname, subfont, warnings) end else -- list all subfonts - logs.names_report(true, 1, "resolve", + log.names_report(true, 1, "resolve", [[%s is a font collection]], basename) for subfont = 1, nfonts do - logs.names_report(true, 1, "resolve", + log.names_report(true, 1, "resolve", [[Showing info for font no. %d]], n) show_info_items(shortinfo[subfont]) if detail == true then @@ -710,7 +712,7 @@ local show_font_info = function (basename, askedname, detail, warnings) end end else - logs.names_report(true, 1, "resolve", + log.names_report(true, 1, "resolve", "Font %s not found", filename) end end @@ -735,10 +737,10 @@ action_pending.generate = false --- this is the default action local actions = { } --- (jobspec -> (bool * bool)) list actions.loglevel = function (job) - logs.set_loglevel(job.log_level) - logs.names_report("info", 3, "util", + log.set_loglevel(job.log_level) + log.names_report("info", 3, "util", "Setting log level", "%d", job.log_level) - logs.names_report("log", 2, "util", "Lua=%s", _VERSION) + log.names_report("log", 2, "util", "Lua=%s", _VERSION) return true, true end @@ -764,7 +766,7 @@ end actions.generate = function (job) local fontnames, savedname fontnames = names.update(fontnames, job.force_reload, job.dry_run) - logs.names_report("info", 2, "db", + log.names_report("info", 2, "db", "Fonts in the database: %i", #fontnames.mappings) if names.data() then return true, true @@ -777,7 +779,7 @@ actions.flush = function (job) if success then local success = names.save_lookups() if success then - logs.names_report("info", 2, "cache", "Lookup cache emptied") + log.names_report("info", 2, "cache", "Lookup cache emptied") return true, true end end @@ -793,7 +795,7 @@ local cache_directives = { actions.cache = function (job) local directive = cache_directives[job.cache] if not directive or type(directive) ~= "function" then - logs.names_report("info", 2, "cache", + log.names_report("info", 2, "cache", "Invalid font cache directive %s.", job.cache) return false, false end @@ -838,14 +840,14 @@ actions.query = function (job) end if success then - logs.names_report(false, 0, + log.names_report(false, 0, "resolve", "Font %q found!", query) if subfont then - logs.names_report(false, 0, "resolve", + log.names_report(false, 0, "resolve", "Resolved file name %q, subfont nr. %q", foundname, subfont) else - logs.names_report(false, 0, "resolve", + log.names_report(false, 0, "resolve", "Resolved file name %q", foundname) end if job.show_info then @@ -853,12 +855,12 @@ actions.query = function (job) iowrite "\n" end else - logs.names_report(false, 0, + log.names_report(false, 0, "resolve", "Cannot find %q in index.", query) - logs.names_report(false, 0, + log.names_report(false, 0, "resolve", "Hint: use the --fuzzy option to display suggestions.", query) if job.fuzzy == true then - logs.names_report(false, 0, + log.names_report(false, 0, "resolve", "Looking for close matches, this may take a while ...") local _success = names.find_closest(query, job.fuzzy_limit) end @@ -957,7 +959,7 @@ actions.list = function (job) local nmappings = #mappings if criterion == "*" then - logs.names_report(false, 1, "list", "All %d entries", nmappings) + log.names_report(false, 1, "list", "All %d entries", nmappings) for i=1, nmappings do local entry = mappings[i] local fields = get_fields(entry, asked_fields) @@ -972,12 +974,12 @@ actions.list = function (job) criterion = criterion[1] asked_fields = set_primary_field(asked_fields, criterion) - logs.names_report(false, 1, "list", "By %s", criterion) + log.names_report(false, 1, "list", "By %s", criterion) --- firstly, build a list of fonts to operate on local targets = { } if asked_value then --- only those whose value matches - logs.names_report(false, 2, "list", "Restricting to value %s", asked_value) + log.names_report(false, 2, "list", "Restricting to value %s", asked_value) for i=1, nmappings do local entry = mappings[i] if entry[criterion] @@ -1022,7 +1024,7 @@ actions.list = function (job) end end local ntargets = #targets - logs.names_report(false, 2, "list", "%d entries", ntargets) + log.names_report(false, 2, "list", "%d entries", ntargets) --- now, output the collection for i=1, ntargets do @@ -1150,7 +1152,7 @@ local process_cmdline = function ( ) -- unit -> jobspec elseif v == "log" then local str = optarg[n] if str then - finalizers = logs.set_logout(str, finalizers) + finalizers = log.set_logout(str, finalizers) end elseif v == "find" then action_pending["query"] = true @@ -1230,23 +1232,23 @@ local main = function ( ) -- unit -> int local actionname = action_sequence[i] local exit = false if action_pending[actionname] then - logs.names_report("log", 3, "util", "Preparing for task", + log.names_report("log", 3, "util", "Preparing for task", "%s", actionname) local action = actions[actionname] local success, continue = action(job) if not success then - logs.names_report(false, 0, "util", + log.names_report(false, 0, "util", "Could not finish task", "%s", actionname) retval = -1 exit = true elseif not continue then - logs.names_report(false, 3, "util", + log.names_report(false, 3, "util", "Task completed, exiting", "%s", actionname) exit = true else - logs.names_report(false, 3, "util", + log.names_report(false, 3, "util", "Task completed successfully", "%s", actionname) end end -- cgit v1.2.3 From 2c6feb979da6435bb9f6636a46be576f688f7f78 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 9 Feb 2014 16:54:13 +0100 Subject: [log,*] s/log.names_report()/logs.report()/g Closing #169 Signed-off-by: Philipp Gesang --- luaotfload-auxiliary.lua | 2 +- luaotfload-database.lua | 2 +- luaotfload-diagnostics.lua | 4 +-- luaotfload-features.lua | 2 +- luaotfload-letterspace.lua | 10 +++--- luaotfload-log.lua | 16 ++++----- luaotfload-main.lua | 17 +++++----- luaotfload-override.lua | 8 ++--- luaotfload-parsers.lua | 2 +- luaotfload-tool.lua | 81 ++++++++++++++++++++++------------------------ 10 files changed, 70 insertions(+), 74 deletions(-) diff --git a/luaotfload-auxiliary.lua b/luaotfload-auxiliary.lua index 9ab78e7..716af98 100644 --- a/luaotfload-auxiliary.lua +++ b/luaotfload-auxiliary.lua @@ -17,7 +17,7 @@ luaotfload.aux = luaotfload.aux or { } local aux = luaotfload.aux local log = luaotfload.log -local report = log.names_report +local report = log.report local fonthashes = fonts.hashes local identifiers = fonthashes.identifiers diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 8f603db..c69fc03 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -45,7 +45,7 @@ local stripslashes = parsers.stripslashes local splitcomma = parsers.splitcomma local log = luaotfload.log -local report = log.names_report +local report = log.report local report_status = log.names_status local report_status_start = log.names_status_start local report_status_stop = log.names_status_stop diff --git a/luaotfload-diagnostics.lua b/luaotfload-diagnostics.lua index 4ffaa06..67119de 100644 --- a/luaotfload-diagnostics.lua +++ b/luaotfload-diagnostics.lua @@ -49,9 +49,9 @@ local lpeg = require "lpeg" local C, Cg, Ct = lpeg.C, lpeg.Cg, lpeg.Ct local lpegmatch = lpeg.match -local names_report = luaotfload.log.names_report +local report = luaotfload.log.report local out = function (...) - names_report (false, 0, "diagnose", ...) + report (false, 0, "diagnose", ...) end local parsers = luaotfload.parsers diff --git a/luaotfload-features.lua b/luaotfload-features.lua index 73edcd2..4237d71 100644 --- a/luaotfload-features.lua +++ b/luaotfload-features.lua @@ -49,7 +49,7 @@ function fonts.definers.getspecification(str) end local log = luaotfload.log -local report = log.names_report +local report = log.report local stringfind = string.find local stringlower = string.lower diff --git a/luaotfload-letterspace.lua b/luaotfload-letterspace.lua index a855bf3..20f29f5 100644 --- a/luaotfload-letterspace.lua +++ b/luaotfload-letterspace.lua @@ -7,7 +7,7 @@ if not modules then modules = { } end modules ['letterspace'] = { } local log = luaotfload.log -local report = log.names_report +local report = log.report local getmetatable = getmetatable local require = require @@ -515,10 +515,10 @@ otffeatures.register { local initializecompatfontkerning = function (tfmdata, percentage) local factor = tonumber (percentage) if not factor then - names_report ("both", 0, "letterspace", - "Invalid argument to letterspace: %s (type %q), " .. - "was expecting percentage as Lua number instead.", - percentage, type (percentage)) + report ("both", 0, "letterspace", + "Invalid argument to letterspace: %s (type %q), " .. + "was expecting percentage as Lua number instead.", + percentage, type (percentage)) return end return initializefontkerning (tfmdata, factor * 0.01) diff --git a/luaotfload-log.lua b/luaotfload-log.lua index 080550a..5698c84 100644 --- a/luaotfload-log.lua +++ b/luaotfload-log.lua @@ -200,7 +200,7 @@ local level_ids = { common = 1, loading = 2, search = 3 } --[[doc-- - The names_report logger is used more or less all over luaotfload. + The report() logger is used more or less all over luaotfload. Its requirements are twofold: 1) Provide two logging channels, the terminal and the log file; @@ -224,7 +224,7 @@ local level_ids = { common = 1, loading = 2, search = 3 } --doc]]-- -local names_report = function (mode, lvl, ...) +local report = function (mode, lvl, ...) if type(lvl) == "string" then lvl = level_ids[lvl] end @@ -242,7 +242,7 @@ local names_report = function (mode, lvl, ...) end end -log.names_report = names_report +log.report = report --[[doc-- @@ -301,7 +301,7 @@ local status_start = function (low, high) or os.getenv "TERM" == "dumb" then status_writer = function (mode, ...) - names_report (mode, high, ...) + report (mode, high, ...) end return end @@ -310,7 +310,7 @@ local status_start = function (low, high) status_writer = status_logger else status_writer = function (mode, ...) - names_report (mode, high, ...) + report (mode, high, ...) end end end @@ -348,7 +348,7 @@ log.names_status_stop = status_stop --doc]]-- local texioreporter = function (message) - names_report("log", 2, message) + report ("log", 2, message) end texio.reporter = texioreporter @@ -386,10 +386,10 @@ if fonts then --- need to be running TeX if k == "unicodes" then local glyphlist = resolvers.findfile"luaotfload-glyphlist.lua" if glyphlist then - names_report("log", 1, "load", "loading the Adobe glyph list") + report ("log", 1, "load", "loading the Adobe glyph list") else glyphlist = resolvers.findfile"font-age.lua" - names_report("both", 0, "load", + report ("both", 0, "load", "loading the extended glyph list from ConTeXt") end local unicodes = dofile(glyphlist) diff --git a/luaotfload-main.lua b/luaotfload-main.lua index 35c01f2..f5f012d 100644 --- a/luaotfload-main.lua +++ b/luaotfload-main.lua @@ -148,7 +148,7 @@ end loadmodule "log.lua" --- messages; used to be part of -override local log = luaotfload.log -local report = log.names_report +local report = log.report log.set_loglevel(config.luaotfload.loglevel) @@ -387,7 +387,8 @@ end --- non-merge fallback scope pop_namespaces(our_environment, false)-- true) -report ("both", 0, "main", "fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) +report ("both", 0, "main", + "fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) --[[doc-- @@ -543,9 +544,9 @@ request_resolvers.anon = function (specification) local exists, _ = lfsisfile(name) if exists then --- garbage; we do this because we are nice, --- not because it is correct - log.names_report("log", 1, "load", "file %q exists", name) - log.names_report("log", 1, "load", - "... overriding borked anon: lookup with path: lookup") + report ("log", 1, "load", "file %q exists", name) + report ("log", 1, "load", + "... overriding borked anon: lookup with path: lookup") specification.name = name request_resolvers.path(specification) return @@ -567,9 +568,9 @@ request_resolvers.path = function (specification) local name = specification.name local exists, _ = lfsisfile(name) if not exists then -- resort to file: lookup - log.names_report("log", 1, "load", - "path lookup of %q unsuccessful, falling back to file:", - name) + report ("log", 1, "load", + "path lookup of %q unsuccessful, falling back to file:", + name) file_resolver (specification) else local suffix = filesuffix (name) diff --git a/luaotfload-override.lua b/luaotfload-override.lua index e726f6f..b75530b 100644 --- a/luaotfload-override.lua +++ b/luaotfload-override.lua @@ -10,7 +10,7 @@ local findfile = resolvers.findfile local encodings = fonts.encodings local log = luaotfload.log -local names_report = log.names_report +local report = log.report --[[doc-- @@ -38,11 +38,11 @@ setmetatable(fonts.encodings.agl, { __index = function (t, k) end local glyphlist = findfile "luaotfload-glyphlist.lua" if glyphlist then - names_report("log", 1, "load", "loading the Adobe glyph list") + report ("log", 1, "load", "loading the Adobe glyph list") else glyphlist = findfile "font-age.lua" - names_report("both", 0, "load", - "loading the extended glyph list from ConTeXt") + report ("both", 0, "load", + "loading the extended glyph list from ConTeXt") end local unicodes = dofile(glyphlist) encodings.agl = { unicodes = unicodes } diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua index 8060d05..5145ca0 100644 --- a/luaotfload-parsers.lua +++ b/luaotfload-parsers.lua @@ -39,7 +39,7 @@ local io = io local ioopen = io.open local log = luaotfload.log -local report = log.names_report +local report = log.report local string = string local stringsub = string.sub diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index e791e3d..35765b5 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -154,7 +154,7 @@ luaotfloadconfig.status = luaotfloadstatus local sanitize_fontname = names.sanitize_fontname local log = luaotfload.log -local names_report = log.names_report -- TODO improve identifier +local report = log.report local pathdata = names.path local names_plain = pathdata.index.lua @@ -683,9 +683,9 @@ local show_font_info = function (basename, askedname, detail, warnings) if nfonts > 0 then -- true type collection local subfont if askedname then - log.names_report(true, 1, "resolve", - [[%s is part of the font collection %s]], - askedname, basename) + report (true, 1, "resolve", + [[%s is part of the font collection %s]], + askedname, basename) subfont = subfont_by_name(shortinfo, askedname) end if subfont then @@ -694,11 +694,11 @@ local show_font_info = function (basename, askedname, detail, warnings) show_full_info(fullname, subfont, warnings) end else -- list all subfonts - log.names_report(true, 1, "resolve", - [[%s is a font collection]], basename) + report (true, 1, "resolve", + [[%s is a font collection]], basename) for subfont = 1, nfonts do - log.names_report(true, 1, "resolve", - [[Showing info for font no. %d]], n) + report (true, 1, "resolve", + [[Showing info for font no. %d]], n) show_info_items(shortinfo[subfont]) if detail == true then show_full_info(fullname, subfont, warnings) @@ -712,8 +712,7 @@ local show_font_info = function (basename, askedname, detail, warnings) end end else - log.names_report(true, 1, "resolve", - "Font %s not found", filename) + report (true, 1, "resolve", "Font %s not found", filename) end end @@ -738,9 +737,8 @@ local actions = { } --- (jobspec -> (bool * bool)) list actions.loglevel = function (job) log.set_loglevel(job.log_level) - log.names_report("info", 3, "util", - "Setting log level", "%d", job.log_level) - log.names_report("log", 2, "util", "Lua=%s", _VERSION) + report ("info", 3, "util", "Setting log level", "%d", job.log_level) + report ("log", 2, "util", "Lua=%q", _VERSION) return true, true end @@ -766,8 +764,7 @@ end actions.generate = function (job) local fontnames, savedname fontnames = names.update(fontnames, job.force_reload, job.dry_run) - log.names_report("info", 2, "db", - "Fonts in the database: %i", #fontnames.mappings) + report ("info", 2, "db", "Fonts in the database: %i", #fontnames.mappings) if names.data() then return true, true end @@ -779,7 +776,7 @@ actions.flush = function (job) if success then local success = names.save_lookups() if success then - log.names_report("info", 2, "cache", "Lookup cache emptied") + report ("info", 2, "cache", "Lookup cache emptied") return true, true end end @@ -795,8 +792,8 @@ local cache_directives = { actions.cache = function (job) local directive = cache_directives[job.cache] if not directive or type(directive) ~= "function" then - log.names_report("info", 2, "cache", - "Invalid font cache directive %s.", job.cache) + report ("info", 2, "cache", + "Invalid font cache directive %s.", job.cache) return false, false end if directive() then @@ -840,28 +837,27 @@ actions.query = function (job) end if success then - log.names_report(false, 0, - "resolve", "Font %q found!", query) + report (false, 0, "resolve", "Font %q found!", query) if subfont then - log.names_report(false, 0, "resolve", - "Resolved file name %q, subfont nr. %q", + report (false, 0, "resolve", + "Resolved file name %q, subfont nr. %q", foundname, subfont) else - log.names_report(false, 0, "resolve", - "Resolved file name %q", foundname) + report (false, 0, "resolve", + "Resolved file name %q", foundname) end if job.show_info then show_font_info (foundname, query, job.full_info, job.warnings) iowrite "\n" end else - log.names_report(false, 0, - "resolve", "Cannot find %q in index.", query) - log.names_report(false, 0, - "resolve", "Hint: use the --fuzzy option to display suggestions.", query) + report (false, 0, "resolve", "Cannot find %q in index.", query) + report (false, 0, "resolve", + "Hint: use the --fuzzy option to display suggestions.", + query) if job.fuzzy == true then - log.names_report(false, 0, - "resolve", "Looking for close matches, this may take a while ...") + report (false, 0, "resolve", + "Looking for close matches, this may take a while ...") local _success = names.find_closest(query, job.fuzzy_limit) end end @@ -959,7 +955,7 @@ actions.list = function (job) local nmappings = #mappings if criterion == "*" then - log.names_report(false, 1, "list", "All %d entries", nmappings) + report (false, 1, "list", "All %d entries", nmappings) for i=1, nmappings do local entry = mappings[i] local fields = get_fields(entry, asked_fields) @@ -974,12 +970,12 @@ actions.list = function (job) criterion = criterion[1] asked_fields = set_primary_field(asked_fields, criterion) - log.names_report(false, 1, "list", "By %s", criterion) + report (false, 1, "list", "By %s", criterion) --- firstly, build a list of fonts to operate on local targets = { } if asked_value then --- only those whose value matches - log.names_report(false, 2, "list", "Restricting to value %s", asked_value) + report (false, 2, "list", "Restricting to value %s", asked_value) for i=1, nmappings do local entry = mappings[i] if entry[criterion] @@ -1024,7 +1020,7 @@ actions.list = function (job) end end local ntargets = #targets - log.names_report(false, 2, "list", "%d entries", ntargets) + report (false, 2, "list", "%d entries", ntargets) --- now, output the collection for i=1, ntargets do @@ -1232,24 +1228,23 @@ local main = function ( ) -- unit -> int local actionname = action_sequence[i] local exit = false if action_pending[actionname] then - log.names_report("log", 3, "util", "Preparing for task", - "%s", actionname) + report ("log", 3, "util", "Preparing for task", "%s", actionname) local action = actions[actionname] local success, continue = action(job) if not success then - log.names_report(false, 0, "util", - "Could not finish task", "%s", actionname) + report (false, 0, "util", + "Could not finish task", "%s", actionname) retval = -1 exit = true elseif not continue then - log.names_report(false, 3, "util", - "Task completed, exiting", "%s", actionname) - exit = true + report (false, 3, "util", + "Task completed, exiting", "%s", actionname) + exit = true else - log.names_report(false, 3, "util", - "Task completed successfully", "%s", actionname) + report (false, 3, "util", + "Task completed successfully", "%s", actionname) end end if exit then break end -- cgit v1.2.3 From cfb1b0117659535e74477fe800891768de78058b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 9 Feb 2014 21:28:51 +0100 Subject: [parsers,features] fix missing localized math.ceil() --- luaotfload-parsers.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua index 5145ca0..1048e1d 100644 --- a/luaotfload-parsers.lua +++ b/luaotfload-parsers.lua @@ -46,6 +46,8 @@ local stringsub = string.sub local stringfind = string.find local stringlower = string.lower +local mathceil = math.ceil + local lfs = lfs local lfsisfile = lfs.isfile local lfsisdir = lfs.isdir -- cgit v1.2.3 From b9897d84cb635d6a6b47aee17e818e3bfc296d2c Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 9 Feb 2014 21:40:13 +0100 Subject: [doc,sty] move luaotfload.sty out of the DTX Signed-off-by: Philipp Gesang --- .gitignore | 1 - luaotfload.dtx | 37 ++++++------------------------------- luaotfload.sty | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 32 deletions(-) create mode 100644 luaotfload.sty diff --git a/.gitignore b/.gitignore index e010a81..0da53bd 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ luaotfload/* # Files generated by 'make world' and removed by 'make mrproper' luaotfload.pdf -luaotfload.sty luaotfload.tds.zip luaotfload.zip diff --git a/luaotfload.dtx b/luaotfload.dtx index 8f8632c..e0311a4 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -67,13 +67,6 @@ and the derived files \endpreamble -\let\MetaPrefix\DoubleperCent - -\generate{% - \usedir{tex/luatex/luaotfload}% - \file{luaotfload.sty}{\from{luaotfload.dtx}{package}}% -} - \obeyspaces \Msg{************************************************************************} \Msg{*} @@ -1558,30 +1551,12 @@ and the derived files % % \section{\fileent{luaotfload.sty}} % -% \iffalse -%<*package> -% \fi -% -% Classical Plain+\LATEX package initialization. -% -% \begin{macrocode} -\csname ifluaotfloadloaded\endcsname -\let\ifluaotfloadloaded\endinput -\bgroup\expandafter\expandafter\expandafter\egroup -\expandafter\ifx\csname ProvidesPackage\endcsname\relax - \input luatexbase.sty -\else - \NeedsTeXFormat{LaTeX2e} - \ProvidesPackage{luaotfload}% - [2014/42/42 v2.5 OpenType layout system] - \RequirePackage{luatexbase} -\fi -\RequireLuaModule{luaotfload-main} -\endinput -% \end{macrocode} -% \iffalse -% -% \fi +% As of version 2.5, the file \fileent{luaotfload.sty} is no longer +% generated from the \abbrev{dtx}. +% Instead, it is maintained separately as a plain \identifier{\TEX} file +% in the Luaotfload \identifier{git} tree. +% The file documentation which used to be found in this section has +% been preserved in the comments. % % \clearpage % \section{The GNU GPL License v2} diff --git a/luaotfload.sty b/luaotfload.sty new file mode 100644 index 0000000..a235d6b --- /dev/null +++ b/luaotfload.sty @@ -0,0 +1,45 @@ +%% Copyright (C) 2009-2014 +%% +%% by Elie Roux +%% and Khaled Hosny +%% and Philipp Gesang +%% +%% This file is part of Luaotfload. +%% +%% Home: https://github.com/lualatex/luaotfload +%% Support: . +%% +%% Luaotfload is under the GPL v2.0 (exactly) license. +%% +%% ---------------------------------------------------------------------------- +%% +%% Luaotfload is free software; you can redistribute it and/or +%% modify it under the terms of the GNU General Public License +%% as published by the Free Software Foundation; version 2 +%% of the License. +%% +%% Luaotfload is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU General Public License +%% along with Luaotfload; if not, see . +%% +%% ---------------------------------------------------------------------------- +%% +%% Classical Plain+\LATEX package initialization. +%% +\csname ifluaotfloadloaded\endcsname +\let\ifluaotfloadloaded\endinput +\bgroup\expandafter\expandafter\expandafter\egroup +\expandafter\ifx\csname ProvidesPackage\endcsname\relax + \input luatexbase.sty +\else + \NeedsTeXFormat{LaTeX2e} + \ProvidesPackage{luaotfload}% + [2014/42/42 v2.5 OpenType layout system] + \RequirePackage{luatexbase} +\fi +\RequireLuaModule{luaotfload-main} + -- cgit v1.2.3 From 9fab25beeac99c0295f1a1f3db5aae711e564d72 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 10 Feb 2014 06:20:00 +0100 Subject: [doc] move documentation to ./doc --- doc/filegraph.dot | 287 +++++++ doc/luaotfload-tool.rst | 263 +++++++ doc/luaotfload.dtx | 1994 +++++++++++++++++++++++++++++++++++++++++++++++ filegraph.dot | 287 ------- luaotfload-tool.rst | 263 ------- luaotfload.dtx | 1994 ----------------------------------------------- 6 files changed, 2544 insertions(+), 2544 deletions(-) create mode 100644 doc/filegraph.dot create mode 100644 doc/luaotfload-tool.rst create mode 100644 doc/luaotfload.dtx delete mode 100644 filegraph.dot delete mode 100644 luaotfload-tool.rst delete mode 100644 luaotfload.dtx diff --git a/doc/filegraph.dot b/doc/filegraph.dot new file mode 100644 index 0000000..47db9ea --- /dev/null +++ b/doc/filegraph.dot @@ -0,0 +1,287 @@ +strict digraph luaotfload_files { //looks weird with circo ... + compound = true; + +// label = "Schematic of the files included in Luaotfload."; +// labelloc = "b"; + + fontsize = "14.4"; + labelfontname = "Iwona Medium Regular"; + fontname = "Iwona Light Regular"; + size = "21cm"; + + rankdir = LR; + ranksep = 0.618; + nodesep = 1.618; + + edge [ + arrowhead = onormal, + fontname = "Iwona Cond Regular", + penwidth = 1.0, + ]; + node [ + //penwidth = 0.7, + fontname = "Liberation Mono", + fontsize = 12, + ]; + +/* ···································································· + * file structure + * ································································· */ + fontdbutil -> font_names [label="--update", + style=dashed] + + luaotfload -> otfl_fonts_merged [label="merged"] + luaotfload -> merged_lua_libs [label="unmerged", style=solid] + luaotfload -> merged_luatex_fonts [label="unmerged", style=solid] + luaotfload -> merged_context_libs [label="unmerged", style=solid] + + luaotfload -> luaotfload_libs + luaotfload -> otfl_blacklist_cnf + + otfl_fonts_merged -> merged_lua_libs [label="merged", + style=dotted, + lhead=cluster_merged] + otfl_fonts_merged -> merged_luatex_fonts [label="merged", + style=dotted, + lhead=cluster_merged] + otfl_fonts_merged -> merged_context_libs [label="merged", + style=dotted, + lhead=cluster_merged] + + merged_luatex_fonts -> font_age [label="luatex-fonts-enc.lua", + ltail=cluster_merged] + + fontdbutil -> fontdbutil_diagnostics [label="--diagnose"] + + fontdbutil -> status [label="version information"] + + fontdbutil_diagnostics -> status [constraint=no, label="hash files"] + + merged_luatex_fonts -> characters [label="luaotfload-auxiliary.lua", + ltail=cluster_merged] + + luaotfload_libs -> font_names [label="luaotfload-database.lua"] + + mkstatus -> status [label="generates from distribution files", + style=dashed] + + mkglyphlist -> font_age [label="generates from glyphlist.txt", + style=dashed] + + mkcharacters -> characters [label="generates from Context’s char-def.lua", + style=dashed] + + subgraph { rank = same; + mkcharacters; + mkglyphlist; + mkstatus; + fontdbutil; + luaotfload } + +/* ···································································· + * main files + * ································································· */ + + fontdbutil [label = "luaotfload-tool.lua", + shape = rect, + width = "3.2cm", + height = "1.2cm", + color = "#01012222", + style = "filled,rounded", + penwidth=2] + + fontdbutil_diagnostics [label = "luaotfload-diagnostics.lua", + shape = rect, + width = "3.2cm", + height = "1.2cm", + color = "#01012222", + style = "filled,rounded", + penwidth=2] + + mkstatus [label = "mkstatus", + shape = rect, + width = "3.2cm", + height = "1.2cm", + color = "#01012222", + style = "filled,rounded", + penwidth=2] + + mkglyphlist [label = "mkglyphlist", + shape = rect, + width = "3.2cm", + height = "1.2cm", + color = "#01012222", + style = "filled,rounded", + penwidth=2] + + mkcharacters [label = "mkcharacters", + shape = rect, + width = "3.2cm", + height = "1.2cm", + color = "#01012222", + style = "filled,rounded", + penwidth=2] + + luaotfload [label = "luaotfload-main.lua", + shape = rect, + width = "3.2cm", + height = "1.2cm", + color = "#01012222", + style = "filled,rounded", + penwidth=2] + /* + *otfl_fonts [label = "luaotfload-fonts.lua", + * shape = rect, + * width = "3.2cm", + * height = "1.2cm", + * color = "#01012222", + * style = "filled,rounded", + * penwidth=2] + */ + otfl_fonts_merged [label = "luaotfload-fontloader.lua", + shape = rect, + width = "3.2cm", + height = "1.2cm", + color = "#01012222", + style = "filled,rounded", + penwidth=2] + +/* ···································································· + * luaotfload files + * ································································· */ + + characters [style = "filled,dashed", + shape = rect, + width = "3.2cm", + fillcolor = "#01012222", + color = grey40, + style = "filled,dotted,rounded", + label = "luaotfload-characters.lua"] + + font_age [style = "filled,dashed", + shape = rect, + width = "3.2cm", + fillcolor = "#01012222", + color = grey40, + style = "filled,dotted,rounded", + label = "luaotfload-glyphlist.lua"] + + font_names [style = "filled,dashed", + shape = rect, + width = "3.2cm", + fillcolor = "#01012222", + color = grey40, + style = "filled,dotted,rounded", + label = "luaotfload-names.lua.gz\nluaotfload-names.luc"] + + status [style = "filled,dashed", + shape = rect, + width = "3.2cm", + fillcolor = "#01012222", + color = grey40, + style = "filled,dotted,rounded", + label = "luaotfload-status.lua"] + + otfl_blacklist_cnf [style = "filled,dashed", + shape = rect, + width = "3.2cm", + fillcolor = "#01012222", + color = grey40, + style = "filled,dotted,rounded", + label = "luaotfload-blacklist.cnf"] + + luaotfload_libs [ + shape = box, + style = "filled,rounded", + color = "grey90:goldenrod4", + fontsize = 10, + label = < + + + + + + + +
Luaotfload Libraries
luaotfload-auxiliary.lua luaotfload-features.lua
luaotfload-override.lua luaotfload-loaders.lua
luaotfload-log.lua luaotfload-letterspace.lua
luaotfload-parsers.lua luaotfload-database.lua
luaotfload-color.lua
+ >, + ] + +/* ···································································· + * merged files + * ································································· */ + + subgraph cluster_merged { + node [style=filled, color=white]; + style = "filled,rounded"; + color = "grey90:dodgerblue4"; + //nodesep = "3.0"; + rank = same; + label = "Merged Libraries"; + gradientangle=0; + merged_lua_libs; + merged_luatex_fonts; + merged_context_libs; + } + + otfl_fonts_merged -> merged_lua_libs + otfl_fonts_merged -> merged_luatex_fonts + otfl_fonts_merged -> merged_context_libs + + merged_lua_libs [ + shape = box, + style = "filled,rounded", + color = "#FFFFFFAA", + fontsize = 10, + label = < + + + + + + +
Lua Libraries from Context
l-lua.lua l-lpeg.lua l-function.lua
l-string.lua l-table.lua l-io.lua
l-file.lua l-boolean.lua l-math.lua
util-str.lua
+ >, + ] + + merged_luatex_fonts [ + shape = box, + style = "filled,rounded", + color = "#FFFFFFAA", + fontsize = 10, + label = < + + + + + + + + + + + + + +
Font Loader (LuaTeX-Fonts)
luatex-basics-gen.lua luatex-basics-nod.lua
luatex-fonts-enc.lua luatex-fonts-syn.lua
luatex-font-tfm.lua luatex-font-afm.lua
luatex-font-afk.lua luatex-fonts-tfm.lua
luatex-fonts-chr.lua luatex-fonts-lua.lua
luatex-fonts-inj.lua luatex-fonts-otn.lua
luatex-fonts-def.lua luatex-fonts-ext.lua
luatex-fonts-cbk.lua
+ >, + ] + + merged_context_libs [ + shape = box, + style = "filled,rounded", + color = "#FFFFFFAA", + fontsize = 10, + label = < + + + + + + +
Font and Node Libraries from Context
data-con.lua font-ini.lua font-con.lua
font-cid.lua font-map.lua font-oti.lua
font-otf.lua font-otb.lua font-ota.lua
font-def.lua
+ >, + ] +} + +// vim:ft=dot:sw=4:ts=4:expandtab diff --git a/doc/luaotfload-tool.rst b/doc/luaotfload-tool.rst new file mode 100644 index 0000000..6863918 --- /dev/null +++ b/doc/luaotfload-tool.rst @@ -0,0 +1,263 @@ +======================================================================= + luaotfload-tool +======================================================================= + +----------------------------------------------------------------------- + generate and query the Luaotfload font names database +----------------------------------------------------------------------- + +:Date: 2014-01-02 +:Copyright: GPL v2.0 +:Version: 2.5 +:Manual section: 1 +:Manual group: text processing + +SYNOPSIS +======================================================================= + +**luaotfload-tool** [ -bDfFiIlnpquvVhw ] + +**luaotfload-tool** --update [ --force ] [ --quiet ] [ --verbose ] + [ --prefer-texmf ] [ --dry-run ] + [ --formats=[+|-]EXTENSIONS ] + [ --no-compress ] [ --no-strip ] + +**luaotfload-tool** --find=FONTNAME [ --fuzzy ] [ --info ] [ --inspect ] + [ --no-reload ] + +**luaotfload-tool** --flush-lookups + +**luaotfload-tool** --cache=DIRECTIVE + +**luaotfload-tool** --list=CRITERION[:VALUE] [ --fields=F1,F2,...,Fn ] + +**luaotfload-tool** --help + +**luaotfload-tool** --version + +**luaotfload-tool** --show-blacklist + +**luaotfload-tool** --diagnose=CHECK + +DESCRIPTION +======================================================================= + +luaotfload-tool accesses the font names database that is required by +the *Luaotfload* package. There are two general modes: **update** and +**query**. + ++ **update**: update the database or rebuild it entirely; ++ **query**: resolve a font name or display close matches. + +OPTIONS +======================================================================= + +update mode +----------------------------------------------------------------------- +--update, -u Update the database; indexes new fonts. +--force, -f Force rebuilding of the database; re-indexes + all fonts. +--no-reload, -n Suppress auto-updates to the database (e.g. + when ``--find`` is passed an unknown name). +--no-strip Do not strip redundant information after + building the database. Warning: this will + inflate the index to about two to three times + the normal size. +--no-compress, -c Do not filter the plain text version of the + font index through gzip. Useful for debugging + if your editor is built without zlib. + +--prefer-texmf, -p Organize the file name database in a way so + that it prefer fonts in the *TEXMF* tree over + system fonts if they are installed in both. +--max-fonts=N Process at most *N* font files, including fonts + already indexed in the count. +--formats=EXTENSIONS Extensions of the font files to index. + Where *EXTENSIONS* is a comma-separated list of + supported file extensions (otf, ttf, ttc, + dfont, pfa, and pfb). If the list is prefixed + with a ``+`` sign, the given list is added to + the currently active one; ``-`` subtracts. + Default: *otf,ttf,ttc,dfont*. + Examples: + + 1) ``--formats=-ttc,ttf`` would skip + TrueType fonts and font collections; + 2) ``--formats=otf`` would scan only OpenType + files; + 3) ``--formats=+pfb`` includes binary + Postscript files. **Warning**: with a + standard TeX Live installation this will + grow the database considerably and slow down + font indexing. + +--dry-run, -D Don’t load fonts, scan directories only. + (For debugging file system related issues.) + +query mode +----------------------------------------------------------------------- +--find=NAME Resolve a font name; this looks up in + the database and prints the file name it is + mapped to. + ``--find`` also understands request syntax, + i.e. ``--find=file:foo.otf`` checks whether + ``foo.otf`` is indexed. +--fuzzy, -F Show approximate matches to the file name if + the lookup was unsuccessful (requires + ``--find``). + +--info, -i Display basic information to a resolved font + file (requires ``--find``). +--inspect, -I Display detailed information by loading the + font and analyzing the font table; very slow! + For the meaning of the returned fields see + the LuaTeX documentation. + (requires ``--find``). +--warnings, -w Print the warnings generated by the fontloader + library (assumes ``-I``). Automatically enabled + if the verbosity level exceeds 2. + +--show-blacklist, -b Show blacklisted files (not directories). +--list=CRITERION Show entries, where *CRITERION* is one of the + following: + + 1) the character ``*``, selecting all entries; + 2) a field of a database entry, for instance + *version* or *format**, according to which + the output will be sorted. + Information in an unstripped database (see + the option ``--no-strip`` above) is nested: + Subfields of a record can be addressed using + the ``->`` separator, e. g. + ``file->location``, ``style->units_per_em``, + or + ``names->sanitized->english->prefmodifiers``. + NB: shell syntax requires that arguments + containing ``->`` be properly quoted! + 3) an expression of the form ``field:value`` to + limit the output to entries whose ``field`` + matches ``value``. + + For example, in order to output file names and + corresponding versions, sorted by the font + format:: + + ./luaotfload-tool.lua --list="format" --fields="file->base,version" + + This prints:: + + otf latinmodern-math.otf Version 1.958 + otf lmromancaps10-oblique.otf 2.004 + otf lmmono8-regular.otf 2.004 + otf lmmonoproplt10-bold.otf 2.004 + otf lmsans10-oblique.otf 2.004 + otf lmromanslant8-regular.otf 2.004 + otf lmroman12-italic.otf 2.004 + otf lmsansdemicond10-oblique.otf 2.004 + ... + +--fields=FIELDS Comma-separated list of fields that should be + printed. + Information in an unstripped database (see the + option ``--no-strip`` above) is nested: + Subfields of a record can be addressed using + the ``->`` separator, e. g. + ``file->location``, ``style->units_per_em``, + or ``names->sanitized->english->subfamily``. + The default is plainname,version*. + (Only meaningful with ``--list``.) + +font and lookup caches +----------------------------------------------------------------------- +--flush-lookups Clear font name lookup cache (experimental). + +--cache=DIRECTIVE Cache control, where *DIRECTIVE* is one of the + following: + + 1) ``purge`` -> delete Lua files from cache; + 2) ``erase`` -> delete Lua and Luc files from + cache; + 3) ``show`` -> print stats. + +miscellaneous +----------------------------------------------------------------------- +--verbose=N, -v Set verbosity level to *n* or the number of + repetitions of ``-v``. +--quiet No verbose output (log level set to zero). +--log=CHANNEL Redirect log output (for database + troubleshooting), where *CHANNEL* can be + + 1) ``stdout`` -> all output will be + dumped to the terminal (default); or + 2) ``file`` -> write to a file to the temporary + directory (the name will be chosen + automatically. + +--version, -V Show version numbers of components as well as + some basic information and exit. +--help, -h Show help message and exit. + +--diagnose=CHECK Run the diagnostic procedure *CHECK*. Available + procedures are: + + 1) ``files`` -> check *Luaotfload* files for + modifications; + 2) ``permissions`` -> check permissions of + cache directories and files; + 3) ``environment`` -> print relevant + environment and kpse variables; + 4) ``repository`` -> check the git repository + for new releases, + 5) ``index`` -> check database, display + information about it. + + Procedures can be chained by concatenating with + commas, e.g. ``--diagnose=files,permissions``. + Specify ``thorough`` to run all checks. + +FILES +======================================================================= + +The font name database is usually located in the directory +``texmf-var/luatex-cache/generic/names/`` (``$TEXMFCACHE`` as set in +``texmf.cnf``) of your *TeX Live* distribution as a zlib-compressed +file ``luaotfload-names.lua.gz``. +The experimental lookup cache will be created as +``luaotfload-lookup-cache.lua`` in the same directory. +These Lua tables are not used directly by Luaotfload, though. +Instead, they are compiled to Lua bytecode which is written to +corresponding files with the extension ``.luc`` in the same directory. +When modifying the files by hand keep in mind that only if the bytecode +files are missing will Luaotfload use the plain version instead. +Both kinds of files are safe to delete, at the cost of regenerating +them with the next run of *LuaTeX*. + +SEE ALSO +======================================================================= + +**luatex** (1), **lua** (1) + +* ``texdoc luaotfload`` to display the manual for the *Luaotfload* + package +* Luaotfload development ``_ +* LuaLaTeX mailing list ``_ +* LuaTeX ``_ +* ConTeXt ``_ +* Luaotfload on CTAN ``_ + +BUGS +======================================================================= + +Tons, probably. + +AUTHORS +======================================================================= + +*Luaotfload* is maintained by the LuaLaTeX dev team +(``__). +The fontloader code is provided by Hans Hagen of Pragma ADE, Hasselt +NL (``__). + +This manual page was written by Philipp Gesang +. + diff --git a/doc/luaotfload.dtx b/doc/luaotfload.dtx new file mode 100644 index 0000000..e0311a4 --- /dev/null +++ b/doc/luaotfload.dtx @@ -0,0 +1,1994 @@ +% \iffalse meta-comment +% +% Copyright (C) 2009-2014 +% by Elie Roux +% and Khaled Hosny +% and Philipp Gesang +% +% Home: https://github.com/lualatex/luaotfload +% Support: . +% +% This work is under the GPL v2.0 license. +% +% This work consists of the main source file luaotfload.dtx +% and the derived files +% luaotfload.sty +% +% Unpacking: +% tex luaotfload.dtx +% +% Documentation: +% lualatex luaotfload.dtx +% +% The class ltxdoc loads the configuration file ltxdoc.cfg +% if available. Here you can specify further options, e.g. +% use A4 as paper format: +% \PassOptionsToClass{a4paper}{article} +% +% +% +%<*ignore> +\begingroup + \def\x{LaTeX2e}% +\expandafter\endgroup +\ifcase 0\ifx\install y1\fi\expandafter + \ifx\csname processbatchFile\endcsname\relax\else1\fi + \ifx\fmtname\x\else 1\fi\relax +\else\csname fi\endcsname +% +%<*install> +\input docstrip.tex +\Msg{************************************************************************} +\Msg{* Installation} +\Msg{* Package: luaotfload v2.5 OpenType layout system} +\Msg{************************************************************************} + +\keepsilent +\askforoverwritefalse + +\let\MetaPrefix\relax + +\preamble +This is a generated file. + +Copyright (C) 2009-2014 + by Elie Roux + and Khaled Hosny + and Philipp Gesang + + Home: https://github.com/lualatex/luaotfload + Support: . + +This work is under the GPL v2.0 license. + +This work consists of the main source file luaotfload.dtx +and the derived files + luaotfload.sty + +\endpreamble + +\obeyspaces +\Msg{************************************************************************} +\Msg{*} +\Msg{* To finish the installation you have to move the following} +\Msg{* files into a directory searched by TeX:} +\Msg{*} +\Msg{* luaotfload.sty} +\Msg{*} +\Msg{* Happy TeXing!} +\Msg{*} +\Msg{************************************************************************} + +\endbatchfile +% +%<*ignore> +\fi +% +%<*driver> +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{luaotfload.drv}% +[2014/**/** v2.5 OpenType layout system]% +\documentclass{ltxdoc} +\usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace} +\usepackage[x11names]{xcolor} +% +\def\primarycolor{DodgerBlue4} %%-> rgb 16 78 139 | #104e8b +\def\secondarycolor{Goldenrod4} %%-> rgb 139 105 200 | #8b6914 +% +\usepackage[ + bookmarks=true, + colorlinks=true, + linkcolor=\primarycolor, + urlcolor=\secondarycolor, + citecolor=\primarycolor, + pdftitle={The luaotfload package}, + pdfsubject={OpenType layout system for Plain TeX and LaTeX}, + pdfauthor={Elie Roux & Khaled Hosny & Philipp Gesang}, + pdfkeywords={luatex, lualatex, unicode, opentype} +]{hyperref} +\usepackage{fontspec} +\usepackage{unicode-math} +\setmainfont[ +% Numbers = OldStyle, %% buggy with font cache + Ligatures = TeX, + BoldFont = {Linux Libertine O Bold}, + ItalicFont = {Linux Libertine O Italic}, + SlantedFont = {Linux Libertine O Italic}, +]{Linux Libertine O} +\setmonofont[Ligatures=TeX,Scale=MatchLowercase]{Liberation Mono} +%setsansfont[Ligatures=TeX]{Linux Biolinum O} +\setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium} +%setmathfont{XITS Math} + +\usepackage{hologo} + +\newcommand\TEX {\TeX\xspace} +\newcommand\LUA {Lua\xspace} +\newcommand\PDFTEX {pdf\TeX\xspace} +\newcommand\LUATEX {Lua\TeX\xspace} +\newcommand\XETEX {\XeTeX\xspace} +\newcommand\LATEX {\LaTeX\xspace} +\newcommand\LUALATEX {Lua\LaTeX\xspace} +\newcommand\CONTEXT {Con\TeX t\xspace} +\newcommand\OpenType {\identifier{Open\kern-.25ex Type}\xspace} + +\def\definehighlight[#1][#2]% + {\ifcsname #1\endcsname\else + \expandafter\def\csname #1\endcsname% + {\bgroup#2\csname #1_indeed\endcsname} + \expandafter\def\csname #1_indeed\endcsname##1% + {##1\egroup}% + \fi} + +\def\restoreunderscore{\catcode`\_=12\relax} + +\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs +\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs +\definehighlight[luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers +\definehighlight [identifier][\sffamily] %% names +\definehighlight [abbrev][\rmfamily\scshape] %% acronyms +\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph + +\newcommand*\email[1]{\href{mailto:#1}{#1}} + +\renewcommand\partname{Part}%% gets rid of the stupid “file” heading + +\usepackage{syntax}%% bnf for font request syntax + +\usepackage{titlesec} + +\def\movecountertomargin#1{\llap{\rmfamily\upshape#1\hskip2em}} +\def\zeropoint{0pt} +\titleformat \part + {\normalsize\rmfamily\bfseries} + {\movecountertomargin\thepart} \zeropoint {} +\titleformat \section + {\normalsize\rmfamily\scshape} + {\movecountertomargin\thesection} \zeropoint {} +\titleformat \subsection + {\small\rmfamily\itshape} + {\movecountertomargin\thesubsection} \zeropoint {} +\titleformat \subsubsection + {\normalsize\rmfamily\upshape} + {\movecountertomargin\thesubsubsection} \zeropoint {} + +\usepackage{tocloft} +\renewcommand \cftpartfont {\rmfamily\upshape} +\renewcommand \cftsecfont {\rmfamily\upshape} +\renewcommand \cftsubsecfont {\rmfamily\upshape} +\setlength \cftbeforepartskip {1ex} +\setlength \cftbeforesecskip {1ex} + +\VerbatimFootnotes +\begin{document} + \DocInput{luaotfload.dtx}% +\end{document} +% +% \fi +% +% \CheckSum{0} +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% \GetFileInfo{luaotfload.drv} +% +% \title{The \identifier{luaotfload} package} +% \date{2014/**/** v2.5} +% \author{Elie Roux · Khaled Hosny · Philipp Gesang\\ +% Home: \url{https://github.com/lualatex/luaotfload}\\ +% Support: \email{lualatex-dev@tug.org}} +% +% \maketitle +% +% \begin{abstract} +% This package is an adaptation of the \CONTEXT font loading system. +% It allows for loading \OpenType fonts with an extended syntax and adds +% support for a variety of font features. +% \end{abstract} +% +% \tableofcontents +% +% \part{Package Description} +% +% \section{Introduction} +% +% Font management and installation has always been painful with \TEX. A lot of +% files are needed for one font (\abbrev{tfm}, \abbrev{pfb}, \abbrev{map}, +% \abbrev{fd}, \abbrev{vf}), and due to the 8-Bit encoding each font is limited +% to 256 characters. +% But the font world has evolved since the original +% \TEX, and new typographic systems have appeared, most notably the so +% called \emphasis{smart font} technologies like \OpenType +% fonts (\abbrev{otf}). +% These fonts can contain many more characters than \TEX fonts, as well as additional +% functionality like ligatures, old-style numbers, small capitals, +% etc., and support more complex writing systems like Arabic and +% Indic\footnote{% +% Unfortunately, \identifier{luaotfload} doesn‘t support many Indic +% scripts right now. +% Assistance in implementing the prerequisites is greatly +% appreciated. +% } +% scripts. +% \OpenType fonts are widely deployed and available for all +% modern operating systems. +% As of 2013 they have become the de facto standard for advanced text +% layout. +% However, until recently the only way to use them directly in the \TEX +% world was with the \XETEX engine. +% +% Unlike \XETEX, \LUATEX has no built-in support for +% \OpenType or technologies other than the original \TEX fonts. +% Instead, it provides hooks for executing \LUA code during the \TEX run +% that allow implementing extensions for loading fonts and manipulating +% how input text is processed without modifying the underlying engine. +% This is where \identifier{luaotfload} comes into play: +% Based on code from \CONTEXT, it extends \LUATEX with functionality necessary +% for handling \OpenType fonts. +% Additionally, it provides means for accessing fonts known to the operating +% system conveniently by indexing the metadata. +% +% +% \section{Thanks} +% +% \identifier{Luaotfload} is part of \LUALATEX, the community-driven +% project to provide a foundation for using the \LATEX format with the +% full capabilites of the \LUATEX engine. +% As such, the distinction between end users, contributors, and project +% maintainers is intentionally kept less strict, lest we unduly +% personalize the common effort. +% +% Nevertheless, the current maintainers would like to express their +% gratitude to Khaled Hosny, Akira Kakuto, Hironori Kitagawa and Dohyun +% Kim. +% Their contributions -- be it patches, advice, or systematic +% testing -- made the switch from version 1.x to 2.2 possible. +% Also, Hans Hagen, the author of the font loader, made porting the +% code to \LATEX a breeze due to the extra effort he invested into +% isolating it from the rest of \CONTEXT, not to mention his assistance +% in the task and willingness to respond to our suggestions. +% +% +% \section{Loading Fonts} +% +% \identifier{luaotfload} supports an extended font request syntax: +% +% \begin{quote} +% |\font\foo={|% +% \meta{prefix}|:|% +% \meta{font name}|:|% +% \meta{font features}|}|% +% \meta{\TEX font features} +% \end{quote} +% +% \noindent +% The curly brackets are optional and escape the spaces in the enclosed +% font name. +% Alternatively, double quotes serve the same purpose. +% A selection of individual parts of the syntax are discussed below; +% for a more formal description see figure \ref{font-syntax}. +% +% \begin{figure}[b] +% \setlength\grammarparsep{12pt plus 2pt minus 2pt} +% \setlength\grammarindent{5cm} +% \begingroup +% \small +% \begin{grammar} +% ::= `\\font', {\sc csname}, `=', , [ ] ; +% +% ::= `at', {\sc dimension} ; +% +% ::= `"', `"' +% \alt `{', `}' +% \alt ; +% +% ::= , [`:', ] +% \alt `[', `]', [ [`:'], ] ; +% +% ::= , [ ], \{ \} +% \alt , \{ \} ; +% +% ::= `file:', +% \alt `name:', ; +% +% ::= \{ \} ; +% +% ::= \{ \} ; +% +% ::= {\sc tfmname} | ; +% +% ::= \{ {\sc all_characters} - `]' \} ; +% +% ::= `/', (`I' | `B' | `BI' | `IB' | `S=', \{ {\sc digit} \} ) ; +% +% ::= `(', \{ {\sc digit} \}, `)' ; +% +% ::= , \{ `;', \} ; +% +% ::= {\sc feature_id}, `=', {\sc feature_value} +% \alt , {\sc feature_id} ; +% +% ::= `+' | `-' ; +% +% ::= {\sc all_characters} - ( `(' | `/' | `:' ) ; +% \end{grammar} +% \endgroup +% \caption{Font request syntax. +% Braces or double quotes around the +% \emphasis{specification} rule will +% preserve whitespace in file names. +% In addition to the font style modifiers +% (\emphasis{slash-notation}) given above, there +% are others that are recognized but will be silently +% ignored: {\ttfamily aat}, +% {\ttfamily icu}, and +% {\ttfamily gr}. +% The special terminals are: +% {\sc feature\textunderscore id} for a valid font +% feature name and +% {\sc feature\textunderscore value} for the corresponding +% value. +% {\sc tfmname} is the name of a \abbrev{tfm} file. +% {\sc digit} again refers to bytes 48--57, and +% {\sc all\textunderscore characters} to all byte values. +% {\sc csname} and {\sc dimension} are the \TEX concepts.} +% \label{font-syntax} +% \end{figure} +% +% \subsection{Prefix -- the \identifier{luaotfload}{ }Way} +% +% In \identifier{luaotfload}, the canonical syntax for font requests +% requires a \emphasis{prefix}: +% \begin{quote} +% |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots +% \end{quote} +% where \meta{prefix} is either \verb|file:| or \verb|name:|.\footnote{% +% The development version also knows two further prefixes, +% \verb|kpse:| and \verb|my:|. +% A \verb|kpse| lookup is restricted to files that can be found by +% \identifier{kpathsea} and +% will not attempt to locate system fonts. +% This behavior can be of value when an extra degree of encapsulation is +% needed, for instance when supplying a customized tex distribution. +% +% The \verb|my| lookup takes this a step further: it lets you define +% a custom resolver function and hook it into the \luafunction{resolve_font} +% callback. +% This ensures full control over how a file is located. +% For a working example see the +% \href{https://bitbucket.org/phg/lua-la-tex-tests/src/5f6a535d/pln-lookup-callback-1.tex} +% {test repo}. +% } +% It determines whether the font loader should interpret the request as +% a \emphasis{file name} or +% \emphasis{font name}, respectively, +% which again influences how it will attempt to locate the font. +% Examples for font names are +% “Latin Modern Italic”, +% “GFS Bodoni Rg”, and +% “PT Serif Caption” +% -- they are the human readable identifiers +% usually listed in drop-down menus and the like.\footnote{% +% Font names may appear like a great choice at first because they +% offer seemingly more intuitive identifiers in comparison to arguably +% cryptic file names: +% “PT Sans Bold” is a lot more descriptive than \fileent{PTS75F.ttf}. +% On the other hand, font names are quite arbitrary and there is no +% universal method to determine their meaning. +% While \identifier{luaotfload} provides fairly sophisticated heuristic +% to figure out a matching font style, weight, and optical size, it +% cannot be relied upon to work satisfactorily for all font files. +% For an in-depth analysis of the situation and how broken font names +% are, please refer to +% \href{http://www.ntg.nl/pipermail/ntg-context/2013/073889.html} +% {this post} +% by Hans Hagen, the author of the font loader. +% If in doubt, use filenames. +% \fileent{luaotfload-tool} can perform the matching for you with the +% option \verb|--find=|, and you can use the file name it returns +% in your font definition. +% } +% In order for fonts installed both in system locations and in your +% \fileent{texmf} to be accessible by font name, \identifier{luaotfload} must +% first collect the metadata included in the files. +% Please refer to section~\ref{sec:fontdb} below for instructions on how to +% create the database. +% +% File names are whatever your file system allows them to be, except +% that that they may not contain the characters +% \verb|(|, +% \verb|:|, and +% \verb|/|. +% As is obvious from the last exception, the \verb|file:| lookup will +% not process paths to the font location -- only those +% files found when generating the database are addressable this way. +% Continue below in the \XETEX section if you need to load your fonts +% by path. +% The file names corresponding to the example font names above are +% \fileent{lmroman12-italic.otf}, +% \fileent{GFSBodoni.otf}, and +% \fileent{PTZ56F.ttf}. +% +% \subsection{Compatibility Layer} +% +% In addition to the regular prefixed requests, \identifier{luaotfload} +% accepts loading fonts the \XETEX way. +% There are again two modes: bracketed and unbracketed. +% A bracketed request looks as follows. +% +% \begin{quote} +% |\font\fontname=[|\meta{path to file}|]| +% \end{quote} +% +% \noindent +% Inside the square brackets, every character except for a closing +% bracket is permitted, allowing for specifying paths to a font file. +% Naturally, path-less file names are equally valid and processed the +% same way as an ordinary \verb|file:| lookup. +% +% \begin{quote} +% |\font\fontname=|\meta{font name} \dots +% \end{quote} +% +% Unbracketed (or, for lack of a better word: \emphasis{anonymous}) +% font requests resemble the conventional \TEX syntax. +% However, they have a broader spectrum of possible interpretations: +% before anything else, \identifier{luaotfload} attempts to load a +% traditional \TEX Font Metric (\abbrev{tfm} or \abbrev{ofm}). +% If this fails, it performs a \verb|name:| lookup, which itself will +% fall back to a \verb|file:| lookup if no database entry matches +% \meta{font name}. +% +% Furthermore, \identifier{luaotfload} supports the slashed (shorthand) +% font style notation from \XETEX. +% +% \begin{quote} +% |\font\fontname=|\meta{font name}|/|\meta{modifier}\dots +% \end{quote} +% +% \noindent +% Currently, four style modifiers are supported: +% \verb|I| for italic shape, +% \verb|B| for bold weight, +% \verb|BI| or \verb|IB| for the combination of both. +% Other “slashed” modifiers are too specific to the \XETEX engine and +% have no meaning in \LUATEX. +% +% \subsection{Examples} +% +% \subsubsection{Loading by File Name} +% +% For example, conventional \abbrev{type1} font can be loaded with a \verb|file:| +% request like so: +% +% \begin{quote} +% \begin{verbatim} +% \font\lmromanten={file:ec-lmr10} at 10pt +% \end{verbatim} +% \end{quote} +% +% The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa +% Półtawskiego}\footnote{% +% \url{http://jmn.pl/antykwa-poltawskiego/}, also available in +% in \TEX Live. +% } +% in its condensed variant can be loaded as follows: +% +% \begin{quote} +% \begin{verbatim} +% \font\apcregular=file:antpoltltcond-regular.otf at 42pt +% \end{verbatim} +% \end{quote} +% +% The next example shows how to load the \emphasis{Porson} font digitized by +% the Greek Font Society using \XETEX-style syntax and an absolute path from a +% non-standard directory: +% +% \begin{quote} +% \begin{verbatim} +% \font\gfsporson="[/tmp/GFSPorson.otf]" at 12pt +% \end{verbatim} +% \end{quote} +% +% \subsubsection{Loading by Font Name} +% +% The \verb|name:| lookup does not depend on cryptic filenames: +% +% \begin{quote} +% \begin{verbatim} +% \font\pagellaregular={name:TeX Gyre Pagella} at 9pt +% \end{verbatim} +% \end{quote} +% +% A bit more specific but essentially the same lookup would be: +% +% \begin{quote} +% \begin{verbatim} +% \font\pagellaregular={name:TeX Gyre Pagella Regular} at 9pt +% \end{verbatim} +% \end{quote} +% +% \noindent +% Which fits nicely with the whole set: +% +% \begin{quote} +% \begin{verbatim} +% \font\pagellaregular ={name:TeX Gyre Pagella Regular} at 9pt +% \font\pagellaitalic ={name:TeX Gyre Pagella Italic} at 9pt +% \font\pagellabold ={name:TeX Gyre Pagella Bold} at 9pt +% \font\pagellabolditalic={name:TeX Gyre Pagella Bolditalic} at 9pt +% +% {\pagellaregular foo bar baz\endgraf} +% {\pagellaitalic foo bar baz\endgraf} +% {\pagellabold foo bar baz\endgraf} +% {\pagellabolditalic foo bar baz\endgraf} +% +% ... +% \end{verbatim} +% \end{quote} +% +% \subsubsection{Modifiers} +% +% If the entire \emphasis{Iwona} family\footnote{% +% \url{http://jmn.pl/kurier-i-iwona/}, +% also in \TEX Live. +% } +% is installed in some location accessible by \identifier{luaotfload}, +% the regular shape can be loaded as follows: +% +% \begin{quote} +% \begin{verbatim} +% \font\iwona=Iwona at 20pt +% \end{verbatim} +% \end{quote} +% +% \noindent +% To load the most common of the other styles, the slash notation can +% be employed as shorthand: +% +% \begin{quote} +% \begin{verbatim} +% \font\iwonaitalic =Iwona/I at 20pt +% \font\iwonabold =Iwona/B at 20pt +% \font\iwonabolditalic=Iwona/BI at 20pt +% \end{verbatim} +% \end{quote} +% +% \noindent +% which is equivalent to these full names: +% +% \begin{quote} +% \begin{verbatim} +% \font\iwonaitalic ="Iwona Italic" at 20pt +% \font\iwonabold ="Iwona Bold" at 20pt +% \font\iwonabolditalic="Iwona BoldItalic" at 20pt +% \end{verbatim} +% \end{quote} +% +% \section{Font features} +% +% \emphasis{Font features} are the second to last component in the +% general scheme for font requests: +% +% \begin{quote} +% |\font\foo={|% +% \meta{prefix}|:|% +% \meta{font name}|:|% +% \meta{font features}|}|% +% \meta{\TEX font features} +% \end{quote} +% +% \noindent +% If style modifiers are present (\XETEX style), they must precede +% \meta{font features}. +% +% The element \meta{font features} is a semicolon-separated list of feature +% tags\footnote{% +% Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm}. +% } +% and font options. +% Prepending a font feature with a |+| (plus sign) enables it, whereas +% a |-| (minus) disables it. For instance, the request +% +% \begin{quote} +% \begin{verbatim} +% \font\test=LatinModernRoman:+clig;-kern +% \end{verbatim} +% \end{quote} +% +% \noindent activates contextual ligatures (|clig|) and disables +% kerning (|kern|). +% Alternatively the options |true| or |false| can be passed to +% the feature in a key/value expression. +% The following request has the same meaning as the last one: +% +% \begin{quote} +% \begin{verbatim} +% \font\test=LatinModernRoman:clig=true;kern=false +% \end{verbatim} +% \end{quote} +% +% \noindent +% Furthermore, this second syntax is required should a font feature +% accept other options besides a true/false switch. +% For example, \emphasis{stylistic alternates} (|salt|) are variants of given +% glyphs. +% They can be selected either explicitly by supplying the variant +% index (starting from one), or randomly by setting the value to, +% obviously, |random|. +% +% \iffalse TODO verify that this actually works with a font that supports +% the salt/random feature!\fi +% \begin{quote} +% \begin{verbatim} +% \font\librmsaltfirst=LatinModernRoman:salt=1 +% \end{verbatim} +% \end{quote} +% +% \noindent Other font options include: +% +% \begin{description} +% +% \item [mode] \hfill \\ +% \identifier{luaotfload} has two \OpenType processing +% \emphasis{modes}: +% \identifier{base} and \identifier{node}. +% +% \identifier{base} mode works by mapping \OpenType +% features to traditional \TEX ligature and kerning mechanisms. +% Supporting only non-contextual substitutions and kerning +% pairs, it is the slightly faster, albeit somewhat limited, variant. +% \identifier{node} mode works by processing \TeX’s internal +% node list directly at the \LUA end and supports +% a wider range of \OpenType features. +% The downside is that the intricate operations required for +% \identifier{node} mode may slow down typesetting especially +% with complex fonts and it does not work in math mode. +% +% By default \identifier{luaotfload} is in \identifier{node} +% mode, and \identifier{base} mode has to be requested where needed, +% e.~g. for math fonts. +% +% \item [script] \label{script-tag} \hfill \\ +% An \OpenType script tag;\footnote{% +% See \url{http://www.microsoft.com/typography/otspec/scripttags.htm} +% for a list of valid values. +% For scripts derived from the Latin alphabet the value +% |latn| is good choice. +% } +% the default value is |dlft|. +% Some fonts, including very popular ones by foundries like Adobe, +% do not assign features to the |dflt| script, in +% which case the script needs to be set explicitly. +% +% \item [language] \hfill \\ +% An \OpenType language system identifier,\footnote{% +% Cf. \url{http://www.microsoft.com/typography/otspec/languagetags.htm}. +% } +% defaulting to |dflt|. +% +% \item [featurefile] \hfill \\ +% A comma-separated list of feature files to be applied to the +% font. +% Feature files contain a textual representation of +% \OpenType tables and extend the features of a font +% on fly. +% After they are applied to a font, features defined in a +% feature file can be enabled or disabled just like any +% other font feature. +% The syntax is documented in \identifier{Adobe}’s +% \OpenType Feature File Specification.\footnote{% +% Cf. \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. +% } +% +% For a demonstration of how to set a |tkrn| feature consult +% the file |tkrn.fea| that is part of \identifier{luaotfload}. +% It can be read and applied as follows: +% +% |\font\test=Latin Modern Roman:featurefile=tkrn.fea;+tkrn| +% +% \item [color] \hfill \\ +% A font color, defined as a triplet of two-digit hexadecimal +% \abbrev{rgb} values, with an optional fourth value for +% transparency +% (where |00| is completely transparent and |FF| is opaque). +% +% For example, in order to set text in semitransparent red: +% +% \begin{quote} +% \begin{verbatim} +% \font\test={Latin Modern Roman}:color=FF0000BB +% \end{verbatim} +% \end{quote} +% +% \item [kernfactor \& letterspace] \hfill \\ +% Define a font with letterspacing (tracking) enabled. +% In \identifier{luaotfload}, letterspacing is implemented by +% inserting additional kerning between glyphs. +% +% This approach is derived from and still quite similar to the +% \emphasis{character kerning} (\texmacro{setcharacterkerning} / +% \texmacro{definecharacterkerning} \& al.) functionality of +% Context, see the file \fileent{typo-krn.lua} there. +% The main difference is that \identifier{luaotfload} does not +% use \LUATEX attributes to assign letterspacing to regions, +% but defines virtual letterspaced versions of a font. +% +% The option \identifier{kernfactor} accepts a numeric value that +% determines the letterspacing factor to be applied to the font +% size. +% E.~g. a kern factor of $0.42$ applied to a $10$ pt font +% results in $4.2$ pt of additional kerning applied to each +% pair of glyphs. +% Ligatures are split into their component glyphs unless +% explicitly ignored (see below). +% +% For compatibility with \XETEX an alternative +% \identifier{letterspace} option is supplied that interprets the +% supplied value as a \emphasis{percentage} of the font size but +% is otherwise identical to \identifier{kernfactor}. +% Consequently, both definitions in below snippet yield the same +% letterspacing width: +% +% \begin{quote} +% \begin{verbatim} +% \font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125" +% \font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5" +% \end{verbatim} +% \end{quote} +% +% Specific pairs of letters and ligatures may be exempt from +% letterspacing by defining the \LUA functions +% \luafunction{keeptogether} and \luafunction{keepligature}, +% respectively, inside the namespace \verb|luaotfload.letterspace|. +% Both functions are called whenever the letterspacing callback +% encounters an appropriate node or set of nodes. +% If they return a true-ish value, no extra kern is inserted at +% the current position. +% \luafunction{keeptogether} receives a pair of consecutive +% glyph nodes in order of their appearance in the node list. +% \luafunction{keepligature} receives a single node which can be +% analyzed into components. +% (For details refer to the \emphasis{glyph nodes} section in the +% \LUATEX reference manual.) +% The implementation of both functions is left entirely to the +% user. +% +% +% \item [protrusion \& expansion] \hfill \\ +% These keys control microtypographic features of the font, +% namely \emphasis{character protrusion} and \emphasis{font +% expansion}. +% Their arguments are names of \LUA tables that contain +% values for the respective features.\footnote{% +% For examples of the table layout please refer to the +% section of the file \fileent{luaotfload-fonts-ext.lua} where the +% default values are defined. +% Alternatively and with loss of information, you can dump +% those tables into your terminal by issuing +% \begin{verbatim} +% \directlua{inspect(fonts.protrusions.setups.default) +% inspect(fonts.expansions.setups.default)} +% \end{verbatim} +% at some point after loading \fileent{luaotfload.sty}. +% } +% For both, only the set \identifier{default} is predefined. +% +% For example, to define a font with the default +% protrusion vector applied\footnote{% +% You also need to set +% \verb|pdfprotrudechars=2| and +% \verb|pdfadjustspacing=2| +% to activate protrusion and expansion, respectively. +% See the +% \href{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% +% {\PDFTEX manual} +% for details. +% }: +% +% \begin{quote} +% \begin{verbatim} +% \font\test=LatinModernRoman:protrusion=default +% \end{verbatim} +% \end{quote} +% \end{description} +% +% \paragraph{Non-standard font features} +% \identifier{luaotfload} adds a number of features that are not defined +% in the original \OpenType specification, most of them +% aiming at emulating the behavior familiar from other \TEX engines. +% Currently (2014) there are three of them: +% +% \begin{description} +% +% \item [anum] +% Substitutes the glyphs in the \abbrev{ascii} number range +% with their counterparts from eastern Arabic or Persian, +% depending on the value of \identifier{language}. +% +% \item [tlig] +% Applies legacy \TEX ligatures: +% +% \begin{tabular}{rlrl} +% `` & \verb|``| & '' & \verb|''| \\ +% ` & \verb|`| & ' & \verb|'| \\ +% " & \verb|"| & -- & \verb|--| \\ +% --- & \verb|---| & !` & \verb|!`| \\ +% ?` & \verb|?`| & & \\ +% \end{tabular} +% +% \footnote{% +% These contain the feature set \verb|trep| of earlier +% versions of \identifier{luaotfload}. +% +% Note to \XETEX users: this is the equivalent of the +% assignment \verb|mapping=text-tex| using \XETEX's input +% remapping feature. +% } +% +% \item [itlc] +% Computes italic correction values (active by default). +% +% \end{description} +% +% +% +% \section{Font names database} +% \label{sec:fontdb} +% +% As mentioned above, \identifier{luaotfload} keeps track of which +% fonts are available to \LUATEX by means of a \emphasis{database}. +% This allows referring to fonts not only by explicit filenames but +% also by the proper names contained in the metadata which is often +% more accessible to humans.\footnote{% +% The tool \href{http://www.lcdf.org/type/}{\fileent{otfinfo}} (comes +% with \TEX Live), when invoked on a font file with the \verb|-i| +% option, lists the variety of name fields defined for it. +% } +% +% When \identifier{luaotfload} is asked to load a font by a font name, +% it will check if the database exists and load it, or else generate a +% fresh one. +% Should it then fail to locate the font, an update to the database is +% performed in case the font has been added to the system only +% recently. As soon as the database is updated, the resolver will try +% and look up the font again, all without user intervention. +% The goal is for \identifier{luaotfload} to act in the background and +% behave as unobtrusively as possible, while providing a convenient +% interface to the fonts installed on the system. +% +% Generating the database for the first time may take a while since it +% inspects every font file on your computer. +% This is particularly noticeable if it occurs during a typesetting run. +% In any case, subsequent updates to the database will be quite fast. +% +% \subsection[luaotfload-tool / mkluatexfontdb.lua]% +% {\fileent{luaotfload-tool} / +% \fileent{mkluatexfontdb.lua}\footnote{% +% The script may be named just \fileent{mkluatexfontdb} in your +% distribution. +% }} +% +% It can still be desirable at times to do some of these steps +% manually, and without having to compile a document. +% To this end, \identifier{luaotfload} comes with the utility +% \fileent{luaotfload-tool} that offers an interface to the database +% functionality. +% Being a \LUA script, there are two ways to run it: +% either make it executable (\verb|chmod +x| on unixoid systems) or +% pass it as an argument to \fileent{texlua}.\footnote{% +% Tests by the maintainer show only marginal performance gain by +% running with Luigi Scarso’s +% \href{https://foundry.supelec.fr/projects/luajittex/}% +% {\identifier{Luajit\kern-.25ex\TEX}}, +% which is probably due to the fact that most of the time is spent +% on file system operations. +% +% \emphasis{Note}: +% On \abbrev{MS} \identifier{Windows} systems, the script can be run +% either by calling the wrapper application +% \fileent{luaotfload-tool.exe} or as +% \verb|texlua.exe luaotfload-tool.lua|. +% } +% Invoked with the argument \verb|--update| it will perform a database +% update, scanning for fonts not indexed. +% +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool --update +% \end{verbatim} +% \end{quote} +% +% Adding the \verb|--force| switch will initiate a complete +% rebuild of the database. +% +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool --update --force +% \end{verbatim} +% \end{quote} +% +% For sake of backwards compatibility, \fileent{luaotfload-tool} may be +% renamed or symlinked to \fileent{mkluatexfontdb}. +% Whenever it is run under this name, it will update the database +% first, mimicking the behavior of earlier versions of +% \identifier{luaotfload}. +% +% \subsection{Search Paths} +% +% \identifier{luaotfload} scans those directories where fonts are +% expected to be located on a given system. +% On a Linux machine it follows the paths listed in the +% \identifier{Fontconfig} configuration files; +% consult \verb|man 5 fonts.conf| for further information. +% On \identifier{Windows} systems, the standard location is +% \verb|Windows\Fonts|, +% while \identifier{Mac OS~X} requires a multitude of paths to +% be examined. +% The complete list is is given in table \ref{table-searchpaths}. +% Other paths can be specified by setting the environment variable +% \verb+OSFONTDIR+. +% If it is non-empty, then search will be extended to the included +% directories. +% +% \begin{table}[t] +% \hrule +% \caption{List of paths searched for each supported operating +% system.} +% \renewcommand{\arraystretch}{1.2} +% \begin{center} +% \begin{tabular}{lp{.5\textwidth}} +% Windows & \verb|%WINDIR%\Fonts| +% \\ +% Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break +% \fileent{/etc/fonts/fonts.conf} +% \\ +% Mac & \fileent{\textasciitilde/Library/Fonts},\break +% \fileent{/Library/Fonts},\break +% \fileent{/System/Library/Fonts}, and\hfill\break +% \fileent{/Network/Library/Fonts} +% \\ +% \end{tabular} +% \end{center} +% \label{table-searchpaths} +% \hrule +% \end{table} +% +% \subsection{Querying from Outside} +% +% \fileent{luaotfload-tool} also provides rudimentary means of +% accessing the information collected in the font database. +% If the option \verb|--find=|\emphasis{name} is given, the script will +% try and search the fonts indexed by \identifier{luaotfload} for a +% matching name. +% For instance, the invocation +% +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool --find="Iwona Regular" +% \end{verbatim} +% \end{quote} +% +% \noindent +% will verify if “Iwona Regular” is found in the database and can be +% readily requested in a document. +% +% If you are unsure about the actual font name, then add the +% \verb|-F| (or \verb|--fuzzy|) switch to the command line to enable +% approximate matching. +% Suppose you cannot precisely remember if the variant of +% \identifier{Iwona} you are looking for was “Bright” or “Light”. +% The query +% +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool -F --find="Iwona Bright" +% \end{verbatim} +% \end{quote} +% +% \noindent +% will tell you that indeed the latter name is correct. +% +% Basic information about fonts in the database can be displayed +% using the \verb|-i| option (\verb|--info|). +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool -i --find="Iwona Light Italic" +% \end{verbatim} +% \end{quote} +% \noindent +% The meaning of the printed values is described in section 4.4 of the +% \LUATEX reference manual.\footnote{% +% In \TEX Live: \fileent{texmf-dist/doc/luatex/base/luatexref-t.pdf}. +% } +% +% For a much more detailed report about a given font try the \verb|-I| option +% instead (\verb|--inspect|). +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool -I --find="Iwona Light Italic" +% \end{verbatim} +% \end{quote} +% +% \verb|luaotfload-tool --help| will list the available command line +% switches, including some not discussed in detail here. +% For a full documentation of \identifier{luaotfload-tool} and its +% capabilities refer to the manpage +% (\verb|man 1 luaotfload-tool|).\footnote{% +% Or see \verb|luaotfload-tool.rst| in the source directory. +% } +% +% \subsection{Blacklisting Fonts} +% \label{font-blacklist} +% +% Some fonts are problematic in general, or just in \LUATEX. +% If you find that compiling your document takes far too long or eats +% away all your system’s memory, you can track down the culprit by +% running \verb|luaotfload-tool -v| to increase verbosity. +% Take a note of the \emphasis{filename} of the font that database +% creation fails with and append it to the file +% \fileent{luaotfload-blacklist.cnf}. +% +% A blacklist file is a list of font filenames, one per line. +% Specifying the full path to where the file is located is optional, the +% plain filename should suffice. +% File extensions (\fileent{.otf}, \fileent{.ttf}, etc.) may be omitted. +% Anything after a percent (|%|) character until the end of the line +% is ignored, so use this to add comments. +% Place this file to some location where the \identifier{kpse} +% library can find it, e.~g. +% \fileent{texmf-local/tex/luatex/luaotfload} if you are running +% \identifier{\TEX Live},\footnote{% +% You may have to run \verb|mktexlsr| if you created a new file in +% your \fileent{texmf} tree. +% } +% or just leave it in the working directory of your document. +% \identifier{luaotfload} reads all files named +% \fileent{luaotfload-blacklist.cnf} it finds, so the fonts in +% \fileent{./luaotfload-blacklist.cnf} extend the global blacklist. +% +% Furthermore, a filename prepended with a dash character (|-|) is +% removed from the blacklist, causing it to be temporarily whitelisted +% without modifying the global file. +% An example with explicit paths: +% +% \begin{verbatim} +% % example otf-blacklist.cnf +% /Library/Fonts/GillSans.ttc % Luaotfload ignores this font. +% -/Library/Fonts/Optima.ttc % This one is usable again, even if +% % blacklisted somewhere else. +% \end{verbatim} +% +% \section{Files from \CONTEXT and \LUATEX-Fonts} +% +% \identifier{luaotfload} relies on code originally written by Hans +% Hagen\footnote{% +% The creator of the \href{http://wiki.contextgarden.net}{\CONTEXT} +% format. +% } +% for and tested with \CONTEXT. +% It integrates the font loader as distributed in +% the \identifier{\LUATEX-Fonts} package. +% The original \LUA source files have been combined using the +% \fileent{mtx-package} script into a single, self-contained blob. +% In this form the font loader has no further dependencies\footnote{% +% It covers, however, to some extent the functionality of the +% \identifier{lualibs} package. +% } +% and requires only minor adaptions to integrate into +% \identifier{luaotfload}. +% The guiding principle is to let \CONTEXT/\LUATEX-Fonts take care of +% the implementation, and update the imported code from time to time. +% As maintainers, we aim at importing files from upstream essentially +% \emphasis{unmodified}, except for renaming them to prevent name +% clashes. +% This job has been greatly alleviated since the advent of +% \LUATEX-Fonts, prior to which the individual dependencies had to be +% manually spotted and extracted from the \CONTEXT source code in a +% complicated and error-prone fashion. +% +% Below is a commented list of the files distributed with +% \identifier{luaotfload} in one way or the other. +% See figure \ref{file-graph} on page \pageref{file-graph} for a +% graphical representation of the dependencies. +% From \LUATEX-Fonts, only the file \fileent{luatex-fonts-merged.lua} +% has been imported as \fileent{luaotfload-fontloader.lua}. +% It is generated by \fileent{mtx-package}, a \LUA source code merging +% too developed by Hans Hagen.\footnote{% +% \fileent{mtx-package} is +% \href +% {http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua} +% {part of \CONTEXT} +% and requires \fileent{mtxrun}. +% Run +% \verb|mtxrun --script package --help| +% to display further information. +% For the actual merging code see the file +% \fileent{util-mrg.lua} that is part of \CONTEXT. +% } +% It houses several \LUA files that can be classed in three +% categories. +% +% \begin{itemize} +% \let\normalitem=\item +% \def\incitem#1{% +% \normalitem{\fileent{#1}} +% } +% \normalitem \emphasis{\LUA utility libraries}, a subset +% of what is provided by the \identifier{lualibs} +% package. +% +% \begin{multicols}{2} +% \begin{itemize} +% \incitem{l-lua.lua} \incitem{l-lpeg.lua} +% \incitem{l-function.lua} \incitem{l-string.lua} +% \incitem{l-table.lua} \incitem{l-io.lua} +% \incitem{l-file.lua} \incitem{l-boolean.lua} +% \incitem{l-math.lua} \incitem{util-str.lua} +% \end{itemize} +% \end{multicols} +% +% \normalitem The \emphasis{font loader} itself. +% These files have been written for +% \LUATEX-Fonts and they are distributed along +% with \identifier{luaotfload}. +% \begin{multicols}{2} +% \begin{itemize} +% \incitem{luatex-basics-gen.lua} +% \incitem{luatex-basics-nod.lua} +% \incitem{luatex-fonts-enc.lua} +% \incitem{luatex-fonts-syn.lua} +% \incitem{luatex-fonts-tfm.lua} +% \incitem{luatex-fonts-chr.lua} +% \incitem{luatex-fonts-lua.lua} +% \incitem{luatex-fonts-inj.lua} +% \incitem{luatex-fonts-otn.lua} +% \incitem{luatex-fonts-def.lua} +% \incitem{luatex-fonts-ext.lua} +% \incitem{luatex-fonts-cbk.lua} +% \end{itemize} +% \end{multicols} +% +% \normalitem Code related to \emphasis{font handling and +% node processing}, taken directly from +% \CONTEXT. +% \begin{multicols}{2} +% \begin{itemize} +% \incitem{data-con.lua} \incitem{font-ini.lua} +% \incitem{font-con.lua} \incitem{font-cid.lua} +% \incitem{font-map.lua} \incitem{font-oti.lua} +% \incitem{font-otf.lua} \incitem{font-otb.lua} +% \incitem{font-ota.lua} \incitem{font-def.lua} +% \incitem{font-otp.lua} +% \end{itemize} +% \end{multicols} +% \end{itemize} +% +% Note that if \identifier{luaotfload} cannot locate the +% merged file, it will load the individual \LUA libraries +% instead. +% Their names remain the same as in \CONTEXT (without the +% \verb|otfl|-prefix) since we imported the relevant section of +% \fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload-main.lua}. +% Thus if you prefer running bleeding edge code from the +% \CONTEXT beta, all you have to do is remove +% \fileent{luaotfload-merged.lua} from the search path. +% +% Also, the merged file at some point +% loads the Adobe Glyph List from a \LUA table that is contained in +% \fileent{luaotfload-glyphlist.lua}, which is automatically generated by the +% script \fileent{mkglyphlist}.\footnote{% +% See \fileent{luaotfload-font-enc.lua}. +% The hard-coded file name is why we have to replace the procedure +% that loads the file in \fileent{luaotfload-override.lua}. +% } +% There is a make target \identifier{glyphs} that will create a fresh +% glyph list so we don’t need to import it from \CONTEXT +% any longer. +% +% In addition to these, \identifier{luaotfload} requires a number of +% files not contained in the merge. Some of these have no equivalent in +% \LUATEX-Fonts or \CONTEXT, some were taken unmodified from the +% latter. +% +% \begin{itemize} +% \let\normalitem=\item +% \def\ouritem#1{% +% \normalitem{\fileent{#1}}% +% \space--\hskip1em +% } +% \ouritem {luaotfload-features.lua} font feature handling; +% incorporates some of the code from +% \fileent{font-otc} from \CONTEXT; +% \ouritem {luaotfload-override.lua} overrides the \CONTEXT logging +% functionality. +% \ouritem {luaotfload-loaders.lua} registers the \OpenType +% font reader as handler for +% Postscript fonts +% (\abbrev{pfa}, \abbrev{pfb}). +% \ouritem {luaotfload-parsers.lua} various \abbrev{lpeg}-based parsers. +% \ouritem {luaotfload-database.lua} font names database. +% \ouritem {luaotfload-colors.lua} color handling. +% \ouritem {luaotfload-auxiliary.lua} access to internal functionality +% for package authors +% (proposals for additions welcome). +% \ouritem {luaotfload-letterspace.lua} font-based letterspacing. +% \end{itemize} +% +% \begin{figure}[b] +% \caption{Schematic of the files in \identifier{Luaotfload}} +% \includegraphics[width=\textwidth]{filegraph.pdf} +% \label{file-graph} +% \end{figure} +% +% \section{Auxiliary Functions} +% +% With release version 2.2, \identifier{luaotfload} received +% additional functions for package authors to call from outside +% (see the file \fileent{luaotfload-auxiliary.lua} for details). +% The purpose of this addition twofold. +% Firstly, \identifier{luaotfload} failed to provide a stable interface +% to internals in the past which resulted in an unmanageable situation +% of different packages abusing the raw access to font objects by means +% of the \luafunction{patch_font} callback. +% When the structure of the font object changed due to an update, all +% of these imploded and several packages had to be fixed while +% simultaneously providing fallbacks for earlier versions. +% Now the patching is done on the \identifier{luaotfload} side and can +% be adapted with future modifications to font objects without touching +% the packages that depend on it. +% Second, some the capabilities of the font loader and the names +% database are not immediately relevant in \identifier{luaotfload} +% itself but might nevertheless be of great value to package authors or +% end users. +% +% Note that the current interface is not yet set in stone and the +% development team is open to suggestions for improvements or +% additions. +% +% \subsection{Callback Functions} +% +% The \luafunction{patch_font} callback is inserted in the wrapper +% \identifier{luaotfload} provides for the font definition callback +% (see below, page \pageref{define-font}). +% At this place it allows manipulating the font object immediately after +% the font loader is done creating it. +% For a short demonstration of its usefulness, here is a snippet that +% writes an entire font object to the file \fileent{fontdump.lua}: +% +% \begin{quote} +% \begin{verbatim} +% \input luaotfload.sty +% \directlua{ +% local dumpfile = "fontdump.lua" +% local dump_font = function (tfmdata) +% local data = table.serialize(tfmdata) +% io.savedata(dumpfile, data) +% end +% +% luatexbase.add_to_callback( +% "luaotfload.patch_font", +% dump_font, +% "my_private_callbacks.dump_font" +% ) +% } +% \font\dumpme=name:Iwona +% \bye +% \end{verbatim} +% \end{quote} +% +% \emphasis{Beware}: this creates a Lua file of around 150,000 lines of +% code, taking up 3~\abbrev{mb} of disk space. +% By inspecting the output you can get a first impression of how a font +% is structured in \LUATEX’s memory, what elements it is composed of, +% and in what ways it can be rearranged. +% +% \subsubsection{Compatibility with Earlier Versions} +% +% As has been touched on in the preface to this section, the structure +% of the object as returned by the fontloader underwent rather drastic +% changes during different stages of its development, and not all +% packages that made use of font patching have kept up with every one +% of it. +% To ensure compatibility with these as well as older versions of +% some packages, \identifier{luaotfload} sets up copies of or references +% to data in the font table where it used to be located. +% For instance, important parameters like the requested point size, the +% units factor, and the font name have again been made accessible from +% the toplevel of the table even though they were migrated to different +% subtables in the meantime. +% +% \subsubsection{Patches} +% +% These are mostly concerned with establishing compatibility with +% \XETEX. +% +% \begin{itemize} +% \let\normalitem=\item +% \def\ouritem#1{% +% \normalitem{\luafunction{#1}}% +% \hfill\break +% } +% +% \ouritem {set_sscale_dimens} +% Calculate \texmacro{fontdimen}s 10 and 11 to emulate \XETEX. +% +% \ouritem {set_capheight} +% Calculates \texmacro{fontdimen} 8 like \XETEX. +% +% \ouritem {patch_cambria_domh} +% Correct some values of the font \emphasis{Cambria Math}. +% +% \end{itemize} +% +% \subsection{Package Author’s Interface} +% +% As \LUATEX release 1.0 is nearing, the demand for a reliable interface +% for package authors increases. +% +% \subsubsection{Font Properties} +% +% Below functions mostly concern querying the different components of a +% font like for instance the glyphs it contains, or what font features +% are defined for which scripts. +% +% \begin{itemize} +% \let\normalitem=\item +% \def\ouritem#1{% +% \normalitem{\luafunction{#1}}% +% \hfill\break +% } +% +% \ouritem {aux.font_has_glyph (id : int, index : int)} +% Predicate that returns true if the font \luafunction{id} +% has glyph \luafunction{index}. +% +% \ouritem {aux.slot_of_name(name : string)} +% Translates an Adobe Glyph name to the corresponding glyph +% slot. +% +% \ouritem {aux.name_of_slot(slot : int)} +% The inverse of \luafunction{slot_of_name}; note that this +% might be incomplete as multiple glyph names may map to the +% same codepoint, only one of which is returned by +% \luafunction{name_of_slot}. +% +% \ouritem {aux.provides_script(id : int, script : string)} +% Test if a font supports \luafunction{script}. +% +% \ouritem {aux.provides_language(id : int, script : string, language : string)} +% Test if a font defines \luafunction{language} for a given +% \luafunction{script}. +% +% \ouritem {aux.provides_feature(id : int, script : string, +% language : string, feature : string)} +% Test if a font defines \luafunction{feature} for +% \luafunction{language} for a given \luafunction{script}. +% +% \ouritem {aux.get_math_dimension(id : int, dimension : string)} +% Get the dimension \luafunction{dimension} of font \luafunction{id}. +% +% \ouritem {aux.sprint_math_dimension(id : int, dimension : string)} +% Same as \luafunction{get_math_dimension()}, but output the value +% in scaled points at the \TEX end. +% +% \end{itemize} +% +% \subsubsection{Database} +% +% \begin{itemize} +% \let\normalitem=\item +% \def\ouritem#1{% +% \normalitem{\luafunction{#1}}% +% \hfill\break +% } +% +% \ouritem {aux.scan_external_dir(dir : string)} +% Include fonts in directory \luafunction{dir} in font lookups without +% adding them to the database. +% +% \end{itemize} +% +% \section{Troubleshooting} +% +% \subsection {Database Generation} +% If you encounter problems with some fonts, please first update to the latest +% version of this package before reporting a bug, as +% \identifier{luaotfload} is under active development and still a +% moving target. +% The development takes place on \identifier{github} at +% \url{https://github.com/lualatex/luaotfload} where there is an issue +% tracker for submitting bug reports, feature requests and the likes +% requests and the likes. +% +% Bug reports are more likely to be addressed if they contain the output of +% +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool --diagnose=environment,files,permissions +% \end{verbatim} +% \end{quote} +% +% \noindent Consult the man page for a description of these options. +% +% Errors during database generation can be traced by increasing the +% verbosity level and redirecting log output to \fileent{stdout}: +% +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool -fuvvv --log=stdout +% \end{verbatim} +% \end{quote} +% +% \noindent or to a file in \fileent{/tmp}: +% +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool -fuvvv --log=file +% \end{verbatim} +% \end{quote} +% +% \noindent In the latter case, invoke the \verb|tail(1)| utility on the file +% for live monitoring of the progress. +% +% If database generation fails, the font last printed to the terminal or log +% file is likely to be the culprit. +% Please specify it when reporting a bug, and blacklist it for the time +% being (see above, page \pageref{font-blacklist}). +% +% \subsection {Font Features} +% A common problem is the lack of features for some +% \OpenType fonts even when specified. +% This can be related to the fact that some fonts do not provide +% features for the \verb|dflt| script (see above on page +% \pageref{script-tag}), +% which is the default one in this package. +% If this happens, assigning a noth script when the font is defined should +% fix it. +% For example with \verb|latn|: +% +% \begin{quote} +% \begin{verbatim} +% \font\test=file:MyFont.otf:script=latn;+liga; +% \end{verbatim} +% \end{quote} +% +% You can get a list of features that a font defines for scripts and languages +% by querying it in \fileent{luaotfload-tool}: +% +% \begin{quote} +% \begin{verbatim} +% luaotfload-tool --find="Iwona" --inspect +% \end{verbatim} +% \end{quote} +% +% \subsection {\LUATEX Programming} +% Another strategy that helps avoiding problems is to not access raw \LUATEX +% internals directly. +% Some of them, even though they are dangerous to access, have not been +% overridden or disabled. +% Thus, whenever possible prefer the functions in the +% \luafunction{aux} namespace over direct manipulation of font objects. +% For example, raw access to the \luafunction{font.fonts} table like: +% +% \begin{quote} +% \begin{verbatim} +% local somefont = font.fonts[2] +% \end{verbatim} +% \end{quote} +% +% \noindent can render already defined fonts unusable. +% Instead, the function \luafunction{font.getfont()} should be used because +% it has been replaced by a safe variant. +% +% However, \luafunction{font.getfont()} only covers fonts handled by the font +% loader, e.~g. \identifier{OpenType} and \identifier{TrueType} fonts, but +% not \abbrev{tfm} or \abbrev{ofm}. +% Should you absolutely require access to all fonts known to \LUATEX, including +% the virtual and autogenerated ones, then you need to query both +% \luafunction{font.getfont()} and \luafunction{font.fonts}. +% In this case, best define you own accessor: +% +% \begin{quote} +% \begin{verbatim} +% local unsafe_getfont = function (id) +% local tfmdata = font.getfont (id) +% if not tfmdata then +% tfmdata = font.fonts[id] +% end +% return tfmdata +% end +% +% --- use like getfont() +% local somefont = unsafe_getfont (2) +% \end{verbatim} +% \end{quote} +% +% \part{Implementation} +% +% \section{\fileent{luaotfload.lua}} +% +% As of version 2.5, the file \fileent{luaotfload.lua} is no longer +% generated from the \abbrev{dtx}. +% Instead, it is maintained separately as a plain \identifier{Lua} file +% \fileent{luaotfload-main.lua} in the Luaotfload \identifier{git} tree. +% The file documentation which used to be found in this section has +% been preserved in the comments. +% +% \section{\fileent{luaotfload.sty}} +% +% As of version 2.5, the file \fileent{luaotfload.sty} is no longer +% generated from the \abbrev{dtx}. +% Instead, it is maintained separately as a plain \identifier{\TEX} file +% in the Luaotfload \identifier{git} tree. +% The file documentation which used to be found in this section has +% been preserved in the comments. +% +% \clearpage +% \section{The GNU GPL License v2} +% +% The GPL requires the complete license text to be distributed along +% with the code. I recommend the canonical source, instead: +% \url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. +% But if you insist on an included copy, here it is. +% You might want to zoom in. +% +% \newsavebox{\gpl} +% \begin{lrbox}{\gpl} +% \begin{minipage}{3\textwidth} +% \columnsep=3\columnsep +% \begin{multicols}{3} +% \begin{center} +% {\Large GNU GENERAL PUBLIC LICENSE\par} +% \bigskip +% {Version 2, June 1991} +% \end{center} +% +% \begin{center} +% {\parindent 0in +% +% Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. +% +% \bigskip +% +% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +% +% \bigskip +% +% Everyone is permitted to copy and distribute verbatim copies +% of this license document, but changing it is not allowed. +% } +% \end{center} +% +% \begin{center} +% {\bf\large Preamble} +% \end{center} +% +% +% The licenses for most software are designed to take away your freedom to +% share and change it. By contrast, the GNU General Public License is +% intended to guarantee your freedom to share and change free software---to +% make sure the software is free for all its users. This General Public +% License applies to most of the Free Software Foundation's software and to +% any other program whose authors commit to using it. (Some other Free +% Software Foundation software is covered by the GNU Library General Public +% License instead.) You can apply it to your programs, too. +% +% When we speak of free software, we are referring to freedom, not price. +% Our General Public Licenses are designed to make sure that you have the +% freedom to distribute copies of free software (and charge for this service +% if you wish), that you receive source code or can get it if you want it, +% that you can change the software or use pieces of it in new free programs; +% and that you know you can do these things. +% +% To protect your rights, we need to make restrictions that forbid anyone to +% deny you these rights or to ask you to surrender the rights. These +% restrictions translate to certain responsibilities for you if you +% distribute copies of the software, or if you modify it. +% +% For example, if you distribute copies of such a program, whether gratis or +% for a fee, you must give the recipients all the rights that you have. You +% must make sure that they, too, receive or can get the source code. And +% you must show them these terms so they know their rights. +% +% We protect your rights with two steps: (1) copyright the software, and (2) +% offer you this license which gives you legal permission to copy, +% distribute and/or modify the software. +% +% Also, for each author's protection and ours, we want to make certain that +% everyone understands that there is no warranty for this free software. If +% the software is modified by someone else and passed on, we want its +% recipients to know that what they have is not the original, so that any +% problems introduced by others will not reflect on the original authors' +% reputations. +% +% Finally, any free program is threatened constantly by software patents. +% We wish to avoid the danger that redistributors of a free program will +% individually obtain patent licenses, in effect making the program +% proprietary. To prevent this, we have made it clear that any patent must +% be licensed for everyone's free use or not licensed at all. +% +% The precise terms and conditions for copying, distribution and +% modification follow. +% +% \begin{center} +% {\Large \sc Terms and Conditions For Copying, Distribution and +% Modification} +% \end{center} +% +% \begin{enumerate} +% \item +% This License applies to any program or other work which contains a notice +% placed by the copyright holder saying it may be distributed under the +% terms of this General Public License. The ``Program'', below, refers to +% any such program or work, and a ``work based on the Program'' means either +% the Program or any derivative work under copyright law: that is to say, a +% work containing the Program or a portion of it, either verbatim or with +% modifications and/or translated into another language. (Hereinafter, +% translation is included without limitation in the term ``modification''.) +% Each licensee is addressed as ``you''. +% +% Activities other than copying, distribution and modification are not +% covered by this License; they are outside its scope. The act of +% running the Program is not restricted, and the output from the Program +% is covered only if its contents constitute a work based on the +% Program (independent of having been made by running the Program). +% Whether that is true depends on what the Program does. +% +% \item You may copy and distribute verbatim copies of the Program's source +% code as you receive it, in any medium, provided that you conspicuously +% and appropriately publish on each copy an appropriate copyright notice +% and disclaimer of warranty; keep intact all the notices that refer to +% this License and to the absence of any warranty; and give any other +% recipients of the Program a copy of this License along with the Program. +% +% You may charge a fee for the physical act of transferring a copy, and you +% may at your option offer warranty protection in exchange for a fee. +% +% \item +% You may modify your copy or copies of the Program or any portion +% of it, thus forming a work based on the Program, and copy and +% distribute such modifications or work under the terms of Section 1 +% above, provided that you also meet all of these conditions: +% +% \begin{enumerate} +% +% \item +% You must cause the modified files to carry prominent notices stating that +% you changed the files and the date of any change. +% +% \item +% You must cause any work that you distribute or publish, that in +% whole or in part contains or is derived from the Program or any +% part thereof, to be licensed as a whole at no charge to all third +% parties under the terms of this License. +% +% \item +% If the modified program normally reads commands interactively +% when run, you must cause it, when started running for such +% interactive use in the most ordinary way, to print or display an +% announcement including an appropriate copyright notice and a +% notice that there is no warranty (or else, saying that you provide +% a warranty) and that users may redistribute the program under +% these conditions, and telling the user how to view a copy of this +% License. (Exception: if the Program itself is interactive but +% does not normally print such an announcement, your work based on +% the Program is not required to print an announcement.) +% +% \end{enumerate} +% +% +% These requirements apply to the modified work as a whole. If +% identifiable sections of that work are not derived from the Program, +% and can be reasonably considered independent and separate works in +% themselves, then this License, and its terms, do not apply to those +% sections when you distribute them as separate works. But when you +% distribute the same sections as part of a whole which is a work based +% on the Program, the distribution of the whole must be on the terms of +% this License, whose permissions for other licensees extend to the +% entire whole, and thus to each and every part regardless of who wrote it. +% +% Thus, it is not the intent of this section to claim rights or contest +% your rights to work written entirely by you; rather, the intent is to +% exercise the right to control the distribution of derivative or +% collective works based on the Program. +% +% In addition, mere aggregation of another work not based on the Program +% with the Program (or with a work based on the Program) on a volume of +% a storage or distribution medium does not bring the other work under +% the scope of this License. +% +% \item +% You may copy and distribute the Program (or a work based on it, +% under Section 2) in object code or executable form under the terms of +% Sections 1 and 2 above provided that you also do one of the following: +% +% \begin{enumerate} +% +% \item +% +% Accompany it with the complete corresponding machine-readable +% source code, which must be distributed under the terms of Sections +% 1 and 2 above on a medium customarily used for software interchange; or, +% +% \item +% +% Accompany it with a written offer, valid for at least three +% years, to give any third party, for a charge no more than your +% cost of physically performing source distribution, a complete +% machine-readable copy of the corresponding source code, to be +% distributed under the terms of Sections 1 and 2 above on a medium +% customarily used for software interchange; or, +% +% \item +% +% Accompany it with the information you received as to the offer +% to distribute corresponding source code. (This alternative is +% allowed only for noncommercial distribution and only if you +% received the program in object code or executable form with such +% an offer, in accord with Subsection b above.) +% +% \end{enumerate} +% +% +% The source code for a work means the preferred form of the work for +% making modifications to it. For an executable work, complete source +% code means all the source code for all modules it contains, plus any +% associated interface definition files, plus the scripts used to +% control compilation and installation of the executable. However, as a +% special exception, the source code distributed need not include +% anything that is normally distributed (in either source or binary +% form) with the major components (compiler, kernel, and so on) of the +% operating system on which the executable runs, unless that component +% itself accompanies the executable. +% +% If distribution of executable or object code is made by offering +% access to copy from a designated place, then offering equivalent +% access to copy the source code from the same place counts as +% distribution of the source code, even though third parties are not +% compelled to copy the source along with the object code. +% +% \item +% You may not copy, modify, sublicense, or distribute the Program +% except as expressly provided under this License. Any attempt +% otherwise to copy, modify, sublicense or distribute the Program is +% void, and will automatically terminate your rights under this License. +% However, parties who have received copies, or rights, from you under +% this License will not have their licenses terminated so long as such +% parties remain in full compliance. +% +% \item +% You are not required to accept this License, since you have not +% signed it. However, nothing else grants you permission to modify or +% distribute the Program or its derivative works. These actions are +% prohibited by law if you do not accept this License. Therefore, by +% modifying or distributing the Program (or any work based on the +% Program), you indicate your acceptance of this License to do so, and +% all its terms and conditions for copying, distributing or modifying +% the Program or works based on it. +% +% \item +% Each time you redistribute the Program (or any work based on the +% Program), the recipient automatically receives a license from the +% original licensor to copy, distribute or modify the Program subject to +% these terms and conditions. You may not impose any further +% restrictions on the recipients' exercise of the rights granted herein. +% You are not responsible for enforcing compliance by third parties to +% this License. +% +% \item +% If, as a consequence of a court judgment or allegation of patent +% infringement or for any other reason (not limited to patent issues), +% conditions are imposed on you (whether by court order, agreement or +% otherwise) that contradict the conditions of this License, they do not +% excuse you from the conditions of this License. If you cannot +% distribute so as to satisfy simultaneously your obligations under this +% License and any other pertinent obligations, then as a consequence you +% may not distribute the Program at all. For example, if a patent +% license would not permit royalty-free redistribution of the Program by +% all those who receive copies directly or indirectly through you, then +% the only way you could satisfy both it and this License would be to +% refrain entirely from distribution of the Program. +% +% If any portion of this section is held invalid or unenforceable under +% any particular circumstance, the balance of the section is intended to +% apply and the section as a whole is intended to apply in other +% circumstances. +% +% It is not the purpose of this section to induce you to infringe any +% patents or other property right claims or to contest validity of any +% such claims; this section has the sole purpose of protecting the +% integrity of the free software distribution system, which is +% implemented by public license practices. Many people have made +% generous contributions to the wide range of software distributed +% through that system in reliance on consistent application of that +% system; it is up to the author/donor to decide if he or she is willing +% to distribute software through any other system and a licensee cannot +% impose that choice. +% +% This section is intended to make thoroughly clear what is believed to +% be a consequence of the rest of this License. +% +% \item +% If the distribution and/or use of the Program is restricted in +% certain countries either by patents or by copyrighted interfaces, the +% original copyright holder who places the Program under this License +% may add an explicit geographical distribution limitation excluding +% those countries, so that distribution is permitted only in or among +% countries not thus excluded. In such case, this License incorporates +% the limitation as if written in the body of this License. +% +% \item +% The Free Software Foundation may publish revised and/or new versions +% of the General Public License from time to time. Such new versions will +% be similar in spirit to the present version, but may differ in detail to +% address new problems or concerns. +% +% Each version is given a distinguishing version number. If the Program +% specifies a version number of this License which applies to it and ``any +% later version'', you have the option of following the terms and conditions +% either of that version or of any later version published by the Free +% Software Foundation. If the Program does not specify a version number of +% this License, you may choose any version ever published by the Free Software +% Foundation. +% +% \item +% If you wish to incorporate parts of the Program into other free +% programs whose distribution conditions are different, write to the author +% to ask for permission. For software which is copyrighted by the Free +% Software Foundation, write to the Free Software Foundation; we sometimes +% make exceptions for this. Our decision will be guided by the two goals +% of preserving the free status of all derivatives of our free software and +% of promoting the sharing and reuse of software generally. +% +% \begin{center} +% {\Large\sc +% No Warranty +% } +% \end{center} +% +% \item +% {\sc Because the program is licensed free of charge, there is no warranty +% for the program, to the extent permitted by applicable law. Except when +% otherwise stated in writing the copyright holders and/or other parties +% provide the program ``as is'' without warranty of any kind, either expressed +% or implied, including, but not limited to, the implied warranties of +% merchantability and fitness for a particular purpose. The entire risk as +% to the quality and performance of the program is with you. Should the +% program prove defective, you assume the cost of all necessary servicing, +% repair or correction.} +% +% \item +% {\sc In no event unless required by applicable law or agreed to in writing +% will any copyright holder, or any other party who may modify and/or +% redistribute the program as permitted above, be liable to you for damages, +% including any general, special, incidental or consequential damages arising +% out of the use or inability to use the program (including but not limited +% to loss of data or data being rendered inaccurate or losses sustained by +% you or third parties or a failure of the program to operate with any other +% programs), even if such holder or other party has been advised of the +% possibility of such damages.} +% +% \end{enumerate} +% +% +% \begin{center} +% {\Large\sc End of Terms and Conditions} +% \end{center} +% +% +% \pagebreak[2] +% +% \section*{Appendix: How to Apply These Terms to Your New Programs} +% +% If you develop a new program, and you want it to be of the greatest +% possible use to the public, the best way to achieve this is to make it +% free software which everyone can redistribute and change under these +% terms. +% +% To do so, attach the following notices to the program. It is safest to +% attach them to the start of each source file to most effectively convey +% the exclusion of warranty; and each file should have at least the +% ``copyright'' line and a pointer to where the full notice is found. +% +% \begin{quote} +% one line to give the program's name and a brief idea of what it does. \\ +% Copyright (C) yyyy name of author \\ +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software +% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +% \end{quote} +% +% Also add information on how to contact you by electronic and paper mail. +% +% If the program is interactive, make it output a short notice like this +% when it starts in an interactive mode: +% +% \begin{quote} +% Gnomovision version 69, Copyright (C) yyyy name of author \\ +% Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ +% This is free software, and you are welcome to redistribute it +% under certain conditions; type `show c' for details. +% \end{quote} +% +% +% The hypothetical commands {\tt show w} and {\tt show c} should show the +% appropriate parts of the General Public License. Of course, the commands +% you use may be called something other than {\tt show w} and {\tt show c}; +% they could even be mouse-clicks or menu items---whatever suits your +% program. +% +% You should also get your employer (if you work as a programmer) or your +% school, if any, to sign a ``copyright disclaimer'' for the program, if +% necessary. Here is a sample; alter the names: +% +% \begin{quote} +% Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\ +% `Gnomovision' (which makes passes at compilers) written by James Hacker. \\ +% +% signature of Ty Coon, 1 April 1989 \\ +% Ty Coon, President of Vice +% \end{quote} +% +% +% This General Public License does not permit incorporating your program +% into proprietary programs. If your program is a subroutine library, you +% may consider it more useful to permit linking proprietary applications +% with the library. If this is what you want to do, use the GNU Library +% General Public License instead of this License. +% +% \end{multicols} +% \end{minipage} +% \end{lrbox} +% +% \begin{center} +% \scalebox{0.33}{\usebox{\gpl}} +% \end{center} +% +% \Finale +\endinput diff --git a/filegraph.dot b/filegraph.dot deleted file mode 100644 index 47db9ea..0000000 --- a/filegraph.dot +++ /dev/null @@ -1,287 +0,0 @@ -strict digraph luaotfload_files { //looks weird with circo ... - compound = true; - -// label = "Schematic of the files included in Luaotfload."; -// labelloc = "b"; - - fontsize = "14.4"; - labelfontname = "Iwona Medium Regular"; - fontname = "Iwona Light Regular"; - size = "21cm"; - - rankdir = LR; - ranksep = 0.618; - nodesep = 1.618; - - edge [ - arrowhead = onormal, - fontname = "Iwona Cond Regular", - penwidth = 1.0, - ]; - node [ - //penwidth = 0.7, - fontname = "Liberation Mono", - fontsize = 12, - ]; - -/* ···································································· - * file structure - * ································································· */ - fontdbutil -> font_names [label="--update", - style=dashed] - - luaotfload -> otfl_fonts_merged [label="merged"] - luaotfload -> merged_lua_libs [label="unmerged", style=solid] - luaotfload -> merged_luatex_fonts [label="unmerged", style=solid] - luaotfload -> merged_context_libs [label="unmerged", style=solid] - - luaotfload -> luaotfload_libs - luaotfload -> otfl_blacklist_cnf - - otfl_fonts_merged -> merged_lua_libs [label="merged", - style=dotted, - lhead=cluster_merged] - otfl_fonts_merged -> merged_luatex_fonts [label="merged", - style=dotted, - lhead=cluster_merged] - otfl_fonts_merged -> merged_context_libs [label="merged", - style=dotted, - lhead=cluster_merged] - - merged_luatex_fonts -> font_age [label="luatex-fonts-enc.lua", - ltail=cluster_merged] - - fontdbutil -> fontdbutil_diagnostics [label="--diagnose"] - - fontdbutil -> status [label="version information"] - - fontdbutil_diagnostics -> status [constraint=no, label="hash files"] - - merged_luatex_fonts -> characters [label="luaotfload-auxiliary.lua", - ltail=cluster_merged] - - luaotfload_libs -> font_names [label="luaotfload-database.lua"] - - mkstatus -> status [label="generates from distribution files", - style=dashed] - - mkglyphlist -> font_age [label="generates from glyphlist.txt", - style=dashed] - - mkcharacters -> characters [label="generates from Context’s char-def.lua", - style=dashed] - - subgraph { rank = same; - mkcharacters; - mkglyphlist; - mkstatus; - fontdbutil; - luaotfload } - -/* ···································································· - * main files - * ································································· */ - - fontdbutil [label = "luaotfload-tool.lua", - shape = rect, - width = "3.2cm", - height = "1.2cm", - color = "#01012222", - style = "filled,rounded", - penwidth=2] - - fontdbutil_diagnostics [label = "luaotfload-diagnostics.lua", - shape = rect, - width = "3.2cm", - height = "1.2cm", - color = "#01012222", - style = "filled,rounded", - penwidth=2] - - mkstatus [label = "mkstatus", - shape = rect, - width = "3.2cm", - height = "1.2cm", - color = "#01012222", - style = "filled,rounded", - penwidth=2] - - mkglyphlist [label = "mkglyphlist", - shape = rect, - width = "3.2cm", - height = "1.2cm", - color = "#01012222", - style = "filled,rounded", - penwidth=2] - - mkcharacters [label = "mkcharacters", - shape = rect, - width = "3.2cm", - height = "1.2cm", - color = "#01012222", - style = "filled,rounded", - penwidth=2] - - luaotfload [label = "luaotfload-main.lua", - shape = rect, - width = "3.2cm", - height = "1.2cm", - color = "#01012222", - style = "filled,rounded", - penwidth=2] - /* - *otfl_fonts [label = "luaotfload-fonts.lua", - * shape = rect, - * width = "3.2cm", - * height = "1.2cm", - * color = "#01012222", - * style = "filled,rounded", - * penwidth=2] - */ - otfl_fonts_merged [label = "luaotfload-fontloader.lua", - shape = rect, - width = "3.2cm", - height = "1.2cm", - color = "#01012222", - style = "filled,rounded", - penwidth=2] - -/* ···································································· - * luaotfload files - * ································································· */ - - characters [style = "filled,dashed", - shape = rect, - width = "3.2cm", - fillcolor = "#01012222", - color = grey40, - style = "filled,dotted,rounded", - label = "luaotfload-characters.lua"] - - font_age [style = "filled,dashed", - shape = rect, - width = "3.2cm", - fillcolor = "#01012222", - color = grey40, - style = "filled,dotted,rounded", - label = "luaotfload-glyphlist.lua"] - - font_names [style = "filled,dashed", - shape = rect, - width = "3.2cm", - fillcolor = "#01012222", - color = grey40, - style = "filled,dotted,rounded", - label = "luaotfload-names.lua.gz\nluaotfload-names.luc"] - - status [style = "filled,dashed", - shape = rect, - width = "3.2cm", - fillcolor = "#01012222", - color = grey40, - style = "filled,dotted,rounded", - label = "luaotfload-status.lua"] - - otfl_blacklist_cnf [style = "filled,dashed", - shape = rect, - width = "3.2cm", - fillcolor = "#01012222", - color = grey40, - style = "filled,dotted,rounded", - label = "luaotfload-blacklist.cnf"] - - luaotfload_libs [ - shape = box, - style = "filled,rounded", - color = "grey90:goldenrod4", - fontsize = 10, - label = < - - - - - - - -
Luaotfload Libraries
luaotfload-auxiliary.lua luaotfload-features.lua
luaotfload-override.lua luaotfload-loaders.lua
luaotfload-log.lua luaotfload-letterspace.lua
luaotfload-parsers.lua luaotfload-database.lua
luaotfload-color.lua
- >, - ] - -/* ···································································· - * merged files - * ································································· */ - - subgraph cluster_merged { - node [style=filled, color=white]; - style = "filled,rounded"; - color = "grey90:dodgerblue4"; - //nodesep = "3.0"; - rank = same; - label = "Merged Libraries"; - gradientangle=0; - merged_lua_libs; - merged_luatex_fonts; - merged_context_libs; - } - - otfl_fonts_merged -> merged_lua_libs - otfl_fonts_merged -> merged_luatex_fonts - otfl_fonts_merged -> merged_context_libs - - merged_lua_libs [ - shape = box, - style = "filled,rounded", - color = "#FFFFFFAA", - fontsize = 10, - label = < - - - - - - -
Lua Libraries from Context
l-lua.lua l-lpeg.lua l-function.lua
l-string.lua l-table.lua l-io.lua
l-file.lua l-boolean.lua l-math.lua
util-str.lua
- >, - ] - - merged_luatex_fonts [ - shape = box, - style = "filled,rounded", - color = "#FFFFFFAA", - fontsize = 10, - label = < - - - - - - - - - - - - - -
Font Loader (LuaTeX-Fonts)
luatex-basics-gen.lua luatex-basics-nod.lua
luatex-fonts-enc.lua luatex-fonts-syn.lua
luatex-font-tfm.lua luatex-font-afm.lua
luatex-font-afk.lua luatex-fonts-tfm.lua
luatex-fonts-chr.lua luatex-fonts-lua.lua
luatex-fonts-inj.lua luatex-fonts-otn.lua
luatex-fonts-def.lua luatex-fonts-ext.lua
luatex-fonts-cbk.lua
- >, - ] - - merged_context_libs [ - shape = box, - style = "filled,rounded", - color = "#FFFFFFAA", - fontsize = 10, - label = < - - - - - - -
Font and Node Libraries from Context
data-con.lua font-ini.lua font-con.lua
font-cid.lua font-map.lua font-oti.lua
font-otf.lua font-otb.lua font-ota.lua
font-def.lua
- >, - ] -} - -// vim:ft=dot:sw=4:ts=4:expandtab diff --git a/luaotfload-tool.rst b/luaotfload-tool.rst deleted file mode 100644 index 6863918..0000000 --- a/luaotfload-tool.rst +++ /dev/null @@ -1,263 +0,0 @@ -======================================================================= - luaotfload-tool -======================================================================= - ------------------------------------------------------------------------ - generate and query the Luaotfload font names database ------------------------------------------------------------------------ - -:Date: 2014-01-02 -:Copyright: GPL v2.0 -:Version: 2.5 -:Manual section: 1 -:Manual group: text processing - -SYNOPSIS -======================================================================= - -**luaotfload-tool** [ -bDfFiIlnpquvVhw ] - -**luaotfload-tool** --update [ --force ] [ --quiet ] [ --verbose ] - [ --prefer-texmf ] [ --dry-run ] - [ --formats=[+|-]EXTENSIONS ] - [ --no-compress ] [ --no-strip ] - -**luaotfload-tool** --find=FONTNAME [ --fuzzy ] [ --info ] [ --inspect ] - [ --no-reload ] - -**luaotfload-tool** --flush-lookups - -**luaotfload-tool** --cache=DIRECTIVE - -**luaotfload-tool** --list=CRITERION[:VALUE] [ --fields=F1,F2,...,Fn ] - -**luaotfload-tool** --help - -**luaotfload-tool** --version - -**luaotfload-tool** --show-blacklist - -**luaotfload-tool** --diagnose=CHECK - -DESCRIPTION -======================================================================= - -luaotfload-tool accesses the font names database that is required by -the *Luaotfload* package. There are two general modes: **update** and -**query**. - -+ **update**: update the database or rebuild it entirely; -+ **query**: resolve a font name or display close matches. - -OPTIONS -======================================================================= - -update mode ------------------------------------------------------------------------ ---update, -u Update the database; indexes new fonts. ---force, -f Force rebuilding of the database; re-indexes - all fonts. ---no-reload, -n Suppress auto-updates to the database (e.g. - when ``--find`` is passed an unknown name). ---no-strip Do not strip redundant information after - building the database. Warning: this will - inflate the index to about two to three times - the normal size. ---no-compress, -c Do not filter the plain text version of the - font index through gzip. Useful for debugging - if your editor is built without zlib. - ---prefer-texmf, -p Organize the file name database in a way so - that it prefer fonts in the *TEXMF* tree over - system fonts if they are installed in both. ---max-fonts=N Process at most *N* font files, including fonts - already indexed in the count. ---formats=EXTENSIONS Extensions of the font files to index. - Where *EXTENSIONS* is a comma-separated list of - supported file extensions (otf, ttf, ttc, - dfont, pfa, and pfb). If the list is prefixed - with a ``+`` sign, the given list is added to - the currently active one; ``-`` subtracts. - Default: *otf,ttf,ttc,dfont*. - Examples: - - 1) ``--formats=-ttc,ttf`` would skip - TrueType fonts and font collections; - 2) ``--formats=otf`` would scan only OpenType - files; - 3) ``--formats=+pfb`` includes binary - Postscript files. **Warning**: with a - standard TeX Live installation this will - grow the database considerably and slow down - font indexing. - ---dry-run, -D Don’t load fonts, scan directories only. - (For debugging file system related issues.) - -query mode ------------------------------------------------------------------------ ---find=NAME Resolve a font name; this looks up in - the database and prints the file name it is - mapped to. - ``--find`` also understands request syntax, - i.e. ``--find=file:foo.otf`` checks whether - ``foo.otf`` is indexed. ---fuzzy, -F Show approximate matches to the file name if - the lookup was unsuccessful (requires - ``--find``). - ---info, -i Display basic information to a resolved font - file (requires ``--find``). ---inspect, -I Display detailed information by loading the - font and analyzing the font table; very slow! - For the meaning of the returned fields see - the LuaTeX documentation. - (requires ``--find``). ---warnings, -w Print the warnings generated by the fontloader - library (assumes ``-I``). Automatically enabled - if the verbosity level exceeds 2. - ---show-blacklist, -b Show blacklisted files (not directories). ---list=CRITERION Show entries, where *CRITERION* is one of the - following: - - 1) the character ``*``, selecting all entries; - 2) a field of a database entry, for instance - *version* or *format**, according to which - the output will be sorted. - Information in an unstripped database (see - the option ``--no-strip`` above) is nested: - Subfields of a record can be addressed using - the ``->`` separator, e. g. - ``file->location``, ``style->units_per_em``, - or - ``names->sanitized->english->prefmodifiers``. - NB: shell syntax requires that arguments - containing ``->`` be properly quoted! - 3) an expression of the form ``field:value`` to - limit the output to entries whose ``field`` - matches ``value``. - - For example, in order to output file names and - corresponding versions, sorted by the font - format:: - - ./luaotfload-tool.lua --list="format" --fields="file->base,version" - - This prints:: - - otf latinmodern-math.otf Version 1.958 - otf lmromancaps10-oblique.otf 2.004 - otf lmmono8-regular.otf 2.004 - otf lmmonoproplt10-bold.otf 2.004 - otf lmsans10-oblique.otf 2.004 - otf lmromanslant8-regular.otf 2.004 - otf lmroman12-italic.otf 2.004 - otf lmsansdemicond10-oblique.otf 2.004 - ... - ---fields=FIELDS Comma-separated list of fields that should be - printed. - Information in an unstripped database (see the - option ``--no-strip`` above) is nested: - Subfields of a record can be addressed using - the ``->`` separator, e. g. - ``file->location``, ``style->units_per_em``, - or ``names->sanitized->english->subfamily``. - The default is plainname,version*. - (Only meaningful with ``--list``.) - -font and lookup caches ------------------------------------------------------------------------ ---flush-lookups Clear font name lookup cache (experimental). - ---cache=DIRECTIVE Cache control, where *DIRECTIVE* is one of the - following: - - 1) ``purge`` -> delete Lua files from cache; - 2) ``erase`` -> delete Lua and Luc files from - cache; - 3) ``show`` -> print stats. - -miscellaneous ------------------------------------------------------------------------ ---verbose=N, -v Set verbosity level to *n* or the number of - repetitions of ``-v``. ---quiet No verbose output (log level set to zero). ---log=CHANNEL Redirect log output (for database - troubleshooting), where *CHANNEL* can be - - 1) ``stdout`` -> all output will be - dumped to the terminal (default); or - 2) ``file`` -> write to a file to the temporary - directory (the name will be chosen - automatically. - ---version, -V Show version numbers of components as well as - some basic information and exit. ---help, -h Show help message and exit. - ---diagnose=CHECK Run the diagnostic procedure *CHECK*. Available - procedures are: - - 1) ``files`` -> check *Luaotfload* files for - modifications; - 2) ``permissions`` -> check permissions of - cache directories and files; - 3) ``environment`` -> print relevant - environment and kpse variables; - 4) ``repository`` -> check the git repository - for new releases, - 5) ``index`` -> check database, display - information about it. - - Procedures can be chained by concatenating with - commas, e.g. ``--diagnose=files,permissions``. - Specify ``thorough`` to run all checks. - -FILES -======================================================================= - -The font name database is usually located in the directory -``texmf-var/luatex-cache/generic/names/`` (``$TEXMFCACHE`` as set in -``texmf.cnf``) of your *TeX Live* distribution as a zlib-compressed -file ``luaotfload-names.lua.gz``. -The experimental lookup cache will be created as -``luaotfload-lookup-cache.lua`` in the same directory. -These Lua tables are not used directly by Luaotfload, though. -Instead, they are compiled to Lua bytecode which is written to -corresponding files with the extension ``.luc`` in the same directory. -When modifying the files by hand keep in mind that only if the bytecode -files are missing will Luaotfload use the plain version instead. -Both kinds of files are safe to delete, at the cost of regenerating -them with the next run of *LuaTeX*. - -SEE ALSO -======================================================================= - -**luatex** (1), **lua** (1) - -* ``texdoc luaotfload`` to display the manual for the *Luaotfload* - package -* Luaotfload development ``_ -* LuaLaTeX mailing list ``_ -* LuaTeX ``_ -* ConTeXt ``_ -* Luaotfload on CTAN ``_ - -BUGS -======================================================================= - -Tons, probably. - -AUTHORS -======================================================================= - -*Luaotfload* is maintained by the LuaLaTeX dev team -(``__). -The fontloader code is provided by Hans Hagen of Pragma ADE, Hasselt -NL (``__). - -This manual page was written by Philipp Gesang -. - diff --git a/luaotfload.dtx b/luaotfload.dtx deleted file mode 100644 index e0311a4..0000000 --- a/luaotfload.dtx +++ /dev/null @@ -1,1994 +0,0 @@ -% \iffalse meta-comment -% -% Copyright (C) 2009-2014 -% by Elie Roux -% and Khaled Hosny -% and Philipp Gesang -% -% Home: https://github.com/lualatex/luaotfload -% Support: . -% -% This work is under the GPL v2.0 license. -% -% This work consists of the main source file luaotfload.dtx -% and the derived files -% luaotfload.sty -% -% Unpacking: -% tex luaotfload.dtx -% -% Documentation: -% lualatex luaotfload.dtx -% -% The class ltxdoc loads the configuration file ltxdoc.cfg -% if available. Here you can specify further options, e.g. -% use A4 as paper format: -% \PassOptionsToClass{a4paper}{article} -% -% -% -%<*ignore> -\begingroup - \def\x{LaTeX2e}% -\expandafter\endgroup -\ifcase 0\ifx\install y1\fi\expandafter - \ifx\csname processbatchFile\endcsname\relax\else1\fi - \ifx\fmtname\x\else 1\fi\relax -\else\csname fi\endcsname -% -%<*install> -\input docstrip.tex -\Msg{************************************************************************} -\Msg{* Installation} -\Msg{* Package: luaotfload v2.5 OpenType layout system} -\Msg{************************************************************************} - -\keepsilent -\askforoverwritefalse - -\let\MetaPrefix\relax - -\preamble -This is a generated file. - -Copyright (C) 2009-2014 - by Elie Roux - and Khaled Hosny - and Philipp Gesang - - Home: https://github.com/lualatex/luaotfload - Support: . - -This work is under the GPL v2.0 license. - -This work consists of the main source file luaotfload.dtx -and the derived files - luaotfload.sty - -\endpreamble - -\obeyspaces -\Msg{************************************************************************} -\Msg{*} -\Msg{* To finish the installation you have to move the following} -\Msg{* files into a directory searched by TeX:} -\Msg{*} -\Msg{* luaotfload.sty} -\Msg{*} -\Msg{* Happy TeXing!} -\Msg{*} -\Msg{************************************************************************} - -\endbatchfile -% -%<*ignore> -\fi -% -%<*driver> -\NeedsTeXFormat{LaTeX2e} -\ProvidesFile{luaotfload.drv}% -[2014/**/** v2.5 OpenType layout system]% -\documentclass{ltxdoc} -\usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace} -\usepackage[x11names]{xcolor} -% -\def\primarycolor{DodgerBlue4} %%-> rgb 16 78 139 | #104e8b -\def\secondarycolor{Goldenrod4} %%-> rgb 139 105 200 | #8b6914 -% -\usepackage[ - bookmarks=true, - colorlinks=true, - linkcolor=\primarycolor, - urlcolor=\secondarycolor, - citecolor=\primarycolor, - pdftitle={The luaotfload package}, - pdfsubject={OpenType layout system for Plain TeX and LaTeX}, - pdfauthor={Elie Roux & Khaled Hosny & Philipp Gesang}, - pdfkeywords={luatex, lualatex, unicode, opentype} -]{hyperref} -\usepackage{fontspec} -\usepackage{unicode-math} -\setmainfont[ -% Numbers = OldStyle, %% buggy with font cache - Ligatures = TeX, - BoldFont = {Linux Libertine O Bold}, - ItalicFont = {Linux Libertine O Italic}, - SlantedFont = {Linux Libertine O Italic}, -]{Linux Libertine O} -\setmonofont[Ligatures=TeX,Scale=MatchLowercase]{Liberation Mono} -%setsansfont[Ligatures=TeX]{Linux Biolinum O} -\setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium} -%setmathfont{XITS Math} - -\usepackage{hologo} - -\newcommand\TEX {\TeX\xspace} -\newcommand\LUA {Lua\xspace} -\newcommand\PDFTEX {pdf\TeX\xspace} -\newcommand\LUATEX {Lua\TeX\xspace} -\newcommand\XETEX {\XeTeX\xspace} -\newcommand\LATEX {\LaTeX\xspace} -\newcommand\LUALATEX {Lua\LaTeX\xspace} -\newcommand\CONTEXT {Con\TeX t\xspace} -\newcommand\OpenType {\identifier{Open\kern-.25ex Type}\xspace} - -\def\definehighlight[#1][#2]% - {\ifcsname #1\endcsname\else - \expandafter\def\csname #1\endcsname% - {\bgroup#2\csname #1_indeed\endcsname} - \expandafter\def\csname #1_indeed\endcsname##1% - {##1\egroup}% - \fi} - -\def\restoreunderscore{\catcode`\_=12\relax} - -\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs -\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs -\definehighlight[luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers -\definehighlight [identifier][\sffamily] %% names -\definehighlight [abbrev][\rmfamily\scshape] %% acronyms -\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph - -\newcommand*\email[1]{\href{mailto:#1}{#1}} - -\renewcommand\partname{Part}%% gets rid of the stupid “file” heading - -\usepackage{syntax}%% bnf for font request syntax - -\usepackage{titlesec} - -\def\movecountertomargin#1{\llap{\rmfamily\upshape#1\hskip2em}} -\def\zeropoint{0pt} -\titleformat \part - {\normalsize\rmfamily\bfseries} - {\movecountertomargin\thepart} \zeropoint {} -\titleformat \section - {\normalsize\rmfamily\scshape} - {\movecountertomargin\thesection} \zeropoint {} -\titleformat \subsection - {\small\rmfamily\itshape} - {\movecountertomargin\thesubsection} \zeropoint {} -\titleformat \subsubsection - {\normalsize\rmfamily\upshape} - {\movecountertomargin\thesubsubsection} \zeropoint {} - -\usepackage{tocloft} -\renewcommand \cftpartfont {\rmfamily\upshape} -\renewcommand \cftsecfont {\rmfamily\upshape} -\renewcommand \cftsubsecfont {\rmfamily\upshape} -\setlength \cftbeforepartskip {1ex} -\setlength \cftbeforesecskip {1ex} - -\VerbatimFootnotes -\begin{document} - \DocInput{luaotfload.dtx}% -\end{document} -% -% \fi -% -% \CheckSum{0} -% -% \CharacterTable -% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z -% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z -% Digits \0\1\2\3\4\5\6\7\8\9 -% Exclamation \! Double quote \" Hash (number) \# -% Dollar \$ Percent \% Ampersand \& -% Acute accent \' Left paren \( Right paren \) -% Asterisk \* Plus \+ Comma \, -% Minus \- Point \. Solidus \/ -% Colon \: Semicolon \; Less than \< -% Equals \= Greater than \> Question mark \? -% Commercial at \@ Left bracket \[ Backslash \\ -% Right bracket \] Circumflex \^ Underscore \_ -% Grave accent \` Left brace \{ Vertical bar \| -% Right brace \} Tilde \~} -% -% \GetFileInfo{luaotfload.drv} -% -% \title{The \identifier{luaotfload} package} -% \date{2014/**/** v2.5} -% \author{Elie Roux · Khaled Hosny · Philipp Gesang\\ -% Home: \url{https://github.com/lualatex/luaotfload}\\ -% Support: \email{lualatex-dev@tug.org}} -% -% \maketitle -% -% \begin{abstract} -% This package is an adaptation of the \CONTEXT font loading system. -% It allows for loading \OpenType fonts with an extended syntax and adds -% support for a variety of font features. -% \end{abstract} -% -% \tableofcontents -% -% \part{Package Description} -% -% \section{Introduction} -% -% Font management and installation has always been painful with \TEX. A lot of -% files are needed for one font (\abbrev{tfm}, \abbrev{pfb}, \abbrev{map}, -% \abbrev{fd}, \abbrev{vf}), and due to the 8-Bit encoding each font is limited -% to 256 characters. -% But the font world has evolved since the original -% \TEX, and new typographic systems have appeared, most notably the so -% called \emphasis{smart font} technologies like \OpenType -% fonts (\abbrev{otf}). -% These fonts can contain many more characters than \TEX fonts, as well as additional -% functionality like ligatures, old-style numbers, small capitals, -% etc., and support more complex writing systems like Arabic and -% Indic\footnote{% -% Unfortunately, \identifier{luaotfload} doesn‘t support many Indic -% scripts right now. -% Assistance in implementing the prerequisites is greatly -% appreciated. -% } -% scripts. -% \OpenType fonts are widely deployed and available for all -% modern operating systems. -% As of 2013 they have become the de facto standard for advanced text -% layout. -% However, until recently the only way to use them directly in the \TEX -% world was with the \XETEX engine. -% -% Unlike \XETEX, \LUATEX has no built-in support for -% \OpenType or technologies other than the original \TEX fonts. -% Instead, it provides hooks for executing \LUA code during the \TEX run -% that allow implementing extensions for loading fonts and manipulating -% how input text is processed without modifying the underlying engine. -% This is where \identifier{luaotfload} comes into play: -% Based on code from \CONTEXT, it extends \LUATEX with functionality necessary -% for handling \OpenType fonts. -% Additionally, it provides means for accessing fonts known to the operating -% system conveniently by indexing the metadata. -% -% -% \section{Thanks} -% -% \identifier{Luaotfload} is part of \LUALATEX, the community-driven -% project to provide a foundation for using the \LATEX format with the -% full capabilites of the \LUATEX engine. -% As such, the distinction between end users, contributors, and project -% maintainers is intentionally kept less strict, lest we unduly -% personalize the common effort. -% -% Nevertheless, the current maintainers would like to express their -% gratitude to Khaled Hosny, Akira Kakuto, Hironori Kitagawa and Dohyun -% Kim. -% Their contributions -- be it patches, advice, or systematic -% testing -- made the switch from version 1.x to 2.2 possible. -% Also, Hans Hagen, the author of the font loader, made porting the -% code to \LATEX a breeze due to the extra effort he invested into -% isolating it from the rest of \CONTEXT, not to mention his assistance -% in the task and willingness to respond to our suggestions. -% -% -% \section{Loading Fonts} -% -% \identifier{luaotfload} supports an extended font request syntax: -% -% \begin{quote} -% |\font\foo={|% -% \meta{prefix}|:|% -% \meta{font name}|:|% -% \meta{font features}|}|% -% \meta{\TEX font features} -% \end{quote} -% -% \noindent -% The curly brackets are optional and escape the spaces in the enclosed -% font name. -% Alternatively, double quotes serve the same purpose. -% A selection of individual parts of the syntax are discussed below; -% for a more formal description see figure \ref{font-syntax}. -% -% \begin{figure}[b] -% \setlength\grammarparsep{12pt plus 2pt minus 2pt} -% \setlength\grammarindent{5cm} -% \begingroup -% \small -% \begin{grammar} -% ::= `\\font', {\sc csname}, `=', , [ ] ; -% -% ::= `at', {\sc dimension} ; -% -% ::= `"', `"' -% \alt `{', `}' -% \alt ; -% -% ::= , [`:', ] -% \alt `[', `]', [ [`:'], ] ; -% -% ::= , [ ], \{ \} -% \alt , \{ \} ; -% -% ::= `file:', -% \alt `name:', ; -% -% ::= \{ \} ; -% -% ::= \{ \} ; -% -% ::= {\sc tfmname} | ; -% -% ::= \{ {\sc all_characters} - `]' \} ; -% -% ::= `/', (`I' | `B' | `BI' | `IB' | `S=', \{ {\sc digit} \} ) ; -% -% ::= `(', \{ {\sc digit} \}, `)' ; -% -% ::= , \{ `;', \} ; -% -% ::= {\sc feature_id}, `=', {\sc feature_value} -% \alt , {\sc feature_id} ; -% -% ::= `+' | `-' ; -% -% ::= {\sc all_characters} - ( `(' | `/' | `:' ) ; -% \end{grammar} -% \endgroup -% \caption{Font request syntax. -% Braces or double quotes around the -% \emphasis{specification} rule will -% preserve whitespace in file names. -% In addition to the font style modifiers -% (\emphasis{slash-notation}) given above, there -% are others that are recognized but will be silently -% ignored: {\ttfamily aat}, -% {\ttfamily icu}, and -% {\ttfamily gr}. -% The special terminals are: -% {\sc feature\textunderscore id} for a valid font -% feature name and -% {\sc feature\textunderscore value} for the corresponding -% value. -% {\sc tfmname} is the name of a \abbrev{tfm} file. -% {\sc digit} again refers to bytes 48--57, and -% {\sc all\textunderscore characters} to all byte values. -% {\sc csname} and {\sc dimension} are the \TEX concepts.} -% \label{font-syntax} -% \end{figure} -% -% \subsection{Prefix -- the \identifier{luaotfload}{ }Way} -% -% In \identifier{luaotfload}, the canonical syntax for font requests -% requires a \emphasis{prefix}: -% \begin{quote} -% |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots -% \end{quote} -% where \meta{prefix} is either \verb|file:| or \verb|name:|.\footnote{% -% The development version also knows two further prefixes, -% \verb|kpse:| and \verb|my:|. -% A \verb|kpse| lookup is restricted to files that can be found by -% \identifier{kpathsea} and -% will not attempt to locate system fonts. -% This behavior can be of value when an extra degree of encapsulation is -% needed, for instance when supplying a customized tex distribution. -% -% The \verb|my| lookup takes this a step further: it lets you define -% a custom resolver function and hook it into the \luafunction{resolve_font} -% callback. -% This ensures full control over how a file is located. -% For a working example see the -% \href{https://bitbucket.org/phg/lua-la-tex-tests/src/5f6a535d/pln-lookup-callback-1.tex} -% {test repo}. -% } -% It determines whether the font loader should interpret the request as -% a \emphasis{file name} or -% \emphasis{font name}, respectively, -% which again influences how it will attempt to locate the font. -% Examples for font names are -% “Latin Modern Italic”, -% “GFS Bodoni Rg”, and -% “PT Serif Caption” -% -- they are the human readable identifiers -% usually listed in drop-down menus and the like.\footnote{% -% Font names may appear like a great choice at first because they -% offer seemingly more intuitive identifiers in comparison to arguably -% cryptic file names: -% “PT Sans Bold” is a lot more descriptive than \fileent{PTS75F.ttf}. -% On the other hand, font names are quite arbitrary and there is no -% universal method to determine their meaning. -% While \identifier{luaotfload} provides fairly sophisticated heuristic -% to figure out a matching font style, weight, and optical size, it -% cannot be relied upon to work satisfactorily for all font files. -% For an in-depth analysis of the situation and how broken font names -% are, please refer to -% \href{http://www.ntg.nl/pipermail/ntg-context/2013/073889.html} -% {this post} -% by Hans Hagen, the author of the font loader. -% If in doubt, use filenames. -% \fileent{luaotfload-tool} can perform the matching for you with the -% option \verb|--find=|, and you can use the file name it returns -% in your font definition. -% } -% In order for fonts installed both in system locations and in your -% \fileent{texmf} to be accessible by font name, \identifier{luaotfload} must -% first collect the metadata included in the files. -% Please refer to section~\ref{sec:fontdb} below for instructions on how to -% create the database. -% -% File names are whatever your file system allows them to be, except -% that that they may not contain the characters -% \verb|(|, -% \verb|:|, and -% \verb|/|. -% As is obvious from the last exception, the \verb|file:| lookup will -% not process paths to the font location -- only those -% files found when generating the database are addressable this way. -% Continue below in the \XETEX section if you need to load your fonts -% by path. -% The file names corresponding to the example font names above are -% \fileent{lmroman12-italic.otf}, -% \fileent{GFSBodoni.otf}, and -% \fileent{PTZ56F.ttf}. -% -% \subsection{Compatibility Layer} -% -% In addition to the regular prefixed requests, \identifier{luaotfload} -% accepts loading fonts the \XETEX way. -% There are again two modes: bracketed and unbracketed. -% A bracketed request looks as follows. -% -% \begin{quote} -% |\font\fontname=[|\meta{path to file}|]| -% \end{quote} -% -% \noindent -% Inside the square brackets, every character except for a closing -% bracket is permitted, allowing for specifying paths to a font file. -% Naturally, path-less file names are equally valid and processed the -% same way as an ordinary \verb|file:| lookup. -% -% \begin{quote} -% |\font\fontname=|\meta{font name} \dots -% \end{quote} -% -% Unbracketed (or, for lack of a better word: \emphasis{anonymous}) -% font requests resemble the conventional \TEX syntax. -% However, they have a broader spectrum of possible interpretations: -% before anything else, \identifier{luaotfload} attempts to load a -% traditional \TEX Font Metric (\abbrev{tfm} or \abbrev{ofm}). -% If this fails, it performs a \verb|name:| lookup, which itself will -% fall back to a \verb|file:| lookup if no database entry matches -% \meta{font name}. -% -% Furthermore, \identifier{luaotfload} supports the slashed (shorthand) -% font style notation from \XETEX. -% -% \begin{quote} -% |\font\fontname=|\meta{font name}|/|\meta{modifier}\dots -% \end{quote} -% -% \noindent -% Currently, four style modifiers are supported: -% \verb|I| for italic shape, -% \verb|B| for bold weight, -% \verb|BI| or \verb|IB| for the combination of both. -% Other “slashed” modifiers are too specific to the \XETEX engine and -% have no meaning in \LUATEX. -% -% \subsection{Examples} -% -% \subsubsection{Loading by File Name} -% -% For example, conventional \abbrev{type1} font can be loaded with a \verb|file:| -% request like so: -% -% \begin{quote} -% \begin{verbatim} -% \font\lmromanten={file:ec-lmr10} at 10pt -% \end{verbatim} -% \end{quote} -% -% The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa -% Półtawskiego}\footnote{% -% \url{http://jmn.pl/antykwa-poltawskiego/}, also available in -% in \TEX Live. -% } -% in its condensed variant can be loaded as follows: -% -% \begin{quote} -% \begin{verbatim} -% \font\apcregular=file:antpoltltcond-regular.otf at 42pt -% \end{verbatim} -% \end{quote} -% -% The next example shows how to load the \emphasis{Porson} font digitized by -% the Greek Font Society using \XETEX-style syntax and an absolute path from a -% non-standard directory: -% -% \begin{quote} -% \begin{verbatim} -% \font\gfsporson="[/tmp/GFSPorson.otf]" at 12pt -% \end{verbatim} -% \end{quote} -% -% \subsubsection{Loading by Font Name} -% -% The \verb|name:| lookup does not depend on cryptic filenames: -% -% \begin{quote} -% \begin{verbatim} -% \font\pagellaregular={name:TeX Gyre Pagella} at 9pt -% \end{verbatim} -% \end{quote} -% -% A bit more specific but essentially the same lookup would be: -% -% \begin{quote} -% \begin{verbatim} -% \font\pagellaregular={name:TeX Gyre Pagella Regular} at 9pt -% \end{verbatim} -% \end{quote} -% -% \noindent -% Which fits nicely with the whole set: -% -% \begin{quote} -% \begin{verbatim} -% \font\pagellaregular ={name:TeX Gyre Pagella Regular} at 9pt -% \font\pagellaitalic ={name:TeX Gyre Pagella Italic} at 9pt -% \font\pagellabold ={name:TeX Gyre Pagella Bold} at 9pt -% \font\pagellabolditalic={name:TeX Gyre Pagella Bolditalic} at 9pt -% -% {\pagellaregular foo bar baz\endgraf} -% {\pagellaitalic foo bar baz\endgraf} -% {\pagellabold foo bar baz\endgraf} -% {\pagellabolditalic foo bar baz\endgraf} -% -% ... -% \end{verbatim} -% \end{quote} -% -% \subsubsection{Modifiers} -% -% If the entire \emphasis{Iwona} family\footnote{% -% \url{http://jmn.pl/kurier-i-iwona/}, -% also in \TEX Live. -% } -% is installed in some location accessible by \identifier{luaotfload}, -% the regular shape can be loaded as follows: -% -% \begin{quote} -% \begin{verbatim} -% \font\iwona=Iwona at 20pt -% \end{verbatim} -% \end{quote} -% -% \noindent -% To load the most common of the other styles, the slash notation can -% be employed as shorthand: -% -% \begin{quote} -% \begin{verbatim} -% \font\iwonaitalic =Iwona/I at 20pt -% \font\iwonabold =Iwona/B at 20pt -% \font\iwonabolditalic=Iwona/BI at 20pt -% \end{verbatim} -% \end{quote} -% -% \noindent -% which is equivalent to these full names: -% -% \begin{quote} -% \begin{verbatim} -% \font\iwonaitalic ="Iwona Italic" at 20pt -% \font\iwonabold ="Iwona Bold" at 20pt -% \font\iwonabolditalic="Iwona BoldItalic" at 20pt -% \end{verbatim} -% \end{quote} -% -% \section{Font features} -% -% \emphasis{Font features} are the second to last component in the -% general scheme for font requests: -% -% \begin{quote} -% |\font\foo={|% -% \meta{prefix}|:|% -% \meta{font name}|:|% -% \meta{font features}|}|% -% \meta{\TEX font features} -% \end{quote} -% -% \noindent -% If style modifiers are present (\XETEX style), they must precede -% \meta{font features}. -% -% The element \meta{font features} is a semicolon-separated list of feature -% tags\footnote{% -% Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm}. -% } -% and font options. -% Prepending a font feature with a |+| (plus sign) enables it, whereas -% a |-| (minus) disables it. For instance, the request -% -% \begin{quote} -% \begin{verbatim} -% \font\test=LatinModernRoman:+clig;-kern -% \end{verbatim} -% \end{quote} -% -% \noindent activates contextual ligatures (|clig|) and disables -% kerning (|kern|). -% Alternatively the options |true| or |false| can be passed to -% the feature in a key/value expression. -% The following request has the same meaning as the last one: -% -% \begin{quote} -% \begin{verbatim} -% \font\test=LatinModernRoman:clig=true;kern=false -% \end{verbatim} -% \end{quote} -% -% \noindent -% Furthermore, this second syntax is required should a font feature -% accept other options besides a true/false switch. -% For example, \emphasis{stylistic alternates} (|salt|) are variants of given -% glyphs. -% They can be selected either explicitly by supplying the variant -% index (starting from one), or randomly by setting the value to, -% obviously, |random|. -% -% \iffalse TODO verify that this actually works with a font that supports -% the salt/random feature!\fi -% \begin{quote} -% \begin{verbatim} -% \font\librmsaltfirst=LatinModernRoman:salt=1 -% \end{verbatim} -% \end{quote} -% -% \noindent Other font options include: -% -% \begin{description} -% -% \item [mode] \hfill \\ -% \identifier{luaotfload} has two \OpenType processing -% \emphasis{modes}: -% \identifier{base} and \identifier{node}. -% -% \identifier{base} mode works by mapping \OpenType -% features to traditional \TEX ligature and kerning mechanisms. -% Supporting only non-contextual substitutions and kerning -% pairs, it is the slightly faster, albeit somewhat limited, variant. -% \identifier{node} mode works by processing \TeX’s internal -% node list directly at the \LUA end and supports -% a wider range of \OpenType features. -% The downside is that the intricate operations required for -% \identifier{node} mode may slow down typesetting especially -% with complex fonts and it does not work in math mode. -% -% By default \identifier{luaotfload} is in \identifier{node} -% mode, and \identifier{base} mode has to be requested where needed, -% e.~g. for math fonts. -% -% \item [script] \label{script-tag} \hfill \\ -% An \OpenType script tag;\footnote{% -% See \url{http://www.microsoft.com/typography/otspec/scripttags.htm} -% for a list of valid values. -% For scripts derived from the Latin alphabet the value -% |latn| is good choice. -% } -% the default value is |dlft|. -% Some fonts, including very popular ones by foundries like Adobe, -% do not assign features to the |dflt| script, in -% which case the script needs to be set explicitly. -% -% \item [language] \hfill \\ -% An \OpenType language system identifier,\footnote{% -% Cf. \url{http://www.microsoft.com/typography/otspec/languagetags.htm}. -% } -% defaulting to |dflt|. -% -% \item [featurefile] \hfill \\ -% A comma-separated list of feature files to be applied to the -% font. -% Feature files contain a textual representation of -% \OpenType tables and extend the features of a font -% on fly. -% After they are applied to a font, features defined in a -% feature file can be enabled or disabled just like any -% other font feature. -% The syntax is documented in \identifier{Adobe}’s -% \OpenType Feature File Specification.\footnote{% -% Cf. \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. -% } -% -% For a demonstration of how to set a |tkrn| feature consult -% the file |tkrn.fea| that is part of \identifier{luaotfload}. -% It can be read and applied as follows: -% -% |\font\test=Latin Modern Roman:featurefile=tkrn.fea;+tkrn| -% -% \item [color] \hfill \\ -% A font color, defined as a triplet of two-digit hexadecimal -% \abbrev{rgb} values, with an optional fourth value for -% transparency -% (where |00| is completely transparent and |FF| is opaque). -% -% For example, in order to set text in semitransparent red: -% -% \begin{quote} -% \begin{verbatim} -% \font\test={Latin Modern Roman}:color=FF0000BB -% \end{verbatim} -% \end{quote} -% -% \item [kernfactor \& letterspace] \hfill \\ -% Define a font with letterspacing (tracking) enabled. -% In \identifier{luaotfload}, letterspacing is implemented by -% inserting additional kerning between glyphs. -% -% This approach is derived from and still quite similar to the -% \emphasis{character kerning} (\texmacro{setcharacterkerning} / -% \texmacro{definecharacterkerning} \& al.) functionality of -% Context, see the file \fileent{typo-krn.lua} there. -% The main difference is that \identifier{luaotfload} does not -% use \LUATEX attributes to assign letterspacing to regions, -% but defines virtual letterspaced versions of a font. -% -% The option \identifier{kernfactor} accepts a numeric value that -% determines the letterspacing factor to be applied to the font -% size. -% E.~g. a kern factor of $0.42$ applied to a $10$ pt font -% results in $4.2$ pt of additional kerning applied to each -% pair of glyphs. -% Ligatures are split into their component glyphs unless -% explicitly ignored (see below). -% -% For compatibility with \XETEX an alternative -% \identifier{letterspace} option is supplied that interprets the -% supplied value as a \emphasis{percentage} of the font size but -% is otherwise identical to \identifier{kernfactor}. -% Consequently, both definitions in below snippet yield the same -% letterspacing width: -% -% \begin{quote} -% \begin{verbatim} -% \font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125" -% \font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5" -% \end{verbatim} -% \end{quote} -% -% Specific pairs of letters and ligatures may be exempt from -% letterspacing by defining the \LUA functions -% \luafunction{keeptogether} and \luafunction{keepligature}, -% respectively, inside the namespace \verb|luaotfload.letterspace|. -% Both functions are called whenever the letterspacing callback -% encounters an appropriate node or set of nodes. -% If they return a true-ish value, no extra kern is inserted at -% the current position. -% \luafunction{keeptogether} receives a pair of consecutive -% glyph nodes in order of their appearance in the node list. -% \luafunction{keepligature} receives a single node which can be -% analyzed into components. -% (For details refer to the \emphasis{glyph nodes} section in the -% \LUATEX reference manual.) -% The implementation of both functions is left entirely to the -% user. -% -% -% \item [protrusion \& expansion] \hfill \\ -% These keys control microtypographic features of the font, -% namely \emphasis{character protrusion} and \emphasis{font -% expansion}. -% Their arguments are names of \LUA tables that contain -% values for the respective features.\footnote{% -% For examples of the table layout please refer to the -% section of the file \fileent{luaotfload-fonts-ext.lua} where the -% default values are defined. -% Alternatively and with loss of information, you can dump -% those tables into your terminal by issuing -% \begin{verbatim} -% \directlua{inspect(fonts.protrusions.setups.default) -% inspect(fonts.expansions.setups.default)} -% \end{verbatim} -% at some point after loading \fileent{luaotfload.sty}. -% } -% For both, only the set \identifier{default} is predefined. -% -% For example, to define a font with the default -% protrusion vector applied\footnote{% -% You also need to set -% \verb|pdfprotrudechars=2| and -% \verb|pdfadjustspacing=2| -% to activate protrusion and expansion, respectively. -% See the -% \href{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% -% {\PDFTEX manual} -% for details. -% }: -% -% \begin{quote} -% \begin{verbatim} -% \font\test=LatinModernRoman:protrusion=default -% \end{verbatim} -% \end{quote} -% \end{description} -% -% \paragraph{Non-standard font features} -% \identifier{luaotfload} adds a number of features that are not defined -% in the original \OpenType specification, most of them -% aiming at emulating the behavior familiar from other \TEX engines. -% Currently (2014) there are three of them: -% -% \begin{description} -% -% \item [anum] -% Substitutes the glyphs in the \abbrev{ascii} number range -% with their counterparts from eastern Arabic or Persian, -% depending on the value of \identifier{language}. -% -% \item [tlig] -% Applies legacy \TEX ligatures: -% -% \begin{tabular}{rlrl} -% `` & \verb|``| & '' & \verb|''| \\ -% ` & \verb|`| & ' & \verb|'| \\ -% " & \verb|"| & -- & \verb|--| \\ -% --- & \verb|---| & !` & \verb|!`| \\ -% ?` & \verb|?`| & & \\ -% \end{tabular} -% -% \footnote{% -% These contain the feature set \verb|trep| of earlier -% versions of \identifier{luaotfload}. -% -% Note to \XETEX users: this is the equivalent of the -% assignment \verb|mapping=text-tex| using \XETEX's input -% remapping feature. -% } -% -% \item [itlc] -% Computes italic correction values (active by default). -% -% \end{description} -% -% -% -% \section{Font names database} -% \label{sec:fontdb} -% -% As mentioned above, \identifier{luaotfload} keeps track of which -% fonts are available to \LUATEX by means of a \emphasis{database}. -% This allows referring to fonts not only by explicit filenames but -% also by the proper names contained in the metadata which is often -% more accessible to humans.\footnote{% -% The tool \href{http://www.lcdf.org/type/}{\fileent{otfinfo}} (comes -% with \TEX Live), when invoked on a font file with the \verb|-i| -% option, lists the variety of name fields defined for it. -% } -% -% When \identifier{luaotfload} is asked to load a font by a font name, -% it will check if the database exists and load it, or else generate a -% fresh one. -% Should it then fail to locate the font, an update to the database is -% performed in case the font has been added to the system only -% recently. As soon as the database is updated, the resolver will try -% and look up the font again, all without user intervention. -% The goal is for \identifier{luaotfload} to act in the background and -% behave as unobtrusively as possible, while providing a convenient -% interface to the fonts installed on the system. -% -% Generating the database for the first time may take a while since it -% inspects every font file on your computer. -% This is particularly noticeable if it occurs during a typesetting run. -% In any case, subsequent updates to the database will be quite fast. -% -% \subsection[luaotfload-tool / mkluatexfontdb.lua]% -% {\fileent{luaotfload-tool} / -% \fileent{mkluatexfontdb.lua}\footnote{% -% The script may be named just \fileent{mkluatexfontdb} in your -% distribution. -% }} -% -% It can still be desirable at times to do some of these steps -% manually, and without having to compile a document. -% To this end, \identifier{luaotfload} comes with the utility -% \fileent{luaotfload-tool} that offers an interface to the database -% functionality. -% Being a \LUA script, there are two ways to run it: -% either make it executable (\verb|chmod +x| on unixoid systems) or -% pass it as an argument to \fileent{texlua}.\footnote{% -% Tests by the maintainer show only marginal performance gain by -% running with Luigi Scarso’s -% \href{https://foundry.supelec.fr/projects/luajittex/}% -% {\identifier{Luajit\kern-.25ex\TEX}}, -% which is probably due to the fact that most of the time is spent -% on file system operations. -% -% \emphasis{Note}: -% On \abbrev{MS} \identifier{Windows} systems, the script can be run -% either by calling the wrapper application -% \fileent{luaotfload-tool.exe} or as -% \verb|texlua.exe luaotfload-tool.lua|. -% } -% Invoked with the argument \verb|--update| it will perform a database -% update, scanning for fonts not indexed. -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --update -% \end{verbatim} -% \end{quote} -% -% Adding the \verb|--force| switch will initiate a complete -% rebuild of the database. -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --update --force -% \end{verbatim} -% \end{quote} -% -% For sake of backwards compatibility, \fileent{luaotfload-tool} may be -% renamed or symlinked to \fileent{mkluatexfontdb}. -% Whenever it is run under this name, it will update the database -% first, mimicking the behavior of earlier versions of -% \identifier{luaotfload}. -% -% \subsection{Search Paths} -% -% \identifier{luaotfload} scans those directories where fonts are -% expected to be located on a given system. -% On a Linux machine it follows the paths listed in the -% \identifier{Fontconfig} configuration files; -% consult \verb|man 5 fonts.conf| for further information. -% On \identifier{Windows} systems, the standard location is -% \verb|Windows\Fonts|, -% while \identifier{Mac OS~X} requires a multitude of paths to -% be examined. -% The complete list is is given in table \ref{table-searchpaths}. -% Other paths can be specified by setting the environment variable -% \verb+OSFONTDIR+. -% If it is non-empty, then search will be extended to the included -% directories. -% -% \begin{table}[t] -% \hrule -% \caption{List of paths searched for each supported operating -% system.} -% \renewcommand{\arraystretch}{1.2} -% \begin{center} -% \begin{tabular}{lp{.5\textwidth}} -% Windows & \verb|%WINDIR%\Fonts| -% \\ -% Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break -% \fileent{/etc/fonts/fonts.conf} -% \\ -% Mac & \fileent{\textasciitilde/Library/Fonts},\break -% \fileent{/Library/Fonts},\break -% \fileent{/System/Library/Fonts}, and\hfill\break -% \fileent{/Network/Library/Fonts} -% \\ -% \end{tabular} -% \end{center} -% \label{table-searchpaths} -% \hrule -% \end{table} -% -% \subsection{Querying from Outside} -% -% \fileent{luaotfload-tool} also provides rudimentary means of -% accessing the information collected in the font database. -% If the option \verb|--find=|\emphasis{name} is given, the script will -% try and search the fonts indexed by \identifier{luaotfload} for a -% matching name. -% For instance, the invocation -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --find="Iwona Regular" -% \end{verbatim} -% \end{quote} -% -% \noindent -% will verify if “Iwona Regular” is found in the database and can be -% readily requested in a document. -% -% If you are unsure about the actual font name, then add the -% \verb|-F| (or \verb|--fuzzy|) switch to the command line to enable -% approximate matching. -% Suppose you cannot precisely remember if the variant of -% \identifier{Iwona} you are looking for was “Bright” or “Light”. -% The query -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -F --find="Iwona Bright" -% \end{verbatim} -% \end{quote} -% -% \noindent -% will tell you that indeed the latter name is correct. -% -% Basic information about fonts in the database can be displayed -% using the \verb|-i| option (\verb|--info|). -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -i --find="Iwona Light Italic" -% \end{verbatim} -% \end{quote} -% \noindent -% The meaning of the printed values is described in section 4.4 of the -% \LUATEX reference manual.\footnote{% -% In \TEX Live: \fileent{texmf-dist/doc/luatex/base/luatexref-t.pdf}. -% } -% -% For a much more detailed report about a given font try the \verb|-I| option -% instead (\verb|--inspect|). -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -I --find="Iwona Light Italic" -% \end{verbatim} -% \end{quote} -% -% \verb|luaotfload-tool --help| will list the available command line -% switches, including some not discussed in detail here. -% For a full documentation of \identifier{luaotfload-tool} and its -% capabilities refer to the manpage -% (\verb|man 1 luaotfload-tool|).\footnote{% -% Or see \verb|luaotfload-tool.rst| in the source directory. -% } -% -% \subsection{Blacklisting Fonts} -% \label{font-blacklist} -% -% Some fonts are problematic in general, or just in \LUATEX. -% If you find that compiling your document takes far too long or eats -% away all your system’s memory, you can track down the culprit by -% running \verb|luaotfload-tool -v| to increase verbosity. -% Take a note of the \emphasis{filename} of the font that database -% creation fails with and append it to the file -% \fileent{luaotfload-blacklist.cnf}. -% -% A blacklist file is a list of font filenames, one per line. -% Specifying the full path to where the file is located is optional, the -% plain filename should suffice. -% File extensions (\fileent{.otf}, \fileent{.ttf}, etc.) may be omitted. -% Anything after a percent (|%|) character until the end of the line -% is ignored, so use this to add comments. -% Place this file to some location where the \identifier{kpse} -% library can find it, e.~g. -% \fileent{texmf-local/tex/luatex/luaotfload} if you are running -% \identifier{\TEX Live},\footnote{% -% You may have to run \verb|mktexlsr| if you created a new file in -% your \fileent{texmf} tree. -% } -% or just leave it in the working directory of your document. -% \identifier{luaotfload} reads all files named -% \fileent{luaotfload-blacklist.cnf} it finds, so the fonts in -% \fileent{./luaotfload-blacklist.cnf} extend the global blacklist. -% -% Furthermore, a filename prepended with a dash character (|-|) is -% removed from the blacklist, causing it to be temporarily whitelisted -% without modifying the global file. -% An example with explicit paths: -% -% \begin{verbatim} -% % example otf-blacklist.cnf -% /Library/Fonts/GillSans.ttc % Luaotfload ignores this font. -% -/Library/Fonts/Optima.ttc % This one is usable again, even if -% % blacklisted somewhere else. -% \end{verbatim} -% -% \section{Files from \CONTEXT and \LUATEX-Fonts} -% -% \identifier{luaotfload} relies on code originally written by Hans -% Hagen\footnote{% -% The creator of the \href{http://wiki.contextgarden.net}{\CONTEXT} -% format. -% } -% for and tested with \CONTEXT. -% It integrates the font loader as distributed in -% the \identifier{\LUATEX-Fonts} package. -% The original \LUA source files have been combined using the -% \fileent{mtx-package} script into a single, self-contained blob. -% In this form the font loader has no further dependencies\footnote{% -% It covers, however, to some extent the functionality of the -% \identifier{lualibs} package. -% } -% and requires only minor adaptions to integrate into -% \identifier{luaotfload}. -% The guiding principle is to let \CONTEXT/\LUATEX-Fonts take care of -% the implementation, and update the imported code from time to time. -% As maintainers, we aim at importing files from upstream essentially -% \emphasis{unmodified}, except for renaming them to prevent name -% clashes. -% This job has been greatly alleviated since the advent of -% \LUATEX-Fonts, prior to which the individual dependencies had to be -% manually spotted and extracted from the \CONTEXT source code in a -% complicated and error-prone fashion. -% -% Below is a commented list of the files distributed with -% \identifier{luaotfload} in one way or the other. -% See figure \ref{file-graph} on page \pageref{file-graph} for a -% graphical representation of the dependencies. -% From \LUATEX-Fonts, only the file \fileent{luatex-fonts-merged.lua} -% has been imported as \fileent{luaotfload-fontloader.lua}. -% It is generated by \fileent{mtx-package}, a \LUA source code merging -% too developed by Hans Hagen.\footnote{% -% \fileent{mtx-package} is -% \href -% {http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua} -% {part of \CONTEXT} -% and requires \fileent{mtxrun}. -% Run -% \verb|mtxrun --script package --help| -% to display further information. -% For the actual merging code see the file -% \fileent{util-mrg.lua} that is part of \CONTEXT. -% } -% It houses several \LUA files that can be classed in three -% categories. -% -% \begin{itemize} -% \let\normalitem=\item -% \def\incitem#1{% -% \normalitem{\fileent{#1}} -% } -% \normalitem \emphasis{\LUA utility libraries}, a subset -% of what is provided by the \identifier{lualibs} -% package. -% -% \begin{multicols}{2} -% \begin{itemize} -% \incitem{l-lua.lua} \incitem{l-lpeg.lua} -% \incitem{l-function.lua} \incitem{l-string.lua} -% \incitem{l-table.lua} \incitem{l-io.lua} -% \incitem{l-file.lua} \incitem{l-boolean.lua} -% \incitem{l-math.lua} \incitem{util-str.lua} -% \end{itemize} -% \end{multicols} -% -% \normalitem The \emphasis{font loader} itself. -% These files have been written for -% \LUATEX-Fonts and they are distributed along -% with \identifier{luaotfload}. -% \begin{multicols}{2} -% \begin{itemize} -% \incitem{luatex-basics-gen.lua} -% \incitem{luatex-basics-nod.lua} -% \incitem{luatex-fonts-enc.lua} -% \incitem{luatex-fonts-syn.lua} -% \incitem{luatex-fonts-tfm.lua} -% \incitem{luatex-fonts-chr.lua} -% \incitem{luatex-fonts-lua.lua} -% \incitem{luatex-fonts-inj.lua} -% \incitem{luatex-fonts-otn.lua} -% \incitem{luatex-fonts-def.lua} -% \incitem{luatex-fonts-ext.lua} -% \incitem{luatex-fonts-cbk.lua} -% \end{itemize} -% \end{multicols} -% -% \normalitem Code related to \emphasis{font handling and -% node processing}, taken directly from -% \CONTEXT. -% \begin{multicols}{2} -% \begin{itemize} -% \incitem{data-con.lua} \incitem{font-ini.lua} -% \incitem{font-con.lua} \incitem{font-cid.lua} -% \incitem{font-map.lua} \incitem{font-oti.lua} -% \incitem{font-otf.lua} \incitem{font-otb.lua} -% \incitem{font-ota.lua} \incitem{font-def.lua} -% \incitem{font-otp.lua} -% \end{itemize} -% \end{multicols} -% \end{itemize} -% -% Note that if \identifier{luaotfload} cannot locate the -% merged file, it will load the individual \LUA libraries -% instead. -% Their names remain the same as in \CONTEXT (without the -% \verb|otfl|-prefix) since we imported the relevant section of -% \fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload-main.lua}. -% Thus if you prefer running bleeding edge code from the -% \CONTEXT beta, all you have to do is remove -% \fileent{luaotfload-merged.lua} from the search path. -% -% Also, the merged file at some point -% loads the Adobe Glyph List from a \LUA table that is contained in -% \fileent{luaotfload-glyphlist.lua}, which is automatically generated by the -% script \fileent{mkglyphlist}.\footnote{% -% See \fileent{luaotfload-font-enc.lua}. -% The hard-coded file name is why we have to replace the procedure -% that loads the file in \fileent{luaotfload-override.lua}. -% } -% There is a make target \identifier{glyphs} that will create a fresh -% glyph list so we don’t need to import it from \CONTEXT -% any longer. -% -% In addition to these, \identifier{luaotfload} requires a number of -% files not contained in the merge. Some of these have no equivalent in -% \LUATEX-Fonts or \CONTEXT, some were taken unmodified from the -% latter. -% -% \begin{itemize} -% \let\normalitem=\item -% \def\ouritem#1{% -% \normalitem{\fileent{#1}}% -% \space--\hskip1em -% } -% \ouritem {luaotfload-features.lua} font feature handling; -% incorporates some of the code from -% \fileent{font-otc} from \CONTEXT; -% \ouritem {luaotfload-override.lua} overrides the \CONTEXT logging -% functionality. -% \ouritem {luaotfload-loaders.lua} registers the \OpenType -% font reader as handler for -% Postscript fonts -% (\abbrev{pfa}, \abbrev{pfb}). -% \ouritem {luaotfload-parsers.lua} various \abbrev{lpeg}-based parsers. -% \ouritem {luaotfload-database.lua} font names database. -% \ouritem {luaotfload-colors.lua} color handling. -% \ouritem {luaotfload-auxiliary.lua} access to internal functionality -% for package authors -% (proposals for additions welcome). -% \ouritem {luaotfload-letterspace.lua} font-based letterspacing. -% \end{itemize} -% -% \begin{figure}[b] -% \caption{Schematic of the files in \identifier{Luaotfload}} -% \includegraphics[width=\textwidth]{filegraph.pdf} -% \label{file-graph} -% \end{figure} -% -% \section{Auxiliary Functions} -% -% With release version 2.2, \identifier{luaotfload} received -% additional functions for package authors to call from outside -% (see the file \fileent{luaotfload-auxiliary.lua} for details). -% The purpose of this addition twofold. -% Firstly, \identifier{luaotfload} failed to provide a stable interface -% to internals in the past which resulted in an unmanageable situation -% of different packages abusing the raw access to font objects by means -% of the \luafunction{patch_font} callback. -% When the structure of the font object changed due to an update, all -% of these imploded and several packages had to be fixed while -% simultaneously providing fallbacks for earlier versions. -% Now the patching is done on the \identifier{luaotfload} side and can -% be adapted with future modifications to font objects without touching -% the packages that depend on it. -% Second, some the capabilities of the font loader and the names -% database are not immediately relevant in \identifier{luaotfload} -% itself but might nevertheless be of great value to package authors or -% end users. -% -% Note that the current interface is not yet set in stone and the -% development team is open to suggestions for improvements or -% additions. -% -% \subsection{Callback Functions} -% -% The \luafunction{patch_font} callback is inserted in the wrapper -% \identifier{luaotfload} provides for the font definition callback -% (see below, page \pageref{define-font}). -% At this place it allows manipulating the font object immediately after -% the font loader is done creating it. -% For a short demonstration of its usefulness, here is a snippet that -% writes an entire font object to the file \fileent{fontdump.lua}: -% -% \begin{quote} -% \begin{verbatim} -% \input luaotfload.sty -% \directlua{ -% local dumpfile = "fontdump.lua" -% local dump_font = function (tfmdata) -% local data = table.serialize(tfmdata) -% io.savedata(dumpfile, data) -% end -% -% luatexbase.add_to_callback( -% "luaotfload.patch_font", -% dump_font, -% "my_private_callbacks.dump_font" -% ) -% } -% \font\dumpme=name:Iwona -% \bye -% \end{verbatim} -% \end{quote} -% -% \emphasis{Beware}: this creates a Lua file of around 150,000 lines of -% code, taking up 3~\abbrev{mb} of disk space. -% By inspecting the output you can get a first impression of how a font -% is structured in \LUATEX’s memory, what elements it is composed of, -% and in what ways it can be rearranged. -% -% \subsubsection{Compatibility with Earlier Versions} -% -% As has been touched on in the preface to this section, the structure -% of the object as returned by the fontloader underwent rather drastic -% changes during different stages of its development, and not all -% packages that made use of font patching have kept up with every one -% of it. -% To ensure compatibility with these as well as older versions of -% some packages, \identifier{luaotfload} sets up copies of or references -% to data in the font table where it used to be located. -% For instance, important parameters like the requested point size, the -% units factor, and the font name have again been made accessible from -% the toplevel of the table even though they were migrated to different -% subtables in the meantime. -% -% \subsubsection{Patches} -% -% These are mostly concerned with establishing compatibility with -% \XETEX. -% -% \begin{itemize} -% \let\normalitem=\item -% \def\ouritem#1{% -% \normalitem{\luafunction{#1}}% -% \hfill\break -% } -% -% \ouritem {set_sscale_dimens} -% Calculate \texmacro{fontdimen}s 10 and 11 to emulate \XETEX. -% -% \ouritem {set_capheight} -% Calculates \texmacro{fontdimen} 8 like \XETEX. -% -% \ouritem {patch_cambria_domh} -% Correct some values of the font \emphasis{Cambria Math}. -% -% \end{itemize} -% -% \subsection{Package Author’s Interface} -% -% As \LUATEX release 1.0 is nearing, the demand for a reliable interface -% for package authors increases. -% -% \subsubsection{Font Properties} -% -% Below functions mostly concern querying the different components of a -% font like for instance the glyphs it contains, or what font features -% are defined for which scripts. -% -% \begin{itemize} -% \let\normalitem=\item -% \def\ouritem#1{% -% \normalitem{\luafunction{#1}}% -% \hfill\break -% } -% -% \ouritem {aux.font_has_glyph (id : int, index : int)} -% Predicate that returns true if the font \luafunction{id} -% has glyph \luafunction{index}. -% -% \ouritem {aux.slot_of_name(name : string)} -% Translates an Adobe Glyph name to the corresponding glyph -% slot. -% -% \ouritem {aux.name_of_slot(slot : int)} -% The inverse of \luafunction{slot_of_name}; note that this -% might be incomplete as multiple glyph names may map to the -% same codepoint, only one of which is returned by -% \luafunction{name_of_slot}. -% -% \ouritem {aux.provides_script(id : int, script : string)} -% Test if a font supports \luafunction{script}. -% -% \ouritem {aux.provides_language(id : int, script : string, language : string)} -% Test if a font defines \luafunction{language} for a given -% \luafunction{script}. -% -% \ouritem {aux.provides_feature(id : int, script : string, -% language : string, feature : string)} -% Test if a font defines \luafunction{feature} for -% \luafunction{language} for a given \luafunction{script}. -% -% \ouritem {aux.get_math_dimension(id : int, dimension : string)} -% Get the dimension \luafunction{dimension} of font \luafunction{id}. -% -% \ouritem {aux.sprint_math_dimension(id : int, dimension : string)} -% Same as \luafunction{get_math_dimension()}, but output the value -% in scaled points at the \TEX end. -% -% \end{itemize} -% -% \subsubsection{Database} -% -% \begin{itemize} -% \let\normalitem=\item -% \def\ouritem#1{% -% \normalitem{\luafunction{#1}}% -% \hfill\break -% } -% -% \ouritem {aux.scan_external_dir(dir : string)} -% Include fonts in directory \luafunction{dir} in font lookups without -% adding them to the database. -% -% \end{itemize} -% -% \section{Troubleshooting} -% -% \subsection {Database Generation} -% If you encounter problems with some fonts, please first update to the latest -% version of this package before reporting a bug, as -% \identifier{luaotfload} is under active development and still a -% moving target. -% The development takes place on \identifier{github} at -% \url{https://github.com/lualatex/luaotfload} where there is an issue -% tracker for submitting bug reports, feature requests and the likes -% requests and the likes. -% -% Bug reports are more likely to be addressed if they contain the output of -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --diagnose=environment,files,permissions -% \end{verbatim} -% \end{quote} -% -% \noindent Consult the man page for a description of these options. -% -% Errors during database generation can be traced by increasing the -% verbosity level and redirecting log output to \fileent{stdout}: -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -fuvvv --log=stdout -% \end{verbatim} -% \end{quote} -% -% \noindent or to a file in \fileent{/tmp}: -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -fuvvv --log=file -% \end{verbatim} -% \end{quote} -% -% \noindent In the latter case, invoke the \verb|tail(1)| utility on the file -% for live monitoring of the progress. -% -% If database generation fails, the font last printed to the terminal or log -% file is likely to be the culprit. -% Please specify it when reporting a bug, and blacklist it for the time -% being (see above, page \pageref{font-blacklist}). -% -% \subsection {Font Features} -% A common problem is the lack of features for some -% \OpenType fonts even when specified. -% This can be related to the fact that some fonts do not provide -% features for the \verb|dflt| script (see above on page -% \pageref{script-tag}), -% which is the default one in this package. -% If this happens, assigning a noth script when the font is defined should -% fix it. -% For example with \verb|latn|: -% -% \begin{quote} -% \begin{verbatim} -% \font\test=file:MyFont.otf:script=latn;+liga; -% \end{verbatim} -% \end{quote} -% -% You can get a list of features that a font defines for scripts and languages -% by querying it in \fileent{luaotfload-tool}: -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --find="Iwona" --inspect -% \end{verbatim} -% \end{quote} -% -% \subsection {\LUATEX Programming} -% Another strategy that helps avoiding problems is to not access raw \LUATEX -% internals directly. -% Some of them, even though they are dangerous to access, have not been -% overridden or disabled. -% Thus, whenever possible prefer the functions in the -% \luafunction{aux} namespace over direct manipulation of font objects. -% For example, raw access to the \luafunction{font.fonts} table like: -% -% \begin{quote} -% \begin{verbatim} -% local somefont = font.fonts[2] -% \end{verbatim} -% \end{quote} -% -% \noindent can render already defined fonts unusable. -% Instead, the function \luafunction{font.getfont()} should be used because -% it has been replaced by a safe variant. -% -% However, \luafunction{font.getfont()} only covers fonts handled by the font -% loader, e.~g. \identifier{OpenType} and \identifier{TrueType} fonts, but -% not \abbrev{tfm} or \abbrev{ofm}. -% Should you absolutely require access to all fonts known to \LUATEX, including -% the virtual and autogenerated ones, then you need to query both -% \luafunction{font.getfont()} and \luafunction{font.fonts}. -% In this case, best define you own accessor: -% -% \begin{quote} -% \begin{verbatim} -% local unsafe_getfont = function (id) -% local tfmdata = font.getfont (id) -% if not tfmdata then -% tfmdata = font.fonts[id] -% end -% return tfmdata -% end -% -% --- use like getfont() -% local somefont = unsafe_getfont (2) -% \end{verbatim} -% \end{quote} -% -% \part{Implementation} -% -% \section{\fileent{luaotfload.lua}} -% -% As of version 2.5, the file \fileent{luaotfload.lua} is no longer -% generated from the \abbrev{dtx}. -% Instead, it is maintained separately as a plain \identifier{Lua} file -% \fileent{luaotfload-main.lua} in the Luaotfload \identifier{git} tree. -% The file documentation which used to be found in this section has -% been preserved in the comments. -% -% \section{\fileent{luaotfload.sty}} -% -% As of version 2.5, the file \fileent{luaotfload.sty} is no longer -% generated from the \abbrev{dtx}. -% Instead, it is maintained separately as a plain \identifier{\TEX} file -% in the Luaotfload \identifier{git} tree. -% The file documentation which used to be found in this section has -% been preserved in the comments. -% -% \clearpage -% \section{The GNU GPL License v2} -% -% The GPL requires the complete license text to be distributed along -% with the code. I recommend the canonical source, instead: -% \url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. -% But if you insist on an included copy, here it is. -% You might want to zoom in. -% -% \newsavebox{\gpl} -% \begin{lrbox}{\gpl} -% \begin{minipage}{3\textwidth} -% \columnsep=3\columnsep -% \begin{multicols}{3} -% \begin{center} -% {\Large GNU GENERAL PUBLIC LICENSE\par} -% \bigskip -% {Version 2, June 1991} -% \end{center} -% -% \begin{center} -% {\parindent 0in -% -% Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. -% -% \bigskip -% -% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -% -% \bigskip -% -% Everyone is permitted to copy and distribute verbatim copies -% of this license document, but changing it is not allowed. -% } -% \end{center} -% -% \begin{center} -% {\bf\large Preamble} -% \end{center} -% -% -% The licenses for most software are designed to take away your freedom to -% share and change it. By contrast, the GNU General Public License is -% intended to guarantee your freedom to share and change free software---to -% make sure the software is free for all its users. This General Public -% License applies to most of the Free Software Foundation's software and to -% any other program whose authors commit to using it. (Some other Free -% Software Foundation software is covered by the GNU Library General Public -% License instead.) You can apply it to your programs, too. -% -% When we speak of free software, we are referring to freedom, not price. -% Our General Public Licenses are designed to make sure that you have the -% freedom to distribute copies of free software (and charge for this service -% if you wish), that you receive source code or can get it if you want it, -% that you can change the software or use pieces of it in new free programs; -% and that you know you can do these things. -% -% To protect your rights, we need to make restrictions that forbid anyone to -% deny you these rights or to ask you to surrender the rights. These -% restrictions translate to certain responsibilities for you if you -% distribute copies of the software, or if you modify it. -% -% For example, if you distribute copies of such a program, whether gratis or -% for a fee, you must give the recipients all the rights that you have. You -% must make sure that they, too, receive or can get the source code. And -% you must show them these terms so they know their rights. -% -% We protect your rights with two steps: (1) copyright the software, and (2) -% offer you this license which gives you legal permission to copy, -% distribute and/or modify the software. -% -% Also, for each author's protection and ours, we want to make certain that -% everyone understands that there is no warranty for this free software. If -% the software is modified by someone else and passed on, we want its -% recipients to know that what they have is not the original, so that any -% problems introduced by others will not reflect on the original authors' -% reputations. -% -% Finally, any free program is threatened constantly by software patents. -% We wish to avoid the danger that redistributors of a free program will -% individually obtain patent licenses, in effect making the program -% proprietary. To prevent this, we have made it clear that any patent must -% be licensed for everyone's free use or not licensed at all. -% -% The precise terms and conditions for copying, distribution and -% modification follow. -% -% \begin{center} -% {\Large \sc Terms and Conditions For Copying, Distribution and -% Modification} -% \end{center} -% -% \begin{enumerate} -% \item -% This License applies to any program or other work which contains a notice -% placed by the copyright holder saying it may be distributed under the -% terms of this General Public License. The ``Program'', below, refers to -% any such program or work, and a ``work based on the Program'' means either -% the Program or any derivative work under copyright law: that is to say, a -% work containing the Program or a portion of it, either verbatim or with -% modifications and/or translated into another language. (Hereinafter, -% translation is included without limitation in the term ``modification''.) -% Each licensee is addressed as ``you''. -% -% Activities other than copying, distribution and modification are not -% covered by this License; they are outside its scope. The act of -% running the Program is not restricted, and the output from the Program -% is covered only if its contents constitute a work based on the -% Program (independent of having been made by running the Program). -% Whether that is true depends on what the Program does. -% -% \item You may copy and distribute verbatim copies of the Program's source -% code as you receive it, in any medium, provided that you conspicuously -% and appropriately publish on each copy an appropriate copyright notice -% and disclaimer of warranty; keep intact all the notices that refer to -% this License and to the absence of any warranty; and give any other -% recipients of the Program a copy of this License along with the Program. -% -% You may charge a fee for the physical act of transferring a copy, and you -% may at your option offer warranty protection in exchange for a fee. -% -% \item -% You may modify your copy or copies of the Program or any portion -% of it, thus forming a work based on the Program, and copy and -% distribute such modifications or work under the terms of Section 1 -% above, provided that you also meet all of these conditions: -% -% \begin{enumerate} -% -% \item -% You must cause the modified files to carry prominent notices stating that -% you changed the files and the date of any change. -% -% \item -% You must cause any work that you distribute or publish, that in -% whole or in part contains or is derived from the Program or any -% part thereof, to be licensed as a whole at no charge to all third -% parties under the terms of this License. -% -% \item -% If the modified program normally reads commands interactively -% when run, you must cause it, when started running for such -% interactive use in the most ordinary way, to print or display an -% announcement including an appropriate copyright notice and a -% notice that there is no warranty (or else, saying that you provide -% a warranty) and that users may redistribute the program under -% these conditions, and telling the user how to view a copy of this -% License. (Exception: if the Program itself is interactive but -% does not normally print such an announcement, your work based on -% the Program is not required to print an announcement.) -% -% \end{enumerate} -% -% -% These requirements apply to the modified work as a whole. If -% identifiable sections of that work are not derived from the Program, -% and can be reasonably considered independent and separate works in -% themselves, then this License, and its terms, do not apply to those -% sections when you distribute them as separate works. But when you -% distribute the same sections as part of a whole which is a work based -% on the Program, the distribution of the whole must be on the terms of -% this License, whose permissions for other licensees extend to the -% entire whole, and thus to each and every part regardless of who wrote it. -% -% Thus, it is not the intent of this section to claim rights or contest -% your rights to work written entirely by you; rather, the intent is to -% exercise the right to control the distribution of derivative or -% collective works based on the Program. -% -% In addition, mere aggregation of another work not based on the Program -% with the Program (or with a work based on the Program) on a volume of -% a storage or distribution medium does not bring the other work under -% the scope of this License. -% -% \item -% You may copy and distribute the Program (or a work based on it, -% under Section 2) in object code or executable form under the terms of -% Sections 1 and 2 above provided that you also do one of the following: -% -% \begin{enumerate} -% -% \item -% -% Accompany it with the complete corresponding machine-readable -% source code, which must be distributed under the terms of Sections -% 1 and 2 above on a medium customarily used for software interchange; or, -% -% \item -% -% Accompany it with a written offer, valid for at least three -% years, to give any third party, for a charge no more than your -% cost of physically performing source distribution, a complete -% machine-readable copy of the corresponding source code, to be -% distributed under the terms of Sections 1 and 2 above on a medium -% customarily used for software interchange; or, -% -% \item -% -% Accompany it with the information you received as to the offer -% to distribute corresponding source code. (This alternative is -% allowed only for noncommercial distribution and only if you -% received the program in object code or executable form with such -% an offer, in accord with Subsection b above.) -% -% \end{enumerate} -% -% -% The source code for a work means the preferred form of the work for -% making modifications to it. For an executable work, complete source -% code means all the source code for all modules it contains, plus any -% associated interface definition files, plus the scripts used to -% control compilation and installation of the executable. However, as a -% special exception, the source code distributed need not include -% anything that is normally distributed (in either source or binary -% form) with the major components (compiler, kernel, and so on) of the -% operating system on which the executable runs, unless that component -% itself accompanies the executable. -% -% If distribution of executable or object code is made by offering -% access to copy from a designated place, then offering equivalent -% access to copy the source code from the same place counts as -% distribution of the source code, even though third parties are not -% compelled to copy the source along with the object code. -% -% \item -% You may not copy, modify, sublicense, or distribute the Program -% except as expressly provided under this License. Any attempt -% otherwise to copy, modify, sublicense or distribute the Program is -% void, and will automatically terminate your rights under this License. -% However, parties who have received copies, or rights, from you under -% this License will not have their licenses terminated so long as such -% parties remain in full compliance. -% -% \item -% You are not required to accept this License, since you have not -% signed it. However, nothing else grants you permission to modify or -% distribute the Program or its derivative works. These actions are -% prohibited by law if you do not accept this License. Therefore, by -% modifying or distributing the Program (or any work based on the -% Program), you indicate your acceptance of this License to do so, and -% all its terms and conditions for copying, distributing or modifying -% the Program or works based on it. -% -% \item -% Each time you redistribute the Program (or any work based on the -% Program), the recipient automatically receives a license from the -% original licensor to copy, distribute or modify the Program subject to -% these terms and conditions. You may not impose any further -% restrictions on the recipients' exercise of the rights granted herein. -% You are not responsible for enforcing compliance by third parties to -% this License. -% -% \item -% If, as a consequence of a court judgment or allegation of patent -% infringement or for any other reason (not limited to patent issues), -% conditions are imposed on you (whether by court order, agreement or -% otherwise) that contradict the conditions of this License, they do not -% excuse you from the conditions of this License. If you cannot -% distribute so as to satisfy simultaneously your obligations under this -% License and any other pertinent obligations, then as a consequence you -% may not distribute the Program at all. For example, if a patent -% license would not permit royalty-free redistribution of the Program by -% all those who receive copies directly or indirectly through you, then -% the only way you could satisfy both it and this License would be to -% refrain entirely from distribution of the Program. -% -% If any portion of this section is held invalid or unenforceable under -% any particular circumstance, the balance of the section is intended to -% apply and the section as a whole is intended to apply in other -% circumstances. -% -% It is not the purpose of this section to induce you to infringe any -% patents or other property right claims or to contest validity of any -% such claims; this section has the sole purpose of protecting the -% integrity of the free software distribution system, which is -% implemented by public license practices. Many people have made -% generous contributions to the wide range of software distributed -% through that system in reliance on consistent application of that -% system; it is up to the author/donor to decide if he or she is willing -% to distribute software through any other system and a licensee cannot -% impose that choice. -% -% This section is intended to make thoroughly clear what is believed to -% be a consequence of the rest of this License. -% -% \item -% If the distribution and/or use of the Program is restricted in -% certain countries either by patents or by copyrighted interfaces, the -% original copyright holder who places the Program under this License -% may add an explicit geographical distribution limitation excluding -% those countries, so that distribution is permitted only in or among -% countries not thus excluded. In such case, this License incorporates -% the limitation as if written in the body of this License. -% -% \item -% The Free Software Foundation may publish revised and/or new versions -% of the General Public License from time to time. Such new versions will -% be similar in spirit to the present version, but may differ in detail to -% address new problems or concerns. -% -% Each version is given a distinguishing version number. If the Program -% specifies a version number of this License which applies to it and ``any -% later version'', you have the option of following the terms and conditions -% either of that version or of any later version published by the Free -% Software Foundation. If the Program does not specify a version number of -% this License, you may choose any version ever published by the Free Software -% Foundation. -% -% \item -% If you wish to incorporate parts of the Program into other free -% programs whose distribution conditions are different, write to the author -% to ask for permission. For software which is copyrighted by the Free -% Software Foundation, write to the Free Software Foundation; we sometimes -% make exceptions for this. Our decision will be guided by the two goals -% of preserving the free status of all derivatives of our free software and -% of promoting the sharing and reuse of software generally. -% -% \begin{center} -% {\Large\sc -% No Warranty -% } -% \end{center} -% -% \item -% {\sc Because the program is licensed free of charge, there is no warranty -% for the program, to the extent permitted by applicable law. Except when -% otherwise stated in writing the copyright holders and/or other parties -% provide the program ``as is'' without warranty of any kind, either expressed -% or implied, including, but not limited to, the implied warranties of -% merchantability and fitness for a particular purpose. The entire risk as -% to the quality and performance of the program is with you. Should the -% program prove defective, you assume the cost of all necessary servicing, -% repair or correction.} -% -% \item -% {\sc In no event unless required by applicable law or agreed to in writing -% will any copyright holder, or any other party who may modify and/or -% redistribute the program as permitted above, be liable to you for damages, -% including any general, special, incidental or consequential damages arising -% out of the use or inability to use the program (including but not limited -% to loss of data or data being rendered inaccurate or losses sustained by -% you or third parties or a failure of the program to operate with any other -% programs), even if such holder or other party has been advised of the -% possibility of such damages.} -% -% \end{enumerate} -% -% -% \begin{center} -% {\Large\sc End of Terms and Conditions} -% \end{center} -% -% -% \pagebreak[2] -% -% \section*{Appendix: How to Apply These Terms to Your New Programs} -% -% If you develop a new program, and you want it to be of the greatest -% possible use to the public, the best way to achieve this is to make it -% free software which everyone can redistribute and change under these -% terms. -% -% To do so, attach the following notices to the program. It is safest to -% attach them to the start of each source file to most effectively convey -% the exclusion of warranty; and each file should have at least the -% ``copyright'' line and a pointer to where the full notice is found. -% -% \begin{quote} -% one line to give the program's name and a brief idea of what it does. \\ -% Copyright (C) yyyy name of author \\ -% -% This program is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% This program is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with this program; if not, write to the Free Software -% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -% \end{quote} -% -% Also add information on how to contact you by electronic and paper mail. -% -% If the program is interactive, make it output a short notice like this -% when it starts in an interactive mode: -% -% \begin{quote} -% Gnomovision version 69, Copyright (C) yyyy name of author \\ -% Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ -% This is free software, and you are welcome to redistribute it -% under certain conditions; type `show c' for details. -% \end{quote} -% -% -% The hypothetical commands {\tt show w} and {\tt show c} should show the -% appropriate parts of the General Public License. Of course, the commands -% you use may be called something other than {\tt show w} and {\tt show c}; -% they could even be mouse-clicks or menu items---whatever suits your -% program. -% -% You should also get your employer (if you work as a programmer) or your -% school, if any, to sign a ``copyright disclaimer'' for the program, if -% necessary. Here is a sample; alter the names: -% -% \begin{quote} -% Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\ -% `Gnomovision' (which makes passes at compilers) written by James Hacker. \\ -% -% signature of Ty Coon, 1 April 1989 \\ -% Ty Coon, President of Vice -% \end{quote} -% -% -% This General Public License does not permit incorporating your program -% into proprietary programs. If your program is a subroutine library, you -% may consider it more useful to permit linking proprietary applications -% with the library. If this is what you want to do, use the GNU Library -% General Public License instead of this License. -% -% \end{multicols} -% \end{minipage} -% \end{lrbox} -% -% \begin{center} -% \scalebox{0.33}{\usebox{\gpl}} -% \end{center} -% -% \Finale -\endinput -- cgit v1.2.3 From 708c689312e75953db431f8838846996ff447789 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 10 Feb 2014 07:14:53 +0100 Subject: [doc] add Makefile for doc subtree --- Makefile | 93 +++++++++++++++++++++++++++++------------------------------- doc/Makefile | 46 ++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 48 deletions(-) create mode 100644 doc/Makefile diff --git a/Makefile b/Makefile index 8405e3a..3ee6dc5 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,7 @@ # Makefile for luaotfload NAME = luaotfload -DOC = $(NAME).pdf -DTX = $(NAME).dtx -OTFL = $(wildcard luaotfload-*.lua) luaotfload-blacklist.cnf +LUAOTFLOAD = $(wildcard luaotfload-*.lua) luaotfload-blacklist.cnf GLYPHSCRIPT = mkglyphlist GLYPHSOURCE = glyphlist.txt @@ -14,36 +12,36 @@ RESOURCESCRIPTS = $(GLYPHSCRIPT) $(CHARSCRIPT) $(STATUSSCRIPT) SCRIPTNAME = luaotfload-tool SCRIPT = $(SCRIPTNAME).lua -MANSOURCE = $(SCRIPTNAME).rst -MANPAGE = $(SCRIPTNAME).1 -OLDSCRIPT = luaotfload-legacy-tool.lua -GRAPH = filegraph -DOTPDF = $(GRAPH).pdf -DOT = $(GRAPH).dot +DOCSRCDIR = ./doc +GRAPH = filegraph +DOCSRC = $(DOCSRCDIR)/$(NAME).dtx +GRAPHSRC = $(DOCSRCDIR)/$(GRAPH).doc +MANSRC = $(DOCSRCDIR)/$(SCRIPTNAME).rst + +DOCPDF = $(DOCSRCDIR)/$(NAME).pdf +DOTPDF = $(DOCSRCDIR)/$(GRAPH).pdf +MANPAGE = $(DOCSRCDIR)/$(SCRIPTNAME).1 + +DOCS = $(DOCPDF) $(DOTPDF) $(MANPAGE) # Files grouped by generation mode GLYPHS = luaotfload-glyphlist.lua CHARS = luaotfload-characters.lua STATUS = luaotfload-status.lua RESOURCES = $(GLYPHS) $(CHARS) $(STATUS) -GRAPHED = $(DOTPDF) -MAN = $(MANPAGE) -COMPILED = $(DOC) -UNPACKED = luaotfload.sty -GENERATED = $(GRAPHED) $(UNPACKED) $(COMPILED) $(RESOURCES) $(MAN) -SOURCE = $(DTX) $(MANSOURCE) $(OTFL) README Makefile NEWS $(RESOURCESCRIPTS) +SOURCE = $(DOCSRC) $(MANSRC) $(LUAOTFLOAD) README Makefile NEWS $(RESOURCESCRIPTS) # Files grouped by installation location SCRIPTSTATUS = $(SCRIPT) $(OLDSCRIPT) $(RESOURCESCRIPTS) -RUNSTATUS = $(UNPACKED) $(filter-out $(SCRIPTSTATUS),$(OTFL)) -DOCSTATUS = $(DOC) $(DOTPDF) README NEWS -MANSTATUS = $(MANPAGE) -SRCSTATUS = $(DTX) Makefile +RUNSTATUS = $(UNPACKED) $(filter-out $(SCRIPTSTATUS),$(LUAOTFLOAD)) +DOCSTATUS = $(DOCPDF) $(DOTPDF) README NEWS +MANSTATUS = $(MANPAGE) +SRCSTATUS = $(DOCSRC) $(MANSRC) $(GRAPHSRC) Makefile # The following definitions should be equivalent # ALL_STATUS = $(RUNSTATUS) $(DOCSTATUS) $(SRCSTATUS) -ALL_STATUS = $(GENERATED) $(SOURCE) +ALL_STATUS = $(RESOURCES) $(SOURCE) # Installation locations FORMAT = luatex @@ -63,20 +61,11 @@ ZIPS = $(CTAN_ZIP) $(TDS_ZIP) LUA = texlua -DO_TEX = luatex --interaction=batchmode $< >/dev/null -DO_LATEXMK = latexmk -e '$$max_repeat = 5' -pdf -lualatex -silent $< >/dev/null -# latexmk does only one run on my machine, so we’re not going to rely on it -DO_LATEX = lualatex -interaction=batchmode $< >/dev/null -DO_GRAPHVIZ = dot -Tpdf -o $@ $< > /dev/null DO_GLYPHS = $(LUA) $(GLYPHSCRIPT) > /dev/null -DO_CHARS = $(LUA) $(CHARSCRIPT) > /dev/null +DO_CHARS = $(LUA) $(CHARSCRIPT) > /dev/null DO_STATUS = $(LUA) $(STATUSSCRIPT) > /dev/null -DO_DOCUTILS = rst2man $< >$@ 2>/dev/null all: $(GENERATED) -graph: $(GRAPHED) -doc: $(GRAPHED) $(COMPILED) $(MAN) -manual: $(MAN) unpack: $(UNPACKED) resources: $(RESOURCES) chars: $(CHARS) @@ -85,6 +74,20 @@ ctan: $(CTAN_ZIP) tds: $(TDS_ZIP) world: all ctan +graph: $(DOTPDF) +doc: $(DOCS) +pdf: $(DOCPDF) +manual: $(MANPAGE) + +$(DOTPDF): + @make -C $(DOCSRCDIR) graph + +$(DOCPDF): + @make -C $(DOCSRCDIR) doc + +$(MANPAGE): + @make -C $(DOCSRCDIR) manual + $(GLYPHS): /dev/null $(DO_GLYPHS) @@ -94,19 +97,6 @@ $(CHARS): /dev/null $(STATUS): /dev/null $(DO_STATUS) -$(GRAPHED): $(DOT) - $(DO_GRAPHVIZ) - -$(COMPILED): $(DTX) - $(DO_LATEX) - $(DO_LATEX) - -$(UNPACKED): $(DTX) - $(DO_TEX) - -$(MAN): $(MANSOURCE) - $(DO_DOCUTILS) - define make-ctandir @$(RM) -rf $(DISTDIR) @mkdir -p $(DISTDIR) && cp $(SOURCE) $(COMPILED) $(DISTDIR) @@ -118,18 +108,22 @@ $(CTAN_ZIP): $(SOURCE) $(COMPILED) $(TDS_ZIP) $(make-ctandir) @zip -r -9 $@ $(TDS_ZIP) $(DISTDIR) >/dev/null -define run-install -@mkdir -p $(SCRIPTDIR) && cp $(SCRIPTSTATUS) $(SCRIPTDIR) -@mkdir -p $(RUNDIR) && cp $(RUNSTATUS) $(RUNDIR) +define run-install-doc @mkdir -p $(DOCDIR) && cp $(DOCSTATUS) $(DOCDIR) @mkdir -p $(SRCDIR) && cp $(SRCSTATUS) $(SRCDIR) @mkdir -p $(MANDIR) && cp $(MANSTATUS) $(MANDIR) endef +define run-install +@mkdir -p $(SCRIPTDIR) && cp $(SCRIPTSTATUS) $(SCRIPTDIR) +@mkdir -p $(RUNDIR) && cp $(RUNSTATUS) $(RUNDIR) +endef + $(TDS_ZIP): TEXMFROOT=./tmp-texmf $(TDS_ZIP): $(ALL_STATUS) @echo "Making TDS-ready archive $@." @$(RM) -- $@ + $(run-install-docs) $(run-install) @cd $(TEXMFROOT) && zip -9 ../$@ -r . >/dev/null @$(RM) -r -- $(TEXMFROOT) @@ -138,19 +132,22 @@ $(TDS_ZIP): $(ALL_STATUS) install: $(ALL_STATUS) @echo "Installing in '$(TEXMFROOT)'." + $(run-install-docs) $(run-install) -manifest: +manifest: @echo "Source files:" @for f in $(SOURCE); do echo $$f; done @echo "" @echo "Derived files:" @for f in $(GENERATED); do echo $$f; done -clean: +clean: + make -C $(DOCSRCDIR) $@ @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out mrproper: clean + make -C $(DOCSRCDIR) $@ @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE) @$(RM) -r -- $(DISTDIR) diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..1ef7243 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,46 @@ +NAME = luaotfload +DOCPDF = $(NAME).pdf +DOCDTX = $(NAME).dtx + +SCRIPTNAME = luaotfload-tool +MANSOURCE = $(SCRIPTNAME).rst +MANPAGE = $(SCRIPTNAME).1 + +GRAPH = filegraph +DOTPDF = $(GRAPH).pdf +DOT = $(GRAPH).dot + +DOCS = $(DOTPDF) $(DOCPDF) $(MANPAGE) + +DO_LATEXMK = @latexmk -e '$$max_repeat = 5' -pdf -lualatex -silent $< >/dev/null +# latexmk does only one run on my machine, so we’re not going to rely on it +DO_LATEX = @lualatex -interaction=batchmode $< >/dev/null +DO_GRAPHVIZ = @dot -Tpdf -o $@ $< > /dev/null +DO_DOCUTILS = @rst2man $< >$@ 2>/dev/null + +doc: graph $(DOCPDF) +all: manual doc +graph: $(DOTPDF) +manual: $(MANPAGE) + +$(DOCPDF): $(DOCDTX) + @echo "creating PDF documentation ($(DOCPDF))" + $(DO_LATEX) + $(DO_LATEX) + +$(MANPAGE): $(MANSOURCE) + @echo "creating man page ($(MANPAGE))" + $(DO_DOCUTILS) + +$(DOTPDF): $(DOT) + @echo "creating file graph ($(DOTPDF))" + $(DO_GRAPHVIZ) + +.PHONY: clean mrproper + +clean: + @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out + +mrproper: clean + @$(RM) -- $(DOCS) + -- cgit v1.2.3 From 06e79fac8810293b5f3e9d03a1c0f838aefb4784 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 10 Feb 2014 08:00:50 +0100 Subject: [doc,*] fix TDS install rules in Makefile --- Makefile | 97 ++++++++++++++++++++++++++++++------------------------------ doc/Makefile | 21 ++++++------- 2 files changed, 60 insertions(+), 58 deletions(-) diff --git a/Makefile b/Makefile index 3ee6dc5..97a733a 100644 --- a/Makefile +++ b/Makefile @@ -1,43 +1,43 @@ # Makefile for luaotfload -NAME = luaotfload -LUAOTFLOAD = $(wildcard luaotfload-*.lua) luaotfload-blacklist.cnf +NAME = luaotfload +LUAOTFLOAD = $(wildcard luaotfload-*.lua) luaotfload-blacklist.cnf -GLYPHSCRIPT = mkglyphlist -GLYPHSOURCE = glyphlist.txt -CHARSCRIPT = mkcharacters -STATUSSCRIPT = mkstatus +GLYPHSCRIPT = mkglyphlist +GLYPHSOURCE = glyphlist.txt +CHARSCRIPT = mkcharacters +STATUSSCRIPT = mkstatus RESOURCESCRIPTS = $(GLYPHSCRIPT) $(CHARSCRIPT) $(STATUSSCRIPT) -SCRIPTNAME = luaotfload-tool -SCRIPT = $(SCRIPTNAME).lua +SCRIPTNAME = luaotfload-tool +SCRIPT = $(SCRIPTNAME).lua -DOCSRCDIR = ./doc -GRAPH = filegraph -DOCSRC = $(DOCSRCDIR)/$(NAME).dtx -GRAPHSRC = $(DOCSRCDIR)/$(GRAPH).doc -MANSRC = $(DOCSRCDIR)/$(SCRIPTNAME).rst +DOCSRCDIR = ./doc +GRAPH = filegraph +DOCSRC = $(DOCSRCDIR)/$(NAME).dtx +GRAPHSRC = $(DOCSRCDIR)/$(GRAPH).dot +MANSRC = $(DOCSRCDIR)/$(SCRIPTNAME).rst -DOCPDF = $(DOCSRCDIR)/$(NAME).pdf -DOTPDF = $(DOCSRCDIR)/$(GRAPH).pdf -MANPAGE = $(DOCSRCDIR)/$(SCRIPTNAME).1 +DOCPDF = $(DOCSRCDIR)/$(NAME).pdf +DOTPDF = $(DOCSRCDIR)/$(GRAPH).pdf +MANPAGE = $(DOCSRCDIR)/$(SCRIPTNAME).1 -DOCS = $(DOCPDF) $(DOTPDF) $(MANPAGE) +DOCS = $(DOCPDF) $(DOTPDF) $(MANPAGE) # Files grouped by generation mode -GLYPHS = luaotfload-glyphlist.lua -CHARS = luaotfload-characters.lua -STATUS = luaotfload-status.lua +GLYPHS = luaotfload-glyphlist.lua +CHARS = luaotfload-characters.lua +STATUS = luaotfload-status.lua RESOURCES = $(GLYPHS) $(CHARS) $(STATUS) -SOURCE = $(DOCSRC) $(MANSRC) $(LUAOTFLOAD) README Makefile NEWS $(RESOURCESCRIPTS) +SOURCE = $(DOCSRC) $(MANSRC) $(LUAOTFLOAD) README Makefile NEWS $(RESOURCESCRIPTS) # Files grouped by installation location -SCRIPTSTATUS = $(SCRIPT) $(OLDSCRIPT) $(RESOURCESCRIPTS) -RUNSTATUS = $(UNPACKED) $(filter-out $(SCRIPTSTATUS),$(LUAOTFLOAD)) -DOCSTATUS = $(DOCPDF) $(DOTPDF) README NEWS -MANSTATUS = $(MANPAGE) -SRCSTATUS = $(DOCSRC) $(MANSRC) $(GRAPHSRC) Makefile +SCRIPTSTATUS = $(SCRIPT) $(OLDSCRIPT) $(RESOURCESCRIPTS) +RUNSTATUS = $(UNPACKED) $(filter-out $(SCRIPTSTATUS),$(LUAOTFLOAD)) +DOCSTATUS = $(DOCPDF) $(DOTPDF) README NEWS +MANSTATUS = $(MANPAGE) +SRCSTATUS = $(DOCSRC) $(MANSRC) $(GRAPHSRC) Makefile # The following definitions should be equivalent # ALL_STATUS = $(RUNSTATUS) $(DOCSTATUS) $(SRCSTATUS) @@ -45,25 +45,25 @@ ALL_STATUS = $(RESOURCES) $(SOURCE) # Installation locations FORMAT = luatex -SCRIPTDIR = $(TEXMFROOT)/scripts/$(NAME) -RUNDIR = $(TEXMFROOT)/tex/$(FORMAT)/$(NAME) -DOCDIR = $(TEXMFROOT)/doc/$(FORMAT)/$(NAME) -MANDIR = $(TEXMFROOT)/doc/man/man1/ -SRCDIR = $(TEXMFROOT)/source/$(FORMAT)/$(NAME) -TEXMFROOT = $(shell kpsewhich --var-value TEXMFHOME) +SCRIPTDIR = $(TEXMFROOT)/scripts/$(NAME) +RUNDIR = $(TEXMFROOT)/tex/$(FORMAT)/$(NAME) +DOCDIR = $(TEXMFROOT)/doc/$(FORMAT)/$(NAME) +MANDIR = $(TEXMFROOT)/doc/man/man1/ +SRCDIR = $(TEXMFROOT)/source/$(FORMAT)/$(NAME) +TEXMFROOT = $(shell kpsewhich --var-value TEXMFHOME) # CTAN-friendly subdirectory for packaging -DISTDIR = ./luaotfload +DISTDIR = ./$(NAME) -CTAN_ZIP = $(NAME).zip -TDS_ZIP = $(NAME).tds.zip -ZIPS = $(CTAN_ZIP) $(TDS_ZIP) +CTAN_ZIP = $(NAME).zip +TDS_ZIP = $(NAME).tds.zip +ZIPS = $(CTAN_ZIP) $(TDS_ZIP) -LUA = texlua +LUA = texlua -DO_GLYPHS = $(LUA) $(GLYPHSCRIPT) > /dev/null -DO_CHARS = $(LUA) $(CHARSCRIPT) > /dev/null -DO_STATUS = $(LUA) $(STATUSSCRIPT) > /dev/null +DO_GLYPHS = $(LUA) $(GLYPHSCRIPT) > /dev/null +DO_CHARS = $(LUA) $(CHARSCRIPT) > /dev/null +DO_STATUS = $(LUA) $(STATUSSCRIPT) > /dev/null all: $(GENERATED) unpack: $(UNPACKED) @@ -102,28 +102,28 @@ define make-ctandir @mkdir -p $(DISTDIR) && cp $(SOURCE) $(COMPILED) $(DISTDIR) endef -$(CTAN_ZIP): $(SOURCE) $(COMPILED) $(TDS_ZIP) +$(CTAN_ZIP): $(DOCS) $(SOURCE) $(COMPILED) $(TDS_ZIP) @echo "Making $@ for CTAN upload." @$(RM) -- $@ $(make-ctandir) @zip -r -9 $@ $(TDS_ZIP) $(DISTDIR) >/dev/null define run-install-doc -@mkdir -p $(DOCDIR) && cp $(DOCSTATUS) $(DOCDIR) -@mkdir -p $(SRCDIR) && cp $(SRCSTATUS) $(SRCDIR) -@mkdir -p $(MANDIR) && cp $(MANSTATUS) $(MANDIR) +@mkdir -p $(DOCDIR) && cp -- $(DOCSTATUS) $(DOCDIR) +@mkdir -p $(SRCDIR) && cp -- $(SRCSTATUS) $(SRCDIR) +@mkdir -p $(MANDIR) && cp -- $(MANSTATUS) $(MANDIR) endef define run-install -@mkdir -p $(SCRIPTDIR) && cp $(SCRIPTSTATUS) $(SCRIPTDIR) -@mkdir -p $(RUNDIR) && cp $(RUNSTATUS) $(RUNDIR) +@mkdir -p $(SCRIPTDIR) && cp -- $(SCRIPTSTATUS) $(SCRIPTDIR) +@mkdir -p $(RUNDIR) && cp -- $(RUNSTATUS) $(RUNDIR) endef $(TDS_ZIP): TEXMFROOT=./tmp-texmf -$(TDS_ZIP): $(ALL_STATUS) +$(TDS_ZIP): $(DOCS) $(ALL_STATUS) @echo "Making TDS-ready archive $@." @$(RM) -- $@ - $(run-install-docs) + $(run-install-doc) $(run-install) @cd $(TEXMFROOT) && zip -9 ../$@ -r . >/dev/null @$(RM) -r -- $(TEXMFROOT) @@ -151,3 +151,4 @@ mrproper: clean @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE) @$(RM) -r -- $(DISTDIR) +# vim:set noexpandtab:tabstop=8:shiftwidth=2 diff --git a/doc/Makefile b/doc/Makefile index 1ef7243..2040f5a 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,20 +1,20 @@ -NAME = luaotfload -DOCPDF = $(NAME).pdf -DOCDTX = $(NAME).dtx +NAME = luaotfload +DOCPDF = $(NAME).pdf +DOCDTX = $(NAME).dtx SCRIPTNAME = luaotfload-tool -MANSOURCE = $(SCRIPTNAME).rst -MANPAGE = $(SCRIPTNAME).1 +MANSOURCE = $(SCRIPTNAME).rst +MANPAGE = $(SCRIPTNAME).1 -GRAPH = filegraph -DOTPDF = $(GRAPH).pdf -DOT = $(GRAPH).dot +GRAPH = filegraph +DOTPDF = $(GRAPH).pdf +DOT = $(GRAPH).dot -DOCS = $(DOTPDF) $(DOCPDF) $(MANPAGE) +DOCS = $(DOTPDF) $(DOCPDF) $(MANPAGE) DO_LATEXMK = @latexmk -e '$$max_repeat = 5' -pdf -lualatex -silent $< >/dev/null # latexmk does only one run on my machine, so we’re not going to rely on it -DO_LATEX = @lualatex -interaction=batchmode $< >/dev/null +DO_LATEX = @lualatex -interaction=batchmode $< >/dev/null DO_GRAPHVIZ = @dot -Tpdf -o $@ $< > /dev/null DO_DOCUTILS = @rst2man $< >$@ 2>/dev/null @@ -44,3 +44,4 @@ clean: mrproper: clean @$(RM) -- $(DOCS) +# vim:set noexpandtab:tabstop=8:shiftwidth=2 -- cgit v1.2.3 From 728f8e0b66b127ceef9aa537939e64e721499aad Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 10 Feb 2014 08:24:08 +0100 Subject: =?UTF-8?q?[*]=20don=E2=80=99t=20hardcode=20name=20of=20gmake=20ex?= =?UTF-8?q?ecutable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 97a733a..3ed893c 100644 --- a/Makefile +++ b/Makefile @@ -80,13 +80,13 @@ pdf: $(DOCPDF) manual: $(MANPAGE) $(DOTPDF): - @make -C $(DOCSRCDIR) graph + @$(MAKE) -C $(DOCSRCDIR) graph $(DOCPDF): - @make -C $(DOCSRCDIR) doc + @$(MAKE) -C $(DOCSRCDIR) doc $(MANPAGE): - @make -C $(DOCSRCDIR) manual + @$(MAKE) -C $(DOCSRCDIR) manual $(GLYPHS): /dev/null $(DO_GLYPHS) @@ -143,11 +143,11 @@ manifest: @for f in $(GENERATED); do echo $$f; done clean: - make -C $(DOCSRCDIR) $@ + $(MAKE) -C $(DOCSRCDIR) $@ @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out mrproper: clean - make -C $(DOCSRCDIR) $@ + $(MAKE) -C $(DOCSRCDIR) $@ @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE) @$(RM) -r -- $(DISTDIR) -- cgit v1.2.3 From c3b803956524b0b5afb873a054556d0cdb89d063 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 10 Feb 2014 21:33:34 +0100 Subject: [db] remove four fonts from blacklist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * remove ``Skia.ttf`` cause the buggy AAT support was removed as of r4647. * remove ``Diablindall.ttf`` is now read fine probably due to lazy loading (no idea if that means that the font actually works, it just stopped making Luatex crap itself in ``fontloader.open()``). * ``LastResort.ttf`` supposedly works now as well. * ``lingoes.ttf`` no longer crashes, cf. http://tracker.luatex.org/view.php?id=826. There’s still a couple fonts left that I’ve never seen and which we might remove from the blocklist as well. Signed-off-by: Philipp Gesang --- luaotfload-blacklist.cnf | 8 -------- 1 file changed, 8 deletions(-) diff --git a/luaotfload-blacklist.cnf b/luaotfload-blacklist.cnf index 5c03dc2..e82669b 100644 --- a/luaotfload-blacklist.cnf +++ b/luaotfload-blacklist.cnf @@ -1,12 +1,4 @@ -% Takes ages to load -LastResort.ttf % a MacOSX font, but also available for free from unicode.org -% Segfaults under LuaTeX 0.76 -lingoes.ttf -% http://tug.org/pipermail/luatex/2013-May/004239.html -Diablindall.ttf spltfgbd.ttf spltfgbi.ttf spltfgit.ttf spltfgrg.ttf -% Buggy Max OS font, see https://github.com/lualatex/luaotfload/issues/139 -Skia.ttf -- cgit v1.2.3 From f874408006c90ba4ecdf96fb84a2a560fb30429f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 10 Feb 2014 21:43:30 +0100 Subject: [db] display name of the blacklist file a font is blacklisted in --- luaotfload-database.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/luaotfload-database.lua b/luaotfload-database.lua index c69fc03..a3f3718 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -1986,13 +1986,16 @@ read_blacklist = function () local whitelist = { } if files and type(files) == "table" then - for _,v in next, files do - for line in iolines(v) do + for _, path in next, files do + for line in iolines (path) do line = stringstrip(line) -- to get rid of lines like " % foo" - local first_chr = stringsub(line, 1, 1) --- faster than find + local first_chr = stringsub(line, 1, 1) if first_chr == "%" or stringis_empty(line) then -- comment or empty line elseif first_chr == "-" then + report ("both", 3, "db", + "Whitelisted file %q via %q.", + line, path) whitelist[#whitelist+1] = stringsub(line, 2, -1) else local cmt = stringfind(line, "%%") @@ -2000,7 +2003,9 @@ read_blacklist = function () line = stringsub(line, 1, cmt - 1) end line = stringstrip(line) - report("log", 2, "db", "Blacklisted file %q.", line) + report ("both", 3, "db", + "Blacklisted file %q via %q.", + line, path) blacklist[#blacklist+1] = line end end -- cgit v1.2.3 From d9825168b0fb7b07e315879584487f9d4d9e9494 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 11 Feb 2014 06:27:04 +0100 Subject: [*] move mkcharacters, mktests, mkglyphlist, mkstatus to separate subdirectory scripts --- mkcharacters | 156 ------------------------------- mkglyphlist | 173 ---------------------------------- mkstatus | 149 ----------------------------- mktests | 260 --------------------------------------------------- scripts/mkcharacters | 156 +++++++++++++++++++++++++++++++ scripts/mkglyphlist | 173 ++++++++++++++++++++++++++++++++++ scripts/mkstatus | 149 +++++++++++++++++++++++++++++ scripts/mktests | 260 +++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 738 insertions(+), 738 deletions(-) delete mode 100755 mkcharacters delete mode 100755 mkglyphlist delete mode 100755 mkstatus delete mode 100755 mktests create mode 100755 scripts/mkcharacters create mode 100755 scripts/mkglyphlist create mode 100755 scripts/mkstatus create mode 100755 scripts/mktests diff --git a/mkcharacters b/mkcharacters deleted file mode 100755 index 5d4a2f4..0000000 --- a/mkcharacters +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env texlua ------------------------------------------------------------------------ --- FILE: mkcharacters.lua --- USAGE: ./mkcharacters.lua --- DESCRIPTION: import parts of char-def.lua --- REQUIREMENTS: lua, ConTeXt, the lualibs package --- AUTHOR: Philipp Gesang (Phg), --- VERSION: 2.4 --- CREATED: 2013-05-17 12:41:39+0200 ------------------------------------------------------------------------ --- we create a stripped-down version of char-def.lua ------------------------------------------------------------------------ - ------------------------------------------------------------------------ --- config ------------------------------------------------------------------------ -local charfile = "./luaotfload-characters.lua" -local chardef = "/home/phg/base/char-def.lua" - ---- for every code point char-def.lua provides a set of fields. they ---- are: ---- ---- * adobename ---- * category ---- * cjkwd ---- * comment ---- * contextname ---- * description ---- * direction ---- * lccode ---- * linebreak ---- * mathclass ---- * mathextensible ---- * mathfiller ---- * mathname ---- * mathspec ---- * mathstretch ---- * mathsymbol ---- * mirror ---- * shcode ---- * specials ---- * textclass ---- * uccode ---- * unicodeslot ---- * variants - -local import = { - "direction", "mirror", --> πολυγλωσσία/uax9 - "category", --> https://gist.github.com/phi-gamma/5812290 - "textclass", --> https://gist.github.com/phi-gamma/6488187 -} - ------------------------------------------------------------------------ --- includes ------------------------------------------------------------------------ - -kpse.set_program_name"luatex" - -for _, lib in next, { "lualibs-lua.lua", - "lualibs-lpeg.lua", - "lualibs-table.lua", } do - local found = assert(kpse.find_file(lib, "lua"), - "Could not locate " .. lib .. ".\n" - .. "Please install the lualibs package.") - require(found) -end - -if not (chardef and lfs.isfile(chardef)) then - --- we could grab the file from contextgarden but as Context is part - --- of TL it’s not worth bothering - chardef = assert(kpse.find_file("char-def.lua", "lua"), - "Could not find ConTeXt.") -end - ------------------------------------------------------------------------ --- functionality ------------------------------------------------------------------------ - -local get_characters = function ( ) - local data - local inchan = io.open(chardef, "r") - if not inchan then - io.write("Could not open file for reading: "..chardef.."\n.") - goto fail - end - data = inchan:read "*all" - inchan:close() - data = loadstring(data) - if data then - data() --> characters.data - data = nil - collectgarbage "collect" - if characters.data and next(characters.data) then - return characters.data - end - io.write "Character table empty.\n" - goto fail - end - io.write(chardef .. " is not a valid Lua file.\n") - ::fail:: - io.write "Emergency exit.\n" - os.exit(1) -end - -local extract_fields_indeed -extract_fields_indeed = function (data, acc, lastidx) - local idx, char = next(data, lastidx) - if idx then - local imported = { } - for i=1, #import do - local field = import[i] - imported[field] = char[field] - end - acc[idx] = imported - return extract_fields_indeed(data, acc, idx) - end - return acc -end - -local extract_fields = function (data) - return extract_fields_indeed(data, {}, nil) -end - -local writedata = function (data) - local outchan = io.open(charfile, "w") - if not outchan then - io.write("Could not open "..charfile.." for writing.\n") - return false - end - outchan:write(data) - outchan:close() - return true -end - -do - local chardata = get_characters() - local stripped = extract_fields(chardata) - local serialized = table.serialize(stripped, true, { - compact = true, - noquotes = true, - hexify = true, --- for consistency with char-def - }) - if writedata(serialized) then - goto done - end - goto fail -end - -::done:: - os.exit(0) - -::fail:: - io.write "Emergency exit.\n" - os.exit(1) - ---- vim:ft=lua:ts=2:et:sw=2 diff --git a/mkglyphlist b/mkglyphlist deleted file mode 100755 index f7a1cb9..0000000 --- a/mkglyphlist +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env texlua ------------------------------------------------------------------------ --- FILE: mkglyphlist.lua --- USAGE: ./mkglyphlist.lua --- DESCRIPTION: part of the luaotfload package --- REQUIREMENTS: lua, lpeg, luasocket, the lualibs package --- AUTHOR: Philipp Gesang (Phg), --- VERSION: 2.4 --- CREATED: 04/23/2013 12:42:17 PM CEST ------------------------------------------------------------------------ --- interesting thread on the Context list: --- http://www.ntg.nl/pipermail/ntg-context/2008/029057.html ------------------------------------------------------------------------ - - ------------------------------------------------------------------------ --- config ------------------------------------------------------------------------ -local glyphfile = "./glyphlist.txt" -local font_age = "./luaotfload-glyphlist.lua" -local glyph_source = "http://partners.adobe.com/public/developer/en/opentype/glyphlist.txt" - ------------------------------------------------------------------------ --- fallbacks ------------------------------------------------------------------------ ---- Hans adds a small list of mappings that are not in the original ---- glyph list but seem to be normalizations of some sort. I trust his ---- experience, so I’ll just include them here. Background: ---- http://www.ntg.nl/pipermail/ntg-context/2013/073089.html - -local fallbacks = { - ["SF10000"]=9484, ["SF20000"]=9492, ["SF30000"]=9488, - ["SF40000"]=9496, ["SF50000"]=9532, ["SF60000"]=9516, - ["SF70000"]=9524, ["SF80000"]=9500, ["SF90000"]=9508, - ["afii208"]=8213, -} - ------------------------------------------------------------------------ --- includes ------------------------------------------------------------------------ -require"lpeg" -require"socket" - -kpse.set_program_name"luatex" -for _, lib in next, { "lualibs-lua.lua", - "lualibs-lpeg.lua", - "lualibs-table.lua", } do - local found = assert(kpse.find_file(lib, "lua"), - "Could not locate " .. lib) - require(found) -end - -local C, Cf, Cg, Ct, P, R = - lpeg.C, lpeg.Cf, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.R - -local http = socket.http - ------------------------------------------------------------------------ --- functionality ------------------------------------------------------------------------ - -local dec_of_hex = function (hex) return tonumber(hex, 16) end - -local separator = P";" -local gartenzaun = P"#" -local eol = P"\n\r" + P"\r\n" + P"\r" + P"\n" -local space = P" " -local alphanum = R("az", "AZ", "09") -local hexdigit = R("af", "AF", "09") -local eof_tag = gartenzaun * P"--end" -local header_line = gartenzaun * (1-eol)^0 * eol -local codepoint = hexdigit^1 -local glyphname = alphanum^1 - -local definition = Cg(C(glyphname) * separator * (C(codepoint)/ dec_of_hex)) - --- With combined glyphs we take only the first - --- value as char-def and font-age do, and skip - --- the rest. - * (space * codepoint)^0 - * eol -local definitions = Cf(Ct"" * definition^1, rawset) - -local p_glyphs = header_line^0 * definitions * eof_tag - -local get_glyphs = function (data) - local res = lpeg.match(p_glyphs, data) - if not res then - print("error: could not parse glyph list") - os.exit(-1) - end - for name, glyph in next, fallbacks do - res[name] = res[name] or glyph - end - return res -end - -local file_header = [==[ -if not modules then modules = { } end modules ["font-age"] = { - version = 2.400, - comment = "part of the luaotfload package", - author = "luaotfload team / mkglyphlist", - copyright = "derived from %s", - original = "Adobe Glyph List, version 2.0, September 20, 2002", - dataonly = true, -} - -if context then - logs.report("fatal error","this module is not for context") - os.exit(-1) -end - ---[[doc-- -Everything below has been autogenerated. Run mkglyphlist to rebuild -luaotfload-glyphlist.lua. ---doc]]-- - -]==] - -local writedata = function (data) - data = table.serialize(data, true) - data = string.format(file_header, glyph_source) .. data - local fh = io.open(font_age, "wb") - if not fh then - print(string.format("error: %s not writable", font_age)) - os.exit(-1) - end - print(string.format("saving %d bytes to %s", #data, font_age)) - fh:write(data) - fh:close() -end - - -local get_raw get_raw = function (retry) - local fh = io.open(glyphfile, "rb") - if fh then - local data = fh:read"*all" - fh:close() - if data then return data end - elseif not retry then --- attempt download - print"info: retrieving glyph list from" - print(glyph_source) - local glyphdata = http.request(glyph_source) - if glyphdata then - local fh = io.open(glyphfile, "wb") - if not fh then - print"error: glyph file not writable" - os.exit(-1) - end - fh:write(glyphdata) - fh:close() - return get_raw(true) - end - print"error: download failed" - os.exit(-1) - end - print("error: could not obtain glyph data from "..glyphfile) - os.exit(-1) -end - -local main = function () - if arg[1] then glyphfile = arg[1] end - if arg[2] then font_age = arg[2] end - - local data = get_raw() - local parsed = get_glyphs(data) - writedata(parsed) - return 0 -end - - -return main() - ---- vim:ft=lua:ts=2:et:sw=2 diff --git a/mkstatus b/mkstatus deleted file mode 100755 index 6e6e375..0000000 --- a/mkstatus +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env texlua ------------------------------------------------------------------------ --- FILE: mkstatus.lua --- USAGE: ./mkstatus.lua --- DESCRIPTION: writes the repository state --- REQUIREMENTS: luatex, the lualibs package --- AUTHOR: Philipp Gesang (Phg), --- VERSION: 1.0 --- CREATED: 2013-07-07 14:01:12+0200 ------------------------------------------------------------------------ --- --- This script generates a list of hashes that serves as the input --- for the file integrity check (option --diagnose). md5 is all we can --- assume in Luatex, so it’s really only a superficial test. - -kpse.set_program_name "luatex" - -local md5 = require "md5" - -require "lualibs" - -local stringformat = string.format -local md5sumhexa = md5.sumhexa -local ioloaddata = io.loaddata -local iosavedata = io.savedata -local iopopen = io.popen - ------------------------------------------------------------------------ --- settings ------------------------------------------------------------------------ - -local filelist = "luaotfload-status.lua" --- result - -local names = { - --- only the runtime files and scripts - "luaotfload-auxiliary.lua", - "luaotfload-basics-gen.lua", - "luaotfload-basics-nod.lua", - "luaotfload-characters.lua", - "luaotfload-colors.lua", - "luaotfload-database.lua", - "luaotfload-diagnostics.lua", - "luaotfload-features.lua", - "luaotfload-fonts-cbk.lua", - "luaotfload-fonts-def.lua", - "luaotfload-fonts-enc.lua", - "luaotfload-fonts-ext.lua", - "luaotfload-fonts-lua.lua", - "luaotfload-fonts-tfm.lua", - "luaotfload-glyphlist.lua", - "luaotfload-letterspace.lua", - "luaotfload-loaders.lua", - "luaotfload-log.lua", - "luaotfload-main.lua", - "luaotfload-fontloader.lua", - "luaotfload-override.lua", - "luaotfload-parsers.lua", - "luaotfload-tool.lua", - "mkcharacters", - "mkglyphlist", - "mkstatus", -} - ------------------------------------------------------------------------ --- helpers ------------------------------------------------------------------------ - -local die = function (...) - io.stderr:write "[fatal error]: " - io.stderr:write (stringformat (...)) - io.stderr:write "\naborting.\n" - os.exit (1) -end - -local gitcmd = "git log -1 \z - --format=\"return {\z - %n revision = [[%H]],\z - %n timestamp = [[%cd]],\z - %n committer = [[%cn <%ce>]],\z - %n}\" \z - --date=iso" - -local git_info = function () - --io.write (gitcmd) - --io.write "\n" - local chan = iopopen (gitcmd) - if not chan then - die ("this script needs to be run inside \z - the luaotfload git repository") - end - - local data = chan:read "*all" - chan:close () - if data and type (data) == "string" and data ~= "" then - data = load (data) - if not data then - die "cannot parse git information" - end - return data () - end - die "cannot read from pipe" -end - ------------------------------------------------------------------------ --- functionality ------------------------------------------------------------------------ - -local hash_file = function (fname) - if not lfs.isfile (fname) then - die ("cannot find %s.", fname) - end - local raw = ioloaddata (fname) - if not raw then - die ("cannot read from %s.", fname) - end - return md5sumhexa (raw) -end - -local hash_all -hash_all = function (list, acc) - if list == nil then - return hash_all (table.fastcopy (names), { }) - end - - local fname = list[#list] - list[#list] = nil - if fname then - local sum = hash_file (fname) - acc[#acc+1] = { fname, sum } - return hash_all (list, acc) - end - return acc -end - -local main = function () - local hashes = hash_all () - local notes = git_info () - local serialized = table.serialize ({ notes = notes, - hashes = hashes }, true) - local success = io.savedata (filelist, serialized) - if success == false then - die ("could not write to %s.", filelist) - end - return 0 -end - -return main () - ---- vim:ft=lua:ts=2:et:sw=2 diff --git a/mktests b/mktests deleted file mode 100755 index 0bf3f64..0000000 --- a/mktests +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env texlua ------------------------------------------------------------------------ --- FILE: mktests --- USAGE: ./mktests --- DESCRIPTION: test the behavior of Luaotfload --- REQUIREMENTS: Luatex > 0.76, Luaotfload --- AUTHOR: Philipp Gesang (Phg), --- VERSION: 2.4 --- MODIFIED: 2013-08-26 09:31:22+0200 ------------------------------------------------------------------------ --- ---===================================================================-- --- NOTE --- this is a stub, to be completed long-term --- suggestions welcome ---===================================================================-- - - -local tests = { } - -config = { luaotfload = { - names_dir = "names", - cache_dir = "fonts", - index_file = "luaotfload-names.lua", - resolver = "normal", - update_live = true, --- suppress db updates -}} - -kpse.set_program_name "luatex" - -require "lualibs" -require "luaotfload-basics-gen.lua" -require "luaotfload-log.lua" -require "luaotfload-parsers" -require "luaotfload-database" - -local names = fonts.names - ------------------------------------------------------------------------ ---- helper functions ------------------------------------------------------------------------ - -local pprint_resolve = function (input, output, result) - texio.write_nl (string.format ("[%s] “%s” -> “%s”", - result == true and "passed" or "failed", - input, - output)) -end - -local pprint_result = function (name, failed, total) - if failed == 0 then - texio.write_nl (string.format ("[%s] all %d passed", name, total)) - else - texio.write_nl (string.format ("[%s] %d of %d failed", - name, - failed, - total)) - end -end - -local pprint_spec = function (spec) - return string.format ("%s/%s*%.2fpt", - spec.specification, - spec.style or "regular", - spec.optsize or 0) -end - ------------------------------------------------------------------------ ---- tool tests ------------------------------------------------------------------------ - - - ------------------------------------------------------------------------ ---- font tests ------------------------------------------------------------------------ - ---- test sets - -local infer_regular_style = { - --- inferring which one is the correct style for “regular”; can be - --- obscured by synonyms like “book” etc. - { "Iwona", "Iwona-Regular.otf" }, -- trivial case - { "DejaVu Serif", "DejaVuSerif.ttf" }, - { "DejaVu Sans", "DejaVuSans.ttf" }, - { "Adobe Garamond Pro", "agaramondpro_regular.otf" }, - { "Garamond Premier Pro", "GaramondPremrPro.otf" }, - { "CMU Serif", "cmunrm.otf" }, - { "CMU Sans Serif", "cmunss.otf" }, - { "Minion Pro", "MinionPro-Regular.otf" }, - --- Below test will succeed only if we match for the - --- splainname (= sanitized tfmdata.fullname) field - --- explicitly. - { "Minion Pro Italic", "MinionPro-It.otf" }, -} - -local choose_optical_size = { - { { name = "Latin Modern Roman", optsize = 1 }, "lmroman5-regular.otf" }, - { { name = "Latin Modern Roman", optsize = 10 }, "lmroman10-regular.otf" }, - { { name = "Latin Modern Roman", optsize = 12 }, "lmroman12-regular.otf" }, - { { name = "Latin Modern Roman", optsize = 42 }, "lmroman17-regular.otf" }, - { { name = "EB Garamond", optsize = 1 }, "EBGaramond08-Regular.otf" }, - { { name = "EB Garamond", optsize = 8 }, "EBGaramond08-Regular.otf" }, - { { name = "EB Garamond", optsize = 12 }, "EBGaramond12-Regular.otf" }, - { { name = "EB Garamond", optsize = 42 }, "EBGaramond12-Regular.otf" }, - { { name = "Garamond Premier Pro", optsize = 1 }, "GaramondPremrPro-Capt.otf" }, - { { name = "Garamond Premier Pro", optsize = 10 }, "GaramondPremrPro.otf" }, - { { name = "Garamond Premier Pro", optsize = 15 }, "GaramondPremrPro-Subh.otf" }, - { { name = "Garamond Premier Pro", optsize = 42 }, "GaramondPremrPro-Disp.otf" }, -} - -local choose_style = { - { { name = "DejaVu Sans", style = "regular" }, "DejaVuSans.ttf" }, - { { name = "DejaVu Sans", style = "italic" }, "DejaVuSans-Oblique.ttf" }, - { { name = "DejaVu Sans", style = "bold" }, "DejaVuSans-Bold.ttf" }, - { { name = "DejaVu Sans", style = "bolditalic" }, "DejaVuSans-BoldOblique.ttf" }, - { { name = "Linux Libertine O", style = "regular" }, "LinLibertine_R.otf" }, - { { name = "Linux Libertine O", style = "italic" }, "LinLibertine_RI.otf" }, - { { name = "Linux Libertine O", style = "bold" }, "LinLibertine_RB.otf" }, - { { name = "Linux Libertine O", style = "bolditalic" }, "LinLibertine_RBI.otf" }, - { { name = "Liberation Serif", style = "regular" }, "LiberationSerif-Regular.ttf" }, - { { name = "Liberation Serif", style = "italic" }, "LiberationSerif-Italic.ttf" }, - { { name = "Liberation Serif", style = "bold" }, "LiberationSerif-Bold.ttf" }, - { { name = "Liberation Serif", style = "bolditalic" }, "LiberationSerif-BoldItalic.ttf" }, - { { name = "CMU Sans Serif", style = "regular" }, "cmunss.otf" }, -- no “regular” but “medium” - { { name = "CMU Sans Serif", style = "italic" }, "cmunsi.otf" }, -- no “italic” but “oblique” - { { name = "CMU Sans Serif", style = "bold" }, "cmunsx.otf" }, - { { name = "CMU Sans Serif", style = "bolditalic" }, "cmunso.otf" }, - --[[-- - Minion Pro Italic is exceptionally weird regarding identifiers in - that the postscript fontname and both info.fontname and - info.fullname are given as “minionproit”. Now its english fullname - (field 18) is “minionproital”. Only the value “fullname” in the root of - the tfmdata structure (not the one returned by fontloader.info()!) - accurately yields “Minion Pro Italic”. - - To complete the picture, the file naming isn’t very consistent either: - we find the suffixes “Regular” and “Bold”, but “It” and “BoldIt”. What - the hell were the designers smoking? - - Also, the full Minion Pro set comes with different optical sizes which - for monetary reasons cannot considered here. - --]]-- - { { name = "Minion Pro", style = "regular" }, "MinionPro-Regular.otf" }, - { { name = "Minion Pro", style = "italic" }, "MinionPro-It.otf" }, - { { name = "Minion Pro", style = "bold" }, "MinionPro-Bold.otf" }, - { { name = "Minion Pro", style = "bolditalic" }, "MinionPro-BoldIt.otf" }, -} - ---- this needs a database built with --formats=+pfa,pfb,afm - -local resolve_t1_font = { - { { name = "URW Gothic L", style = "regular" }, "a010013l.pfb" }, --> “book” --- { { name = "URW Gothic L", style = "italic" }, "a010033l.pfb" }, --> “book oblique” --- { { name = "URW Gothic L", style = "bold" }, "a010015l.pfb" }, --> “demi” --- { { name = "URW Gothic L", style = "bolditalic" }, "a010035l.pfb" }, --> “demi oblique” - { { name = "Century Schoolbook L", style = "regular" }, "c059013l.pfb" }, - { { name = "Century Schoolbook L", style = "italic" }, "c059033l.pfb" }, - { { name = "Century Schoolbook L", style = "bold" }, "c059016l.pfb" }, - { { name = "Century Schoolbook L", style = "bolditalic" }, "c059036l.pfb" }, - { { name = "Nimbus Roman No9 L", style = "regular" }, "n021003l.pfb" }, - { { name = "Nimbus Roman No9 L", style = "italic" }, "n021023l.pfb" }, - { { name = "Nimbus Roman No9 L", style = "bold" }, "n021004l.pfb" }, --- medium, actually - { { name = "Nimbus Roman No9 L", style = "bolditalic" }, "n021024l.pfb" }, -} - -local translate_style = { - regular = "r", - italic = "i", - bold = "b", - bolditalic = "bi", -} - -local font_name_tests = { - infer_regular_style, - choose_optical_size, - choose_style, - resolve_t1_font, -} - -local default_spec = { - name = false, - lookup = "name", - specification = false, - optsize = 0, -} - -local resolve_font_name = function () - local failed, total = 0, 0 - local resolve_name = names.resolve_name - for nset = 1, #font_name_tests do - local set = font_name_tests[nset] - - for ntest = 1, #set do - local test = set[ntest] - local input, output = test[1], test[2] - - if type (input) == "string" then - local input_spec = table.copy (default_spec) - input_spec.name = input - input_spec.specification = input_spec.lookup .. ":" .. input - local result = resolve_name (input_spec) == output - total = total + 1 - if not result then - failed = failed + 1 - end - pprint_resolve (input, output, result) - - else - local input_spec, output = test[1], test[2] - input_spec.specification = (input_spec.lookup - or default_spec.lookup) - .. ":" .. input_spec.name - input_spec.optsize = input_spec.optsize or default_spec.optsize - input_spec.style = translate_style [input_spec.style] - local result = resolve_name (input_spec) == output - total = total + 1 - if not result then - failed = failed + 1 - end - pprint_resolve (pprint_spec (input_spec), output, result) - end - - end - end - return failed, total -end - -tests ["resolve_font_name"] = resolve_font_name - ------------------------------------------------------------------------ ---- runner ------------------------------------------------------------------------ - -local main = function () - local failed, total = 0, 0 - for name, test in next, tests do - texio.write_nl ("[" .. name .. "]") - local newfailed, newtotal = test () - total = total + 1 - pprint_result (name, newfailed, newtotal) - failed = failed + newfailed - total = total + newtotal - end - - if failed == 0 then - texio.write_nl (string.format ("[report] all %d tests passed.", total)) - else - texio.write_nl (string.format ("[report] %d of %d tests failed (%d %%).", - failed, - total, - failed / total * 100)) - end - texio.write_nl "" - os.exit (0) -end - -return main () - ---- vim:ft=lua:ts=2:et:sw=2 diff --git a/scripts/mkcharacters b/scripts/mkcharacters new file mode 100755 index 0000000..5d4a2f4 --- /dev/null +++ b/scripts/mkcharacters @@ -0,0 +1,156 @@ +#!/usr/bin/env texlua +----------------------------------------------------------------------- +-- FILE: mkcharacters.lua +-- USAGE: ./mkcharacters.lua +-- DESCRIPTION: import parts of char-def.lua +-- REQUIREMENTS: lua, ConTeXt, the lualibs package +-- AUTHOR: Philipp Gesang (Phg), +-- VERSION: 2.4 +-- CREATED: 2013-05-17 12:41:39+0200 +----------------------------------------------------------------------- +-- we create a stripped-down version of char-def.lua +----------------------------------------------------------------------- + +----------------------------------------------------------------------- +-- config +----------------------------------------------------------------------- +local charfile = "./luaotfload-characters.lua" +local chardef = "/home/phg/base/char-def.lua" + +--- for every code point char-def.lua provides a set of fields. they +--- are: +--- +--- * adobename +--- * category +--- * cjkwd +--- * comment +--- * contextname +--- * description +--- * direction +--- * lccode +--- * linebreak +--- * mathclass +--- * mathextensible +--- * mathfiller +--- * mathname +--- * mathspec +--- * mathstretch +--- * mathsymbol +--- * mirror +--- * shcode +--- * specials +--- * textclass +--- * uccode +--- * unicodeslot +--- * variants + +local import = { + "direction", "mirror", --> πολυγλωσσία/uax9 + "category", --> https://gist.github.com/phi-gamma/5812290 + "textclass", --> https://gist.github.com/phi-gamma/6488187 +} + +----------------------------------------------------------------------- +-- includes +----------------------------------------------------------------------- + +kpse.set_program_name"luatex" + +for _, lib in next, { "lualibs-lua.lua", + "lualibs-lpeg.lua", + "lualibs-table.lua", } do + local found = assert(kpse.find_file(lib, "lua"), + "Could not locate " .. lib .. ".\n" + .. "Please install the lualibs package.") + require(found) +end + +if not (chardef and lfs.isfile(chardef)) then + --- we could grab the file from contextgarden but as Context is part + --- of TL it’s not worth bothering + chardef = assert(kpse.find_file("char-def.lua", "lua"), + "Could not find ConTeXt.") +end + +----------------------------------------------------------------------- +-- functionality +----------------------------------------------------------------------- + +local get_characters = function ( ) + local data + local inchan = io.open(chardef, "r") + if not inchan then + io.write("Could not open file for reading: "..chardef.."\n.") + goto fail + end + data = inchan:read "*all" + inchan:close() + data = loadstring(data) + if data then + data() --> characters.data + data = nil + collectgarbage "collect" + if characters.data and next(characters.data) then + return characters.data + end + io.write "Character table empty.\n" + goto fail + end + io.write(chardef .. " is not a valid Lua file.\n") + ::fail:: + io.write "Emergency exit.\n" + os.exit(1) +end + +local extract_fields_indeed +extract_fields_indeed = function (data, acc, lastidx) + local idx, char = next(data, lastidx) + if idx then + local imported = { } + for i=1, #import do + local field = import[i] + imported[field] = char[field] + end + acc[idx] = imported + return extract_fields_indeed(data, acc, idx) + end + return acc +end + +local extract_fields = function (data) + return extract_fields_indeed(data, {}, nil) +end + +local writedata = function (data) + local outchan = io.open(charfile, "w") + if not outchan then + io.write("Could not open "..charfile.." for writing.\n") + return false + end + outchan:write(data) + outchan:close() + return true +end + +do + local chardata = get_characters() + local stripped = extract_fields(chardata) + local serialized = table.serialize(stripped, true, { + compact = true, + noquotes = true, + hexify = true, --- for consistency with char-def + }) + if writedata(serialized) then + goto done + end + goto fail +end + +::done:: + os.exit(0) + +::fail:: + io.write "Emergency exit.\n" + os.exit(1) + +--- vim:ft=lua:ts=2:et:sw=2 diff --git a/scripts/mkglyphlist b/scripts/mkglyphlist new file mode 100755 index 0000000..f7a1cb9 --- /dev/null +++ b/scripts/mkglyphlist @@ -0,0 +1,173 @@ +#!/usr/bin/env texlua +----------------------------------------------------------------------- +-- FILE: mkglyphlist.lua +-- USAGE: ./mkglyphlist.lua +-- DESCRIPTION: part of the luaotfload package +-- REQUIREMENTS: lua, lpeg, luasocket, the lualibs package +-- AUTHOR: Philipp Gesang (Phg), +-- VERSION: 2.4 +-- CREATED: 04/23/2013 12:42:17 PM CEST +----------------------------------------------------------------------- +-- interesting thread on the Context list: +-- http://www.ntg.nl/pipermail/ntg-context/2008/029057.html +----------------------------------------------------------------------- + + +----------------------------------------------------------------------- +-- config +----------------------------------------------------------------------- +local glyphfile = "./glyphlist.txt" +local font_age = "./luaotfload-glyphlist.lua" +local glyph_source = "http://partners.adobe.com/public/developer/en/opentype/glyphlist.txt" + +----------------------------------------------------------------------- +-- fallbacks +----------------------------------------------------------------------- +--- Hans adds a small list of mappings that are not in the original +--- glyph list but seem to be normalizations of some sort. I trust his +--- experience, so I’ll just include them here. Background: +--- http://www.ntg.nl/pipermail/ntg-context/2013/073089.html + +local fallbacks = { + ["SF10000"]=9484, ["SF20000"]=9492, ["SF30000"]=9488, + ["SF40000"]=9496, ["SF50000"]=9532, ["SF60000"]=9516, + ["SF70000"]=9524, ["SF80000"]=9500, ["SF90000"]=9508, + ["afii208"]=8213, +} + +----------------------------------------------------------------------- +-- includes +----------------------------------------------------------------------- +require"lpeg" +require"socket" + +kpse.set_program_name"luatex" +for _, lib in next, { "lualibs-lua.lua", + "lualibs-lpeg.lua", + "lualibs-table.lua", } do + local found = assert(kpse.find_file(lib, "lua"), + "Could not locate " .. lib) + require(found) +end + +local C, Cf, Cg, Ct, P, R = + lpeg.C, lpeg.Cf, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.R + +local http = socket.http + +----------------------------------------------------------------------- +-- functionality +----------------------------------------------------------------------- + +local dec_of_hex = function (hex) return tonumber(hex, 16) end + +local separator = P";" +local gartenzaun = P"#" +local eol = P"\n\r" + P"\r\n" + P"\r" + P"\n" +local space = P" " +local alphanum = R("az", "AZ", "09") +local hexdigit = R("af", "AF", "09") +local eof_tag = gartenzaun * P"--end" +local header_line = gartenzaun * (1-eol)^0 * eol +local codepoint = hexdigit^1 +local glyphname = alphanum^1 + +local definition = Cg(C(glyphname) * separator * (C(codepoint)/ dec_of_hex)) + --- With combined glyphs we take only the first + --- value as char-def and font-age do, and skip + --- the rest. + * (space * codepoint)^0 + * eol +local definitions = Cf(Ct"" * definition^1, rawset) + +local p_glyphs = header_line^0 * definitions * eof_tag + +local get_glyphs = function (data) + local res = lpeg.match(p_glyphs, data) + if not res then + print("error: could not parse glyph list") + os.exit(-1) + end + for name, glyph in next, fallbacks do + res[name] = res[name] or glyph + end + return res +end + +local file_header = [==[ +if not modules then modules = { } end modules ["font-age"] = { + version = 2.400, + comment = "part of the luaotfload package", + author = "luaotfload team / mkglyphlist", + copyright = "derived from %s", + original = "Adobe Glyph List, version 2.0, September 20, 2002", + dataonly = true, +} + +if context then + logs.report("fatal error","this module is not for context") + os.exit(-1) +end + +--[[doc-- +Everything below has been autogenerated. Run mkglyphlist to rebuild +luaotfload-glyphlist.lua. +--doc]]-- + +]==] + +local writedata = function (data) + data = table.serialize(data, true) + data = string.format(file_header, glyph_source) .. data + local fh = io.open(font_age, "wb") + if not fh then + print(string.format("error: %s not writable", font_age)) + os.exit(-1) + end + print(string.format("saving %d bytes to %s", #data, font_age)) + fh:write(data) + fh:close() +end + + +local get_raw get_raw = function (retry) + local fh = io.open(glyphfile, "rb") + if fh then + local data = fh:read"*all" + fh:close() + if data then return data end + elseif not retry then --- attempt download + print"info: retrieving glyph list from" + print(glyph_source) + local glyphdata = http.request(glyph_source) + if glyphdata then + local fh = io.open(glyphfile, "wb") + if not fh then + print"error: glyph file not writable" + os.exit(-1) + end + fh:write(glyphdata) + fh:close() + return get_raw(true) + end + print"error: download failed" + os.exit(-1) + end + print("error: could not obtain glyph data from "..glyphfile) + os.exit(-1) +end + +local main = function () + if arg[1] then glyphfile = arg[1] end + if arg[2] then font_age = arg[2] end + + local data = get_raw() + local parsed = get_glyphs(data) + writedata(parsed) + return 0 +end + + +return main() + +--- vim:ft=lua:ts=2:et:sw=2 diff --git a/scripts/mkstatus b/scripts/mkstatus new file mode 100755 index 0000000..6e6e375 --- /dev/null +++ b/scripts/mkstatus @@ -0,0 +1,149 @@ +#!/usr/bin/env texlua +----------------------------------------------------------------------- +-- FILE: mkstatus.lua +-- USAGE: ./mkstatus.lua +-- DESCRIPTION: writes the repository state +-- REQUIREMENTS: luatex, the lualibs package +-- AUTHOR: Philipp Gesang (Phg), +-- VERSION: 1.0 +-- CREATED: 2013-07-07 14:01:12+0200 +----------------------------------------------------------------------- +-- +-- This script generates a list of hashes that serves as the input +-- for the file integrity check (option --diagnose). md5 is all we can +-- assume in Luatex, so it’s really only a superficial test. + +kpse.set_program_name "luatex" + +local md5 = require "md5" + +require "lualibs" + +local stringformat = string.format +local md5sumhexa = md5.sumhexa +local ioloaddata = io.loaddata +local iosavedata = io.savedata +local iopopen = io.popen + +----------------------------------------------------------------------- +-- settings +----------------------------------------------------------------------- + +local filelist = "luaotfload-status.lua" --- result + +local names = { + --- only the runtime files and scripts + "luaotfload-auxiliary.lua", + "luaotfload-basics-gen.lua", + "luaotfload-basics-nod.lua", + "luaotfload-characters.lua", + "luaotfload-colors.lua", + "luaotfload-database.lua", + "luaotfload-diagnostics.lua", + "luaotfload-features.lua", + "luaotfload-fonts-cbk.lua", + "luaotfload-fonts-def.lua", + "luaotfload-fonts-enc.lua", + "luaotfload-fonts-ext.lua", + "luaotfload-fonts-lua.lua", + "luaotfload-fonts-tfm.lua", + "luaotfload-glyphlist.lua", + "luaotfload-letterspace.lua", + "luaotfload-loaders.lua", + "luaotfload-log.lua", + "luaotfload-main.lua", + "luaotfload-fontloader.lua", + "luaotfload-override.lua", + "luaotfload-parsers.lua", + "luaotfload-tool.lua", + "mkcharacters", + "mkglyphlist", + "mkstatus", +} + +----------------------------------------------------------------------- +-- helpers +----------------------------------------------------------------------- + +local die = function (...) + io.stderr:write "[fatal error]: " + io.stderr:write (stringformat (...)) + io.stderr:write "\naborting.\n" + os.exit (1) +end + +local gitcmd = "git log -1 \z + --format=\"return {\z + %n revision = [[%H]],\z + %n timestamp = [[%cd]],\z + %n committer = [[%cn <%ce>]],\z + %n}\" \z + --date=iso" + +local git_info = function () + --io.write (gitcmd) + --io.write "\n" + local chan = iopopen (gitcmd) + if not chan then + die ("this script needs to be run inside \z + the luaotfload git repository") + end + + local data = chan:read "*all" + chan:close () + if data and type (data) == "string" and data ~= "" then + data = load (data) + if not data then + die "cannot parse git information" + end + return data () + end + die "cannot read from pipe" +end + +----------------------------------------------------------------------- +-- functionality +----------------------------------------------------------------------- + +local hash_file = function (fname) + if not lfs.isfile (fname) then + die ("cannot find %s.", fname) + end + local raw = ioloaddata (fname) + if not raw then + die ("cannot read from %s.", fname) + end + return md5sumhexa (raw) +end + +local hash_all +hash_all = function (list, acc) + if list == nil then + return hash_all (table.fastcopy (names), { }) + end + + local fname = list[#list] + list[#list] = nil + if fname then + local sum = hash_file (fname) + acc[#acc+1] = { fname, sum } + return hash_all (list, acc) + end + return acc +end + +local main = function () + local hashes = hash_all () + local notes = git_info () + local serialized = table.serialize ({ notes = notes, + hashes = hashes }, true) + local success = io.savedata (filelist, serialized) + if success == false then + die ("could not write to %s.", filelist) + end + return 0 +end + +return main () + +--- vim:ft=lua:ts=2:et:sw=2 diff --git a/scripts/mktests b/scripts/mktests new file mode 100755 index 0000000..0bf3f64 --- /dev/null +++ b/scripts/mktests @@ -0,0 +1,260 @@ +#!/usr/bin/env texlua +----------------------------------------------------------------------- +-- FILE: mktests +-- USAGE: ./mktests +-- DESCRIPTION: test the behavior of Luaotfload +-- REQUIREMENTS: Luatex > 0.76, Luaotfload +-- AUTHOR: Philipp Gesang (Phg), +-- VERSION: 2.4 +-- MODIFIED: 2013-08-26 09:31:22+0200 +----------------------------------------------------------------------- +-- +--===================================================================-- +-- NOTE +-- this is a stub, to be completed long-term +-- suggestions welcome +--===================================================================-- + + +local tests = { } + +config = { luaotfload = { + names_dir = "names", + cache_dir = "fonts", + index_file = "luaotfload-names.lua", + resolver = "normal", + update_live = true, --- suppress db updates +}} + +kpse.set_program_name "luatex" + +require "lualibs" +require "luaotfload-basics-gen.lua" +require "luaotfload-log.lua" +require "luaotfload-parsers" +require "luaotfload-database" + +local names = fonts.names + +----------------------------------------------------------------------- +--- helper functions +----------------------------------------------------------------------- + +local pprint_resolve = function (input, output, result) + texio.write_nl (string.format ("[%s] “%s” -> “%s”", + result == true and "passed" or "failed", + input, + output)) +end + +local pprint_result = function (name, failed, total) + if failed == 0 then + texio.write_nl (string.format ("[%s] all %d passed", name, total)) + else + texio.write_nl (string.format ("[%s] %d of %d failed", + name, + failed, + total)) + end +end + +local pprint_spec = function (spec) + return string.format ("%s/%s*%.2fpt", + spec.specification, + spec.style or "regular", + spec.optsize or 0) +end + +----------------------------------------------------------------------- +--- tool tests +----------------------------------------------------------------------- + + + +----------------------------------------------------------------------- +--- font tests +----------------------------------------------------------------------- + +--- test sets + +local infer_regular_style = { + --- inferring which one is the correct style for “regular”; can be + --- obscured by synonyms like “book” etc. + { "Iwona", "Iwona-Regular.otf" }, -- trivial case + { "DejaVu Serif", "DejaVuSerif.ttf" }, + { "DejaVu Sans", "DejaVuSans.ttf" }, + { "Adobe Garamond Pro", "agaramondpro_regular.otf" }, + { "Garamond Premier Pro", "GaramondPremrPro.otf" }, + { "CMU Serif", "cmunrm.otf" }, + { "CMU Sans Serif", "cmunss.otf" }, + { "Minion Pro", "MinionPro-Regular.otf" }, + --- Below test will succeed only if we match for the + --- splainname (= sanitized tfmdata.fullname) field + --- explicitly. + { "Minion Pro Italic", "MinionPro-It.otf" }, +} + +local choose_optical_size = { + { { name = "Latin Modern Roman", optsize = 1 }, "lmroman5-regular.otf" }, + { { name = "Latin Modern Roman", optsize = 10 }, "lmroman10-regular.otf" }, + { { name = "Latin Modern Roman", optsize = 12 }, "lmroman12-regular.otf" }, + { { name = "Latin Modern Roman", optsize = 42 }, "lmroman17-regular.otf" }, + { { name = "EB Garamond", optsize = 1 }, "EBGaramond08-Regular.otf" }, + { { name = "EB Garamond", optsize = 8 }, "EBGaramond08-Regular.otf" }, + { { name = "EB Garamond", optsize = 12 }, "EBGaramond12-Regular.otf" }, + { { name = "EB Garamond", optsize = 42 }, "EBGaramond12-Regular.otf" }, + { { name = "Garamond Premier Pro", optsize = 1 }, "GaramondPremrPro-Capt.otf" }, + { { name = "Garamond Premier Pro", optsize = 10 }, "GaramondPremrPro.otf" }, + { { name = "Garamond Premier Pro", optsize = 15 }, "GaramondPremrPro-Subh.otf" }, + { { name = "Garamond Premier Pro", optsize = 42 }, "GaramondPremrPro-Disp.otf" }, +} + +local choose_style = { + { { name = "DejaVu Sans", style = "regular" }, "DejaVuSans.ttf" }, + { { name = "DejaVu Sans", style = "italic" }, "DejaVuSans-Oblique.ttf" }, + { { name = "DejaVu Sans", style = "bold" }, "DejaVuSans-Bold.ttf" }, + { { name = "DejaVu Sans", style = "bolditalic" }, "DejaVuSans-BoldOblique.ttf" }, + { { name = "Linux Libertine O", style = "regular" }, "LinLibertine_R.otf" }, + { { name = "Linux Libertine O", style = "italic" }, "LinLibertine_RI.otf" }, + { { name = "Linux Libertine O", style = "bold" }, "LinLibertine_RB.otf" }, + { { name = "Linux Libertine O", style = "bolditalic" }, "LinLibertine_RBI.otf" }, + { { name = "Liberation Serif", style = "regular" }, "LiberationSerif-Regular.ttf" }, + { { name = "Liberation Serif", style = "italic" }, "LiberationSerif-Italic.ttf" }, + { { name = "Liberation Serif", style = "bold" }, "LiberationSerif-Bold.ttf" }, + { { name = "Liberation Serif", style = "bolditalic" }, "LiberationSerif-BoldItalic.ttf" }, + { { name = "CMU Sans Serif", style = "regular" }, "cmunss.otf" }, -- no “regular” but “medium” + { { name = "CMU Sans Serif", style = "italic" }, "cmunsi.otf" }, -- no “italic” but “oblique” + { { name = "CMU Sans Serif", style = "bold" }, "cmunsx.otf" }, + { { name = "CMU Sans Serif", style = "bolditalic" }, "cmunso.otf" }, + --[[-- + Minion Pro Italic is exceptionally weird regarding identifiers in + that the postscript fontname and both info.fontname and + info.fullname are given as “minionproit”. Now its english fullname + (field 18) is “minionproital”. Only the value “fullname” in the root of + the tfmdata structure (not the one returned by fontloader.info()!) + accurately yields “Minion Pro Italic”. + + To complete the picture, the file naming isn’t very consistent either: + we find the suffixes “Regular” and “Bold”, but “It” and “BoldIt”. What + the hell were the designers smoking? + + Also, the full Minion Pro set comes with different optical sizes which + for monetary reasons cannot considered here. + --]]-- + { { name = "Minion Pro", style = "regular" }, "MinionPro-Regular.otf" }, + { { name = "Minion Pro", style = "italic" }, "MinionPro-It.otf" }, + { { name = "Minion Pro", style = "bold" }, "MinionPro-Bold.otf" }, + { { name = "Minion Pro", style = "bolditalic" }, "MinionPro-BoldIt.otf" }, +} + +--- this needs a database built with --formats=+pfa,pfb,afm + +local resolve_t1_font = { + { { name = "URW Gothic L", style = "regular" }, "a010013l.pfb" }, --> “book” +-- { { name = "URW Gothic L", style = "italic" }, "a010033l.pfb" }, --> “book oblique” +-- { { name = "URW Gothic L", style = "bold" }, "a010015l.pfb" }, --> “demi” +-- { { name = "URW Gothic L", style = "bolditalic" }, "a010035l.pfb" }, --> “demi oblique” + { { name = "Century Schoolbook L", style = "regular" }, "c059013l.pfb" }, + { { name = "Century Schoolbook L", style = "italic" }, "c059033l.pfb" }, + { { name = "Century Schoolbook L", style = "bold" }, "c059016l.pfb" }, + { { name = "Century Schoolbook L", style = "bolditalic" }, "c059036l.pfb" }, + { { name = "Nimbus Roman No9 L", style = "regular" }, "n021003l.pfb" }, + { { name = "Nimbus Roman No9 L", style = "italic" }, "n021023l.pfb" }, + { { name = "Nimbus Roman No9 L", style = "bold" }, "n021004l.pfb" }, --- medium, actually + { { name = "Nimbus Roman No9 L", style = "bolditalic" }, "n021024l.pfb" }, +} + +local translate_style = { + regular = "r", + italic = "i", + bold = "b", + bolditalic = "bi", +} + +local font_name_tests = { + infer_regular_style, + choose_optical_size, + choose_style, + resolve_t1_font, +} + +local default_spec = { + name = false, + lookup = "name", + specification = false, + optsize = 0, +} + +local resolve_font_name = function () + local failed, total = 0, 0 + local resolve_name = names.resolve_name + for nset = 1, #font_name_tests do + local set = font_name_tests[nset] + + for ntest = 1, #set do + local test = set[ntest] + local input, output = test[1], test[2] + + if type (input) == "string" then + local input_spec = table.copy (default_spec) + input_spec.name = input + input_spec.specification = input_spec.lookup .. ":" .. input + local result = resolve_name (input_spec) == output + total = total + 1 + if not result then + failed = failed + 1 + end + pprint_resolve (input, output, result) + + else + local input_spec, output = test[1], test[2] + input_spec.specification = (input_spec.lookup + or default_spec.lookup) + .. ":" .. input_spec.name + input_spec.optsize = input_spec.optsize or default_spec.optsize + input_spec.style = translate_style [input_spec.style] + local result = resolve_name (input_spec) == output + total = total + 1 + if not result then + failed = failed + 1 + end + pprint_resolve (pprint_spec (input_spec), output, result) + end + + end + end + return failed, total +end + +tests ["resolve_font_name"] = resolve_font_name + +----------------------------------------------------------------------- +--- runner +----------------------------------------------------------------------- + +local main = function () + local failed, total = 0, 0 + for name, test in next, tests do + texio.write_nl ("[" .. name .. "]") + local newfailed, newtotal = test () + total = total + 1 + pprint_result (name, newfailed, newtotal) + failed = failed + newfailed + total = total + newtotal + end + + if failed == 0 then + texio.write_nl (string.format ("[report] all %d tests passed.", total)) + else + texio.write_nl (string.format ("[report] %d of %d tests failed (%d %%).", + failed, + total, + failed / total * 100)) + end + texio.write_nl "" + os.exit (0) +end + +return main () + +--- vim:ft=lua:ts=2:et:sw=2 -- cgit v1.2.3 From 9e4c3a5390bd101a382ed959f81cd5bce62ed98b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 11 Feb 2014 06:42:03 +0100 Subject: [status] adapt status script to work on subdirectories --- scripts/mkstatus | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/scripts/mkstatus b/scripts/mkstatus index 6e6e375..cc0a62c 100755 --- a/scripts/mkstatus +++ b/scripts/mkstatus @@ -24,11 +24,14 @@ local md5sumhexa = md5.sumhexa local ioloaddata = io.loaddata local iosavedata = io.savedata local iopopen = io.popen +local iowrite = io.write +local lfsisdir = lfs.isdir ----------------------------------------------------------------------- -- settings ----------------------------------------------------------------------- +local verbose = false local filelist = "luaotfload-status.lua" --- result local names = { @@ -56,9 +59,9 @@ local names = { "luaotfload-override.lua", "luaotfload-parsers.lua", "luaotfload-tool.lua", - "mkcharacters", - "mkglyphlist", - "mkstatus", + { "scripts", "mkcharacters", }, + { "scripts", "mkglyphlist", }, + { "scripts", "mkstatus", }, } ----------------------------------------------------------------------- @@ -122,17 +125,38 @@ hash_all = function (list, acc) return hash_all (table.fastcopy (names), { }) end - local fname = list[#list] + local finfo = list[#list] list[#list] = nil - if fname then - local sum = hash_file (fname) - acc[#acc+1] = { fname, sum } + if finfo then + local fpath + if type (finfo) == "table" then + local d, f = finfo [1], finfo [2] + if lfs.isdir (d) then + fpath = file.join (d, f) + else + fpath = f + end + else + fpath = finfo + end + if verbose then + iowrite "· md5(" + iowrite (fpath) + end + local sum = hash_file (fpath) + if verbose then + iowrite ") = \"" + iowrite (sum) + iowrite "\"\n" + end + acc[#acc+1] = { fpath, sum } return hash_all (list, acc) end return acc end local main = function () + if arg [1] == "-v" then verbose = true end local hashes = hash_all () local notes = git_info () local serialized = table.serialize ({ notes = notes, -- cgit v1.2.3 From 7c5b3005c07314b2ba916b1577a4825213a2a454 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 11 Feb 2014 07:05:31 +0100 Subject: [status] fix target path --- scripts/mkstatus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mkstatus b/scripts/mkstatus index cc0a62c..efd9874 100755 --- a/scripts/mkstatus +++ b/scripts/mkstatus @@ -32,7 +32,7 @@ local lfsisdir = lfs.isdir ----------------------------------------------------------------------- local verbose = false -local filelist = "luaotfload-status.lua" --- result +local filelist = "./build/luaotfload-status.lua" --- result local names = { --- only the runtime files and scripts -- cgit v1.2.3 From e2daf4cca54b627a7255c34703b48e753de34c62 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 11 Feb 2014 07:23:17 +0100 Subject: [glyphs] adapt mkglyphlist --- scripts/mkglyphlist | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/mkglyphlist b/scripts/mkglyphlist index f7a1cb9..e660a57 100755 --- a/scripts/mkglyphlist +++ b/scripts/mkglyphlist @@ -5,19 +5,21 @@ -- DESCRIPTION: part of the luaotfload package -- REQUIREMENTS: lua, lpeg, luasocket, the lualibs package -- AUTHOR: Philipp Gesang (Phg), --- VERSION: 2.4 --- CREATED: 04/23/2013 12:42:17 PM CEST +-- VERSION: 2.5 +-- MODIFIED: 2014-02-11 06:44:50+0100 ----------------------------------------------------------------------- -- interesting thread on the Context list: -- http://www.ntg.nl/pipermail/ntg-context/2008/029057.html +-- +-- N.B. this script assumes network connectivity! ----------------------------------------------------------------------- ----------------------------------------------------------------------- -- config ----------------------------------------------------------------------- -local glyphfile = "./glyphlist.txt" -local font_age = "./luaotfload-glyphlist.lua" +local glyphfile = "./build/glyphlist.txt" +local font_age = "./build/luaotfload-glyphlist.lua" local glyph_source = "http://partners.adobe.com/public/developer/en/opentype/glyphlist.txt" ----------------------------------------------------------------------- @@ -133,6 +135,7 @@ end local get_raw get_raw = function (retry) local fh = io.open(glyphfile, "rb") if fh then + print ("info: reading glyph list from", glyphfile) local data = fh:read"*all" fh:close() if data then return data end -- cgit v1.2.3 From 9b388c64281999742759f39853efa337c049660d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 11 Feb 2014 07:26:12 +0100 Subject: [chars] adapt mkcharacters --- scripts/mkcharacters | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/mkcharacters b/scripts/mkcharacters index 5d4a2f4..abed2c9 100755 --- a/scripts/mkcharacters +++ b/scripts/mkcharacters @@ -5,8 +5,8 @@ -- DESCRIPTION: import parts of char-def.lua -- REQUIREMENTS: lua, ConTeXt, the lualibs package -- AUTHOR: Philipp Gesang (Phg), --- VERSION: 2.4 --- CREATED: 2013-05-17 12:41:39+0200 +-- VERSION: 2.5 +-- MODIFIED: 2014-02-11 07:24:25+0100 ----------------------------------------------------------------------- -- we create a stripped-down version of char-def.lua ----------------------------------------------------------------------- @@ -14,7 +14,7 @@ ----------------------------------------------------------------------- -- config ----------------------------------------------------------------------- -local charfile = "./luaotfload-characters.lua" +local charfile = "./build/luaotfload-characters.lua" local chardef = "/home/phg/base/char-def.lua" --- for every code point char-def.lua provides a set of fields. they -- cgit v1.2.3 From e9d34a8805f14361272864ea0470def2914315b4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 11 Feb 2014 07:28:32 +0100 Subject: [status] adapt mkstatus to respect the ./build directory --- scripts/mkstatus | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mkstatus b/scripts/mkstatus index efd9874..fcf8b24 100755 --- a/scripts/mkstatus +++ b/scripts/mkstatus @@ -39,7 +39,7 @@ local names = { "luaotfload-auxiliary.lua", "luaotfload-basics-gen.lua", "luaotfload-basics-nod.lua", - "luaotfload-characters.lua", + { "build", "luaotfload-characters.lua", }, "luaotfload-colors.lua", "luaotfload-database.lua", "luaotfload-diagnostics.lua", @@ -50,7 +50,7 @@ local names = { "luaotfload-fonts-ext.lua", "luaotfload-fonts-lua.lua", "luaotfload-fonts-tfm.lua", - "luaotfload-glyphlist.lua", + { "build", "luaotfload-glyphlist.lua", }, "luaotfload-letterspace.lua", "luaotfload-loaders.lua", "luaotfload-log.lua", -- cgit v1.2.3 From 67932f829fbf7aaa269725e646d1c35f74d97be5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 11 Feb 2014 07:29:32 +0100 Subject: [*] adapt Makefile to ./scripts and ./build --- Makefile | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 3ed893c..689d9da 100644 --- a/Makefile +++ b/Makefile @@ -3,17 +3,21 @@ NAME = luaotfload LUAOTFLOAD = $(wildcard luaotfload-*.lua) luaotfload-blacklist.cnf -GLYPHSCRIPT = mkglyphlist -GLYPHSOURCE = glyphlist.txt -CHARSCRIPT = mkcharacters -STATUSSCRIPT = mkstatus +DOCSRCDIR = ./doc +SCRIPTSRCDIR = ./scripts +BUILDDIR = ./build + +GLYPHSCRIPT = $(SCRIPTSRCDIR)/mkglyphlist +CHARSCRIPT = $(SCRIPTSRCDIR)/mkcharacters +STATUSSCRIPT = $(SCRIPTSRCDIR)/mkstatus + +GLYPHSOURCE = $(BUILDDIR)/glyphlist.txt RESOURCESCRIPTS = $(GLYPHSCRIPT) $(CHARSCRIPT) $(STATUSSCRIPT) SCRIPTNAME = luaotfload-tool SCRIPT = $(SCRIPTNAME).lua -DOCSRCDIR = ./doc GRAPH = filegraph DOCSRC = $(DOCSRCDIR)/$(NAME).dtx GRAPHSRC = $(DOCSRCDIR)/$(GRAPH).dot @@ -26,15 +30,15 @@ MANPAGE = $(DOCSRCDIR)/$(SCRIPTNAME).1 DOCS = $(DOCPDF) $(DOTPDF) $(MANPAGE) # Files grouped by generation mode -GLYPHS = luaotfload-glyphlist.lua -CHARS = luaotfload-characters.lua -STATUS = luaotfload-status.lua +GLYPHS = $(BUILDDIR)/$(NAME)-glyphlist.lua +CHARS = $(BUILDDIR)/$(NAME)-characters.lua +STATUS = $(BUILDDIR)/$(NAME)-status.lua RESOURCES = $(GLYPHS) $(CHARS) $(STATUS) SOURCE = $(DOCSRC) $(MANSRC) $(LUAOTFLOAD) README Makefile NEWS $(RESOURCESCRIPTS) # Files grouped by installation location SCRIPTSTATUS = $(SCRIPT) $(OLDSCRIPT) $(RESOURCESCRIPTS) -RUNSTATUS = $(UNPACKED) $(filter-out $(SCRIPTSTATUS),$(LUAOTFLOAD)) +RUNSTATUS = $(filter-out $(SCRIPTSTATUS),$(LUAOTFLOAD)) DOCSTATUS = $(DOCPDF) $(DOTPDF) README NEWS MANSTATUS = $(MANPAGE) SRCSTATUS = $(DOCSRC) $(MANSRC) $(GRAPHSRC) Makefile @@ -61,15 +65,18 @@ ZIPS = $(CTAN_ZIP) $(TDS_ZIP) LUA = texlua +## For now the $(BUILDDIR) is hardcoded in the scripts +## but we might just as well pass it to them by as environment +## variables. DO_GLYPHS = $(LUA) $(GLYPHSCRIPT) > /dev/null DO_CHARS = $(LUA) $(CHARSCRIPT) > /dev/null DO_STATUS = $(LUA) $(STATUSSCRIPT) > /dev/null all: $(GENERATED) -unpack: $(UNPACKED) -resources: $(RESOURCES) -chars: $(CHARS) -status: $(STATUS) +builddir: $(BUILDDIR) +resources: builddir $(RESOURCES) +chars: builddir $(CHARS) +status: builddir $(STATUS) ctan: $(CTAN_ZIP) tds: $(TDS_ZIP) world: all ctan @@ -97,6 +104,9 @@ $(CHARS): /dev/null $(STATUS): /dev/null $(DO_STATUS) +$(BUILDDIR): /dev/null + mkdir -p $(BUILDDIR) + define make-ctandir @$(RM) -rf $(DISTDIR) @mkdir -p $(DISTDIR) && cp $(SOURCE) $(COMPILED) $(DISTDIR) @@ -142,13 +152,17 @@ manifest: @echo "Derived files:" @for f in $(GENERATED); do echo $$f; done +CLEANEXTS = log aux toc idx ind ilg out +CLEANME = $(foreach ext,$(CLEANEXTS),$(wildcard *.$(ext))) +CLEANME += $(foreach ext,$(CLEANEXTS),$(wildcard $(BUILDDIR)/*$(ext))) + clean: $(MAKE) -C $(DOCSRCDIR) $@ - @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out + @$(RM) -- $(CLEANME) mrproper: clean $(MAKE) -C $(DOCSRCDIR) $@ @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE) - @$(RM) -r -- $(DISTDIR) + @$(RM) -r -- $(DISTDIR) $(BUILDDIR) # vim:set noexpandtab:tabstop=8:shiftwidth=2 -- cgit v1.2.3 From ffa5a347f68805e218c61c344c0b8a895c4bb8db Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 12 Feb 2014 07:15:50 +0100 Subject: [*] cleanup --- .gitignore | 2 ++ luaotfload-database.lua | 28 +++++++++++----------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 0da53bd..850a861 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ tests/*.ovf tests/*.sty tests/luaotfload* +# temporary directory +tmp/* diff --git a/luaotfload-database.lua b/luaotfload-database.lua index a3f3718..4b2d201 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -76,6 +76,7 @@ local lfscurrentdir = lfs.currentdir local lfsdir = lfs.dir local mathabs = math.abs local mathmin = math.min +local osgetenv = os.getenv local osgettimeofday = os.gettimeofday local osremove = os.remove local stringfind = string.find @@ -164,7 +165,7 @@ end if not luaotfloadconfig.termwidth then local tw = 79 if not ( os.type == "windows" --- Assume broken terminal. - or os.getenv "TERM" == "dumb") + or osgetenv "TERM" == "dumb") then local p = iopopen "tput cols" if p then @@ -802,29 +803,20 @@ We’ll just store successful name: lookups in a separate cache file. type lookup_cache = (string, (string * num)) dict -Complete, needs testing: - × 1) add cache to dbobj - × 2) wrap lookups in cached versions - × 3) make caching optional (via the config table) for debugging - × 4) make names_update() cache aware (nil if “force”) - × 5) add logging - × 6) add cache control to luaotfload-tool - × 7) incr db version (now 2.203) - × 8) save cache only at the end of a run - -The spec is modified in place (ugh), so we’ll have to catalogue what -fields actually influence its behavior. +The spec is expected to be modified in place (ugh), so we’ll have to +catalogue what fields actually influence its behavior. Idk what the “spec” resolver is for. lookup inspects modifies + ---------- ----------------- --------------------------- file: name forced, name - name:* name, style, sub, resolved, sub, name, forced + name:[*] name, style, sub, resolved, sub, name, forced optsize, size spec: name, sub resolved, sub, name, forced -* name: contains both the name resolver from luatex-fonts and - resolve_name() below +[*] name: contains both the name resolver from luatex-fonts and + resolve_name() below From my reading of font-def.lua, what a resolver does is basically rewrite the “name” field of the specification record @@ -2278,10 +2270,12 @@ end local path_separator = ostype == "windows" and ";" or ":" --[[doc-- + scan_texmf_fonts() scans all fonts in the texmf tree through the kpathsea variables OPENTYPEFONTS and TTFONTS of texmf.cnf. The current working directory comes as “.” (texlive) or absolute path (miktex) and will always be filtered out. + --doc]]-- --- dbobj -> dbobj -> bool? -> (int * int) @@ -2337,7 +2331,7 @@ local function get_os_dirs () "/Network/Library/Fonts", } elseif os.type == "windows" or os.type == "msdos" then - local windir = os.getenv("WINDIR") + local windir = osgetenv("WINDIR") return { filejoin(windir, 'Fonts') } else local fonts_conves = { --- plural, much? -- cgit v1.2.3 From 9138da7d4a53d65bc15f3a5dc73fd373db40bdf7 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 12 Feb 2014 07:50:06 +0100 Subject: [*] move source files to ./src --- luaotfload-auxiliary.lua | 783 --- luaotfload-basics-gen.lua | 343 - luaotfload-basics-nod.lua | 167 - luaotfload-colors.lua | 312 - luaotfload-database.lua | 3445 ---------- luaotfload-diagnostics.lua | 682 -- luaotfload-features.lua | 1276 ---- luaotfload-fontloader.lua | 13617 --------------------------------------- luaotfload-fonts-cbk.lua | 68 - luaotfload-fonts-def.lua | 97 - luaotfload-fonts-enc.lua | 28 - luaotfload-fonts-ext.lua | 272 - luaotfload-fonts-inj.lua | 526 -- luaotfload-fonts-lua.lua | 33 - luaotfload-fonts-otn.lua | 2848 -------- luaotfload-fonts-tfm.lua | 38 - luaotfload-letterspace.lua | 544 -- luaotfload-loaders.lua | 30 - luaotfload-log.lua | 404 -- luaotfload-main.lua | 711 -- luaotfload-override.lua | 52 - luaotfload-parsers.lua | 578 -- luaotfload-tool.lua | 1263 ---- luaotfload.sty | 45 - src/luaotfload-auxiliary.lua | 783 +++ src/luaotfload-basics-gen.lua | 343 + src/luaotfload-basics-nod.lua | 167 + src/luaotfload-colors.lua | 312 + src/luaotfload-database.lua | 3445 ++++++++++ src/luaotfload-diagnostics.lua | 682 ++ src/luaotfload-features.lua | 1276 ++++ src/luaotfload-fontloader.lua | 13617 +++++++++++++++++++++++++++++++++++++++ src/luaotfload-fonts-cbk.lua | 68 + src/luaotfload-fonts-def.lua | 97 + src/luaotfload-fonts-enc.lua | 28 + src/luaotfload-fonts-ext.lua | 272 + src/luaotfload-fonts-inj.lua | 526 ++ src/luaotfload-fonts-lua.lua | 33 + src/luaotfload-fonts-otn.lua | 2848 ++++++++ src/luaotfload-fonts-tfm.lua | 38 + src/luaotfload-letterspace.lua | 544 ++ src/luaotfload-loaders.lua | 30 + src/luaotfload-log.lua | 404 ++ src/luaotfload-main.lua | 711 ++ src/luaotfload-override.lua | 52 + src/luaotfload-parsers.lua | 578 ++ src/luaotfload-tool.lua | 1263 ++++ src/luaotfload.sty | 45 + 48 files changed, 28162 insertions(+), 28162 deletions(-) delete mode 100644 luaotfload-auxiliary.lua delete mode 100644 luaotfload-basics-gen.lua delete mode 100644 luaotfload-basics-nod.lua delete mode 100644 luaotfload-colors.lua delete mode 100644 luaotfload-database.lua delete mode 100644 luaotfload-diagnostics.lua delete mode 100644 luaotfload-features.lua delete mode 100644 luaotfload-fontloader.lua delete mode 100644 luaotfload-fonts-cbk.lua delete mode 100644 luaotfload-fonts-def.lua delete mode 100644 luaotfload-fonts-enc.lua delete mode 100644 luaotfload-fonts-ext.lua delete mode 100644 luaotfload-fonts-inj.lua delete mode 100644 luaotfload-fonts-lua.lua delete mode 100644 luaotfload-fonts-otn.lua delete mode 100644 luaotfload-fonts-tfm.lua delete mode 100644 luaotfload-letterspace.lua delete mode 100644 luaotfload-loaders.lua delete mode 100644 luaotfload-log.lua delete mode 100644 luaotfload-main.lua delete mode 100644 luaotfload-override.lua delete mode 100644 luaotfload-parsers.lua delete mode 100755 luaotfload-tool.lua delete mode 100644 luaotfload.sty create mode 100644 src/luaotfload-auxiliary.lua create mode 100644 src/luaotfload-basics-gen.lua create mode 100644 src/luaotfload-basics-nod.lua create mode 100644 src/luaotfload-colors.lua create mode 100644 src/luaotfload-database.lua create mode 100644 src/luaotfload-diagnostics.lua create mode 100644 src/luaotfload-features.lua create mode 100644 src/luaotfload-fontloader.lua create mode 100644 src/luaotfload-fonts-cbk.lua create mode 100644 src/luaotfload-fonts-def.lua create mode 100644 src/luaotfload-fonts-enc.lua create mode 100644 src/luaotfload-fonts-ext.lua create mode 100644 src/luaotfload-fonts-inj.lua create mode 100644 src/luaotfload-fonts-lua.lua create mode 100644 src/luaotfload-fonts-otn.lua create mode 100644 src/luaotfload-fonts-tfm.lua create mode 100644 src/luaotfload-letterspace.lua create mode 100644 src/luaotfload-loaders.lua create mode 100644 src/luaotfload-log.lua create mode 100644 src/luaotfload-main.lua create mode 100644 src/luaotfload-override.lua create mode 100644 src/luaotfload-parsers.lua create mode 100755 src/luaotfload-tool.lua create mode 100644 src/luaotfload.sty diff --git a/luaotfload-auxiliary.lua b/luaotfload-auxiliary.lua deleted file mode 100644 index 716af98..0000000 --- a/luaotfload-auxiliary.lua +++ /dev/null @@ -1,783 +0,0 @@ -#!/usr/bin/env texlua ------------------------------------------------------------------------ --- FILE: luaotfload-auxiliary.lua --- DESCRIPTION: part of luaotfload --- REQUIREMENTS: luaotfload 2.5 --- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang --- VERSION: 2.5 --- MODIFIED: 2014-01-02 21:24:25+0100 ------------------------------------------------------------------------ --- - ---- this file addresses issue #24 ---- https://github.com/lualatex/luaotfload/issues/24# - -luaotfload = luaotfload or {} -luaotfload.aux = luaotfload.aux or { } - -local aux = luaotfload.aux -local log = luaotfload.log -local report = log.report -local fonthashes = fonts.hashes -local identifiers = fonthashes.identifiers - -local fontid = font.id -local texsprint = tex.sprint - -local dofile = dofile -local getmetatable = getmetatable -local setmetatable = setmetatable -local utf8 = unicode.utf8 -local stringlower = string.lower -local stringformat = string.format -local stringgsub = string.gsub -local stringbyte = string.byte -local stringfind = string.find -local tablecopy = table.copy - ------------------------------------------------------------------------ ---- font patches ------------------------------------------------------------------------ - ---- https://github.com/khaledhosny/luaotfload/issues/54 - -local rewrite_fontname = function (tfmdata, specification) - tfmdata.name = [["]] .. specification .. [["]] -end - -local rewriting = false - -local start_rewrite_fontname = function () - if rewriting == false then - luatexbase.add_to_callback ( - "luaotfload.patch_font", - rewrite_fontname, - "luaotfload.rewrite_fontname") - rewriting = true - report ("log", 0, "aux", - "start rewriting tfmdata.name field") - end -end - -aux.start_rewrite_fontname = start_rewrite_fontname - -local stop_rewrite_fontname = function () - if rewriting == true then - luatexbase.remove_fromt_callback - ("luaotfload.patch_font", "luaotfload.rewrite_fontname") - rewriting = false - report ("log", 0, "aux", - "stop rewriting tfmdata.name field") - end -end - -aux.stop_rewrite_fontname = stop_rewrite_fontname - - ---[[doc-- -This sets two dimensions apparently relied upon by the unicode-math -package. ---doc]]-- - -local set_sscale_dimens = function (fontdata) - local mathconstants = fontdata.MathConstants - local parameters = fontdata.parameters - if mathconstants then - parameters[10] = mathconstants.ScriptPercentScaleDown or 70 - parameters[11] = mathconstants.ScriptScriptPercentScaleDown or 50 - end - return fontdata -end - -luatexbase.add_to_callback( - "luaotfload.patch_font", - set_sscale_dimens, - "luaotfload.aux.set_sscale_dimens") - ---- fontobj -> int -local lookup_units = function (fontdata) - local metadata = fontdata.shared and fontdata.shared.rawdata.metadata - if metadata and metadata.units_per_em then - return metadata.units_per_em - elseif fontdata.parameters and fontdata.parameters.units then - return fontdata.parameters.units - elseif fontdata.units then --- v1.x - return fontdata.units - end - return 1000 -end - ---[[doc-- -This callback corrects some values of the Cambria font. ---doc]]-- ---- fontobj -> unit -local patch_cambria_domh = function (fontdata) - local mathconstants = fontdata.MathConstants - if mathconstants and fontdata.psname == "CambriaMath" then - --- my test Cambria has 2048 - local units_per_em = fontdata.units_per_em or lookup_units(fontdata) - local sz = fontdata.parameters.size or fontdata.size - local mh = 2800 / units_per_em * sz - if mathconstants.DisplayOperatorMinHeight < mh then - mathconstants.DisplayOperatorMinHeight = mh - end - end -end - -luatexbase.add_to_callback( - "luaotfload.patch_font", - patch_cambria_domh, - "luaotfload.aux.patch_cambria_domh") - ---[[doc-- - -Comment from fontspec: - - “Here we patch fonts tfm table to emulate \XeTeX's \cs{fontdimen8}, - which stores the caps-height of the font. (Cf.\ \cs{fontdimen5} which - stores the x-height.) - - Falls back to measuring the glyph if the font doesn't contain the - necessary information. - This needs to be extended for fonts that don't contain an `X'.” - ---doc]]-- - -local set_capheight = function (fontdata) - local shared = fontdata.shared - local parameters = fontdata.parameters - local capheight - if shared and shared.rawdata.metadata.pfminfo then - local units_per_em = parameters.units - local size = parameters.size - local os2_capheight = shared.rawdata.metadata.pfminfo.os2_capheight - - if os2_capheight > 0 then - capheight = os2_capheight / units_per_em * size - else - local X8 = stringbyte"X" - if fontdata.characters[X8] then - capheight = fontdata.characters[X8].height - else - capheight = parameters.ascender / units_per_em * size - end - end - else - local X8 = stringbyte"X" - if fontdata.characters[X8] then - capheight = fontdata.characters[X8].height - end - end - if capheight then - --- is this legit? afaics there’s nothing else on the - --- array part of that table - fontdata.parameters[8] = capheight - end -end - -luatexbase.add_to_callback( - "luaotfload.patch_font", - set_capheight, - "luaotfload.aux.set_capheight") - ------------------------------------------------------------------------ ---- glyphs and characters ------------------------------------------------------------------------ - -local agl = fonts.encodings.agl - ---- int -> int -> bool -local font_has_glyph = function (font_id, codepoint) - local fontdata = fonts.hashes.identifiers[font_id] - if fontdata then - if fontdata.characters[codepoint] ~= nil then return true end - end - return false -end - -aux.font_has_glyph = font_has_glyph - ---- undocumented - -local raw_slot_of_name = function (font_id, glyphname) - local fontdata = font.fonts[font_id] - if fontdata.type == "virtual" then --- get base font for glyph idx - local codepoint = agl.unicodes[glyphname] - local glyph = fontdata.characters[codepoint] - if fontdata.characters[codepoint] then - return codepoint - end - end - return false -end - ---[[doc-- - - This one is approximately “name_to_slot” from the microtype package; - note that it is all about Adobe Glyph names and glyph slots in the - font. The names and values may diverge from actual Unicode. - - http://www.adobe.com/devnet/opentype/archives/glyph.html - - The “unsafe” switch triggers a fallback lookup in the raw fonts - table. As some of the information is stored as references, this may - have unpredictable side-effects. - ---doc]]-- - ---- int -> string -> bool -> (int | false) -local slot_of_name = function (font_id, glyphname, unsafe) - local fontdata = identifiers[font_id] - if fontdata then - local unicode = fontdata.resources.unicodes[glyphname] - if unicode then - if type(unicode) == "number" then - return unicode - else - return unicode[1] --- for multiple components - end --- else --- --- missing - end - elseif unsafe == true then -- for Robert - return raw_slot_of_name(font_id, glyphname) - end - return false -end - -aux.slot_of_name = slot_of_name - ---[[doc-- - - Inverse of above; not authoritative as to my knowledge the official - inverse of the AGL is the AGLFN. Maybe this whole issue should be - dealt with in a separate package that loads char-def.lua and thereby - solves the problem for the next couple decades. - - http://partners.adobe.com/public/developer/en/opentype/aglfn13.txt - ---doc]]-- - -local indices - ---- int -> (string | false) -local name_of_slot = function (codepoint) - if not indices then --- this will load the glyph list - local unicodes = agl.unicodes - indices = table.swapped(unicodes) - end - local glyphname = indices[codepoint] - if glyphname then - return glyphname - end - return false -end - -aux.name_of_slot = name_of_slot - ---[[doc-- - - In Context, characters.data is where the data from char-def.lua - resides. The file is huge (>3.7 MB as of 2013) and not part of the - isolated font loader. Nevertheless, we include a partial version - generated by the mkcharacters script that contains only the - a subset of the fields of each character defined. - - Currently, these are (compare the mkcharacters script!) - - · "direction" - · "mirror" - · "category" - · "textclass" - - The directional information is required for packages like Simurgh [0] - to work correctly. In an early stage [1] it was necessary to load - further files from Context directly, including the full blown version - of char-def. Since we have no use for most of the so imported - functionality, the required parts have been isolated and are now - instated along with luaotfload-characters.lua. We can extend the set - of imported features easily should it not be enough. - - [0] https://github.com/persian-tex/simurgh - [1] http://tex.stackexchange.com/a/132301/14066 - ---doc]]-- - -characters = characters or { } --- should be created in basics-gen -characters.data = nil -local chardef = "luaotfload-characters" - -do - local setmetatableindex = function (t, f) - local mt = getmetatable (t) - if mt then - mt.__index = f - else - setmetatable (t, { __index = f }) - end - end - - --- there are some special tables for each field that provide access - --- to fields of the character table by means of a metatable - - local mkcharspecial = function (characters, tablename, field) - - local chardata = characters.data - - if chardata then - local newspecial = { } - characters [tablename] = newspecial --> e.g. “characters.data.mirrors” - - local idx = function (t, char) - local c = chardata [char] - if c then - local m = c [field] --> e.g. “mirror” - if m then - t [char] = m - return m - end - end - newspecial [char] = false - return char - end - - setmetatableindex (newspecial, idx) - end - - end - - local mkcategories = function (characters) -- different from the others - - local chardata = characters.data - - setmetatable (characters, { __index = function (t, char) - if char then - local c = chardata [char] - c = c.category or char - t [char] = c - return c - end - end}) - - end - - local load_failed = false - local chardata --> characters.data; loaded on demand - - local load_chardef = function () - - report ("both", 1, "aux", "Loading character metadata from %s.", chardef) - chardata = dofile (kpse.find_file (chardef, "lua")) - - if chardata == nil then - warning ("Could not load %s; continuing \z - with empty character table.", - chardef) - chardata = { } - load_failed = true - end - - characters = { } --- nuke metatable - characters.data = chardata - - --- institute some of the functionality from char-ini.lua - - mkcharspecial (characters, "mirrors", "mirror") - mkcharspecial (characters, "directions", "direction") - mkcharspecial (characters, "textclasses", "textclass") - mkcategories (characters) - - end - - local charindex = function (t, k) - if chardata == nil and load_failed ~= true then - load_chardef () - end - - return characters [k] - end - - setmetatableindex (characters, charindex) - -end - ------------------------------------------------------------------------ ---- features / scripts / languages ------------------------------------------------------------------------ ---- lots of arrowcode ahead - ---[[doc-- -This function, modeled after “check_script()” from fontspec, returns -true if in the given font, the script “asked_script” is accounted for in at -least one feature. ---doc]]-- - ---- int -> string -> bool -local provides_script = function (font_id, asked_script) - asked_script = stringlower(asked_script) - if font_id and font_id > 0 then - local fontdata = identifiers[font_id].shared.rawdata - if fontdata then - local fontname = fontdata.metadata.fontname - local features = fontdata.resources.features - for method, featuredata in next, features do - --- where method: "gpos" | "gsub" - for feature, data in next, featuredata do - if data[asked_script] then - report ("log", 1, "aux", - "font no %d (%s) defines feature %s for script %s", - font_id, fontname, feature, asked_script) - return true - end - end - end - report ("log", 0, "aux", - "font no %d (%s) defines no feature for script %s", - font_id, fontname, asked_script) - end - end - report ("log", 0, "aux", "no font with id %d", font_id) - return false -end - -aux.provides_script = provides_script - ---[[doc-- -This function, modeled after “check_language()” from fontspec, returns -true if in the given font, the language with tage “asked_language” is -accounted for in the script with tag “asked_script” in at least one -feature. ---doc]]-- - ---- int -> string -> string -> bool -local provides_language = function (font_id, asked_script, asked_language) - asked_script = stringlower(asked_script) - asked_language = stringlower(asked_language) - if font_id and font_id > 0 then - local fontdata = identifiers[font_id].shared.rawdata - if fontdata then - local fontname = fontdata.metadata.fontname - local features = fontdata.resources.features - for method, featuredata in next, features do - --- where method: "gpos" | "gsub" - for feature, data in next, featuredata do - local scriptdata = data[asked_script] - if scriptdata and scriptdata[asked_language] then - report ("log", 1, "aux", - "font no %d (%s) defines feature %s " - .. "for script %s with language %s", - font_id, fontname, feature, - asked_script, asked_language) - return true - end - end - end - report ("log", 0, "aux", - "font no %d (%s) defines no feature " - .. "for script %s with language %s", - font_id, fontname, asked_script, asked_language) - end - end - report ("log", 0, "aux", "no font with id %d", font_id) - return false -end - -aux.provides_language = provides_language - ---[[doc-- -We strip the syntax elements from feature definitions (shouldn’t -actually be there in the first place, but who cares ...) ---doc]]-- - -local lpeg = require"lpeg" -local C, P, S = lpeg.C, lpeg.P, lpeg.S -local lpegmatch = lpeg.match - -local sign = S"+-" -local rhs = P"=" * P(1)^0 * P(-1) -local strip_garbage = sign^-1 * C((1 - rhs)^1) - ---s = "+foo" --> foo ---ss = "-bar" --> bar ---sss = "baz" --> baz ---t = "foo=bar" --> foo ---tt = "+bar=baz" --> bar ---ttt = "-baz=true" --> baz --- ---print(lpeg.match(strip_garbage, s)) ---print(lpeg.match(strip_garbage, ss)) ---print(lpeg.match(strip_garbage, sss)) ---print(lpeg.match(strip_garbage, t)) ---print(lpeg.match(strip_garbage, tt)) ---print(lpeg.match(strip_garbage, ttt)) - ---[[doc-- -This function, modeled after “check_feature()” from fontspec, returns -true if in the given font, the language with tag “asked_language” is -accounted for in the script with tag “asked_script” in feature -“asked_feature”. ---doc]]-- - ---- int -> string -> string -> string -> bool -local provides_feature = function (font_id, asked_script, - asked_language, asked_feature) - asked_script = stringlower(asked_script) - asked_language = stringlower(asked_language) - asked_feature = lpegmatch(strip_garbage, asked_feature) - - if font_id and font_id > 0 then - local fontdata = identifiers[font_id].shared.rawdata - if fontdata then - local features = fontdata.resources.features - local fontname = fontdata.metadata.fontname - for method, featuredata in next, features do - --- where method: "gpos" | "gsub" - local feature = featuredata[asked_feature] - if feature then - local scriptdata = feature[asked_script] - if scriptdata and scriptdata[asked_language] then - report ("log", 1, "aux", - "font no %d (%s) defines feature %s " - .. "for script %s with language %s", - font_id, fontname, asked_feature, - asked_script, asked_language) - return true - end - end - end - report ("log", 0, "aux", - "font no %d (%s) does not define feature %s for script %s with language %s", - font_id, fontname, asked_feature, asked_script, asked_language) - end - end - report ("log", 0, "aux", "no font with id %d", font_id) - return false -end - -aux.provides_feature = provides_feature - ------------------------------------------------------------------------ ---- font dimensions ------------------------------------------------------------------------ - ---- int -> string -> int -local get_math_dimension = function (font_id, dimenname) - if type(font_id) == "string" then - font_id = fontid(font_id) --- safeguard - end - local fontdata = identifiers[font_id] - local mathdata = fontdata.mathparameters - if mathdata then - return mathdata[dimenname] or 0 - end - return 0 -end - -aux.get_math_dimension = get_math_dimension - ---- int -> string -> unit -local sprint_math_dimension = function (font_id, dimenname) - if type(font_id) == "string" then - font_id = fontid(font_id) - end - local dim = get_math_dimension(font_id, dimenname) - texsprint(luatexbase.catcodetables["latex-package"], dim, "sp") -end - -aux.sprint_math_dimension = sprint_math_dimension - ------------------------------------------------------------------------ ---- extra database functions ------------------------------------------------------------------------ - -local namesresolve = fonts.names.resolve -local namesscan_dir = fonts.names.scan_dir - ---[====[-- TODO -> port this to new db model - ---- local directories ------------------------------------------------- - ---- migrated from luaotfload-database.lua ---- https://github.com/lualatex/luaotfload/pull/61#issuecomment-17776975 - ---- string -> (int * int) -local scan_external_dir = function (dir) - local old_names, new_names = names.data() - if not old_names then - old_names = load_names() - end - new_names = tablecopy(old_names) - local n_scanned, n_new = scan_dir(dir, old_names, new_names) - --- FIXME - --- This doesn’t seem right. If a db update is triggered after this - --- point, then the added fonts will be saved along with it -- - --- which is not as “temporarily” as it should be. (This should be - --- addressed during a refactoring of names_resolve().) - names.data = new_names - return n_scanned, n_new -end - -aux.scan_external_dir = scan_external_dir - ---]====]-- - -aux.scan_external_dir = function () - print "ERROR: scan_external_dir() is not implemented" -end - ---- db queries -------------------------------------------------------- - ---- https://github.com/lualatex/luaotfload/issues/74 ---- string -> (string * int) -local resolve_fontname = function (name) - local foundname, subfont, success = namesresolve(nil, nil, { - name = name, - lookup = "name", - optsize = 0, - specification = "name:" .. name, - }) - if success then - return foundname, subfont - end - return false, false -end - -aux.resolve_fontname = resolve_fontname - ---- string list -> (string * int) -local resolve_fontlist -resolve_fontlist = function (names, n) - if not n then - return resolve_fontlist(names, 1) - end - local this = names[n] - if this then - local foundname, subfont = resolve_fontname(this) - if foundname then - return foundname, subfont - end - return resolve_fontlist(names, n+1) - end - return false, false -end - -aux.resolve_fontlist = resolve_fontlist - ---- loaded fonts ------------------------------------------------------ - ---- just a proof of concept - ---- fontobj -> string list -> (string list) list -local get_font_data get_font_data = function (tfmdata, keys, acc, n) - if not acc then - return get_font_data(tfmdata, keys, {}, 1) - end - local key = keys[n] - if key then - local val = tfmdata[key] - if val then - acc[#acc+1] = val - else - acc[#acc+1] = false - end - return get_font_data(tfmdata, keys, acc, n+1) - end - return acc -end - ---[[doc-- - - The next one operates on the fonts.hashes.identifiers table. - It returns a list containing tuples of font ids and the - contents of the fields specified in the first argument. - Font table entries that were created indirectly -- e.g. by - \letterspacefont or during font expansion -- will not be - listed. - ---doc]]-- - -local default_keys = { "fullname" } - ---- string list -> (int * string list) list -local get_loaded_fonts get_loaded_fonts = function (keys, acc, lastid) - if not acc then - if not keys then - keys = default_keys - end - return get_loaded_fonts(keys, {}, lastid) - end - local id, tfmdata = next(identifiers, lastid) - if id then - local data = get_font_data(tfmdata, keys) - acc[#acc+1] = { id, data } - return get_loaded_fonts (keys, acc, id) - end - return acc -end - -aux.get_loaded_fonts = get_loaded_fonts - ---- Raw access to the font.* namespace is unsafe so no documentation on ---- this one. -local get_raw_fonts = function ( ) - local res = { } - for i, v in font.each() do - if v.filename then - res[#res+1] = { i, v } - end - end - return res -end - -aux.get_raw_fonts = get_raw_fonts - ------------------------------------------------------------------------ ---- font parameters ------------------------------------------------------------------------ ---- analogy of font-hsh - -fonthashes.parameters = fonthashes.parameters or { } -fonthashes.quads = fonthashes.quads or { } - -local parameters = fonthashes.parameters or { } -local quads = fonthashes.quads or { } - -setmetatable(parameters, { __index = function (t, font_id) - local tfmdata = identifiers[font_id] - if not tfmdata then --- unsafe; avoid - tfmdata = font.fonts[font_id] - end - if tfmdata and type(tfmdata) == "table" then - local fontparameters = tfmdata.parameters - t[font_id] = fontparameters - return fontparameters - end - return nil -end}) - ---[[doc-- - - Note that the reason as to why we prefer functions over table indices - is that functions are much safer against unintended manipulation. - This justifies the overhead they cost. - ---doc]]-- - ---- int -> (number | false) -local get_quad = function (font_id) - local quad = quads[font_id] - if quad then - return quad - end - local fontparameters = parameters[font_id] - if fontparameters then - local quad = fontparameters.quad or 0 - quads[font_id] = quad - return quad - end - return false -end - -aux.get_quad = get_quad - --- vim:tw=71:sw=2:ts=2:expandtab diff --git a/luaotfload-basics-gen.lua b/luaotfload-basics-gen.lua deleted file mode 100644 index 9cf5b93..0000000 --- a/luaotfload-basics-gen.lua +++ /dev/null @@ -1,343 +0,0 @@ -if not modules then modules = { } end modules ['luat-basics-gen'] = { - version = 1.100, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end - -local dummyfunction = function() -end - -local dummyreporter = function(c) - return function(...) - (texio.reporter or texio.write_nl)(c .. " : " .. string.formatters(...)) - end -end - -statistics = { - register = dummyfunction, - starttiming = dummyfunction, - stoptiming = dummyfunction, - elapsedtime = nil, -} - -directives = { - register = dummyfunction, - enable = dummyfunction, - disable = dummyfunction, -} - -trackers = { - register = dummyfunction, - enable = dummyfunction, - disable = dummyfunction, -} - -experiments = { - register = dummyfunction, - enable = dummyfunction, - disable = dummyfunction, -} - -storage = { -- probably no longer needed - register = dummyfunction, - shared = { }, -} - -logs = { - new = dummyreporter, - reporter = dummyreporter, - messenger = dummyreporter, - report = dummyfunction, -} - -callbacks = { - register = function(n,f) return callback.register(n,f) end, - -} - -utilities = { - storage = { - allocate = function(t) return t or { } end, - mark = function(t) return t or { } end, - }, -} - -characters = characters or { - data = { } -} - --- we need to cheat a bit here - -texconfig.kpse_init = true - -resolvers = resolvers or { } -- no fancy file helpers used - -local remapper = { - otf = "opentype fonts", - ttf = "truetype fonts", - ttc = "truetype fonts", - dfont = "truetype fonts", -- "truetype dictionary", - cid = "cid maps", - cidmap = "cid maps", - fea = "font feature files", - pfa = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! - pfb = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! - afm = "afm", -} - -function resolvers.findfile(name,fileformat) - name = string.gsub(name,"\\","/") - if not fileformat or fileformat == "" then - fileformat = file.suffix(name) - if fileformat == "" then - fileformat = "tex" - end - end - fileformat = string.lower(fileformat) - fileformat = remapper[fileformat] or fileformat - local found = kpse.find_file(name,fileformat) - if not found or found == "" then - found = kpse.find_file(name,"other text files") - end - return found -end - --- function resolvers.findbinfile(name,fileformat) --- if not fileformat or fileformat == "" then --- fileformat = file.suffix(name) --- end --- return resolvers.findfile(name,(fileformat and remapper[fileformat]) or fileformat) --- end - -resolvers.findbinfile = resolvers.findfile - -function resolvers.loadbinfile(filename,filetype) - local data = io.loaddata(filename) - return true, data, #data -end - -function resolvers.resolve(s) - return s -end - -function resolvers.unresolve(s) - return s -end - --- Caches ... I will make a real stupid version some day when I'm in the --- mood. After all, the generic code does not need the more advanced --- ConTeXt features. Cached data is not shared between ConTeXt and other --- usage as I don't want any dependency at all. Also, ConTeXt might have --- different needs and tricks added. - ---~ containers.usecache = true - -caches = { } - -local writable = nil -local readables = { } -local usingjit = jit - -if not caches.namespace or caches.namespace == "" or caches.namespace == "context" then - caches.namespace = 'generic' -end - -do - - -- standard context tree setup - - local cachepaths = kpse.expand_var('$TEXMFCACHE') or "" - - -- quite like tex live or so (the weird $TEXMFCACHE test seems to be needed on miktex) - - if cachepaths == "" or cachepaths == "$TEXMFCACHE" then - cachepaths = kpse.expand_var('$TEXMFVAR') or "" - end - - -- this also happened to be used (the weird $TEXMFVAR test seems to be needed on miktex) - - if cachepaths == "" or cachepaths == "$TEXMFVAR" then - cachepaths = kpse.expand_var('$VARTEXMF') or "" - end - - -- and this is a last resort (hm, we could use TEMP or TEMPDIR) - - if cachepaths == "" then - local fallbacks = { "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" } - for i=1,#fallbacks do - cachepaths = os.getenv(fallbacks[i]) or "" - if cachepath ~= "" and lfs.isdir(cachepath) then - break - end - end - end - - if cachepaths == "" then - cachepaths = "." - end - - cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") - - for i=1,#cachepaths do - local cachepath = cachepaths[i] - if not lfs.isdir(cachepath) then - lfs.mkdirs(cachepath) -- needed for texlive and latex - if lfs.isdir(cachepath) then - texio.write(string.format("(created cache path: %s)",cachepath)) - end - end - if file.is_writable(cachepath) then - writable = file.join(cachepath,"luatex-cache") - lfs.mkdir(writable) - writable = file.join(writable,caches.namespace) - lfs.mkdir(writable) - break - end - end - - for i=1,#cachepaths do - if file.is_readable(cachepaths[i]) then - readables[#readables+1] = file.join(cachepaths[i],"luatex-cache",caches.namespace) - end - end - - if not writable then - texio.write_nl("quiting: fix your writable cache path") - os.exit() - elseif #readables == 0 then - texio.write_nl("quiting: fix your readable cache path") - os.exit() - elseif #readables == 1 and readables[1] == writable then - texio.write(string.format("(using cache: %s)",writable)) - else - texio.write(string.format("(using write cache: %s)",writable)) - texio.write(string.format("(using read cache: %s)",table.concat(readables, " "))) - end - -end - -function caches.getwritablepath(category,subcategory) - local path = file.join(writable,category) - lfs.mkdir(path) - path = file.join(path,subcategory) - lfs.mkdir(path) - return path -end - -function caches.getreadablepaths(category,subcategory) - local t = { } - for i=1,#readables do - t[i] = file.join(readables[i],category,subcategory) - end - return t -end - -local function makefullname(path,name) - if path and path ~= "" then - return file.addsuffix(file.join(path,name),"lua"), file.addsuffix(file.join(path,name),usingjit and "lub" or "luc") - end -end - -function caches.is_writable(path,name) - local fullname = makefullname(path,name) - return fullname and file.is_writable(fullname) -end - -function caches.loaddata(paths,name) - for i=1,#paths do - local data = false - local luaname, lucname = makefullname(paths[i],name) - if lucname and lfs.isfile(lucname) then -- maybe also check for size - texio.write(string.format("(load luc: %s)",lucname)) - data = loadfile(lucname) - if data then - data = data() - end - if data then - return data - else - texio.write(string.format("(loading failed: %s)",lucname)) - end - end - if luaname and lfs.isfile(luaname) then - texio.write(string.format("(load lua: %s)",luaname)) - data = loadfile(luaname) - if data then - data = data() - end - if data then - return data - end - end - end -end - -function caches.savedata(path,name,data) - local luaname, lucname = makefullname(path,name) - if luaname then - texio.write(string.format("(save: %s)",luaname)) - table.tofile(luaname,data,true) - if lucname and type(caches.compile) == "function" then - os.remove(lucname) -- better be safe - texio.write(string.format("(save: %s)",lucname)) - caches.compile(data,luaname,lucname) - end - end -end - --- According to KH os.execute is not permitted in plain/latex so there is --- no reason to use the normal context way. So the method here is slightly --- different from the one we have in context. We also use different suffixes --- as we don't want any clashes (sharing cache files is not that handy as --- context moves on faster.) --- --- Beware: serialization might fail on large files (so maybe we should pcall --- this) in which case one should limit the method to luac and enable support --- for execution. - --- function caches.compile(data,luaname,lucname) --- local d = io.loaddata(luaname) --- if not d or d == "" then --- d = table.serialize(data,true) -- slow --- end --- if d and d ~= "" then --- local f = io.open(lucname,'w') --- if f then --- local s = loadstring(d) --- if s then --- f:write(string.dump(s,true)) --- end --- f:close() --- end --- end --- end - -function caches.compile(data,luaname,lucname) - local d = io.loaddata(luaname) - if not d or d == "" then - d = table.serialize(data,true) -- slow - end - if d and d ~= "" then - local f = io.open(lucname,'wb') - if f then - local s = loadstring(d) - if s then - f:write(string.dump(s,true)) - end - f:close() - end - end -end - --- - -function table.setmetatableindex(t,f) - setmetatable(t,{ __index = f }) -end diff --git a/luaotfload-basics-nod.lua b/luaotfload-basics-nod.lua deleted file mode 100644 index 50a1e86..0000000 --- a/luaotfload-basics-nod.lua +++ /dev/null @@ -1,167 +0,0 @@ -if not modules then modules = { } end modules ['luatex-fonts-nod'] = { - version = 1.001, - comment = "companion to luatex-fonts.lua", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end - --- Don't depend on code here as it is only needed to complement the --- font handler code. - --- Attributes: - -if tex.attribute[0] ~= 0 then - - texio.write_nl("log","!") - texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") - texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") - texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.") - texio.write_nl("log","!") - - tex.attribute[0] = 0 -- else no features - -end - -attributes = attributes or { } -attributes.unsetvalue = -0x7FFFFFFF - -local numbers, last = { }, 127 - -attributes.private = attributes.private or function(name) - local number = numbers[name] - if not number then - if last < 255 then - last = last + 1 - end - number = last - numbers[name] = number - end - return number -end - --- Nodes: - -nodes = { } -nodes.pool = { } -nodes.handlers = { } - -local nodecodes = { } for k,v in next, node.types () do nodecodes[string.gsub(v,"_","")] = k end -local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gsub(v,"_","")] = k end -local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" } - -nodes.nodecodes = nodecodes -nodes.whatcodes = whatcodes -nodes.whatsitcodes = whatcodes -nodes.glyphcodes = glyphcodes - -local free_node = node.free -local remove_node = node.remove -local new_node = node.new -local traverse_id = node.traverse_id - -local math_code = nodecodes.math - -nodes.handlers.protectglyphs = node.protect_glyphs -nodes.handlers.unprotectglyphs = node.unprotect_glyphs - -function nodes.remove(head, current, free_too) - local t = current - head, current = remove_node(head,current) - if t then - if free_too then - free_node(t) - t = nil - else - t.next, t.prev = nil, nil - end - end - return head, current, t -end - -function nodes.delete(head,current) - return nodes.remove(head,current,true) -end - -function nodes.pool.kern(k) - local n = new_node("kern",1) - n.kern = k - return n -end - --- experimental - -local getfield = node.getfield or function(n,tag) return n[tag] end -local setfield = node.setfield or function(n,tag,value) n[tag] = value end - -nodes.getfield = getfield -nodes.setfield = setfield - -nodes.getattr = getfield -nodes.setattr = setfield - -if node.getid then nodes.getid = node.getid else function nodes.getid (n) return getfield(n,"id") end end -if node.getsubtype then nodes.getsubtype = node.getsubtype else function nodes.getsubtype(n) return getfield(n,"subtype") end end -if node.getnext then nodes.getnext = node.getnext else function nodes.getnext (n) return getfield(n,"next") end end -if node.getprev then nodes.getprev = node.getprev else function nodes.getprev (n) return getfield(n,"prev") end end -if node.getchar then nodes.getchar = node.getchar else function nodes.getchar (n) return getfield(n,"char") end end -if node.getfont then nodes.getfont = node.getfont else function nodes.getfont (n) return getfield(n,"font") end end -if node.getlist then nodes.getlist = node.getlist else function nodes.getlist (n) return getfield(n,"list") end end - -function nodes.tonut (n) return n end -function nodes.tonode(n) return n end - --- being lazy ... just copy a bunch ... not all needed in generic but we assume --- nodes to be kind of private anyway - -nodes.tostring = node.tostring or tostring -nodes.copy = node.copy -nodes.copy_list = node.copy_list -nodes.delete = node.delete -nodes.dimensions = node.dimensions -nodes.end_of_math = node.end_of_math -nodes.flush_list = node.flush_list -nodes.flush_node = node.flush_node -nodes.free = node.free -nodes.insert_after = node.insert_after -nodes.insert_before = node.insert_before -nodes.hpack = node.hpack -nodes.new = node.new -nodes.tail = node.tail -nodes.traverse = node.traverse -nodes.traverse_id = node.traverse_id -nodes.slide = node.slide -nodes.vpack = node.vpack - -nodes.first_glyph = node.first_glyph -nodes.first_character = node.first_character -nodes.has_glyph = node.has_glyph or node.first_glyph - -nodes.current_attr = node.current_attr -nodes.do_ligature_n = node.do_ligature_n -nodes.has_field = node.has_field -nodes.last_node = node.last_node -nodes.usedlist = node.usedlist -nodes.protrusion_skippable = node.protrusion_skippable -nodes.write = node.write - -nodes.has_attribute = node.has_attribute -nodes.set_attribute = node.set_attribute -nodes.unset_attribute = node.unset_attribute - -nodes.protect_glyphs = node.protect_glyphs -nodes.unprotect_glyphs = node.unprotect_glyphs -nodes.kerning = node.kerning -nodes.ligaturing = node.ligaturing -nodes.mlist_to_hlist = node.mlist_to_hlist - --- in generic code, at least for some time, we stay nodes, while in context --- we can go nuts (e.g. experimental); this split permits us us keep code --- used elsewhere stable but at the same time play around in context - -nodes.nuts = nodes diff --git a/luaotfload-colors.lua b/luaotfload-colors.lua deleted file mode 100644 index d999df6..0000000 --- a/luaotfload-colors.lua +++ /dev/null @@ -1,312 +0,0 @@ -if not modules then modules = { } end modules ['luaotfload-colors'] = { - version = "2.5", - comment = "companion to luaotfload-main.lua (font color)", - author = "Khaled Hosny, Elie Roux, Philipp Gesang", - copyright = "Luaotfload Development Team", - license = "GNU GPL v2.0" -} - ---[[doc-- - -buggy coloring with the pre_output_filter when expansion is enabled - · tfmdata for different expansion values is split over different objects - · in ``initializeexpansion()``, chr.expansion_factor is set, and only - those characters that have it are affected - · in constructors.scale: chr.expansion_factor = ve*1000 if commented out - makes the bug vanish - -explanation: http://tug.org/pipermail/luatex/2013-May/004305.html - ---doc]]-- - - -local color_callback = config.luaotfload.color_callback -if not color_callback then - --- maybe this would be better as a method: "early" | "late" - color_callback = "pre_linebreak_filter" --- color_callback = "pre_output_filter" --- old behavior, breaks expansion -end - - -local newnode = node.new -local nodetype = node.id -local traverse_nodes = node.traverse -local insert_node_before = node.insert_before -local insert_node_after = node.insert_after - -local stringformat = string.format -local stringgsub = string.gsub -local stringfind = string.find -local stringsub = string.sub - -local otffeatures = fonts.constructors.newfeatures("otf") -local identifiers = fonts.hashes.identifiers -local registerotffeature = otffeatures.register - -local add_color_callback --[[ this used to be a global‽ ]] - ---[[doc-- -This converts a single octet into a decimal with three digits of -precision. The optional second argument limits precision to a single -digit. ---doc]]-- - ---- string -> bool? -> string -local hex_to_dec = function (hex,one) --- one isn’t actually used anywhere ... - if one then - return stringformat("%.1g", tonumber(hex, 16)/255) - else - return stringformat("%.3g", tonumber(hex, 16)/255) - end -end - ---[[doc-- -Color string validator / parser. ---doc]]-- - -local lpeg = require"lpeg" -local lpegmatch = lpeg.match -local C, Cg, Ct, P, R, S = lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.R, lpeg.S - -local digit16 = R("09", "af", "AF") -local octet = C(digit16 * digit16) - -local p_rgb = octet * octet * octet -local p_rgba = p_rgb * octet -local valid_digits = C(p_rgba + p_rgb) -- matches eight or six hex digits - -local p_Crgb = Cg(octet/hex_to_dec, "red") --- for captures - * Cg(octet/hex_to_dec, "green") - * Cg(octet/hex_to_dec, "blue") -local p_Crgba = p_Crgb * Cg(octet/hex_to_dec, "alpha") -local extract_color = Ct(p_Crgba + p_Crgb) - ---- string -> (string | nil) -local sanitize_color_expression = function (digits) - digits = tostring(digits) - local sanitized = lpegmatch(valid_digits, digits) - if not sanitized then - luaotfload.warning( - "%q is not a valid rgb[a] color expression", digits) - return nil - end - return sanitized -end - ---[[doc-- -``setcolor`` modifies tfmdata.properties.color in place ---doc]]-- - ---- fontobj -> string -> unit ---- ---- (where “string” is a rgb value as three octet ---- hexadecimal, with an optional fourth transparency ---- value) ---- -local setcolor = function (tfmdata, value) - local sanitized = sanitize_color_expression(value) - local properties = tfmdata.properties - - if sanitized then - properties.color = sanitized - add_color_callback() - end -end - -registerotffeature { - name = "color", - description = "color", - initializers = { - base = setcolor, - node = setcolor, - } -} - - ---- something is carried around in ``res`` ---- for later use by color_handler() --- but what? - -local res = nil - ---- float -> unit -local function pageresources(alpha) - res = res or {} - res[alpha] = true -end - ---- we store results of below color handler as tuples of ---- push/pop strings -local color_cache = { } --- (string, (string * string)) hash_t - ---- string -> (string * string) -local hex_to_rgba = function (digits) - if not digits then - return - end - - --- this is called like a thousand times, so some - --- memoizing is in order. - local cached = color_cache[digits] - if not cached then - local push, pop - local rgb = lpegmatch(extract_color, digits) - if rgb.alpha then - pageresources(rgb.alpha) - push = stringformat( - "/TransGs%g gs %s %s %s rg", - rgb.alpha, - rgb.red, - rgb.green, - rgb.blue) - pop = "0 g /TransGs1 gs" - else - push = stringformat( - "%s %s %s rg", - rgb.red, - rgb.green, - rgb.blue) - pop = "0 g" - end - color_cache[digits] = { push, pop } - return push, pop - end - - return cached[1], cached[2] -end - ---- Luatex internal types - -local glyph_t = nodetype("glyph") -local hlist_t = nodetype("hlist") -local vlist_t = nodetype("vlist") -local whatsit_t = nodetype("whatsit") -local page_insert_t = nodetype("page_insert") -local sub_box_t = nodetype("sub_box") - ---- node -> nil | -1 | color‽ -local lookup_next_color -lookup_next_color = function (head) --- paragraph material - for n in traverse_nodes(head) do - local n_id = n.id - - if n_id == glyph_t then - local n_font - if identifiers[n_font] - and identifiers[n_font].properties - and identifiers[n_font].properties.color - then - return identifiers[n.font].properties.color - else - return -1 - end - - elseif n_id == vlist_t or n_id == hlist_t or n_id == sub_box_t then - local r = lookup_next_color(n.list) - if r then - return r - end - - elseif n_id == whatsit_t or n_id == page_insert_t then - return -1 - end - end - return nil -end - ---[[doc-- -While the second argument and second returned value are apparently -always nil when the function is called, they temporarily take string -values during the node list traversal. ---doc]]-- - -local cnt = 0 ---- node -> string -> int -> (node * string) -local node_colorize -node_colorize = function (head, current_color, next_color) - for n in traverse_nodes(head) do - local n_id = n.id - local nextnode = n.next - - if n_id == hlist_t or n_id == vlist_t or n_id == sub_box_t then - local next_color_in = lookup_next_color(nextnode) or next_color - n.list, current_color = node_colorize(n.list, current_color, next_color_in) - - elseif n_id == glyph_t then - cnt = cnt + 1 - local tfmdata = identifiers[n.font] - - --- colorization is restricted to those fonts - --- that received the “color” property upon - --- loading (see ``setcolor()`` above) - if tfmdata and tfmdata.properties and tfmdata.properties.color then - local font_color = tfmdata.properties.color --- luaotfload.info( --- "n: %d; %s; %d %s, %s", --- cnt, utf.char(n.char), n.font, "", font_color) - if font_color ~= current_color then - local pushcolor = hex_to_rgba(font_color) - local push = newnode(whatsit_t, 8) - push.mode = 1 - push.data = pushcolor - head = insert_node_before(head, n, push) - current_color = font_color - end - local next_color_in = lookup_next_color (nextnode) or next_color - if next_color_in ~= font_color then - local _, popcolor = hex_to_rgba(font_color) - local pop = newnode(whatsit_t, 8) - pop.mode = 1 - pop.data = popcolor - head = insert_node_after(head, n, pop) - current_color = nil - end - --- else --- luaotfload.info( --- "n: %d; %s; %d %s", --- cnt, utf.char(n.char), n.font, "") - end - end - end - return head, current_color -end - ---- node -> node -local color_handler = function (head) - local new_head = node_colorize(head, nil, nil) - -- now append our page resources - if res then - res["1"] = true - local tpr, t = tex.pdfpageresources, "" - for k in pairs(res) do - local str = stringformat("/TransGs%s<>", k, k, k) - if not stringfind(tpr,str) then - t = t .. str - end - end - if t ~= "" then - if not stringfind(tpr,"/ExtGState<<.*>>") then - tpr = tpr.."/ExtGState<<>>" - end - tpr = stringgsub(tpr,"/ExtGState<<","%1"..t) - tex.pdfpageresources = tpr - end - res = nil -- reset res - end - return new_head -end - -local color_callback_activated = 0 - ---- unit -> unit -add_color_callback = function ( ) - if color_callback_activated == 0 then - luatexbase.add_to_callback(color_callback, - color_handler, - "luaotfload.color_handler") - color_callback_activated = 1 - end -end - --- vim:tw=71:sw=4:ts=4:expandtab - diff --git a/luaotfload-database.lua b/luaotfload-database.lua deleted file mode 100644 index 4b2d201..0000000 --- a/luaotfload-database.lua +++ /dev/null @@ -1,3445 +0,0 @@ -if not modules then modules = { } end modules ['luaotfload-database'] = { - version = "2.5", - comment = "companion to luaotfload-main.lua", - author = "Khaled Hosny, Elie Roux, Philipp Gesang", - copyright = "Luaotfload Development Team", - license = "GNU GPL v2.0" -} - ---[[doc-- - - Some statistics: - - a) TL 2012, mkluatexfontdb --force - b) v2.4, luaotfload-tool --update --force - c) v2.4, luaotfload-tool --update --force --formats=+afm,pfa,pfb - d) Context, mtxrun --script fonts --reload --force - - (Keep in mind that Context does index fewer fonts since it - considers only the contents of the minimals tree, not the - tex live one!) - - time (m:s) peak VmSize (kB) - a 1:19 386 018 - b 0:37 715 797 - c 2:27 1 017 674 - d 0:44 1 082 313 - - Most of the increase in memory consumption from version 1.x to 2.2+ - can be attributed to the move from single-pass to a multi-pass - approach to building the index: Information is first gathered from - all reachable fonts and only afterwards processed, classified and - discarded. Also, there is a good deal of additional stuff kept in - the database now: two extra tables for file names and font families - have been added, making font lookups more efficient while improving - maintainability of the code. - ---doc]]-- - -local lpeg = require "lpeg" -local P, Cc, lpegmatch = lpeg.P, lpeg.Cc, lpeg.match - -local parsers = luaotfload.parsers -local read_fonts_conf = parsers.read_fonts_conf -local stripslashes = parsers.stripslashes -local splitcomma = parsers.splitcomma - -local log = luaotfload.log -local report = log.report -local report_status = log.names_status -local report_status_start = log.names_status_start -local report_status_stop = log.names_status_stop - - ---- Luatex builtins -local load = load -local next = next -local require = require -local tonumber = tonumber -local unpack = table.unpack - -local fontloaderinfo = fontloader.info -local fontloaderclose = fontloader.close -local fontloaderopen = fontloader.open ------ fontloaderto_table = fontloader.to_table -local gzipopen = gzip.open -local iolines = io.lines -local ioopen = io.open -local iopopen = io.popen -local kpseexpand_path = kpse.expand_path -local kpsefind_file = kpse.find_file -local kpselookup = kpse.lookup -local kpsereadable_file = kpse.readable_file -local lfsattributes = lfs.attributes -local lfschdir = lfs.chdir -local lfscurrentdir = lfs.currentdir -local lfsdir = lfs.dir -local mathabs = math.abs -local mathmin = math.min -local osgetenv = os.getenv -local osgettimeofday = os.gettimeofday -local osremove = os.remove -local stringfind = string.find -local stringformat = string.format -local stringgmatch = string.gmatch -local stringgsub = string.gsub -local stringlower = string.lower -local stringsub = string.sub -local stringupper = string.upper -local tableconcat = table.concat -local tablesort = table.sort -local utf8gsub = unicode.utf8.gsub -local utf8lower = unicode.utf8.lower -local utf8len = unicode.utf8.len -local zlibcompress = zlib.compress - ---- these come from Lualibs/Context -local filebasename = file.basename -local filecollapsepath = file.collapsepath or file.collapse_path -local filedirname = file.dirname -local fileextname = file.extname -local fileiswritable = file.iswritable -local filejoin = file.join -local filenameonly = file.nameonly -local filereplacesuffix = file.replacesuffix -local filesplitpath = file.splitpath or file.split_path -local filesuffix = file.suffix -local getwritablepath = caches.getwritablepath -local lfsisdir = lfs.isdir -local lfsisfile = lfs.isfile -local lfsmkdirs = lfs.mkdirs -local lpegsplitat = lpeg.splitat -local stringis_empty = string.is_empty -local stringsplit = string.split -local stringstrip = string.strip -local tableappend = table.append -local tablecontains = table.contains -local tablecopy = table.copy -local tablefastcopy = table.fastcopy -local tabletofile = table.tofile -local tabletohash = table.tohash -local tableserialize = table.serialize -local runasscript = caches == nil ---- the font loader namespace is “fonts”, same as in Context ---- we need to put some fallbacks into place for when running ---- as a script -fonts = fonts or { } -fonts.names = fonts.names or { } -fonts.definers = fonts.definers or { } - -local luaotfloadconfig = config.luaotfload --- always present -luaotfloadconfig.resolver = luaotfloadconfig.resolver or "normal" -luaotfloadconfig.formats = luaotfloadconfig.formats or "otf,ttf,ttc,dfont" -luaotfloadconfig.strip = luaotfloadconfig.strip == true - ---- this option allows for disabling updates ---- during a TeX run -luaotfloadconfig.update_live = luaotfloadconfig.update_live ~= false -luaotfloadconfig.compress = luaotfloadconfig.compress ~= false - -local names = fonts.names -local name_index = nil --> upvalue for names.data -local lookup_cache = nil --> for names.lookups -names.version = 2.5 -names.data = nil --- contains the loaded database -names.lookups = nil --- contains the lookup cache - -names.path = { index = { }, lookups = { } } -names.path.globals = { - prefix = "", --- writable_path/names_dir - names_dir = luaotfloadconfig.names_dir or "names", - index_file = luaotfloadconfig.index_file - or "luaotfload-names.lua", - lookups_file = "luaotfload-lookup-cache.lua", -} - ---- string -> (string * string) -local make_luanames = function (path) - return filereplacesuffix(path, "lua"), - filereplacesuffix(path, "luc") -end - ---- The “termwidth” value is only considered when printing ---- short status messages, e.g. when building the database ---- online. -if not luaotfloadconfig.termwidth then - local tw = 79 - if not ( os.type == "windows" --- Assume broken terminal. - or osgetenv "TERM" == "dumb") - then - local p = iopopen "tput cols" - if p then - result = tonumber (p:read "*all") - p:close () - if result then - tw = result - else - report ("log", 2, "db", "tput returned non-number.") - end - else - report ("log", 2, "db", "Shell escape disabled or tput executable missing.") - report ("log", 2, "db", "Assuming 79 cols terminal width.") - end - end - luaotfloadconfig.termwidth = tw -end - -local format_precedence = { - "otf", "ttc", "ttf", - "dfont", "afm", "pfb", - "pfa", -} - -local location_precedence = { - "local", "system", "texmf", -} - -local set_location_precedence = function (precedence) - location_precedence = precedence -end - ---[[doc-- - We use the functions in the cache.* namespace that come with the - fontloader (see luat-basics-gen). it’s safe to use for the most part - since most checks and directory creations are already done. It - uses TEXMFCACHE or TEXMFVAR as starting points. - - There is one quirk, though: ``getwritablepath()`` will always - assume that files in subdirectories of the cache tree are writable. - It gives no feedback at all if it fails to open a file in write - mode. This may cause trouble when the index or lookup cache were - created by different user. ---doc]]-- - -if not runasscript then - local globals = names.path.globals - local names_dir = globals.names_dir - - prefix = getwritablepath (names_dir, "") - if not prefix then - luaotfload.error - ("Impossible to find a suitable writeable cache...") - else - prefix = lpegmatch (stripslashes, prefix) - report ("log", 0, "db", - "Root cache directory is %s.", prefix) - end - - globals.prefix = prefix - local lookup_path = names.path.lookups - local index = names.path.index - local lookups_file = filejoin (prefix, globals.lookups_file) - local index_file = filejoin (prefix, globals.index_file) - lookup_path.lua, lookup_path.luc = make_luanames (lookups_file) - index.lua, index.luc = make_luanames (index_file) -else --- running as script, inject some dummies - caches = { } - local dummy_function = function () end - log = { report = dummy_function, - report_status = dummy_function, - report_status_start = dummy_function, - report_status_stop = dummy_function, } -end - - ---[[doc-- -Auxiliary functions ---doc]]-- - ---- fontnames contain all kinds of garbage; as a precaution we ---- lowercase and strip them of non alphanumerical characters - ---- string -> string - -local invalidchars = "[^%a%d]" - -local sanitize_fontname = function (str) - if str ~= nil then - str = utf8gsub (utf8lower (str), invalidchars, "") - return str - end - return nil -end - -local sanitize_fontnames = function (rawnames) - local result = { } - for category, namedata in next, rawnames do - - if type (namedata) == "string" then - result [category] = utf8gsub (utf8lower (namedata), - invalidchars, - "") - else - local target = { } - for field, name in next, namedata do - target [field] = utf8gsub (utf8lower (name), - invalidchars, - "") - end - result [category] = target - end - end - return result -end - -local find_files_indeed -find_files_indeed = function (acc, dirs, filter) - if not next (dirs) then --- done - return acc - end - - local pwd = lfscurrentdir () - local dir = dirs[#dirs] - dirs[#dirs] = nil - - if lfschdir (dir) then - lfschdir (pwd) - - local newfiles = { } - for ent in lfsdir (dir) do - if ent ~= "." and ent ~= ".." then - local fullpath = dir .. "/" .. ent - if filter (fullpath) == true then - if lfsisdir (fullpath) then - dirs[#dirs+1] = fullpath - elseif lfsisfile (fullpath) then - newfiles[#newfiles+1] = fullpath - end - end - end - end - return find_files_indeed (tableappend (acc, newfiles), - dirs, filter) - end - --- could not cd into, so we skip it - return find_files_indeed (acc, dirs, filter) -end - -local dummyfilter = function () return true end - ---- the optional filter function receives the full path of a file ---- system entity. a filter applies if the first argument it returns is ---- true. - ---- string -> function? -> string list -local find_files = function (root, filter) - if lfsisdir (root) then - return find_files_indeed ({}, { root }, filter or dummyfilter) - end -end - - ---[[doc-- -This is a sketch of the luaotfload db: - - type dbobj = { - families : familytable; - files : filemap; - status : filestatus; - mappings : fontentry list; - meta : metadata; - names : namedata; // TODO: check for relevance after db is finalized - } - and familytable = { - local : (format, familyentry) hash; // specified with include dir - texmf : (format, familyentry) hash; - system : (format, familyentry) hash; - } - and familyentry = { - regular : sizes; - italic : sizes; - bold : sizes; - bolditalic : sizes; - } - and sizes = { - default : int; // points into mappings or names - optical : (int, int) list; // design size -> index entry - } - and metadata = { - formats : string list; // { "otf", "ttf", "ttc", "dfont" } - statistics : TODO; - version : float; - } - and filemap = { - base : { - local : (string, int) hash; // basename -> idx - system : (string, int) hash; - texmf : (string, int) hash; - }; - bare : { - local : (string, (string, int) hash) hash; // location -> (barename -> idx) - system : (string, (string, int) hash) hash; - texmf : (string, (string, int) hash) hash; - }; - full : (int, string) hash; // idx -> full path - } - and fontentry = { - barename : string; - familyname : string; - filename : string; - fontname : string; // <- metadata - fullname : string; // <- metadata - sanitized : { - family : string; - fontstyle_name : string; // <- new in 2.4 - fontname : string; // <- metadata - fullname : string; // <- namedata.names - metafamily : string; - pfullname : string; - prefmodifiers : string; - psname : string; - subfamily : string; - }; - size : int list; - slant : int; - subfont : int; - location : local | system | texmf; - weight : int; - width : int; - units_per_em : int; // mainly 1000, but also 2048 or 256 - } - and filestatus = (string, // fullname - { index : int list; // pointer into mappings - timestamp : int; }) dict - -beware that this is a reconstruction and may be incomplete. - -mtx-fonts has in names.tma: - - type names = { - cache_uuid : uuid; - cache_version : float; - datastate : uuid list; - fallbacks : (filetype, (basename, int) hash) hash; - families : (basename, int list) hash; - files : (filename, fullname) hash; - indices : (fullname, int) hash; - mappings : (filetype, (basename, int) hash) hash; - names : ? (empty hash) ?; - rejected : (basename, int) hash; - specifications: fontentry list; - } - and fontentry = { - designsize : int; - familyname : string; - filename : string; - fontname : string; - format : string; - fullname : string; - maxsize : int; - minsize : int; - modification : int; - rawname : string; - style : string; - subfamily : string; - variant : string; - weight : string; - width : string; - } - ---doc]]-- - -local initialize_namedata = function (formats) --- returns dbobj - return { - --families = { }, - status = { }, -- was: status; map abspath -> mapping - mappings = { }, -- TODO: check if still necessary after rewrite - names = { }, --- files = { }, -- created later - meta = { - formats = formats, - statistics = { }, - version = names.version, - }, - } -end - ---[[doc-- - - Since Luaotfload does not depend on the lualibs anymore we - have to put our own small wrappers for the gzip library in - place. - - load_gzipped -- Read and decompress and entire gzipped file. - Returns the uncompressed content as a string. - ---doc]]-- - -local load_gzipped = function (filename) - local gh = gzipopen (filename,"rb") - if gh then - local data = gh:read "*all" - gh:close () - return data - end -end - ---[[doc-- - - save_gzipped -- Compress and write a string to file. The return - value is the number of bytes written. Zlib parameters are: best - compression and default strategy. - ---doc]]-- - -local save_gzipped = function (filename, data) - local gh = gzipopen (filename, "wb9") - if gh then - gh:write (data) - local bytes = gh:seek () - gh:close () - return bytes - end -end - ---- When loading a lua file we try its binary complement first, which ---- is assumed to be located at an identical path, carrying the suffix ---- .luc. - ---- string -> (string * table) -local load_lua_file = function (path) - local foundname = filereplacesuffix (path, "luc") - local code = nil - - local fh = ioopen (foundname, "rb") -- try bin first - if fh then - local chunk = fh:read"*all" - fh:close() - code = load (chunk, "b") - end - - if not code then --- fall back to text file - foundname = filereplacesuffix (path, "lua") - fh = ioopen(foundname, "rb") - if fh then - local chunk = fh:read"*all" - fh:close() - code = load (chunk, "t") - end - end - - if not code then --- probe gzipped file - foundname = filereplacesuffix (path, "lua.gz") - local chunk = load_gzipped (foundname) - if chunk then - code = load (chunk, "t") - end - end - - if not code then return nil, nil end - return foundname, code () -end - ---- define locals in scope -local crude_file_lookup -local crude_file_lookup_verbose -local find_closest -local flush_lookup_cache -local ot_fullinfo -local t1_fullinfo -local load_names -local load_lookups -local read_blacklist -local reload_db -local resolve_name -local resolve_cached -local resolve_fullpath -local save_names -local save_lookups -local update_names -local get_font_filter -local set_font_filter - ---- state of the database -local fonts_reloaded = false -local fonts_read = 0 - ---- limit output when approximate font matching (luaotfload-tool -F) -local fuzzy_limit = 1 --- display closest only - ---- bool? -> dbobj -load_names = function (dry_run) - local starttime = osgettimeofday () - local foundname, data = load_lua_file (names.path.index.lua) - - if data then - report ("both", 2, "db", - "Font names database loaded", "%s", foundname) - report ("info", 3, "db", "Loading took %0.f ms.", - 1000 * (osgettimeofday () - starttime)) - - local db_version, nms_version - if data.meta then - db_version = data.meta.version - else - --- Compatibility branch; the version info used to be - --- stored in the table root which is why updating from - --- an earlier index version broke. - db_version = data.version or -42 --- invalid - end - nms_version = names.version - if db_version ~= nms_version then - report ("both", 0, "db", - [[Version mismatch; expected %4.3f, got %4.3f.]], - nms_version, db_version) - if not fonts_reloaded then - report ("both", 0, "db", [[Force rebuild.]]) - data = update_names ({ }, true, false) - if not data then - report ("both", 0, "db", - "Database creation unsuccessful.") - end - end - end - else - report ("both", 0, "db", - [[Font names database not found, generating new one.]]) - report ("both", 0, "db", - [[This can take several minutes; please be patient.]]) - data = update_names (initialize_namedata (get_font_filter ()), - nil, dry_run) - if not data then - report ("both", 0, "db", "Database creation unsuccessful.") - end - end - return data -end - ---- unit -> unit -load_lookups = function ( ) - local foundname, data = load_lua_file(names.path.lookups.lua) - if data then - report("both", 3, "cache", - "Lookup cache loaded from %s.", foundname) - else - report("both", 1, "cache", - "No lookup cache, creating empty.") - data = { } - end - lookup_cache = data -end - -local regular_synonym = { - book = "r", - normal = "r", - plain = "r", - regular = "r", - roman = "r", -} - -local italic_synonym = { - oblique = true, - slanted = true, - italic = true, -} - -local style_category = { - regular = "r", - bold = "b", - bolditalic = "bi", - italic = "i", - r = "regular", - b = "bold", - bi = "bolditalic", - i = "italic", -} - -local type1_formats = { "tfm", "ofm", } - -local dummy_findfile = resolvers.findfile -- from basics-gen - ---- filemap -> string -> string -> (string | bool) -local verbose_lookup = function (data, kind, filename) - local found = data[kind][filename] - if found ~= nil then - found = data.full[found] - if found == nil then --> texmf - report("info", 0, "db", - "Crude file lookup: req=%s; hit=%s => kpse.", - filename, kind) - found = dummy_findfile(filename) - else - report("info", 0, "db", - "Crude file lookup: req=%s; hit=%s; ret=%s.", - filename, kind, found) - end - return found - end - return false -end - ---- string -> (string * string * bool) -crude_file_lookup_verbose = function (filename) - if not name_index then name_index = load_names() end - local mappings = name_index.mappings - local files = name_index.files - local found - - --- look up in db first ... - found = verbose_lookup(files, "bare", filename) - if found then - return found, nil, true - end - found = verbose_lookup(files, "base", filename) - if found then - return found, nil, true - end - - --- ofm and tfm, returns pair - for i=1, #type1_formats do - local format = type1_formats[i] - if resolvers.findfile(filename, format) then - return file.addsuffix(filename, format), format, true - end - end - return filename, nil, false -end - -local lookup_filename = function (filename) - if not name_index then name_index = load_names () end - local files = name_index.files - local basedata = files.base - local baredata = files.bare - for i = 1, #location_precedence do - local location = location_precedence [i] - local basenames = basedata [location] - local barenames = baredata [location] - local idx - if basenames ~= nil then - idx = basenames [filename] - if idx then - goto done - end - end - if barenames ~= nil then - for j = 1, #format_precedence do - local format = format_precedence [j] - local filemap = barenames [format] - if filemap then - idx = barenames [format] [filename] - if idx then - break - end - end - end - end -::done:: - if idx then - return files.full [idx] - end - end -end - ---- string -> (string * string * bool) -crude_file_lookup = function (filename) - local found = lookup_filename (filename) - - if not found then - found = dummy_findfile(filename) - end - - if found then - return found, nil, true - end - - for i=1, #type1_formats do - local format = type1_formats[i] - if resolvers.findfile(filename, format) then - return file.addsuffix(filename, format), format, true - end - end - - return filename, nil, false -end - ---[[doc-- -Existence of the resolved file name is verified differently depending -on whether the index entry has a texmf flag set. ---doc]]-- - -local get_font_file = function (index) - local entry = name_index.mappings [index] - if not entry then - return false - end - local basename = entry.basename - if entry.location == "texmf" then - if kpselookup(basename) then - return true, basename, entry.subfont - end - else --- system, local - local fullname = name_index.files.full [index] - if lfsisfile (fullname) then - return true, basename, entry.subfont - end - end - return false -end - ---[[doc-- -We need to verify if the result of a cached lookup actually exists in -the texmf or filesystem. Again, due to the schizoprenic nature of the -font managment we have to check both the system path and the texmf. ---doc]]-- - -local verify_font_file = function (basename) - local path = resolve_fullpath (basename) - if path and lfsisfile(path) then - return true - end - if kpsefind_file(basename) then - return true - end - return false -end - ---[[doc-- -Lookups can be quite costly, more so the less specific they are. -Even if we find a matching font eventually, the next time the -user compiles Eir document E will have to stand through the delay -again. -Thus, some caching of results -- even between runs -- is in order. -We’ll just store successful name: lookups in a separate cache file. - -type lookup_cache = (string, (string * num)) dict - -The spec is expected to be modified in place (ugh), so we’ll have to -catalogue what fields actually influence its behavior. - -Idk what the “spec” resolver is for. - - lookup inspects modifies - ---------- ----------------- --------------------------- - file: name forced, name - name:[*] name, style, sub, resolved, sub, name, forced - optsize, size - spec: name, sub resolved, sub, name, forced - -[*] name: contains both the name resolver from luatex-fonts and - resolve_name() below - -From my reading of font-def.lua, what a resolver does is -basically rewrite the “name” field of the specification record -with the resolution. -Also, the fields “resolved”, “sub”, “force” etc. influence the outcome. - ---doc]]-- - -local concat_char = "#" -local hash_fields = { - --- order is important - "specification", "style", "sub", "optsize", "size", -} -local n_hash_fields = #hash_fields - ---- spec -> string -local hash_request = function (specification) - local key = { } --- segments of the hash - for i=1, n_hash_fields do - local field = specification[hash_fields[i]] - if field then - key[#key+1] = field - end - end - return tableconcat(key, concat_char) -end - ---- 'a -> 'a -> table -> (string * int|boolean * boolean) -resolve_cached = function (specification) - if not lookup_cache then load_lookups () end - local request = hash_request(specification) - report("both", 4, "cache", "Looking for %q in cache ...", - request) - - local found = lookup_cache [request] - - --- case 1) cache positive ---------------------------------------- - if found then --- replay fields from cache hit - report("info", 4, "cache", "Found!") - local basename = found[1] - --- check the presence of the file in case it’s been removed - local success = verify_font_file (basename) - if success == true then - return basename, found[2], true - end - report("both", 4, "cache", "Cached file not found; resolving again.") - else - report("both", 4, "cache", "Not cached; resolving.") - end - - --- case 2) cache negative ---------------------------------------- - --- first we resolve normally ... - local filename, subfont = resolve_name (specification) - if not filename then - return nil, nil - end - --- ... then we add the fields to the cache ... ... - local entry = { filename, subfont } - report("both", 4, "cache", "New entry: %s.", request) - lookup_cache [request] = entry - - --- obviously, the updated cache needs to be stored. - --- TODO this should trigger a save only once the - --- document is compiled (finish_pdffile callback?) - report("both", 5, "cache", "Saving updated cache.") - local success = save_lookups () - if not success then --- sad, but not critical - report("both", 0, "cache", "Error writing cache.") - end - return filename, subfont -end - ---- this used to be inlined; with the lookup cache we don’t ---- have to be parsimonious wrt function calls anymore ---- “found” is the match accumulator -local add_to_match = function (found, size, face) - - local continue = true - - local optsize = face.size - - if optsize and next (optsize) then - local dsnsize, maxsize, minsize - dsnsize = optsize[1] - maxsize = optsize[2] - minsize = optsize[3] - - if size ~= nil - and (dsnsize == size or (size > minsize and size <= maxsize)) - then - found[1] = face - continue = false ---> break - else - found[#found+1] = face - end - else - found[1] = face - continue = false ---> break - end - - return found, continue -end - -local choose_closest = function (distances) - local closest = 2^51 - local match - for i = 1, #distances do - local d, index = unpack (distances [i]) - if d < closest then - closest = d - match = index - end - end - return match -end - ---[[doc-- - - choose_size -- Pick a font face of appropriate size from the list - of family members with matching style. There are three categories: - - 1. exact matches: if there is a face whose design size equals - the asked size, it is returned immediately and no further - candidates are inspected. - - 2. range matches: of all faces in whose design range the - requested size falls the one whose center the requested - size is closest to is returned. - - 3. out-of-range matches: of all other faces (i. e. whose range - is above or below the asked size) the one is chosen whose - boundary (upper or lower) is closest to the requested size. - - 4. default matches: if no design size or a design size of zero - is requested, the face with the default size is returned. - ---doc]]-- - ---- int * int * int * int list -> int -> int -local choose_size = function (sizes, askedsize) - local mappings = name_index.mappings - local match = sizes.default - local exact - local inrange = { } --- distance * index list - local norange = { } --- distance * index list - local fontname, subfont - if askedsize ~= 0 then - --- firstly, look for an exactly matching design size or - --- matching range - for i = 1, #sizes do - local dsnsize, high, low, index = unpack (sizes [i]) - if dsnsize == askedsize then - --- exact match, this is what we were looking for - exact = index - goto skip - elseif askedsize < low then - --- below range, add to the norange table - local d = low - askedsize - norange [#norange + 1] = { d, index } - elseif askedsize > high then - --- beyond range, add to the norange table - local d = askedsize - high - norange [#norange + 1] = { d, index } - else - --- range match - local d = ((low + high) / 2) - askedsize - if d < 0 then - d = -d - end - inrange [#inrange + 1] = { d, index } - end - end - end -::skip:: - if exact then - match = exact - elseif #inrange > 0 then - match = choose_closest (inrange) - elseif #norange > 0 then - match = choose_closest (norange) - end - return match -end - ---[[doc-- - - resolve_familyname -- Query the families table for an entry - matching the specification. - The parameters “name” and “style” are pre-sanitized. - ---doc]]-- ---- spec -> string -> string -> int -> string * int -local resolve_familyname = function (specification, name, style, askedsize) - local families = name_index.families - local mappings = name_index.mappings - local candidates = nil - --- arrow code alert - for i = 1, #location_precedence do - local location = location_precedence [i] - local locgroup = families [location] - for j = 1, #format_precedence do - local format = format_precedence [j] - local fmtgroup = locgroup [format] - if fmtgroup then - local familygroup = fmtgroup [name] - if familygroup then - local stylegroup = familygroup [style] - if stylegroup then --- suitable match - candidates = stylegroup - goto done - end - end - end - end - end - if true then - return nil, nil - end -::done:: - index = choose_size (candidates, askedsize) - local success, resolved, subfont = get_font_file (index) - if not success then - return nil, nil - end - report ("info", 2, "db", "Match found: %s(%d).", - resolved, subfont or 0) - return resolved, subfont -end - -local resolve_fontname = function (specification, name, style) - local mappings = name_index.mappings - local fallback = nil - local lastresort = nil - style = style_category [style] - for i = 1, #mappings do - local face = mappings [i] - local prefmodifiers = face.prefmodifiers - local subfamily = face.subfamily - if face.fontname == name - or face.splainname == name - or face.fullname == name - or face.psname == name - then - return face.basename, face.subfont - elseif face.familyname == name then - if prefmodifiers == style - or subfamily == style - then - fallback = face - elseif regular_synonym [prefmodifiers] - or regular_synonym [subfamily] - then - lastresort = face - end - elseif face.metafamily == name - and (regular_synonym [prefmodifiers] - or regular_synonym [subfamily]) - then - lastresort = face - end - end - if fallback then - return fallback.basename, fallback.subfont - end - if lastresort then - return lastresort.basename, lastresort.subfont - end - return nil, nil -end - ---[[doc-- - - resolve_name -- Perform a name: lookup. This first queries the - font families table and, if there is no match for the spec, the - font names table. - The return value is a pair consisting of the file name and the - subfont index if appropriate.. - - the request specification has the fields: - - · features: table - · normal: set of { ccmp clig itlc kern liga locl mark mkmk rlig } - · ??? - · forced: string - · lookup: "name" - · method: string - · name: string - · resolved: string - · size: int - · specification: string (== ":" ) - · sub: string - - The “size” field deserves special attention: if its value is - negative, then it actually specifies a scalefactor of the - design size of the requested font. This happens e.g. if a font is - requested without an explicit “at size”. If the font is part of a - larger collection with different design sizes, this complicates - matters a bit: Normally, the resolver prefers fonts that have a - design size as close as possible to the requested size. If no - size specified, then the design size is implied. But which design - size should that be? Xetex appears to pick the “normal” (unmarked) - size: with Adobe fonts this would be the one that is neither - “caption” nor “subhead” nor “display” &c ... For fonts by Adobe this - seems to be the one that does not receive a “prefmodifiers” field. - (IOW Adobe uses the “prefmodifiers” field to encode the design size - in more or less human readable format.) However, this is not true - of LM and EB Garamond. As this matters only where there are - multiple design sizes to a given font/style combination, we put a - workaround in place that chooses that unmarked version. - - The first return value of “resolve_name” is the file name of the - requested font (string). It can be passed to the fullname resolver - get_font_file(). - The second value is either “false” or an integer indicating the - subfont index in a TTC. - ---doc]]-- - ---- table -> string * (int | bool) -resolve_name = function (specification) - local resolved, subfont - if not name_index then name_index = load_names () end - local name = sanitize_fontname (specification.name) - local style = sanitize_fontname (specification.style) or "r" - local askedsize = specification.optsize - - if askedsize then - askedsize = tonumber (askedsize) - else - askedsize = specification.size - if askedsize and askedsize >= 0 then - askedsize = askedsize / 65536 - else - askedsize = 0 - end - end - - resolved, subfont = resolve_familyname (specification, - name, - style, - askedsize) - if not resolved then - resolved, subfont = resolve_fontname (specification, - name, - style) - end - if not resolved then - resolved = specification.name, false - end - - if not resolved then - if not fonts_reloaded then - return reload_db ("Font not found.", - resolve_name, - specification) - end - end - return resolved, subfont -end - -resolve_fullpath = function (fontname, ext) --- getfilename() - if not name_index then name_index = load_names () end - local files = name_index.files - local basedata = files.base - local baredata = files.bare - for i = 1, #location_precedence do - local location = location_precedence [i] - local basenames = basedata [location] - local idx - if basenames ~= nil then - idx = basenames [fontname] - end - if ext then - local barenames = baredata [location] [ext] - if not idx and barenames ~= nil then - idx = barenames [fontname] - end - end - if idx then - return files.full [idx] - end - end - return "" -end - ---- when reload is triggered we update the database ---- and then re-run the caller with the arg list - ---- string -> ('a -> 'a) -> 'a list -> 'a -reload_db = function (why, caller, ...) - local namedata = name_index - local formats = tableconcat (namedata.meta.formats, ",") - - report ("both", 1, "db", - "Reload initiated (formats: %s); reason: %q.", - formats, why) - - set_font_filter (formats) - namedata = update_names (namedata, false, false) - - if namedata then - fonts_reloaded = true - name_index = namedata - return caller (...) - end - - report ("both", 0, "db", "Database update unsuccessful.") -end - ---- string -> string -> int -local iterative_levenshtein = function (s1, s2) - - local costs = { } - local len1, len2 = #s1, #s2 - - for i = 0, len1 do - local last = i - for j = 0, len2 do - if i == 0 then - costs[j] = j - else - if j > 0 then - local current = costs[j-1] - if stringsub(s1, i, i) ~= stringsub(s2, j, j) then - current = mathmin(current, last, costs[j]) + 1 - end - costs[j-1] = last - last = current - end - end - end - if i > 0 then costs[len2] = last end - end - - return costs[len2]--- lower right has the distance -end - ---- string -> int -> bool -find_closest = function (name, limit) - local name = sanitize_fontname (name) - limit = limit or fuzzy_limit - - if not name_index then name_index = load_names () end - if not name_index or type (name_index) ~= "table" then - if not fonts_reloaded then - return reload_db("no database", find_closest, name) - end - return false - end - - local by_distance = { } --- (int, string list) dict - local distances = { } --- int list - local cached = { } --- (string, int) dict - local mappings = name_index.mappings - local n_fonts = #mappings - - for n = 1, n_fonts do - local current = mappings[n] - --[[ - This is simplistic but surpisingly fast. - Matching is performed against the “fullname” field - of a db record in preprocessed form. We then store the - raw “fullname” at its edit distance. - We should probably do some weighting over all the - font name categories as well as whatever agrep - does. - --]] - local fullname = current.plainname - local sfullname = current.fullname - local dist = cached[sfullname]--- maybe already calculated - - if not dist then - dist = iterative_levenshtein(name, sfullname) - cached[sfullname] = dist - end - local namelst = by_distance[dist] - if not namelst then --- first entry - namelst = { fullname } - distances[#distances+1] = dist - else --- append - namelst[#namelst+1] = fullname - end - by_distance[dist] = namelst - end - - --- print the matches according to their distance - local n_distances = #distances - if n_distances > 0 then --- got some data - tablesort(distances) - limit = mathmin(n_distances, limit) - report(false, 1, "query", - "Displaying %d distance levels.", limit) - - for i = 1, limit do - local dist = distances[i] - local namelst = by_distance[dist] - report(false, 0, "query", - "Distance from \"%s\": %s\n " - .. tableconcat (namelst, "\n "), - name, dist) - end - - return true - end - return false -end --- find_closest() - ---[[doc-- - - load_font_file -- Safely open a font file. See - - regarding the omission of ``fontloader.close()``. - - TODO -- check if fontloader.info() is ready for prime in 0.78+ - -- fields /tables needed: - -- names - -- postscriptname - -- validation_state - -- .. - ---doc]]-- - -local load_font_file = function (filename, subfont) - local rawfont, _msg = fontloaderopen (filename, subfont) - if not rawfont then - report ("log", 1, "db", "ERROR: failed to open %s.", filename) - return - end - return rawfont -end - ---- rawdata -> (int * int * int | bool) - -local get_size_info = function (metadata) - local design_size = metadata.design_size - local design_range_top = metadata.design_range_top - local design_range_bottom = metadata.design_range_bottom - - local fallback_size = design_size ~= 0 and design_size - or design_range_bottom ~= 0 and design_range_bottom - or design_range_top ~= 0 and design_range_top - - if fallback_size then - design_size = (design_size or fallback_size) / 10 - design_range_top = (design_range_top or fallback_size) / 10 - design_range_bottom = (design_range_bottom or fallback_size) / 10 - return { - design_size, design_range_top, design_range_bottom, - } - end - - return false -end - -local get_english_names = function (metadata) - local names = metadata.names - local english_names - - if names then - --inspect(names) - for _, raw_namedata in next, names do - if raw_namedata.lang == "English (US)" then - return raw_namedata.names - end - end - end - - -- no (English) names table, probably a broken font - report("both", 3, "db", - "%s: missing or broken English names table.", basename) - return { fontname = metadata.fontname, - fullname = metadata.fullname, } -end - ---[[-- - In case of broken PS names we set some dummies. However, we cannot - directly modify the font data as returned by fontloader.open() because - it is a userdata object. - - For this reason we copy what is necessary whilst keeping the table - structure the same as in the tfmdata. ---]]-- -local get_raw_info = function (metadata, basename) - local fullname - local fontname - local psname - - local validation_state = metadata.validation_state - if validation_state - and tablecontains (validation_state, "bad_ps_fontname") - then - --- Broken names table, e.g. avkv.ttf with UTF-16 strings; - --- we put some dummies in place like the fontloader - --- (font-otf.lua) does. - report("both", 3, "db", - "%s has invalid postscript font names, using dummies.", - basename) - fontname = "bad-fontname-" .. basename - fullname = "bad-fullname-" .. basename - else - fontname = metadata.fontname - fullname = metadata.fullname - end - - return { - familyname = metadata.familyname, - fontname = fontname, - fontstyle_name = metadata.fontstyle_name, - fullname = fullname, - italicangle = metadata.italicangle, - names = metadata.names, - pfminfo = metadata.pfminfo, - units_per_em = metadata.units_per_em, - version = metadata.version, - design_size = metadata.design_size, - design_range_top = metadata.design_range_top, - design_range_bottom = metadata.design_range_bottom, - } -end - -local organize_namedata = function (rawinfo, - english_names, - basename, - info) - local default_name = english_names.compatfull - or english_names.fullname - or english_names.postscriptname - or rawinfo.fullname - or rawinfo.fontname - or info.fullname - or info.fontname - local default_family = english_names.preffamily - or english_names.family - or rawinfo.familyname - or info.familyname --- local default_modifier = english_names.prefmodifiers --- or english_names.subfamily - local fontnames = { - --- see - --- https://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html - --- http://www.microsoft.com/typography/OTSPEC/name.htm#NameIDs - english = { - --- where a “compatfull” field is given, the value of “fullname” is - --- either identical or differs by separating the style - --- with a hyphen and omitting spaces. (According to the - --- spec, “compatfull” is “Macintosh only”.) - --- Of the three “fullname” fields, this one appears to be the one - --- with the entire name given in a legible, - --- non-abbreviated fashion, for most fonts at any rate. - --- However, in some fonts (e.g. CMU) all three fields are - --- identical. - fullname = --[[ 18 ]] english_names.compatfull - or --[[ 4 ]] english_names.fullname - or default_name, - --- we keep both the “preferred family” and the “family” - --- values around since both are valid but can turn out - --- quite differently, e.g. with Latin Modern: - --- preffamily: “Latin Modern Sans”, - --- family: “LM Sans 10” - preffamily = --[[ 16 ]] english_names.preffamilyname, - family = --[[ 1 ]] english_names.family or default_family, - prefmodifiers = --[[ 17 ]] english_names.prefmodifiers, - subfamily = --[[ 2 ]] english_names.subfamily, - psname = --[[ 6 ]] english_names.postscriptname, - }, - - metadata = { - fullname = rawinfo.fullname, - fontname = rawinfo.fontname, - familyname = rawinfo.familyname, - }, - - info = { - fullname = info.fullname, - familyname = info.familyname, - fontname = info.fontname, - }, - } - - -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size - if rawinfo.fontstyle_name then - --- not present in all fonts, often differs from the preferred - --- subfamily as well as subfamily fields, e.g. with - --- LMSans10-BoldOblique: - --- subfamily: “Bold Italic” - --- prefmodifiers: “10 Bold Oblique” - --- fontstyle_name: “Bold Oblique” - for _, name in next, rawinfo.fontstyle_name do - if name.lang == 1033 then --- I hate magic numbers - fontnames.fontstyle_name = name.name - end - end - end - - return { - sanitized = sanitize_fontnames (fontnames), - fontname = rawinfo.fontname, - fullname = rawinfo.fullname, - familyname = rawinfo.familyname, - } -end - - -local dashsplitter = lpegsplitat "-" - -local split_fontname = function (fontname) - --- sometimes the style hides in the latter part of the - --- fontname, separated by a dash, e.g. “Iwona-Regular”, - --- “GFSSolomos-Regular” - local splitted = { lpegmatch (dashsplitter, fontname) } - if next (splitted) then - return sanitize_fontname (splitted [#splitted]) - end -end - -local organize_styledata = function (fontname, - metadata, - english_names, - info) - local pfminfo = metadata.pfminfo - local names = metadata.names - - return { - --- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size - size = get_size_info (metadata), - weight = pfminfo.weight or 400, - split = split_fontname (fontname), - width = pfminfo.width, - italicangle = metadata.italicangle, - --- this is for querying, see www.ntg.nl/maps/40/07.pdf for details - units_per_em = metadata.units_per_em, - version = metadata.version, - } -end - ---[[doc-- -The data inside an Opentype font file can be quite heterogeneous. -Thus in order to get the relevant information, parts of the original -table as returned by the font file reader need to be relocated. ---doc]]-- - ---- string -> int -> bool -> string -> fontentry - -ot_fullinfo = function (filename, - subfont, - location, - basename, - format, - info) - - local metadata = load_font_file (filename, subfont) - if not metadata then - return nil - end - - local rawinfo = get_raw_info (metadata, basename) - --- Closing the file manually is a tad faster and more memory - --- efficient than having it closed by the gc - fontloaderclose (metadata) - - local english_names = get_english_names (rawinfo) - local namedata = organize_namedata (rawinfo, - english_names, - basename, - info) - local style = organize_styledata (namedata.fontname, - rawinfo, - english_names, - info) - - local res = { - file = { base = basename, - full = filename, - subfont = subfont, - location = location or "system" }, - format = format, - names = namedata, - style = style, - version = rawinfo.version, - } - return res -end - ---[[doc-- - - Type1 font inspector. In comparison with OTF, PFB’s contain a good - deal less name fields which makes it tricky in some parts to find a - meaningful representation for the database. - - Good read: http://www.adobe.com/devnet/font/pdfs/5004.AFM_Spec.pdf - ---doc]]-- - ---- string -> int -> bool -> string -> fontentry - -t1_fullinfo = function (filename, _subfont, location, basename, format) - local sanitized - local metadata = load_font_file (filename) - local fontname = metadata.fontname - local fullname = metadata.fullname - local familyname = metadata.familyname - local italicangle = metadata.italicangle - local splitstyle = split_fontname (fontname) - local style = "" - local weight - - sanitized = sanitize_fontnames ({ - fontname = fontname, - psname = fullname, - pfullname = fullname, - metafamily = family, - familyname = familyname, - weight = metadata.weight, --- string identifier - prefmodifiers = style, - }) - - weight = sanitized.weight - - if weight == "bold" then - style = weight - end - - if italicangle ~= 0 then - style = style .. "italic" - end - - return { - basename = basename, - fullpath = filename, - subfont = false, - location = location or "system", - format = format, - fullname = sanitized.fullname, - fontname = sanitized.fontname, - familyname = sanitized.familyname, - plainname = fullname, - splainname = sanitized.fullname, - psname = sanitized.fontname, - version = metadata.version, - size = false, - splitstyle = splitstyle, - fontstyle_name = style ~= "" and style or weight, - weight = metadata.pfminfo.weight or 400, - italicangle = italicangle, - } -end - -local loaders = { - dfont = ot_fullinfo, - otf = ot_fullinfo, - ttc = ot_fullinfo, - ttf = ot_fullinfo, - - pfb = t1_fullinfo, - pfa = t1_fullinfo, -} - ---- not side-effect free! - -local compare_timestamps = function (fullname, - currentstatus, - currententrystatus, - currentmappings, - targetstatus, - targetentrystatus, - targetmappings) - - local currenttimestamp = currententrystatus - and currententrystatus.timestamp - local targettimestamp = lfsattributes (fullname, "modification") - - if targetentrystatus ~= nil - and targetentrystatus.timestamp == targettimestamp then - report ("log", 3, "db", "Font %q already read.", fullname) - return false - end - - targetentrystatus.timestamp = targettimestamp - targetentrystatus.index = targetentrystatus.index or { } - - if currenttimestamp == targettimestamp - and not targetentrystatus.index [1] - then - --- copy old namedata into new - - for _, currentindex in next, currententrystatus.index do - - local targetindex = #targetentrystatus.index - local fullinfo = currentmappings [currentindex] - local location = #targetmappings + 1 - - targetmappings [location] = fullinfo - targetentrystatus.index [targetindex + 1] = location - end - - report ("log", 3, "db", "Font %q already indexed.", fullname) - - return false - end - - return true -end - -local insert_fullinfo = function (fullname, - basename, - n_font, - loader, - format, - location, - targetmappings, - targetentrystatus, - info) - - local subfont - if n_font ~= false then - subfont = n_font - 1 - else - subfont = false - n_font = 1 - end - - local fullinfo = loader (fullname, subfont, - location, basename, - format, info) - - if not fullinfo then - return false - end - - local index = targetentrystatus.index [n_font] - - if not index then - index = #targetmappings + 1 - end - - targetmappings [index] = fullinfo - targetentrystatus.index [n_font] = index - - return true -end - - - ---- we return true if the font is new or re-indexed ---- string -> dbobj -> dbobj -> bool - -local read_font_names = function (fullname, - currentnames, - targetnames, - location) - - local targetmappings = targetnames.mappings - local targetstatus = targetnames.status --- by full path - local targetentrystatus = targetstatus [fullname] - - if targetentrystatus == nil then - targetentrystatus = { } - targetstatus [fullname] = targetentrystatus - end - - local currentmappings = currentnames.mappings - local currentstatus = currentnames.status - local currententrystatus = currentstatus [fullname] - - local basename = filebasename (fullname) - local barename = filenameonly (fullname) - local entryname = fullname - - if location == "texmf" then - entryname = basename - end - - --- 1) skip if blacklisted - - if names.blacklist[fullname] or names.blacklist[basename] then - report("log", 2, "db", - "Ignoring blacklisted font %q.", fullname) - return false - end - - --- 2) skip if known with same timestamp - - if not compare_timestamps (fullname, - currentstatus, - currententrystatus, - currentmappings, - targetstatus, - targetentrystatus, - targetmappings) - then - return false - end - - --- 3) new font; choose a loader, abort if unknown - - local format = stringlower (filesuffix (basename)) - local loader = loaders [format] --- ot_fullinfo, t1_fullinfo - - if not loader then - report ("both", 0, "db", - "Unknown format: %q, skipping.", format) - return false - end - - --- 4) get basic info, abort if fontloader can’t read it - - local info = fontloaderinfo (fullname) - - if not info then - report ("log", 1, "db", - "Failed to read basic information from %q", basename) - return false - end - - - --- 5) check for subfonts and process each of them - - if type (info) == "table" and #info > 1 then --- ttc - - local success = false --- true if at least one subfont got read - - for n_font = 1, #info do - if insert_fullinfo (fullname, basename, n_font, - loader, format, location, - targetmappings, targetentrystatus, - info) - then - success = true - end - end - - return success - end - - return insert_fullinfo (fullname, basename, false, - loader, format, location, - targetmappings, targetentrystatus, - info) -end - -local path_normalize -do - --- os.type and os.name are constants so we - --- choose a normalization function in advance - --- instead of testing with every call - local os_type, os_name = os.type, os.name - local filecollapsepath = filecollapsepath - local lfsreadlink = lfs.readlink - - --- windows and dos - if os_type == "windows" or os_type == "msdos" then - --- ms platfom specific stuff - path_normalize = function (path) - path = stringgsub(path, '\\', '/') - path = stringlower(path) - path = filecollapsepath(path) - return path - end ---[[doc-- - The special treatment for cygwin was removed with a patch submitted - by Ken Brown. - Reference: http://cygwin.com/ml/cygwin/2013-05/msg00006.html ---doc]]-- - - else -- posix - path_normalize = function (path) - local dest = lfsreadlink(path) - if dest then - if kpsereadable_file(dest) then - path = dest - elseif kpsereadable_file(filejoin(filedirname(path), dest)) then - path = filejoin(file.dirname(path), dest) - else - -- broken symlink? - end - end - path = filecollapsepath(path) - return path - end - end -end - -fonts.path_normalize = path_normalize - -names.blacklist = { } - -local blacklist = names.blacklist -local p_blacklist --- prefixes of dirs - ---- string list -> string list -local collapse_prefixes = function (lst) - --- avoid redundancies in blacklist - if #lst < 2 then - return lst - end - - tablesort(lst) - local cur = lst[1] - local result = { cur } - for i=2, #lst do - local elm = lst[i] - if stringsub(elm, 1, #cur) ~= cur then - --- different prefix - cur = elm - result[#result+1] = cur - end - end - return result -end - ---- string list -> string list -> (string, bool) hash_t -local create_blacklist = function (blacklist, whitelist) - local result = { } - local dirs = { } - - report("info", 2, "db", "Blacklisting %d files and directories.", - #blacklist) - for i=1, #blacklist do - local entry = blacklist[i] - if lfsisdir(entry) then - dirs[#dirs+1] = entry - else - result[blacklist[i]] = true - end - end - - report("info", 2, "db", "Whitelisting %d files.", #whitelist) - for i=1, #whitelist do - result[whitelist[i]] = nil - end - - dirs = collapse_prefixes(dirs) - - --- build the disjunction of the blacklisted directories - for i=1, #dirs do - local p_dir = P(dirs[i]) - if p_blacklist then - p_blacklist = p_blacklist + p_dir - else - p_blacklist = p_dir - end - end - - if p_blacklist == nil then - --- always return false - p_blacklist = Cc(false) - end - - return result -end - ---- unit -> unit -read_blacklist = function () - local files = { - kpselookup ("luaotfload-blacklist.cnf", - {all=true, format="tex"}) - } - local blacklist = { } - local whitelist = { } - - if files and type(files) == "table" then - for _, path in next, files do - for line in iolines (path) do - line = stringstrip(line) -- to get rid of lines like " % foo" - local first_chr = stringsub(line, 1, 1) - if first_chr == "%" or stringis_empty(line) then - -- comment or empty line - elseif first_chr == "-" then - report ("both", 3, "db", - "Whitelisted file %q via %q.", - line, path) - whitelist[#whitelist+1] = stringsub(line, 2, -1) - else - local cmt = stringfind(line, "%%") - if cmt then - line = stringsub(line, 1, cmt - 1) - end - line = stringstrip(line) - report ("both", 3, "db", - "Blacklisted file %q via %q.", - line, path) - blacklist[#blacklist+1] = line - end - end - end - end - names.blacklist = create_blacklist(blacklist, whitelist) -end - -local p_font_filter - -do - local current_formats = { } - - local extension_pattern = function (list) - local pat - for i=#list, 1, -1 do - local e = list[i] - if not pat then - pat = P(e) - else - pat = pat + P(e) - end - end - pat = pat * P(-1) - return (1 - pat)^1 * pat - end - - --- small helper to adjust the font filter pattern (--formats - --- option) - - set_font_filter = function (formats) - - if not formats or type (formats) ~= "string" then - return - end - - if stringsub (formats, 1, 1) == "+" then -- add - formats = lpegmatch (splitcomma, stringsub (formats, 2)) - if formats then - current_formats = tableappend (current_formats, formats) - end - elseif stringsub (formats, 1, 1) == "-" then -- add - formats = lpegmatch (splitcomma, stringsub (formats, 2)) - if formats then - local newformats = { } - for i = 1, #current_formats do - local fmt = current_formats[i] - local include = true - for j = 1, #formats do - if current_formats[i] == formats[j] then - include = false - goto skip - end - end - newformats[#newformats+1] = fmt - ::skip:: - end - current_formats = newformats - end - else -- set - formats = lpegmatch (splitcomma, formats) - if formats then - current_formats = formats - end - end - - p_font_filter = extension_pattern (current_formats) - end - - get_font_filter = function (formats) - return tablefastcopy (current_formats) - end - - --- initialize - set_font_filter (luaotfloadconfig.formats) -end - -local process_dir_tree -process_dir_tree = function (acc, dirs) - if not next (dirs) then --- done - return acc - end - - local pwd = lfscurrentdir () - local dir = dirs[#dirs] - dirs[#dirs] = nil - - if lfschdir (dir) then - lfschdir (pwd) - - local newfiles = { } - local blacklist = names.blacklist - for ent in lfsdir (dir) do - --- filter right away - if ent ~= "." and ent ~= ".." and not blacklist[ent] then - local fullpath = dir .. "/" .. ent - if lfsisdir (fullpath) - and not lpegmatch (p_blacklist, fullpath) - then - dirs[#dirs+1] = fullpath - elseif lfsisfile (fullpath) then - ent = stringlower (ent) - - if lpegmatch (p_font_filter, ent) then - if filesuffix (ent) == "afm" then - --- fontloader.open() will load the afm - --- iff both files are in the same directory - local pfbpath = filereplacesuffix - (fullpath, "pfb") - if lfsisfile (pfbpath) then - newfiles[#newfiles+1] = pfbpath - end - else - newfiles[#newfiles+1] = fullpath - end - end - - end - end - end - return process_dir_tree (tableappend (acc, newfiles), dirs) - end - --- cannot cd; skip - return process_dir_tree (acc, dirs) -end - -local process_dir = function (dir) - local pwd = lfscurrentdir () - if lfschdir (dir) then - lfschdir (pwd) - - local files = { } - local blacklist = names.blacklist - for ent in lfsdir (dir) do - if ent ~= "." and ent ~= ".." and not blacklist[ent] then - local fullpath = dir .. "/" .. ent - if lfsisfile (fullpath) then - ent = stringlower (ent) - if lpegmatch (p_font_filter, ent) - then - if filesuffix (ent) == "afm" then - --- fontloader.open() will load the afm - --- iff both files are in the same - --- directory - local pfbpath = filereplacesuffix - (fullpath, "pfb") - if lfsisfile (pfbpath) then - files[#files+1] = pfbpath - end - else - files[#files+1] = fullpath - end - end - end - end - end - return files - end - return { } -end - ---- string -> bool -> string list -local find_font_files = function (root, recurse) - if lfsisdir (root) then - if recurse == true then - return process_dir_tree ({}, { root }) - else --- kpathsea already delivered the necessary subdirs - return process_dir (root) - end - end -end - ---- truncate_string -- Cut the first part of a string to fit it ---- into a given terminal width. The parameter “restrict” (int) ---- indicates the number of characters already consumed on the ---- line. -local truncate_string = function (str, restrict) - local tw = luaotfloadconfig.termwidth - local wd = tw - restrict - local len = utf8len (str) - if wd - len < 0 then - --- combined length exceeds terminal, - str = ".." .. stringsub(str, len - wd + 2) - end - return str -end - ---[[doc-- - - scan_dir() scans a directory and populates the list of fonts - with all the fonts it finds. - - · dirname : name of the directory to scan - · currentnames : current font db object - · targetnames : font db object to fill - · dry_run : don’t touch anything - ---doc]]-- - ---- string -> dbobj -> dbobj -> bool -> bool -> (int * int) - -local scan_dir = function (dirname, currentnames, targetnames, - dry_run, location) - if lpegmatch (p_blacklist, dirname) then - report ("both", 4, "db", - "Skipping blacklisted directory %s.", dirname) - --- ignore - return 0, 0 - end - local found = find_font_files (dirname, location ~= "texmf") - if not found then - report ("both", 4, "db", - "No such directory: %q; skipping.", dirname) - return 0, 0 - end - report ("both", 4, "db", "Scanning directory %s.", dirname) - - local n_new = 0 --- total of fonts collected - local n_found = #found - local max_fonts = luaotfloadconfig.max_fonts - - report ("both", 4, "db", "%d font files detected.", n_found) - for j=1, n_found do - if max_fonts and fonts_read >= max_fonts then - break - end - - local fullname = found[j] - fullname = path_normalize(fullname) - local new - - if dry_run == true then - local truncated = truncate_string (fullname, 43) - report ("log", 2, "db", - "Would have been loading %s.", fullname) - report_status ("term", "db", - "Would have been loading %s", truncated) - else - local truncated = truncate_string (fullname, 32) - report ("log", 2, "db", "Loading font %s.", fullname) - report_status ("term", "db", "Loading font %s", truncated) - local new = read_font_names (fullname, currentnames, - targetnames, texmf) - if new == true then - fonts_read = fonts_read + 1 - n_new = n_new + 1 - end - end - end - report ("both", 4, "db", "Done. %d fonts indexed in %q.", - n_found, dirname) - return n_found, n_new -end - ---- string list -> string list -local filter_out_pwd = function (dirs) - local result = { } - local pwd = path_normalize (lpegmatch (stripslashes, - lfscurrentdir ())) - for i = 1, #dirs do - --- better safe than sorry - local dir = path_normalize (lpegmatch (stripslashes, dirs[i])) - if not (dir == "." or dir == pwd) then - result[#result+1] = dir - end - end - return result -end - -local path_separator = ostype == "windows" and ";" or ":" - ---[[doc-- - - scan_texmf_fonts() scans all fonts in the texmf tree through the - kpathsea variables OPENTYPEFONTS and TTFONTS of texmf.cnf. - The current working directory comes as “.” (texlive) or absolute - path (miktex) and will always be filtered out. - ---doc]]-- - ---- dbobj -> dbobj -> bool? -> (int * int) - -local scan_texmf_fonts = function (currentnames, targetnames, dry_run) - - local n_scanned, n_new, fontdirs = 0, 0 - local osfontdir = kpseexpand_path "$OSFONTDIR" - - if stringis_empty (osfontdir) then - report ("info", 1, "db", "Scanning TEXMF fonts...") - else - report ("info", 1, "db", "Scanning TEXMF and OS fonts...") - if log.get_loglevel () > 3 then - local osdirs = filesplitpath (osfontdir) - report ("info", 0, "db", - "$OSFONTDIR has %d entries:", #osdirs) - for i = 1, #osdirs do - report ("info", 0, "db", "[%d] %s", i, osdirs[i]) - end - end - end - - fontdirs = kpseexpand_path "$OPENTYPEFONTS" - fontdirs = fontdirs .. path_separator .. kpseexpand_path "$TTFONTS" - fontdirs = fontdirs .. path_separator .. kpseexpand_path "$T1FONTS" - - if not stringis_empty (fontdirs) then - local tasks = filter_out_pwd (filesplitpath (fontdirs)) - report ("info", 3, "db", - "Initiating scan of %d directories.", #tasks) - report_status_start (2, 4) - for _, d in next, tasks do - local found, new = scan_dir (d, currentnames, targetnames, - dry_run, "texmf") - n_scanned = n_scanned + found - n_new = n_new + new - end - report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new) - end - - return n_scanned, n_new -end - ---- TODO stuff those paths into some writable table ---- unit -> string list -local function get_os_dirs () - if os.name == 'macosx' then - return { - filejoin(kpseexpand_path('~'), "Library/Fonts"), - "/Library/Fonts", - "/System/Library/Fonts", - "/Network/Library/Fonts", - } - elseif os.type == "windows" or os.type == "msdos" then - local windir = osgetenv("WINDIR") - return { filejoin(windir, 'Fonts') } - else - local fonts_conves = { --- plural, much? - "/usr/local/etc/fonts/fonts.conf", - "/etc/fonts/fonts.conf", - } - local os_dirs = read_fonts_conf(fonts_conves, find_files) - return os_dirs - end - return {} -end - ---[[doc-- - - scan_os_fonts() scans the OS fonts through - - fontconfig for Unix (reads the fonts.conf file[s] and scans the - directories) - - a static set of directories for Windows and MacOSX - - **NB**: If $OSFONTDIR is nonempty, as it appears to be by default - on Windows setups, the system fonts will have already been - processed while scanning the TEXMF. Thus, this function is - never called. - ---doc]]-- - ---- dbobj -> dbobj -> bool? -> (int * int) -local scan_os_fonts = function (currentnames, - targetnames, - dry_run) - - local n_scanned, n_new = 0, 0 - report ("info", 1, "db", "Scanning OS fonts...") - report ("info", 2, "db", - "Searching in static system directories...") - - report_status_start (2, 4) - for _, d in next, get_os_dirs () do - local found, new = scan_dir (d, currentnames, - targetnames, dry_run) - n_scanned = n_scanned + found - n_new = n_new + new - end - report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new) - - return n_scanned, n_new -end - ---- unit -> (bool, lookup_cache) -flush_lookup_cache = function () - lookup_cache = { } - collectgarbage "collect" - return true, lookup_cache -end - - ---- fontentry list -> filemap - -local generate_filedata = function (mappings) - - report ("both", 2, "db", "Creating filename map.") - - local nmappings = #mappings - - local files = { - bare = { - ["local"] = { }, - system = { }, --- mapped to mapping format -> index in full - texmf = { }, --- mapped to mapping format -> “true” - }, - base = { - ["local"] = { }, - system = { }, --- mapped to index in “full” - texmf = { }, --- set; all values are “true” - }, - full = { }, --- non-texmf - } - - local base = files.base - local bare = files.bare - local full = files.full - - local conflicts = { - basenames = 0, - barenames = 0, - } - - for index = 1, nmappings do - local entry = mappings [index] - - local filedata = entry.file - local format - local location - local fullpath - local basename - local barename - local subfont - - if filedata then --- new entry - format = entry.format --- otf, afm, ... - location = filedata.location --- texmf, system, ... - fullpath = filedata.full - basename = filedata.base - barename = filenameonly (fullpath) - subfont = filedata.subfont - else - format = entry.format --- otf, afm, ... - location = entry.location --- texmf, system, ... - fullpath = entry.fullpath - basename = entry.basename - barename = filenameonly (fullpath) - subfont = entry.subfont - end - - entry.index = index - - --- 1) add to basename table - - local inbase = base [location] --- no format since the suffix is known - - if inbase then - local present = inbase [basename] - if present then - report ("both", 4, "db", - "Conflicting basename: %q already indexed \z - in category %s, ignoring.", - barename, location) - conflicts.basenames = conflicts.basenames + 1 - - --- track conflicts per font - local conflictdata = entry.conflicts - - if not conflictdata then - entry.conflicts = { basename = present } - else -- some conflicts already detected - conflictdata.basename = present - end - - else - inbase [basename] = index - end - else - inbase = { basename = index } - base [location] = inbase - end - - --- 2) add to barename table - - local inbare = bare [location] [format] - - if inbare then - local present = inbare [barename] - if present then - report ("both", 4, "db", - "Conflicting barename: %q already indexed \z - in category %s/%s, ignoring.", - barename, location, format) - conflicts.barenames = conflicts.barenames + 1 - - --- track conflicts per font - local conflictdata = entry.conflicts - - if not conflictdata then - entry.conflicts = { barename = present } - else -- some conflicts already detected - conflictdata.barename = present - end - - else - inbare [barename] = index - end - else - inbare = { [barename] = index } - bare [location] [format] = inbare - end - - --- 3) add to fullpath map - - full [index] = fullpath - end - - return files -end - -local pick_style -local check_regular - -do - local splitfontname = lpeg.splitat "-" - - local choose_exact = function (field) - --- only clean matches, without guessing - if italic_synonym [field] then - return "i" - end - - if field == "bold" then - return "b" - end - - if field == "bolditalic" or field == "boldoblique" then - return "bi" - end - - return false - end - - pick_style = function (fontstyle_name, - prefmodifiers, - subfamily, - splitstyle) - local style - if fontstyle_name then - style = choose_exact (fontstyle_name) - end - if not style then - if prefmodifiers then - style = choose_exact (prefmodifiers) - elseif subfamily then - style = choose_exact (subfamily) - end - end - return style - end - - pick_fallback_style = function (italicangle, weight) - --- more aggressive, but only to determine bold faces - if weight > 500 then --- bold spectrum matches - if italicangle == 0 then - return tostring (weight) - else - return tostring (weight) .. "i" - end - end - return false - end - - --- we use only exact matches here since there are constructs - --- like “regularitalic” (Cabin, Bodoni Old Fashion) - - check_regular = function (fontstyle_name, - prefmodifiers, - subfamily, - splitstyle, - italicangle, - weight) - - if fontstyle_name then - return regular_synonym [fontstyle_name] - elseif prefmodifiers then - return regular_synonym [prefmodifiers] - elseif subfamily then - return regular_synonym [subfamily] - elseif splitstyle then - return regular_synonym [splitstyle] - elseif italicangle == 0 and weight == 400 then - return true - end - - return nil - end -end - -local pull_values = function (entry) - local file = entry.file - local names = entry.names - local style = entry.style - local sanitized = names.sanitized - local english = sanitized.english - local info = sanitized.info - local metadata = sanitized.metadata - - --- pull file info ... - entry.basename = file.base - entry.fullpath = file.full - entry.location = file.location - entry.subfont = file.subfont - - --- pull name info ... - entry.psname = english.psname - entry.fontname = info.fontname or metadata.fontname - entry.fullname = english.fullname or info.fullname - entry.splainname = metadata.fullname - entry.prefmodifiers = english.prefmodifiers - local metafamily = metadata.familyname - local familyname = english.preffamily or english.family - entry.familyname = familyname - if familyname ~= metafamily then - entry.metafamily = metadata.familyname - end - entry.fontstyle_name = sanitized.fontstyle_name - entry.plainname = names.fullname - entry.subfamily = english.subfamily - - --- pull style info ... - entry.italicangle = style.italicangle - entry.size = style.size - entry.splitstyle = style.split - entry.weight = style.weight - - if luaotfloadconfig.strip == true then - entry.file = nil - entry.names = nil - entry.style = nil - end -end - -local add_family = function (name, subtable, modifier, entry) - if not name then --- probably borked font - return - end - local familytable = subtable [name] - if not familytable then - familytable = { } - subtable [name] = familytable - end - - local size = entry.size - - familytable [#familytable + 1] = { - index = entry.index, - modifier = modifier, - } -end - -local get_subtable = function (families, entry) - local location = entry.location - local format = entry.format - local subtable = families [location] [format] - if not subtable then - subtable = { } - families [location] [format] = subtable - end - return subtable -end - -local collect_families = function (mappings) - - report ("info", 2, "db", "Analyzing families.") - - local families = { - ["local"] = { }, - system = { }, - texmf = { }, - } - - for i = 1, #mappings do - - local entry = mappings [i] - - if entry.file then - pull_values (entry) - end - - local subtable = get_subtable (families, entry) - - local familyname = entry.familyname - local metafamily = entry.metafamily - local fontstyle_name = entry.fontstyle_name - local prefmodifiers = entry.prefmodifiers - local subfamily = entry.subfamily - - local weight = entry.weight - local italicangle = entry.italicangle - local splitstyle = entry.splitstyle - - local modifier = pick_style (fontstyle_name, - prefmodifiers, - subfamily, - splitstyle) - - if not modifier then --- regular, exact only - modifier = check_regular (fontstyle_name, - prefmodifiers, - subfamily, - splitstyle, - italicangle, - weight) - end - - if modifier then - add_family (familyname, subtable, modifier, entry) - --- registering the metafamilies is unreliable within the - --- same table as identifiers might interfere with an - --- unmarked style that lacks a metafamily, e.g. - --- - --- iwona condensed regular -> - --- family: iwonacond - --- metafamily: iwona - --- iwona regular -> - --- family: iwona - --- metafamily: ø - --- - --- Both would be registered as under the same family, - --- i.e. “iwona”, and depending on the loading order - --- the query “name:iwona” can resolve to the condensed - --- version instead of the actual unmarked one. The only - --- way around this would be to introduce a separate - --- table for metafamilies and do fallback queries on it. - --- At the moment this is not pressing enough to justify - --- further increasing the index size, maybe if need - --- arises from the user side. --- if metafamily and metafamily ~= familyname then --- add_family (metafamily, subtable, modifier, entry) --- end - elseif weight > 500 then -- in bold spectrum - modifier = pick_fallback_style (italicangle, weight) - if modifier then - add_family (familyname, subtable, modifier, entry) - end - end - end - - collectgarbage "collect" - return families -end - ---[[doc-- - - add_bold_spectrum -- For not-quite-bold faces, determine whether - they can fill in for a missing bold face slot in a matching family. - - Some families like Lucida do not contain real bold / bold italic - members. Instead, they have semibold variants at weight 600 which - we must add in a separate pass. - ---doc]]-- - -local bold_spectrum_low = 501 --- 500 is medium, 900 heavy/black -local bold_weight = 700 -local style_categories = { "r", "b", "i", "bi" } -local bold_categories = { "b", "bi" } - -local group_modifiers = function (mappings, families) - report ("info", 2, "db", "Analyzing shapes, weights, and styles.") - for location, location_data in next, families do - for format, format_data in next, location_data do - for familyname, collected in next, format_data do - local styledata = { } --- will replace the “collected” table - --- First, fill in the ordinary style data that - --- fits neatly into the four relevant modifier - --- categories. - for _, modifier in next, style_categories do - local entries - for key, info in next, collected do - if info.modifier == modifier then - if not entries then - entries = { } - end - local index = info.index - local entry = mappings [index] - local size = entry.size - if size then - entries [#entries + 1] = { - size [1], - size [2], - size [3], - index, - } - else - entries.default = index - end - collected [key] = nil - end - styledata [modifier] = entries - end - end - - --- At this point the family set may still lack - --- entries for bold or bold italic. We will fill - --- those in using the modifier with the numeric - --- weight that is closest to bold (700). - if next (collected) then --- there are uncategorized entries - for _, modifier in next, bold_categories do - if not styledata [modifier] then - local closest - local minimum = 2^51 - for key, info in next, collected do - local info_modifier = tonumber (info.modifier) and "b" or "bi" - if modifier == info_modifier then - local index = info.index - local entry = mappings [index] - local weight = entry.weight - local diff = weight < 700 and 700 - weight or weight - 700 - if diff < minimum then - minimum = diff - closest = weight - end - end - end - if closest then - --- We know there is a substitute face for the modifier. - --- Now we scan the list again to extract the size data - --- in case the shape is available at multiple sizes. - local entries = { } - for key, info in next, collected do - local info_modifier = tonumber (info.modifier) and "b" or "bi" - if modifier == info_modifier then - local index = info.index - local entry = mappings [index] - local size = entry.size - if entry.weight == closest then - if size then - entries [#entries + 1] = { - size [1], - size [2], - size [3], - index, - } - else - entries.default = index - end - end - end - end - styledata [modifier] = entries - end - end - end - end - format_data [familyname] = styledata - end - end - end - return families -end - -local cmp_sizes = function (a, b) - return a [1] < b [1] -end - -local order_design_sizes = function (families) - - report ("info", 2, "db", "Ordering design sizes.") - - for location, data in next, families do - for format, data in next, data do - for familyname, data in next, data do - for style, data in next, data do - tablesort (data, cmp_sizes) - end - end - end - end - - return families -end - -local retrieve_namedata = function (currentnames, - targetnames, - dry_run, - n_rawnames, - n_newnames) - - local rawnames, new = scan_texmf_fonts (currentnames, - targetnames, - dry_run) - - n_rawnames = n_rawnames + rawnames - n_newnames = n_newnames + new - - rawnames, new = scan_os_fonts (currentnames, targetnames, dry_run) - - n_rawnames = n_rawnames + rawnames - n_newnames = n_newnames + new - - return n_rawnames, n_newnames -end - - ---- dbobj -> stats - -local collect_statistics = function (mappings) - local sum_dsnsize, n_dsnsize = 0, 0 - - local fullname, family, families = { }, { }, { } - local subfamily, prefmodifiers, fontstyle_name = { }, { }, { } - - local addtohash = function (hash, item) - if item then - local times = hash [item] - if times then - hash [item] = times + 1 - else - hash [item] = 1 - end - end - end - - local appendtohash = function (hash, key, value) - if key and value then - local entry = hash [key] - if entry then - entry [#entry + 1] = value - else - hash [key] = { value } - end - end - end - - local addtoset = function (hash, key, value) - if key and value then - local set = hash [key] - if set then - set [value] = true - else - hash [key] = { [value] = true } - end - end - end - - local setsize = function (set) - local n = 0 - for _, _ in next, set do - n = n + 1 - end - return n - end - - local hashsum = function (hash) - local n = 0 - for _, m in next, hash do - n = n + m - end - return n - end - - for _, entry in next, mappings do - local style = entry.style - local names = entry.names.sanitized - local englishnames = names.english - - addtohash (fullname, englishnames.fullname) - addtohash (family, englishnames.family) - addtohash (subfamily, englishnames.subfamily) - addtohash (prefmodifiers, englishnames.prefmodifiers) - addtohash (fontstyle_name, names.fontstyle_name) - - addtoset (families, englishnames.family, englishnames.fullname) - - local sizeinfo = entry.style.size - if sizeinfo then - sum_dsnsize = sum_dsnsize + sizeinfo [1] - n_dsnsize = n_dsnsize + 1 - end - end - - --inspect (families) - - local n_fullname = setsize (fullname) - local n_family = setsize (family) - - if log.get_loglevel () > 1 then - local pprint_top = function (hash, n, set) - - local freqs = { } - local items = { } - - for item, value in next, hash do - if set then - freq = setsize (value) - else - freq = value - end - local ifreq = items [freq] - if ifreq then - ifreq [#ifreq + 1] = item - else - items [freq] = { item } - freqs [#freqs + 1] = freq - end - end - - tablesort (freqs) - - local from = #freqs - local to = from - (n - 1) - if to < 1 then - to = 1 - end - - for i = from, to, -1 do - local freq = freqs [i] - local itemlist = items [freq] - - if type (itemlist) == "table" then - itemlist = tableconcat (itemlist, ", ") - end - - report ("both", 0, "db", - " · %4d × %s.", - freq, itemlist) - end - end - - report ("both", 0, "", "~~~~ font index statistics ~~~~") - report ("both", 0, "db", - " · Collected %d fonts (%d names) in %d families.", - #mappings, n_fullname, n_family) - pprint_top (families, 4, true) - - report ("both", 0, "db", - " · %d different “subfamily” kinds.", - setsize (subfamily)) - pprint_top (subfamily, 4) - - report ("both", 0, "db", - " · %d different “prefmodifiers” kinds.", - setsize (prefmodifiers)) - pprint_top (prefmodifiers, 4) - - report ("both", 0, "db", - " · %d different “fontstyle_name” kinds.", - setsize (fontstyle_name)) - pprint_top (fontstyle_name, 4) - end - - local mean_dsnsize = 0 - if n_dsnsize > 0 then - mean_dsnsize = sum_dsnsize / n_dsnsize - end - - return { - mean_dsnsize = mean_dsnsize, - names = { - fullname = n_fullname, - families = n_family, - }, --- style = { --- subfamily = subfamily, --- prefmodifiers = prefmodifiers, --- fontstyle_name = fontstyle_name, --- }, - } -end - ---- force: dictate rebuild from scratch ---- dry_dun: don’t write to the db, just scan dirs - ---- dbobj? -> bool? -> bool? -> dbobj -update_names = function (currentnames, force, dry_run) - - local targetnames - - if luaotfloadconfig.update_live == false then - report ("info", 2, "db", - "Skipping database update.") - --- skip all db updates - return currentnames or name_index - end - - local starttime = osgettimeofday () - local n_rawnames, n_newnames = 0, 0 - - --[[ - The main function, scans everything - - “targetnames” is the final table to return - - force is whether we rebuild it from scratch or not - ]] - report("both", 1, "db", "Updating the font names database" - .. (force and " forcefully." or ".")) - - --- pass 1 get raw data: read font files (normal case) or reuse - --- information present in index - - if luaotfloadconfig.skip_read == true then - --- the difference to a “dry run” is that we don’t search - --- for font files entirely. we also ignore the “force” - --- parameter since it concerns only the font files. - report ("info", 2, "db", - "Ignoring font files, reusing old data.") - currentnames = load_names (false) - targetnames = currentnames - else - if force then - currentnames = initialize_namedata (get_font_filter ()) - else - if not currentnames then - currentnames = load_names (dry_run) - end - if currentnames.meta.version ~= names.version then - report ("both", 1, "db", "No font names database or old " - .. "one found; generating new one.") - currentnames = initialize_namedata (get_font_filter ()) - end - end - - targetnames = initialize_namedata (get_font_filter ()) - - read_blacklist () - - local n_raw, n_new= retrieve_namedata (currentnames, - targetnames, - dry_run, - n_rawnames, - n_newnames) - report ("info", 3, "db", - "Scanned %d font files; %d new entries.", - n_rawnames, n_newnames) - end - - --- pass 2 (optional): collect some stats about the raw font info - if luaotfloadconfig.statistics == true then - targetnames.meta.statistics = collect_statistics - (targetnames.mappings) - end - - --- we always generate the file lookup tables because - --- non-texmf entries are redirected there and the mapping - --- needs to be 100% consistent - - --- pass 3: build filename table - targetnames.files = generate_filedata (targetnames.mappings) - - --- pass 4: build family lookup table - targetnames.families = collect_families (targetnames.mappings) - - --- pass 5: arrange style and size info - targetnames.families = group_modifiers (targetnames.mappings, - targetnames.families) - - --- pass 6: order design size tables - targetnames.families = order_design_sizes (targetnames.families) - - - report ("info", 3, "db", - "Rebuilt in %0.f ms.", - 1000 * (osgettimeofday () - starttime)) - name_index = targetnames - - if dry_run ~= true then - - save_names () - - local success, _lookups = flush_lookup_cache () - if success then - local success = save_lookups () - if success then - report ("info", 2, "cache", - "Lookup cache emptied.") - return targetnames - end - end - end - return targetnames -end - ---- unit -> bool -save_lookups = function ( ) - local path = names.path.lookups - local luaname, lucname = path.lua, path.luc - if fileiswritable (luaname) and fileiswritable (lucname) then - tabletofile (luaname, lookup_cache, true) - osremove (lucname) - caches.compile (lookup_cache, luaname, lucname) - --- double check ... - if lfsisfile (luaname) and lfsisfile (lucname) then - report ("both", 3, "cache", "Lookup cache saved.") - return true - end - report ("info", 0, "cache", "Could not compile lookup cache.") - return false - end - report ("info", 0, "cache", "Lookup cache file not writable.") - if not fileiswritable (luaname) then - report ("info", 0, "cache", "Failed to write %s.", luaname) - end - if not fileiswritable (lucname) then - report ("info", 0, "cache", "Failed to write %s.", lucname) - end - return false -end - ---- save_names() is usually called without the argument ---- dbobj? -> bool -save_names = function (currentnames) - if not currentnames then - currentnames = name_index - end - local path = names.path.index - local luaname, lucname = path.lua, path.luc - if fileiswritable (luaname) and fileiswritable (lucname) then - osremove (lucname) - local gzname = luaname .. ".gz" - if luaotfloadconfig.compress then - local serialized = tableserialize (currentnames, true) - save_gzipped (gzname, serialized) - caches.compile (currentnames, "", lucname) - else - tabletofile (luaname, currentnames, true) - caches.compile (currentnames, luaname, lucname) - end - report ("info", 2, "db", "Font index saved at ...") - local success = false - if lfsisfile (luaname) then - report ("info", 2, "db", "Text: " .. luaname) - success = true - end - if lfsisfile (gzname) then - report ("info", 2, "db", "Gzip: " .. gzname) - success = true - end - if lfsisfile (lucname) then - report ("info", 2, "db", "Byte: " .. lucname) - success = true - end - if success then - return true - else - report ("info", 0, "db", "Could not compile font index.") - return false - end - end - report ("info", 0, "db", "Index file not writable") - if not fileiswritable (luaname) then - report ("info", 0, "db", "Failed to write %s.", luaname) - end - if not fileiswritable (lucname) then - report ("info", 0, "db", "Failed to write %s.", lucname) - end - return false -end - ---[[doc-- - - Below set of functions is modeled after mtx-cache. - ---doc]]-- - ---- string -> string -> string list -> string list -> string list -> unit -local print_cache = function (category, path, luanames, lucnames, rest) - local report_indeed = function (...) - report("info", 0, "cache", ...) - end - report_indeed("Luaotfload cache: %s", category) - report_indeed("location: %s", path) - report_indeed("[raw] %4i", #luanames) - report_indeed("[compiled] %4i", #lucnames) - report_indeed("[other] %4i", #rest) - report_indeed("[total] %4i", #luanames + #lucnames + #rest) -end - ---- string -> string -> string list -> bool -> bool -local purge_from_cache = function (category, path, list, all) - report("info", 1, "cache", "Luaotfload cache: %s %s", - (all and "erase" or "purge"), category) - report("info", 1, "cache", "location: %s",path) - local n = 0 - for i=1,#list do - local filename = list[i] - if stringfind(filename,"luatex%-cache") then -- safeguard - if all then - report("info", 5, "cache", "Removing %s.", filename) - osremove(filename) - n = n + 1 - else - local suffix = filesuffix(filename) - if suffix == "lua" then - local checkname = file.replacesuffix( - filename, "lua", "luc") - if lfsisfile(checkname) then - report("info", 5, "cache", "Removing %s.", filename) - osremove(filename) - n = n + 1 - end - end - end - end - end - report("info", 1, "cache", "Removed lua files : %i", n) - return true -end - ---- string -> string list -> int -> string list -> string list -> string list -> ---- (string list * string list * string list * string list) -local collect_cache collect_cache = function (path, all, n, luanames, - lucnames, rest) - if not all then - local all = find_files (path) - - local luanames, lucnames, rest = { }, { }, { } - return collect_cache(nil, all, 1, luanames, lucnames, rest) - end - - local filename = all[n] - if filename then - local suffix = filesuffix(filename) - if suffix == "lua" then - luanames[#luanames+1] = filename - elseif suffix == "luc" then - lucnames[#lucnames+1] = filename - else - rest[#rest+1] = filename - end - return collect_cache(nil, all, n+1, luanames, lucnames, rest) - end - return luanames, lucnames, rest, all -end - -local getwritablecachepath = function ( ) - --- fonts.handlers.otf doesn’t exist outside a Luatex run, - --- so we have to improvise - local writable = getwritablepath (luaotfloadconfig.cache_dir) - if writable then - return writable - end -end - -local getreadablecachepaths = function ( ) - local readables = caches.getreadablepaths - (luaotfloadconfig.cache_dir) - local result = { } - if readables then - for i=1, #readables do - local readable = readables[i] - if lfsisdir (readable) then - result[#result+1] = readable - end - end - end - return result -end - ---- unit -> unit -local purge_cache = function ( ) - local writable_path = getwritablecachepath () - local luanames, lucnames, rest = collect_cache(writable_path) - if log.get_loglevel() > 1 then - print_cache("writable path", writable_path, luanames, lucnames, rest) - end - local success = purge_from_cache("writable path", writable_path, luanames, false) - return success -end - ---- unit -> unit -local erase_cache = function ( ) - local writable_path = getwritablecachepath () - local luanames, lucnames, rest, all = collect_cache(writable_path) - if log.get_loglevel() > 1 then - print_cache("writable path", writable_path, luanames, lucnames, rest) - end - local success = purge_from_cache("writable path", writable_path, all, true) - return success -end - -local separator = function ( ) - report("info", 0, string.rep("-", 67)) -end - ---- unit -> unit -local show_cache = function ( ) - local readable_paths = getreadablecachepaths () - local writable_path = getwritablecachepath () - local luanames, lucnames, rest = collect_cache(writable_path) - - separator () - print_cache ("writable path", writable_path, - luanames, lucnames, rest) - texio.write_nl"" - for i=1,#readable_paths do - local readable_path = readable_paths[i] - if readable_path ~= writable_path then - local luanames, lucnames = collect_cache (readable_path) - print_cache ("readable path", - readable_path, luanames, lucnames, rest) - end - end - separator() - return true -end - ------------------------------------------------------------------------ ---- export functionality to the namespace “fonts.names” ------------------------------------------------------------------------ - -names.scan_dir = scan_dir -names.set_font_filter = set_font_filter -names.flush_lookup_cache = flush_lookup_cache -names.save_lookups = save_lookups -names.load = load_names -names.data = function () return name_index end -names.save = save_names -names.update = update_names -names.crude_file_lookup = crude_file_lookup -names.crude_file_lookup_verbose = crude_file_lookup_verbose -names.read_blacklist = read_blacklist -names.sanitize_fontname = sanitize_fontname -names.getfilename = resolve_fullpath -names.set_location_precedence = set_location_precedence - ---- font cache -names.purge_cache = purge_cache -names.erase_cache = erase_cache -names.show_cache = show_cache - ---- replace the resolver from luatex-fonts -if luaotfloadconfig.resolver == "cached" then - report("both", 2, "cache", "Caching of name: lookups active.") - names.resolvespec = resolve_cached - names.resolve_name = resolve_cached -else - names.resolvespec = resolve_name - names.resolve_name = resolve_name -end - -names.find_closest = find_closest - --- for testing purpose -names.read_fonts_conf = read_fonts_conf - --- vim:tw=71:sw=4:ts=4:expandtab diff --git a/luaotfload-diagnostics.lua b/luaotfload-diagnostics.lua deleted file mode 100644 index 67119de..0000000 --- a/luaotfload-diagnostics.lua +++ /dev/null @@ -1,682 +0,0 @@ -#!/usr/bin/env texlua ------------------------------------------------------------------------ --- FILE: luaotfload-diagnostics.lua --- DESCRIPTION: functionality accessible by the --diagnose option --- REQUIREMENTS: luaotfload-tool.lua --- AUTHOR: Philipp Gesang (Phg), --- VERSION: 2.5 --- MODIFIED: 2014-01-02 21:23:06+0100 ------------------------------------------------------------------------ --- -local names = fonts.names -local luatexstatus = status -local status = config.luaotfload.status - -local kpse = require "kpse" -local kpseexpand_path = kpse.expand_path -local kpseexpand_var = kpse.expand_var -local kpsefind_file = kpse.find_file - -local lfs = require "lfs" -local lfsattributes = lfs.attributes -local lfsisfile = lfs.isfile -local lfsreadlink = lfs.readlink - -local md5 = require "md5" -local md5sumhexa = md5.sumhexa - -local ioopen = io.open - -local osgetenv = os.getenv -local osname = os.name -local osremove = os.remove -local ostype = os.type -local stringformat = string.format -local stringlower = string.lower -local stringsub = string.sub - -local fileisreadable = file.isreadable -local fileiswritable = file.iswritable -local filesplitpath = file.splitpath -local filesuffix = file.suffix -local ioloaddata = io.loaddata -local lua_of_json = utilities.json.tolua -local tableconcat = table.concat -local tablesortedkeys = table.sortedkeys -local tabletohash = table.tohash - -local lpeg = require "lpeg" -local C, Cg, Ct = lpeg.C, lpeg.Cg, lpeg.Ct -local lpegmatch = lpeg.match - -local report = luaotfload.log.report -local out = function (...) - report (false, 0, "diagnose", ...) -end - -local parsers = luaotfload.parsers -local stripslashes = parsers.stripslashes -local splitcomma = parsers.splitcomma - -local check_index = function (errcnt) - - out "================= font names ==================" - local namedata = names.data() - - if not namedata then - namedata = names.load () - end - - local mappings = namedata.mappings - - if not namedata and namedata.formats and namedata.version then - out "Database corrupt." - return errcnt + 1 - end - - out ("Database version: %.3f.", namedata.meta.version) - out ("Font formats indexed: %s.", - tableconcat (namedata.meta.formats, ", ")) - out ("%d font files indexed.", #mappings) - - local by_format = { } - for i = 1, #mappings do - local record = mappings[i] - local format = stringlower (filesuffix (record.filename)) - local count = by_format[format] - if count then - by_format[format] = count + 1 - else - by_format[format] = 1 - end - end - - local formats = tablesortedkeys (by_format) - for i = 1, #formats do - local format = formats[i] - out ("%20s: %5d", format, by_format[format]) - end - return errcnt -end - -local verify_files = function (errcnt, status) - out "================ verify files =================" - local hashes = status.hashes - local notes = status.notes - if not hashes or #hashes == 0 then - out ("FAILED: cannot read checksums from %s.", status_file) - return 1/0 - elseif not notes then - out ("FAILED: cannot read commit metadata from %s.", - status_file) - return 1/0 - end - - out ("Luaotfload revision %s.", notes.revision) - out ("Committed by %s.", notes.committer) - out ("Timestamp %s.", notes.timestamp) - - local nhashes = #hashes - out ("Testing %d files for integrity.", nhashes) - for i = 1, nhashes do - local fname, canonicalsum = unpack (hashes[i]) - local location = kpsefind_file (fname) - or kpsefind_file (fname, "texmfscripts") - if not location then - errcnt = errcnt + 1 - out ("FAILED: file %s missing.", fname) - else - out ("File: %s.", location) - local raw = ioloaddata (location) - if not raw then - errcnt = errcnt + 1 - out ("FAILED: file %d not readable.", fname) - else - local sum = md5sumhexa (raw) - if sum ~= canonicalsum then - errcnt = errcnt + 1 - out ("FAILED: checksum mismatch for file %s.", - fname) - out ("Expected %s.", canonicalsum) - out ("Got %s.", sum) - else - out ("Ok, %s passed.", fname) - end - end - end - end - return errcnt -end - -local get_tentative_attributes = function (file) - if not lfsisfile (file) then - local chan = ioopen (file, "w") - if chan then - chan:close () - local attributes = lfsattributes (file) - os.remove (file) - return attributes - end - end -end - -local p_permissions = Ct(Cg(Ct(C(1) * C(1) * C(1)), "u") - * Cg(Ct(C(1) * C(1) * C(1)), "g") - * Cg(Ct(C(1) * C(1) * C(1)), "o")) - -local analyze_permissions = function (raw) - return lpegmatch (p_permissions, raw) -end - -local get_permissions = function (t, location) - if stringsub (location, #location) == "/" then - --- strip trailing slashes (lfs idiosyncrasy on Win) - location = lpegmatch (stripslashes, location) - end - local attributes = lfsattributes (location) - - if not attributes and t == "f" then - attributes = get_tentative_attributes (location) - if not attributes then - return false - end - end - - local permissions - - if fileisreadable (location) then - --- link handling appears to be unnecessary because - --- lfs.attributes() will return the information on - --- the link target. - if mode == "link" then --follow and repeat - location = lfsreadlink (location) - attributes = lfsattributes (location) - end - end - - permissions = analyze_permissions (attributes.permissions) - - return { - location = location, - mode = attributes.mode, - owner = attributes.uid, --- useless on windows - permissions = permissions, - attributes = attributes, - } -end - -local check_conformance = function (spec, permissions, errcnt) - local uid = permissions.attributes.uid - local gid = permissions.attributes.gid - local raw = permissions.attributes.permissions - - out ("Owner: %d, group %d, permissions %s.", uid, gid, raw) - if ostype == "unix" then - if uid == 0 or gid == 0 then - out "Owned by the superuser, permission conflict likely." - errcnt = errcnt + 1 - end - end - - local user = permissions.permissions.u - if spec.r == true then - if user[1] == "r" then - out "Readable: ok." - else - out "Not readable: permissions need fixing." - errcnt = errcnt + 1 - end - end - - if spec.w == true then - if user[2] == "w" - or fileiswritable (permissions.location) then - out "Writable: ok." - else - out "Not writable: permissions need fixing." - errcnt = errcnt + 1 - end - end - - return errcnt -end - -local path = names.path - -local desired_permissions = { - { "d", {"r","w"}, function () return caches.getwritablepath () end }, - { "d", {"r","w"}, path.globals.prefix }, - { "f", {"r","w"}, path.index.lua .. ".gz" }, - { "f", {"r","w"}, path.index.luc }, - { "f", {"r","w"}, path.lookups.lua }, - { "f", {"r","w"}, path.lookups.luc }, -} - -local check_permissions = function (errcnt) - out [[=============== file permissions ==============]] - for i = 1, #desired_permissions do - local t, spec, path = unpack (desired_permissions[i]) - if type (path) == "function" then - path = path () - end - - spec = tabletohash (spec) - - out ("Checking permissions of %s.", path) - - local permissions = get_permissions (t, path) - if permissions then - --inspect (permissions) - errcnt = check_conformance (spec, permissions, errcnt) - else - errcnt = errcnt + 1 - end - end - return errcnt -end - -local check_upstream - -if kpsefind_file ("https.lua", "lua") == nil then - check_upstream = function (errcnt) - out [[============= upstream repository ============= - WARNING: Cannot retrieve repository data. - Github API access requires the luasec library. - Grab it from - and retry.]] - return errcnt - end -else ---- github api stuff begin - local https = require "ssl.https" - - local gh_api_root = [[https://api.github.com]] - local release_url = [[https://github.com/lualatex/luaotfload/releases]] - local luaotfload_repo = [[lualatex/luaotfload]] - local user_agent = [[lualatex/luaotfload integrity check]] - local shortbytes = 8 - - local gh_shortrevision = function (rev) - return stringsub (rev, 1, shortbytes) - end - - local gh_encode_parameters = function (parameters) - local acc = {} - for field, value in next, parameters do - --- unsafe, non-urlencoded coz it’s all ascii chars - acc[#acc+1] = field .. "=" .. value - end - return "?" .. tableconcat (acc, "&") - end - - local gh_make_url = function (components, parameters) - local url = tableconcat ({ gh_api_root, - unpack (components) }, - "/") - if parameters then - url = url .. gh_encode_parameters (parameters) - end - return url - end - - local alright = [[HTTP/1.1 200 OK]] - - local gh_api_request = function (...) - local args = {...} - local nargs = #args - local final = args[nargs] - local request = { - url = "", - headers = { ["user-agent"] = user_agent }, - } - if type (final) == "table" then - args[nargs] = nil - request = gh_make_url (args, final) - else - request = gh_make_url (args) - end - - out ("Requesting <%s>.", request) - local response, code, headers, status - = https.request (request) - if status ~= alright then - out "Request failed!" - return false - end - return response - end - - local gh_api_checklimit = function (headers) - local rawlimit = gh_api_request "rate_limit" - local limitdata = lua_of_json (rawlimit) - if not limitdata and limitdata.rate then - out "Cannot parse API rate limit." - return false - end - limitdata = limitdata.rate - - local limit = tonumber (limitdata.limit) - local left = tonumber (limitdata.remaining) - local reset = tonumber (limitdata.reset) - - out ("%d of %d Github API requests left.", left, limit) - if left == 0 then - out ("Cannot make any more API requests.") - if ostype == "unix" then - out ("Try again later at %s.", osdate ("%F %T", reset)) - else --- windows doesn’t C99 - out ("Try again later at %s.", - osdate ("%Y-%m-d %H:%M:%S", reset)) - end - end - return true - end - - local gh_tags = function () - out "Fetching tags from repository, please stand by." - local rawtags = gh_api_request ("repos", - luaotfload_repo, - "tags") - local taglist = lua_of_json (rawtags) - if not taglist or #taglist == 0 then - out "Cannot parse response." - return false - end - - local ntags = #taglist - out ("Repository contains %d tags.", ntags) - local _idx, latest = next (taglist) - out ("The most recent release is %s (revision %s).", - latest.name, - gh_shortrevision (latest.commit.sha)) - return latest - end - - local gh_compare = function (head, base) - if base == nil then - base = "HEAD" - end - out ("Fetching comparison between %s and %s, \z - please stand by.", - gh_shortrevision (head), - gh_shortrevision (base)) - local comparison = base .. "..." .. head - local rawstatus = gh_api_request ("repos", - luaotfload_repo, - "compare", - comparison) - local status = lua_of_json (rawstatus) - if not status then - out "Cannot parse response for status request." - return false - end - return status - end - - local gh_news = function (since) - local compared = gh_compare (since) - if not compared then - return false - end - local behind_by = compared.behind_by - local ahead_by = compared.ahead_by - local status = compared.status - out ("Comparison state: %s.", status) - if behind_by > 0 then - out ("Your Luaotfload is %d \z - revisions behind upstream.", - behind_by) - return behind_by - elseif status == "ahead" then - out "Since you are obviously from the future \z - I assume you already know the repository state." - else - out "Everything up to date. \z - Luaotfload is in sync with upstream." - end - return false - end - - local gh_catchup = function (current, latest) - local compared = gh_compare (latest, current) - local ahead_by = tonumber (compared.ahead_by) - if ahead_by > 0 then - local permalink_url = compared.permalink_url - out ("Your Luaotfload is %d revisions \z - behind the most recent release.", - ahead_by) - out ("To view the commit log, visit <%s>.", - permalink_url) - out ("You can grab an up to date tarball at <%s>.", - release_url) - return true - else - out "There weren't any new releases in the meantime." - out "Luaotfload is up to date." - end - return false - end - - check_upstream = function (current) - out "============= upstream repository =============" - local _succ = gh_api_checklimit () - local behind = gh_news (current) - if behind then - local latest = gh_tags () - local _behind = gh_catchup (current, - latest.commit.sha, - latest.name) - end - end - - --- trivium: diff since the first revision as pushed by Élie - --- in 2009 - --- local firstrevision = "c3ccb3ee07e0a67171c24960966ae974e0dd8e98" - --- check_upstream (firstrevision) -end ---- github api stuff end - -local print_envvar = function (var) - local val = osgetenv (var) - if val then - out ("%20s: %q", stringformat ("$%s", var), val) - return val - else - out ("%20s: ", stringformat ("$%s", var)) - end -end - -local print_path = function (var) - local val = osgetenv (var) - if val then - local paths = filesplitpath (val) - if paths then - local npaths = #paths - if npaths == 1 then - out ("%20s: %q", stringformat ("$%s", var), val) - elseif npaths > 1 then - out ("%20s: <%d items>", stringformat ("$%s", var), npaths) - for i = 1, npaths do - out (" +: %q", paths[i]) - end - else - out ("%20s: ") - end - end - else - out ("%20s: ", stringformat ("$%s", var)) - end -end - -local print_kpsevar = function (var) - var = "$" .. var - local val = kpseexpand_var (var) - if val and val ~= var then - out ("%20s: %q", var, val) - return val - else - out ("%20s: ", var) - end -end - -local print_kpsepath = function (var) - var = "$" .. var - local val = kpseexpand_path (var) - if val and val ~= "" then - local paths = filesplitpath (val) - if paths then - local npaths = #paths - if npaths == 1 then - out ("%20s: %q", var, paths[1]) - elseif npaths > 1 then - out ("%20s: <%d items>", var, npaths) - for i = 1, npaths do - out (" +: %q", paths[i]) - end - else - out ("%20s: ") - end - end - else - out ("%20s: ", var) - end -end - ---- this test first if a variable is set and then expands the ---- paths; this is necessitated by the fact that expand-path will ---- return the empty string both if the variable is unset and if ---- the directory does not exist - -local print_kpsepathvar = function (var) - local vvar = "$" .. var - local val = kpseexpand_var (vvar) - if val and val ~= vvar then - out ("%20s: %q", vvar, val) - print_kpsepath (var) - else - out ("%20s: ", var) - end -end - -local check_environment = function (errcnt) - out "============ environment settings =============" - out ("system: %s/%s", ostype, osname) - if ostype == "unix" and io.popen then - local chan = io.popen ("uname -a", "r") - if chan then - out ("info: %s", chan:read "*all") - chan:close () - end - end - - out "1) *shell environment*" - print_envvar "SHELL" - print_path "PATH" - print_path "OSFONTDIR" - print_envvar "USER" - if ostype == "windows" then - print_envvar "WINDIR" - print_envvar "CD" - print_path "TEMP" - elseif ostype == "unix" then - print_envvar "HOME" - print_envvar "PWD" - print_path "TMPDIR" - end - - out "2) *kpathsea*" - print_kpsepathvar "OPENTYPEFONTS" - print_kpsepathvar "TTFONTS" - - print_kpsepathvar "TEXMFCACHE" - print_kpsepathvar "TEXMFVAR" - - --- the expansion of these can be quite large; as they aren’t - --- usually essential to luaotfload, we won’t dump every single - --- path - print_kpsevar "LUAINPUTS" - print_kpsevar "CLUAINPUTS" - - return errcnt -end - -local anamneses = { - "environment", - "files", - "index", - "repository", - "permissions" -} - -local diagnose = function (job) - local errcnt = 0 - local asked = job.asked_diagnostics - if asked == "all" or asked == "thorough" then - asked = tabletohash (anamneses, true) - else - asked = lpegmatch (splitcomma, asked) - asked = tabletohash (asked, true) - end - - if asked.index == true then - errcnt = check_index (errcnt) - asked.index = nil - end - - if asked.environment == true then - errcnt = check_environment (errcnt) - asked.environment = nil - end - - if asked.files == true then - errcnt = verify_files (errcnt, status) - asked.files = nil - end - - if asked.permissions == true then - errcnt = check_permissions (errcnt) - asked.permissions = nil - end - - if asked.repository == true then - check_upstream (status.notes.revision) - asked.repository = nil - end - - local rest = next (asked) - if rest ~= nil then --> something unknown - out ("Unknown diagnostic %q.", rest) - end - if errcnt == 0 then --> success - out ("Everything appears to be in order, \z - you may sleep well.") - return true, false - end - out ( [[=============================================== - WARNING - =============================================== - - The diagnostic detected %d errors. - - This version of luaotfload may have been - tampered with. Modified versions of the - luaotfload source are unsupported. Read the log - carefully and get a clean version from CTAN or - github: - - × http://ctan.org/tex-archive/macros/luatex/generic/luaotfload - × https://github.com/lualatex/luaotfload/releases - - If you are uncertain as to how to proceed, then - ask on the lualatex mailing list: - - http://www.tug.org/mailman/listinfo/lualatex-dev - - =============================================== -]], errcnt) - return true, false -end - -return diagnose - --- vim:tw=71:sw=4:ts=4:expandtab diff --git a/luaotfload-features.lua b/luaotfload-features.lua deleted file mode 100644 index 4237d71..0000000 --- a/luaotfload-features.lua +++ /dev/null @@ -1,1276 +0,0 @@ -if not modules then modules = { } end modules ["features"] = { - version = "2.5", - comment = "companion to luaotfload-main.lua", - author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local type, next = type, next -local tonumber = tonumber -local tostring = tostring - -local lpeg = require "lpeg" -local lpegmatch = lpeg.match -local P = lpeg.P -local R = lpeg.R -local C = lpeg.C - ----[[ begin included font-ltx.lua ]] ---- this appears to be based in part on luatex-fonts-def.lua - -local fonts = fonts -local definers = fonts.definers -local handlers = fonts.handlers - -local as_script, normalize - -if handlers then - normalize = handlers.otf.features.normalize -else - normalize = function () end - as_script = true -end - - ---HH A bit of tuning for definitions. - -if fonts.constructors then - fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload -end - ---[[HH-- - tricky: we sort of bypass the parser and directly feed all into - the sub parser ---HH]]-- - -function fonts.definers.getspecification(str) - return "", str, "", ":", str -end - -local log = luaotfload.log -local report = log.report - -local stringfind = string.find -local stringlower = string.lower -local stringgsub = string.gsub -local stringsub = string.sub -local stringformat = string.format -local stringis_empty = string.is_empty -local mathceil = math.ceil - -local defaults = { - dflt = { - "ccmp", "locl", "rlig", "liga", "clig", - "kern", "mark", "mkmk", 'itlc', - }, - arab = { - "ccmp", "locl", "isol", "fina", "fin2", - "fin3", "medi", "med2", "init", "rlig", - "calt", "liga", "cswh", "mset", "curs", - "kern", "mark", "mkmk", - }, - deva = { - "ccmp", "locl", "init", "nukt", "akhn", - "rphf", "blwf", "half", "pstf", "vatu", - "pres", "blws", "abvs", "psts", "haln", - "calt", "blwm", "abvm", "dist", "kern", - "mark", "mkmk", - }, - khmr = { - "ccmp", "locl", "pref", "blwf", "abvf", - "pstf", "pres", "blws", "abvs", "psts", - "clig", "calt", "blwm", "abvm", "dist", - "kern", "mark", "mkmk", - }, - thai = { - "ccmp", "locl", "liga", "kern", "mark", - "mkmk", - }, - hang = { - "ccmp", "ljmo", "vjmo", "tjmo", - }, -} - -local global_defaults = { mode = "node" } - -defaults.beng = defaults.deva -defaults.guru = defaults.deva -defaults.gujr = defaults.deva -defaults.orya = defaults.deva -defaults.taml = defaults.deva -defaults.telu = defaults.deva -defaults.knda = defaults.deva -defaults.mlym = defaults.deva -defaults.sinh = defaults.deva - -defaults.syrc = defaults.arab -defaults.mong = defaults.arab -defaults.nko = defaults.arab - -defaults.tibt = defaults.khmr - -defaults.lao = defaults.thai - ----[[ begin excerpt from font-ott.lua ]] - -local scripts = { - ['arab'] = 'arabic', - ['armn'] = 'armenian', - ['bali'] = 'balinese', - ['beng'] = 'bengali', - ['bopo'] = 'bopomofo', - ['brai'] = 'braille', - ['bugi'] = 'buginese', - ['buhd'] = 'buhid', - ['byzm'] = 'byzantine music', - ['cans'] = 'canadian syllabics', - ['cher'] = 'cherokee', - ['copt'] = 'coptic', - ['cprt'] = 'cypriot syllabary', - ['cyrl'] = 'cyrillic', - ['deva'] = 'devanagari', - ['dsrt'] = 'deseret', - ['ethi'] = 'ethiopic', - ['geor'] = 'georgian', - ['glag'] = 'glagolitic', - ['goth'] = 'gothic', - ['grek'] = 'greek', - ['gujr'] = 'gujarati', - ['guru'] = 'gurmukhi', - ['hang'] = 'hangul', - ['hani'] = 'cjk ideographic', - ['hano'] = 'hanunoo', - ['hebr'] = 'hebrew', - ['ital'] = 'old italic', - ['jamo'] = 'hangul jamo', - ['java'] = 'javanese', - ['kana'] = 'hiragana and katakana', - ['khar'] = 'kharosthi', - ['khmr'] = 'khmer', - ['knda'] = 'kannada', - ['lao' ] = 'lao', - ['latn'] = 'latin', - ['limb'] = 'limbu', - ['linb'] = 'linear b', - ['math'] = 'mathematical alphanumeric symbols', - ['mlym'] = 'malayalam', - ['mong'] = 'mongolian', - ['musc'] = 'musical symbols', - ['mymr'] = 'myanmar', - ['nko' ] = "n'ko", - ['ogam'] = 'ogham', - ['orya'] = 'oriya', - ['osma'] = 'osmanya', - ['phag'] = 'phags-pa', - ['phnx'] = 'phoenician', - ['runr'] = 'runic', - ['shaw'] = 'shavian', - ['sinh'] = 'sinhala', - ['sylo'] = 'syloti nagri', - ['syrc'] = 'syriac', - ['tagb'] = 'tagbanwa', - ['tale'] = 'tai le', - ['talu'] = 'tai lu', - ['taml'] = 'tamil', - ['telu'] = 'telugu', - ['tfng'] = 'tifinagh', - ['tglg'] = 'tagalog', - ['thaa'] = 'thaana', - ['thai'] = 'thai', - ['tibt'] = 'tibetan', - ['ugar'] = 'ugaritic cuneiform', - ['xpeo'] = 'old persian cuneiform', - ['xsux'] = 'sumero-akkadian cuneiform', - ['yi' ] = 'yi', -} - -local languages = { - ['aba'] = 'abaza', - ['abk'] = 'abkhazian', - ['ady'] = 'adyghe', - ['afk'] = 'afrikaans', - ['afr'] = 'afar', - ['agw'] = 'agaw', - ['als'] = 'alsatian', - ['alt'] = 'altai', - ['amh'] = 'amharic', - ['ara'] = 'arabic', - ['ari'] = 'aari', - ['ark'] = 'arakanese', - ['asm'] = 'assamese', - ['ath'] = 'athapaskan', - ['avr'] = 'avar', - ['awa'] = 'awadhi', - ['aym'] = 'aymara', - ['aze'] = 'azeri', - ['bad'] = 'badaga', - ['bag'] = 'baghelkhandi', - ['bal'] = 'balkar', - ['bau'] = 'baule', - ['bbr'] = 'berber', - ['bch'] = 'bench', - ['bcr'] = 'bible cree', - ['bel'] = 'belarussian', - ['bem'] = 'bemba', - ['ben'] = 'bengali', - ['bgr'] = 'bulgarian', - ['bhi'] = 'bhili', - ['bho'] = 'bhojpuri', - ['bik'] = 'bikol', - ['bil'] = 'bilen', - ['bkf'] = 'blackfoot', - ['bli'] = 'balochi', - ['bln'] = 'balante', - ['blt'] = 'balti', - ['bmb'] = 'bambara', - ['bml'] = 'bamileke', - ['bos'] = 'bosnian', - ['bre'] = 'breton', - ['brh'] = 'brahui', - ['bri'] = 'braj bhasha', - ['brm'] = 'burmese', - ['bsh'] = 'bashkir', - ['bti'] = 'beti', - ['cat'] = 'catalan', - ['ceb'] = 'cebuano', - ['che'] = 'chechen', - ['chg'] = 'chaha gurage', - ['chh'] = 'chattisgarhi', - ['chi'] = 'chichewa', - ['chk'] = 'chukchi', - ['chp'] = 'chipewyan', - ['chr'] = 'cherokee', - ['chu'] = 'chuvash', - ['cmr'] = 'comorian', - ['cop'] = 'coptic', - ['cos'] = 'corsican', - ['cre'] = 'cree', - ['crr'] = 'carrier', - ['crt'] = 'crimean tatar', - ['csl'] = 'church slavonic', - ['csy'] = 'czech', - ['dan'] = 'danish', - ['dar'] = 'dargwa', - ['dcr'] = 'woods cree', - ['deu'] = 'german', - ['dgr'] = 'dogri', - ['div'] = 'divehi', - ['djr'] = 'djerma', - ['dng'] = 'dangme', - ['dnk'] = 'dinka', - ['dri'] = 'dari', - ['dun'] = 'dungan', - ['dzn'] = 'dzongkha', - ['ebi'] = 'ebira', - ['ecr'] = 'eastern cree', - ['edo'] = 'edo', - ['efi'] = 'efik', - ['ell'] = 'greek', - ['eng'] = 'english', - ['erz'] = 'erzya', - ['esp'] = 'spanish', - ['eti'] = 'estonian', - ['euq'] = 'basque', - ['evk'] = 'evenki', - ['evn'] = 'even', - ['ewe'] = 'ewe', - ['fan'] = 'french antillean', - ['far'] = 'farsi', - ['fin'] = 'finnish', - ['fji'] = 'fijian', - ['fle'] = 'flemish', - ['fne'] = 'forest nenets', - ['fon'] = 'fon', - ['fos'] = 'faroese', - ['fra'] = 'french', - ['fri'] = 'frisian', - ['frl'] = 'friulian', - ['fta'] = 'futa', - ['ful'] = 'fulani', - ['gad'] = 'ga', - ['gae'] = 'gaelic', - ['gag'] = 'gagauz', - ['gal'] = 'galician', - ['gar'] = 'garshuni', - ['gaw'] = 'garhwali', - ['gez'] = "ge'ez", - ['gil'] = 'gilyak', - ['gmz'] = 'gumuz', - ['gon'] = 'gondi', - ['grn'] = 'greenlandic', - ['gro'] = 'garo', - ['gua'] = 'guarani', - ['guj'] = 'gujarati', - ['hai'] = 'haitian', - ['hal'] = 'halam', - ['har'] = 'harauti', - ['hau'] = 'hausa', - ['haw'] = 'hawaiin', - ['hbn'] = 'hammer-banna', - ['hil'] = 'hiligaynon', - ['hin'] = 'hindi', - ['hma'] = 'high mari', - ['hnd'] = 'hindko', - ['ho'] = 'ho', - ['hri'] = 'harari', - ['hrv'] = 'croatian', - ['hun'] = 'hungarian', - ['hye'] = 'armenian', - ['ibo'] = 'igbo', - ['ijo'] = 'ijo', - ['ilo'] = 'ilokano', - ['ind'] = 'indonesian', - ['ing'] = 'ingush', - ['inu'] = 'inuktitut', - ['iri'] = 'irish', - ['irt'] = 'irish traditional', - ['isl'] = 'icelandic', - ['ism'] = 'inari sami', - ['ita'] = 'italian', - ['iwr'] = 'hebrew', - ['jan'] = 'japanese', - ['jav'] = 'javanese', - ['jii'] = 'yiddish', - ['jud'] = 'judezmo', - ['jul'] = 'jula', - ['kab'] = 'kabardian', - ['kac'] = 'kachchi', - ['kal'] = 'kalenjin', - ['kan'] = 'kannada', - ['kar'] = 'karachay', - ['kat'] = 'georgian', - ['kaz'] = 'kazakh', - ['keb'] = 'kebena', - ['kge'] = 'khutsuri georgian', - ['kha'] = 'khakass', - ['khk'] = 'khanty-kazim', - ['khm'] = 'khmer', - ['khs'] = 'khanty-shurishkar', - ['khv'] = 'khanty-vakhi', - ['khw'] = 'khowar', - ['kik'] = 'kikuyu', - ['kir'] = 'kirghiz', - ['kis'] = 'kisii', - ['kkn'] = 'kokni', - ['klm'] = 'kalmyk', - ['kmb'] = 'kamba', - ['kmn'] = 'kumaoni', - ['kmo'] = 'komo', - ['kms'] = 'komso', - ['knr'] = 'kanuri', - ['kod'] = 'kodagu', - ['koh'] = 'korean old hangul', - ['kok'] = 'konkani', - ['kon'] = 'kikongo', - ['kop'] = 'komi-permyak', - ['kor'] = 'korean', - ['koz'] = 'komi-zyrian', - ['kpl'] = 'kpelle', - ['kri'] = 'krio', - ['krk'] = 'karakalpak', - ['krl'] = 'karelian', - ['krm'] = 'karaim', - ['krn'] = 'karen', - ['krt'] = 'koorete', - ['ksh'] = 'kashmiri', - ['ksi'] = 'khasi', - ['ksm'] = 'kildin sami', - ['kui'] = 'kui', - ['kul'] = 'kulvi', - ['kum'] = 'kumyk', - ['kur'] = 'kurdish', - ['kuu'] = 'kurukh', - ['kuy'] = 'kuy', - ['kyk'] = 'koryak', - ['lad'] = 'ladin', - ['lah'] = 'lahuli', - ['lak'] = 'lak', - ['lam'] = 'lambani', - ['lao'] = 'lao', - ['lat'] = 'latin', - ['laz'] = 'laz', - ['lcr'] = 'l-cree', - ['ldk'] = 'ladakhi', - ['lez'] = 'lezgi', - ['lin'] = 'lingala', - ['lma'] = 'low mari', - ['lmb'] = 'limbu', - ['lmw'] = 'lomwe', - ['lsb'] = 'lower sorbian', - ['lsm'] = 'lule sami', - ['lth'] = 'lithuanian', - ['ltz'] = 'luxembourgish', - ['lub'] = 'luba', - ['lug'] = 'luganda', - ['luh'] = 'luhya', - ['luo'] = 'luo', - ['lvi'] = 'latvian', - ['maj'] = 'majang', - ['mak'] = 'makua', - ['mal'] = 'malayalam traditional', - ['man'] = 'mansi', - ['map'] = 'mapudungun', - ['mar'] = 'marathi', - ['maw'] = 'marwari', - ['mbn'] = 'mbundu', - ['mch'] = 'manchu', - ['mcr'] = 'moose cree', - ['mde'] = 'mende', - ['men'] = "me'en", - ['miz'] = 'mizo', - ['mkd'] = 'macedonian', - ['mle'] = 'male', - ['mlg'] = 'malagasy', - ['mln'] = 'malinke', - ['mlr'] = 'malayalam reformed', - ['mly'] = 'malay', - ['mnd'] = 'mandinka', - ['mng'] = 'mongolian', - ['mni'] = 'manipuri', - ['mnk'] = 'maninka', - ['mnx'] = 'manx gaelic', - ['moh'] = 'mohawk', - ['mok'] = 'moksha', - ['mol'] = 'moldavian', - ['mon'] = 'mon', - ['mor'] = 'moroccan', - ['mri'] = 'maori', - ['mth'] = 'maithili', - ['mts'] = 'maltese', - ['mun'] = 'mundari', - ['nag'] = 'naga-assamese', - ['nan'] = 'nanai', - ['nas'] = 'naskapi', - ['ncr'] = 'n-cree', - ['ndb'] = 'ndebele', - ['ndg'] = 'ndonga', - ['nep'] = 'nepali', - ['new'] = 'newari', - ['ngr'] = 'nagari', - ['nhc'] = 'norway house cree', - ['nis'] = 'nisi', - ['niu'] = 'niuean', - ['nkl'] = 'nkole', - ['nko'] = "n'ko", - ['nld'] = 'dutch', - ['nog'] = 'nogai', - ['nor'] = 'norwegian', - ['nsm'] = 'northern sami', - ['nta'] = 'northern tai', - ['nto'] = 'esperanto', - ['nyn'] = 'nynorsk', - ['oci'] = 'occitan', - ['ocr'] = 'oji-cree', - ['ojb'] = 'ojibway', - ['ori'] = 'oriya', - ['oro'] = 'oromo', - ['oss'] = 'ossetian', - ['paa'] = 'palestinian aramaic', - ['pal'] = 'pali', - ['pan'] = 'punjabi', - ['pap'] = 'palpa', - ['pas'] = 'pashto', - ['pgr'] = 'polytonic greek', - ['pil'] = 'pilipino', - ['plg'] = 'palaung', - ['plk'] = 'polish', - ['pro'] = 'provencal', - ['ptg'] = 'portuguese', - ['qin'] = 'chin', - ['raj'] = 'rajasthani', - ['rbu'] = 'russian buriat', - ['rcr'] = 'r-cree', - ['ria'] = 'riang', - ['rms'] = 'rhaeto-romanic', - ['rom'] = 'romanian', - ['roy'] = 'romany', - ['rsy'] = 'rusyn', - ['rua'] = 'ruanda', - ['rus'] = 'russian', - ['sad'] = 'sadri', - ['san'] = 'sanskrit', - ['sat'] = 'santali', - ['say'] = 'sayisi', - ['sek'] = 'sekota', - ['sel'] = 'selkup', - ['sgo'] = 'sango', - ['shn'] = 'shan', - ['sib'] = 'sibe', - ['sid'] = 'sidamo', - ['sig'] = 'silte gurage', - ['sks'] = 'skolt sami', - ['sky'] = 'slovak', - ['sla'] = 'slavey', - ['slv'] = 'slovenian', - ['sml'] = 'somali', - ['smo'] = 'samoan', - ['sna'] = 'sena', - ['snd'] = 'sindhi', - ['snh'] = 'sinhalese', - ['snk'] = 'soninke', - ['sog'] = 'sodo gurage', - ['sot'] = 'sotho', - ['sqi'] = 'albanian', - ['srb'] = 'serbian', - ['srk'] = 'saraiki', - ['srr'] = 'serer', - ['ssl'] = 'south slavey', - ['ssm'] = 'southern sami', - ['sur'] = 'suri', - ['sva'] = 'svan', - ['sve'] = 'swedish', - ['swa'] = 'swadaya aramaic', - ['swk'] = 'swahili', - ['swz'] = 'swazi', - ['sxt'] = 'sutu', - ['syr'] = 'syriac', - ['tab'] = 'tabasaran', - ['taj'] = 'tajiki', - ['tam'] = 'tamil', - ['tat'] = 'tatar', - ['tcr'] = 'th-cree', - ['tel'] = 'telugu', - ['tgn'] = 'tongan', - ['tgr'] = 'tigre', - ['tgy'] = 'tigrinya', - ['tha'] = 'thai', - ['tht'] = 'tahitian', - ['tib'] = 'tibetan', - ['tkm'] = 'turkmen', - ['tmn'] = 'temne', - ['tna'] = 'tswana', - ['tne'] = 'tundra nenets', - ['tng'] = 'tonga', - ['tod'] = 'todo', - ['trk'] = 'turkish', - ['tsg'] = 'tsonga', - ['tua'] = 'turoyo aramaic', - ['tul'] = 'tulu', - ['tuv'] = 'tuvin', - ['twi'] = 'twi', - ['udm'] = 'udmurt', - ['ukr'] = 'ukrainian', - ['urd'] = 'urdu', - ['usb'] = 'upper sorbian', - ['uyg'] = 'uyghur', - ['uzb'] = 'uzbek', - ['ven'] = 'venda', - ['vit'] = 'vietnamese', - ['wa' ] = 'wa', - ['wag'] = 'wagdi', - ['wcr'] = 'west-cree', - ['wel'] = 'welsh', - ['wlf'] = 'wolof', - ['xbd'] = 'tai lue', - ['xhs'] = 'xhosa', - ['yak'] = 'yakut', - ['yba'] = 'yoruba', - ['ycr'] = 'y-cree', - ['yic'] = 'yi classic', - ['yim'] = 'yi modern', - ['zhh'] = 'chinese hong kong', - ['zhp'] = 'chinese phonetic', - ['zhs'] = 'chinese simplified', - ['zht'] = 'chinese traditional', - ['znd'] = 'zande', - ['zul'] = 'zulu' -} - -local features = { - ['aalt'] = 'access all alternates', - ['abvf'] = 'above-base forms', - ['abvm'] = 'above-base mark positioning', - ['abvs'] = 'above-base substitutions', - ['afrc'] = 'alternative fractions', - ['akhn'] = 'akhands', - ['blwf'] = 'below-base forms', - ['blwm'] = 'below-base mark positioning', - ['blws'] = 'below-base substitutions', - ['c2pc'] = 'petite capitals from capitals', - ['c2sc'] = 'small capitals from capitals', - ['calt'] = 'contextual alternates', - ['case'] = 'case-sensitive forms', - ['ccmp'] = 'glyph composition/decomposition', - ['cjct'] = 'conjunct forms', - ['clig'] = 'contextual ligatures', - ['cpsp'] = 'capital spacing', - ['cswh'] = 'contextual swash', - ['curs'] = 'cursive positioning', - ['dflt'] = 'default processing', - ['dist'] = 'distances', - ['dlig'] = 'discretionary ligatures', - ['dnom'] = 'denominators', - ['dtls'] = 'dotless forms', -- math - ['expt'] = 'expert forms', - ['falt'] = 'final glyph alternates', - ['fin2'] = 'terminal forms #2', - ['fin3'] = 'terminal forms #3', - ['fina'] = 'terminal forms', - ['flac'] = 'flattened accents over capitals', -- math - ['frac'] = 'fractions', - ['fwid'] = 'full width', - ['half'] = 'half forms', - ['haln'] = 'halant forms', - ['halt'] = 'alternate half width', - ['hist'] = 'historical forms', - ['hkna'] = 'horizontal kana alternates', - ['hlig'] = 'historical ligatures', - ['hngl'] = 'hangul', - ['hojo'] = 'hojo kanji forms', - ['hwid'] = 'half width', - ['init'] = 'initial forms', - ['isol'] = 'isolated forms', - ['ital'] = 'italics', - ['jalt'] = 'justification alternatives', - ['jp04'] = 'jis2004 forms', - ['jp78'] = 'jis78 forms', - ['jp83'] = 'jis83 forms', - ['jp90'] = 'jis90 forms', - ['kern'] = 'kerning', - ['lfbd'] = 'left bounds', - ['liga'] = 'standard ligatures', - ['ljmo'] = 'leading jamo forms', - ['lnum'] = 'lining figures', - ['locl'] = 'localized forms', - ['mark'] = 'mark positioning', - ['med2'] = 'medial forms #2', - ['medi'] = 'medial forms', - ['mgrk'] = 'mathematical greek', - ['mkmk'] = 'mark to mark positioning', - ['mset'] = 'mark positioning via substitution', - ['nalt'] = 'alternate annotation forms', - ['nlck'] = 'nlc kanji forms', - ['nukt'] = 'nukta forms', - ['numr'] = 'numerators', - ['onum'] = 'old style figures', - ['opbd'] = 'optical bounds', - ['ordn'] = 'ordinals', - ['ornm'] = 'ornaments', - ['palt'] = 'proportional alternate width', - ['pcap'] = 'petite capitals', - ['pnum'] = 'proportional figures', - ['pref'] = 'pre-base forms', - ['pres'] = 'pre-base substitutions', - ['pstf'] = 'post-base forms', - ['psts'] = 'post-base substitutions', - ['pwid'] = 'proportional widths', - ['qwid'] = 'quarter widths', - ['rand'] = 'randomize', - ['rkrf'] = 'rakar forms', - ['rlig'] = 'required ligatures', - ['rphf'] = 'reph form', - ['rtbd'] = 'right bounds', - ['rtla'] = 'right-to-left alternates', - ['rtlm'] = 'right to left math', -- math - ['ruby'] = 'ruby notation forms', - ['salt'] = 'stylistic alternates', - ['sinf'] = 'scientific inferiors', - ['size'] = 'optical size', - ['smcp'] = 'small capitals', - ['smpl'] = 'simplified forms', - -- ['ss01'] = 'stylistic set 1', - -- ['ss02'] = 'stylistic set 2', - -- ['ss03'] = 'stylistic set 3', - -- ['ss04'] = 'stylistic set 4', - -- ['ss05'] = 'stylistic set 5', - -- ['ss06'] = 'stylistic set 6', - -- ['ss07'] = 'stylistic set 7', - -- ['ss08'] = 'stylistic set 8', - -- ['ss09'] = 'stylistic set 9', - -- ['ss10'] = 'stylistic set 10', - -- ['ss11'] = 'stylistic set 11', - -- ['ss12'] = 'stylistic set 12', - -- ['ss13'] = 'stylistic set 13', - -- ['ss14'] = 'stylistic set 14', - -- ['ss15'] = 'stylistic set 15', - -- ['ss16'] = 'stylistic set 16', - -- ['ss17'] = 'stylistic set 17', - -- ['ss18'] = 'stylistic set 18', - -- ['ss19'] = 'stylistic set 19', - -- ['ss20'] = 'stylistic set 20', - ['ssty'] = 'script style', -- math - ['subs'] = 'subscript', - ['sups'] = 'superscript', - ['swsh'] = 'swash', - ['titl'] = 'titling', - ['tjmo'] = 'trailing jamo forms', - ['tnam'] = 'traditional name forms', - ['tnum'] = 'tabular figures', - ['trad'] = 'traditional forms', - ['twid'] = 'third widths', - ['unic'] = 'unicase', - ['valt'] = 'alternate vertical metrics', - ['vatu'] = 'vattu variants', - ['vert'] = 'vertical writing', - ['vhal'] = 'alternate vertical half metrics', - ['vjmo'] = 'vowel jamo forms', - ['vkna'] = 'vertical kana alternates', - ['vkrn'] = 'vertical kerning', - ['vpal'] = 'proportional alternate vertical metrics', - ['vrt2'] = 'vertical rotation', - ['zero'] = 'slashed zero', - - ['trep'] = 'traditional tex replacements', - ['tlig'] = 'traditional tex ligatures', - - ['ss..'] = 'stylistic set ..', - ['cv..'] = 'character variant ..', - ['js..'] = 'justification ..', - - ["dv.."] = "devanagari ..", -} - -local baselines = { - ['hang'] = 'hanging baseline', - ['icfb'] = 'ideographic character face bottom edge baseline', - ['icft'] = 'ideographic character face tope edige baseline', - ['ideo'] = 'ideographic em-box bottom edge baseline', - ['idtp'] = 'ideographic em-box top edge baseline', - ['math'] = 'mathmatical centered baseline', - ['romn'] = 'roman baseline' -} - -local swapped = function (h) - local r = { } - for k, v in next, h do - r[stringgsub(v,"[^a-z0-9]","")] = k -- is already lower - end - return r -end - -local verbosescripts = swapped(scripts ) -local verboselanguages = swapped(languages) -local verbosefeatures = swapped(features ) -local verbosebaselines = swapped(baselines) - ----[[ end excerpt from font-ott.lua ]] - ---[[doc-- - - As discussed, we will issue a warning because of incomplete support - when one of the scripts below is requested. - - Reference: https://github.com/lualatex/luaotfload/issues/31 - ---doc]]-- - -local support_incomplete = table.tohash({ - "deva", "beng", "guru", "gujr", - "orya", "taml", "telu", "knda", - "mlym", "sinh", -}, true) - ---[[doc-- - - Which features are active by default depends on the script - requested. - ---doc]]-- - ---- (string, string) dict -> (string, string) dict -local set_default_features = function (speclist) - speclist = speclist or { } - speclist[""] = nil --- invalid options stub - - --- handle language tag - local language = speclist.language - if language then --- already lowercase at this point - language = stringgsub(language, "[^a-z0-9]", "") - language = rawget(verboselanguages, language) -- srsly, rawget? - or (languages[language] and language) - or "dflt" - else - language = "dflt" - end - speclist.language = language - - --- handle script tag - local script = speclist.script - if script then - script = stringgsub(script, "[^a-z0-9]","") - script = rawget(verbosescripts, script) - or (scripts[script] and script) - or "dflt" - if support_incomplete[script] then - report("log", 0, "load", - "support for the requested script: " - .. "%q may be incomplete", script) - end - else - script = "dflt" - end - speclist.script = script - - report("log", 0, "load", - "auto-selecting default features for script: %s", - script) - - local requested = defaults[script] - if not requested then - report("log", 0, "load", - "no defaults for script %q, falling back to \"dflt\"", - script) - requested = defaults.dflt - end - - for i=1, #requested do - local feat = requested[i] - if speclist[feat] ~= false then speclist[feat] = true end - end - - for feat, state in next, global_defaults do - --- This is primarily intended for setting node - --- mode unless “base” is requested, as stated - --- in the manual. - if not speclist[feat] then speclist[feat] = state end - end - return speclist -end - -local import_values = { - --- That’s what the 1.x parser did, not quite as graciously, - --- with an array of branch expressions. - -- "style", "optsize",--> from slashed notation; handled otherwise - { "lookup", false }, - { "sub", false }, - { "mode", true }, -} - -local lookup_types = { "anon", "file", "kpse", "my", "name", "path" } - -local select_lookup = function (request) - for i=1, #lookup_types do - local lookup = lookup_types[i] - local value = request[lookup] - if value then - return lookup, value - end - end -end - -local supported = { - b = "b", - i = "i", - bi = "bi", - aat = false, - icu = false, - gr = false, -} - ---- (string | (string * string) | bool) list -> (string * number) -local handle_slashed = function (modifiers) - local style, optsize - for i=1, #modifiers do - local mod = modifiers[i] - if type(mod) == "table" and mod[1] == "optsize" then --> optical size - optsize = tonumber(mod[2]) - elseif mod == false then - --- ignore - report("log", 0, - "load", "unsupported font option: %s", v) - elseif supported[mod] then - style = supported[mod] - elseif not stringis_empty(mod) then - style = stringgsub(mod, "[^%a%d]", "") - end - end - return style, optsize -end - -local extract_subfont -do - local eof = P(-1) - local digit = R"09" - --- Theoretically a valid subfont address can be up to ten - --- digits long. - local sub_expr = P"(" * C(digit^1) * P")" * eof - local full_path = C(P(1 - sub_expr)^1) - extract_subfont = full_path * sub_expr -end - ---- spec -> spec -local handle_request = function (specification) - local request = lpegmatch(luaotfload.parsers.font_request, - specification.specification) - if not request then - --- happens when called with an absolute path - --- in an anonymous lookup; - --- we try to behave as friendly as possible - --- just go with it ... - report("log", 1, "load", "invalid request %q of type anon", - specification.specification) - report("log", 1, "load", - "use square bracket syntax or consult the documentation.") - --- The result of \fontname must be re-feedable into \font - --- which is expected by the Latex font mechanism. Now this - --- is complicated with TTC fonts that need to pass the - --- number of the requested subfont along with the file name. - --- Thus we test whether the request is a bare path only or - --- ends in a subfont expression (decimal digits inside - --- parentheses). - --- https://github.com/lualatex/luaotfload/issues/57 - local fullpath, sub = lpegmatch(extract_subfont, - specification.specification) - if fullpath and sub then - specification.sub = tonumber(sub) - specification.name = fullpath - else - specification.name = specification.specification - end - specification.lookup = "path" - return specification - end - local lookup, name = select_lookup(request) - request.features = set_default_features(request.features) - - if name then - specification.name = name - specification.lookup = lookup or specification.lookup - end - - if request.modifiers then - local style, optsize = handle_slashed(request.modifiers) - specification.style, specification.optsize = style, optsize - end - - for n=1, #import_values do - local feat = import_values[n][1] - local keep = import_values[n][2] - local newvalue = request.features[feat] - if newvalue then - specification[feat] = request.features[feat] - if not keep then - request.features[feat] = nil - end - end - end - - --- The next line sets the “rand” feature to “random”; I haven’t - --- investigated it any further (luatex-fonts-ext), so it will - --- just stay here. - specification.features.normal = normalize (request.features) - return specification -end - -if as_script == true then --- skip the remainder of the file - fonts.names.handle_request = handle_request - report ("log", 5, "load", - "Exiting early from luaotfload-features.lua.") - return -else - local registersplit = definers.registersplit - registersplit (":", handle_request, "cryptic") - registersplit ("", handle_request, "more cryptic") -- catches \font\text=[names] -end - ----[[ end included font-ltx.lua ]] - ---[[doc-- -This uses the code from luatex-fonts-merged (<- font-otc.lua) instead -of the removed luaotfload-font-otc.lua. - -TODO find out how far we get setting features without these lines, -relying on luatex-fonts only (it *does* handle features somehow, after -all). ---doc]]-- - --- we assume that the other otf stuff is loaded already - ----[[ begin snippet from font-otc.lua ]] -local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -local report_otf = logs.reporter("fonts","otf loading") - -local otf = fonts.handlers.otf -local registerotffeature = otf.features.register -local setmetatableindex = table.setmetatableindex - ---[[HH-- - - In the userdata interface we can not longer tweak the loaded font as - conveniently as before. For instance, instead of pushing extra data in - in the table using the original structure, we now have to operate on - the mkiv representation. And as the fontloader interface is modelled - after fontforge we cannot change that one too much either. - ---HH]]-- - -local types = { - substitution = "gsub_single", - ligature = "gsub_ligature", - alternate = "gsub_alternate", -} - -setmetatableindex(types, function(t,k) t[k] = k return k end) -- "key" - -local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } } -local noflags = { } - -local function addfeature(data,feature,specifications) - local descriptions = data.descriptions - local resources = data.resources - local lookups = resources.lookups - local gsubfeatures = resources.features.gsub - if gsubfeatures and gsubfeatures[feature] then - -- already present - else - local sequences = resources.sequences - local fontfeatures = resources.features - local unicodes = resources.unicodes - local lookuptypes = resources.lookuptypes - local splitter = lpeg.splitter(" ",unicodes) - local done = 0 - local skip = 0 - if not specifications[1] then - -- so we accept a one entry specification - specifications = { specifications } - end - -- subtables are tables themselves but we also accept flattened singular subtables - for s=1,#specifications do - local specification = specifications[s] - local valid = specification.valid - if not valid or valid(data,specification,feature) then - local initialize = specification.initialize - if initialize then - -- when false is returned we initialize only once - specification.initialize = initialize(specification) and initialize or nil - end - local askedfeatures = specification.features or everywhere - local subtables = specification.subtables or { specification.data } or { } - local featuretype = types[specification.type or "substitution"] - local featureflags = specification.flags or noflags - local added = false - local featurename = stringformat("ctx_%s_%s",feature,s) - local st = { } - for t=1,#subtables do - local list = subtables[t] - local full = stringformat("%s_%s",featurename,t) - st[t] = full - if featuretype == "gsub_ligature" then - lookuptypes[full] = "ligature" - for code, ligature in next, list do - local unicode = tonumber(code) or unicodes[code] - local description = descriptions[unicode] - if description then - local slookups = description.slookups - if type(ligature) == "string" then - ligature = { lpegmatch(splitter,ligature) } - end - local present = true - for i=1,#ligature do - if not descriptions[ligature[i]] then - present = false - break - end - end - if present then - if slookups then - slookups[full] = ligature - else - description.slookups = { [full] = ligature } - end - done, added = done + 1, true - else - skip = skip + 1 - end - end - end - elseif featuretype == "gsub_single" then - lookuptypes[full] = "substitution" - for code, replacement in next, list do - local unicode = tonumber(code) or unicodes[code] - local description = descriptions[unicode] - if description then - local slookups = description.slookups - replacement = tonumber(replacement) or unicodes[replacement] - if descriptions[replacement] then - if slookups then - slookups[full] = replacement - else - description.slookups = { [full] = replacement } - end - done, added = done + 1, true - end - end - end - end - end - if added then - -- script = { lang1, lang2, lang3 } or script = { lang1 = true, ... } - for k, v in next, askedfeatures do - if v[1] then - askedfeatures[k] = table.tohash(v) - end - end - sequences[#sequences+1] = { - chain = 0, - features = { [feature] = askedfeatures }, - flags = featureflags, - name = featurename, - subtables = st, - type = featuretype, - } - -- register in metadata (merge as there can be a few) - if not gsubfeatures then - gsubfeatures = { } - fontfeatures.gsub = gsubfeatures - end - local k = gsubfeatures[feature] - if not k then - k = { } - gsubfeatures[feature] = k - end - for script, languages in next, askedfeatures do - local kk = k[script] - if not kk then - kk = { } - k[script] = kk - end - for language, value in next, languages do - kk[language] = value - end - end - end - end - end - if trace_loading then - report_otf("registering feature %a, affected glyphs %a, skipped glyphs %a",feature,done,skip) - end - end -end - -otf.enhancers.addfeature = addfeature - -local extrafeatures = { } - -function otf.addfeature(name,specification) - extrafeatures[name] = specification -end - -local function enhance(data,filename,raw) - for feature, specification in next, extrafeatures do - addfeature(data,feature,specification) - end -end - -otf.enhancers.register("check extra features",enhance) - ----[[ end snippet from font-otc.lua ]] - -local tlig = { - { - type = "substitution", - features = everywhere, - data = { - [0x0022] = 0x201D, -- quotedblright - [0x0027] = 0x2019, -- quoteleft - [0x0060] = 0x2018, -- quoteright - }, - flags = { }, - }, - { - type = "ligature", - features = everywhere, - data = { - [0x2013] = {0x002D, 0x002D}, -- endash - [0x2014] = {0x002D, 0x002D, 0x002D}, -- emdash - [0x201C] = {0x2018, 0x2018}, -- quotedblleft - [0x201D] = {0x2019, 0x2019}, -- quotedblright - [0x00A1] = {0x0021, 0x2018}, -- exclamdown - [0x00BF] = {0x003F, 0x2018}, -- questiondown - --- next three originate in T1 encoding; Xetex applies - --- them too - [0x201E] = {0x002C, 0x002C}, -- quotedblbase - [0x00AB] = {0x003C, 0x003C}, -- LEFT-POINTING DOUBLE ANGLE QUOTATION MARK - [0x00BB] = {0x003E, 0x003E}, -- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK - }, - flags = { }, - }, - { - type = "ligature", - features = everywhere, - data = { - [0x201C] = {0x0060, 0x0060}, -- quotedblleft - [0x201D] = {0x0027, 0x0027}, -- quotedblright - [0x00A1] = {0x0021, 0x0060}, -- exclamdown - [0x00BF] = {0x003F, 0x0060}, -- questiondown - }, - flags = { }, - }, -} - -otf.addfeature ("tlig", tlig) -otf.addfeature ("trep", { }) - -local anum_arabic = { --- these are the same as in font-otc - [0x0030] = 0x0660, - [0x0031] = 0x0661, - [0x0032] = 0x0662, - [0x0033] = 0x0663, - [0x0034] = 0x0664, - [0x0035] = 0x0665, - [0x0036] = 0x0666, - [0x0037] = 0x0667, - [0x0038] = 0x0668, - [0x0039] = 0x0669, -} - -local anum_persian = {--- these are the same as in font-otc - [0x0030] = 0x06F0, - [0x0031] = 0x06F1, - [0x0032] = 0x06F2, - [0x0033] = 0x06F3, - [0x0034] = 0x06F4, - [0x0035] = 0x06F5, - [0x0036] = 0x06F6, - [0x0037] = 0x06F7, - [0x0038] = 0x06F8, - [0x0039] = 0x06F9, -} - -local function valid(data) - local features = data.resources.features - if features then - for k, v in next, features do - for k, v in next, v do - if v.arab then - return true - end - end - end - end -end - -local anum_specification = { - { - type = "substitution", - features = { arab = { far = true, urd = true, snd = true } }, - data = anum_persian, - flags = { }, - valid = valid, - }, - { - type = "substitution", - features = { arab = { ["*"] = true } }, - data = anum_arabic, - flags = { }, - valid = valid, - }, -} - ---[[doc-- - - Below the specifications as given in the removed font-otc.lua. - The rest was identical to what this file had from the beginning. - Both make the “anum.tex” test pass anyways. - ---doc]]-- - -otf.addfeature("anum",anum_specification) - -registerotffeature { - name = 'anum', - description = 'arabic digits', -} - --- vim:tw=71:sw=4:ts=4:expandtab diff --git a/luaotfload-fontloader.lua b/luaotfload-fontloader.lua deleted file mode 100644 index 8c31750..0000000 --- a/luaotfload-fontloader.lua +++ /dev/null @@ -1,13617 +0,0 @@ --- merged file : luatex-fonts-merged.lua --- parent file : luatex-fonts.lua --- merge date : 02/07/14 00:57:35 - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['l-lua']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local major,minor=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") -_MAJORVERSION=tonumber(major) or 5 -_MINORVERSION=tonumber(minor) or 1 -_LUAVERSION=_MAJORVERSION+_MINORVERSION/10 -if not lpeg then - lpeg=require("lpeg") -end -if loadstring then - local loadnormal=load - function load(first,...) - if type(first)=="string" then - return loadstring(first,...) - else - return loadnormal(first,...) - end - end -else - loadstring=load -end -if not ipairs then - local function iterate(a,i) - i=i+1 - local v=a[i] - if v~=nil then - return i,v - end - end - function ipairs(a) - return iterate,a,0 - end -end -if not pairs then - function pairs(t) - return next,t - end -end -if not table.unpack then - table.unpack=_G.unpack -elseif not unpack then - _G.unpack=table.unpack -end -if not package.loaders then - package.loaders=package.searchers -end -local print,select,tostring=print,select,tostring -local inspectors={} -function setinspector(inspector) - inspectors[#inspectors+1]=inspector -end -function inspect(...) - for s=1,select("#",...) do - local value=select(s,...) - local done=false - for i=1,#inspectors do - done=inspectors[i](value) - if done then - break - end - end - if not done then - print(tostring(value)) - end - end -end -local dummy=function() end -function optionalrequire(...) - local ok,result=xpcall(require,dummy,...) - if ok then - return result - end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['l-lpeg']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -lpeg=require("lpeg") -if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end -local type,next,tostring=type,next,tostring -local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format -local floor=math.floor -local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt -local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print -if setinspector then - setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) -end -lpeg.patterns=lpeg.patterns or {} -local patterns=lpeg.patterns -local anything=P(1) -local endofstring=P(-1) -local alwaysmatched=P(true) -patterns.anything=anything -patterns.endofstring=endofstring -patterns.beginofstring=alwaysmatched -patterns.alwaysmatched=alwaysmatched -local sign=S('+-') -local zero=P('0') -local digit=R('09') -local octdigit=R("07") -local lowercase=R("az") -local uppercase=R("AZ") -local underscore=P("_") -local hexdigit=digit+lowercase+uppercase -local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") -local newline=crlf+S("\r\n") -local escaped=P("\\")*anything -local squote=P("'") -local dquote=P('"') -local space=P(" ") -local period=P(".") -local comma=P(",") -local utfbom_32_be=P('\000\000\254\255') -local utfbom_32_le=P('\255\254\000\000') -local utfbom_16_be=P('\254\255') -local utfbom_16_le=P('\255\254') -local utfbom_8=P('\239\187\191') -local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8 -local utftype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")+alwaysmatched*Cc("utf-8") -local utfstricttype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8") -local utfoffset=utfbom_32_be*Cc(4)+utfbom_32_le*Cc(4)+utfbom_16_be*Cc(2)+utfbom_16_le*Cc(2)+utfbom_8*Cc(3)+Cc(0) -local utf8next=R("\128\191") -patterns.utfbom_32_be=utfbom_32_be -patterns.utfbom_32_le=utfbom_32_le -patterns.utfbom_16_be=utfbom_16_be -patterns.utfbom_16_le=utfbom_16_le -patterns.utfbom_8=utfbom_8 -patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") -patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") -patterns.utf8one=R("\000\127") -patterns.utf8two=R("\194\223")*utf8next -patterns.utf8three=R("\224\239")*utf8next*utf8next -patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next -patterns.utfbom=utfbom -patterns.utftype=utftype -patterns.utfstricttype=utfstricttype -patterns.utfoffset=utfoffset -local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four -local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false) -local utf8character=P(1)*R("\128\191")^0 -patterns.utf8=utf8char -patterns.utf8char=utf8char -patterns.utf8character=utf8character -patterns.validutf8=validutf8char -patterns.validutf8char=validutf8char -local eol=S("\n\r") -local spacer=S(" \t\f\v") -local whitespace=eol+spacer -local nonspacer=1-spacer -local nonwhitespace=1-whitespace -patterns.eol=eol -patterns.spacer=spacer -patterns.whitespace=whitespace -patterns.nonspacer=nonspacer -patterns.nonwhitespace=nonwhitespace -local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) -local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0)) -patterns.stripper=stripper -patterns.collapser=collapser -patterns.lowercase=lowercase -patterns.uppercase=uppercase -patterns.letter=patterns.lowercase+patterns.uppercase -patterns.space=space -patterns.tab=P("\t") -patterns.spaceortab=patterns.space+patterns.tab -patterns.newline=newline -patterns.emptyline=newline^1 -patterns.equal=P("=") -patterns.comma=comma -patterns.commaspacer=comma*spacer^0 -patterns.period=period -patterns.colon=P(":") -patterns.semicolon=P(";") -patterns.underscore=underscore -patterns.escaped=escaped -patterns.squote=squote -patterns.dquote=dquote -patterns.nosquote=(escaped+(1-squote))^0 -patterns.nodquote=(escaped+(1-dquote))^0 -patterns.unsingle=(squote/"")*patterns.nosquote*(squote/"") -patterns.undouble=(dquote/"")*patterns.nodquote*(dquote/"") -patterns.unquoted=patterns.undouble+patterns.unsingle -patterns.unspacer=((patterns.spacer^1)/"")^0 -patterns.singlequoted=squote*patterns.nosquote*squote -patterns.doublequoted=dquote*patterns.nodquote*dquote -patterns.quoted=patterns.doublequoted+patterns.singlequoted -patterns.digit=digit -patterns.octdigit=octdigit -patterns.hexdigit=hexdigit -patterns.sign=sign -patterns.cardinal=digit^1 -patterns.integer=sign^-1*digit^1 -patterns.unsigned=digit^0*period*digit^1 -patterns.float=sign^-1*patterns.unsigned -patterns.cunsigned=digit^0*comma*digit^1 -patterns.cfloat=sign^-1*patterns.cunsigned -patterns.number=patterns.float+patterns.integer -patterns.cnumber=patterns.cfloat+patterns.integer -patterns.oct=zero*octdigit^1 -patterns.octal=patterns.oct -patterns.HEX=zero*P("X")*(digit+uppercase)^1 -patterns.hex=zero*P("x")*(digit+lowercase)^1 -patterns.hexadecimal=zero*S("xX")*hexdigit^1 -patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigit^1+hexdigit^1*period*hexdigit^0+hexdigit^1)*(S("pP")*sign^-1*hexdigit^1)^-1 -patterns.decafloat=sign^-1*(digit^0*period*digit^1+digit^1*period*digit^0+digit^1)*S("eE")*sign^-1*digit^1 -patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring -patterns.somecontent=(anything-newline-space)^1 -patterns.beginline=#(1-newline) -patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(P(-1)+Cc(" ")))^0)) -local function anywhere(pattern) - return P { P(pattern)+1*V(1) } -end -lpeg.anywhere=anywhere -function lpeg.instringchecker(p) - p=anywhere(p) - return function(str) - return lpegmatch(p,str) and true or false - end -end -function lpeg.splitter(pattern,action) - return (((1-P(pattern))^1)/action+1)^0 -end -function lpeg.tsplitter(pattern,action) - return Ct((((1-P(pattern))^1)/action+1)^0) -end -local splitters_s,splitters_m,splitters_t={},{},{} -local function splitat(separator,single) - local splitter=(single and splitters_s[separator]) or splitters_m[separator] - if not splitter then - separator=P(separator) - local other=C((1-separator)^0) - if single then - local any=anything - splitter=other*(separator*C(any^0)+"") - splitters_s[separator]=splitter - else - splitter=other*(separator*other)^0 - splitters_m[separator]=splitter - end - end - return splitter -end -local function tsplitat(separator) - local splitter=splitters_t[separator] - if not splitter then - splitter=Ct(splitat(separator)) - splitters_t[separator]=splitter - end - return splitter -end -lpeg.splitat=splitat -lpeg.tsplitat=tsplitat -function string.splitup(str,separator) - if not separator then - separator="," - end - return lpegmatch(splitters_m[separator] or splitat(separator),str) -end -local cache={} -function lpeg.split(separator,str) - local c=cache[separator] - if not c then - c=tsplitat(separator) - cache[separator]=c - end - return lpegmatch(c,str) -end -function string.split(str,separator) - if separator then - local c=cache[separator] - if not c then - c=tsplitat(separator) - cache[separator]=c - end - return lpegmatch(c,str) - else - return { str } - end -end -local spacing=patterns.spacer^0*newline -local empty=spacing*Cc("") -local nonempty=Cs((1-spacing)^1)*spacing^-1 -local content=(empty+nonempty)^1 -patterns.textline=content -local linesplitter=tsplitat(newline) -patterns.linesplitter=linesplitter -function string.splitlines(str) - return lpegmatch(linesplitter,str) -end -local cache={} -function lpeg.checkedsplit(separator,str) - local c=cache[separator] - if not c then - separator=P(separator) - local other=C((1-separator)^1) - c=Ct(separator^0*other*(separator^1*other)^0) - cache[separator]=c - end - return lpegmatch(c,str) -end -function string.checkedsplit(str,separator) - local c=cache[separator] - if not c then - separator=P(separator) - local other=C((1-separator)^1) - c=Ct(separator^0*other*(separator^1*other)^0) - cache[separator]=c - end - return lpegmatch(c,str) -end -local function f2(s) local c1,c2=byte(s,1,2) return c1*64+c2-12416 end -local function f3(s) local c1,c2,c3=byte(s,1,3) return (c1*64+c2)*64+c3-925824 end -local function f4(s) local c1,c2,c3,c4=byte(s,1,4) return ((c1*64+c2)*64+c3)*64+c4-63447168 end -local utf8byte=patterns.utf8one/byte+patterns.utf8two/f2+patterns.utf8three/f3+patterns.utf8four/f4 -patterns.utf8byte=utf8byte -local cache={} -function lpeg.stripper(str) - if type(str)=="string" then - local s=cache[str] - if not s then - s=Cs(((S(str)^1)/""+1)^0) - cache[str]=s - end - return s - else - return Cs(((str^1)/""+1)^0) - end -end -local cache={} -function lpeg.keeper(str) - if type(str)=="string" then - local s=cache[str] - if not s then - s=Cs((((1-S(str))^1)/""+1)^0) - cache[str]=s - end - return s - else - return Cs((((1-str)^1)/""+1)^0) - end -end -function lpeg.frontstripper(str) - return (P(str)+P(true))*Cs(anything^0) -end -function lpeg.endstripper(str) - return Cs((1-P(str)*endofstring)^0) -end -function lpeg.replacer(one,two,makefunction,isutf) - local pattern - local u=isutf and utf8char or 1 - if type(one)=="table" then - local no=#one - local p=P(false) - if no==0 then - for k,v in next,one do - p=p+P(k)/v - end - pattern=Cs((p+u)^0) - elseif no==1 then - local o=one[1] - one,two=P(o[1]),o[2] - pattern=Cs((one/two+u)^0) - else - for i=1,no do - local o=one[i] - p=p+P(o[1])/o[2] - end - pattern=Cs((p+u)^0) - end - else - pattern=Cs((P(one)/(two or "")+u)^0) - end - if makefunction then - return function(str) - return lpegmatch(pattern,str) - end - else - return pattern - end -end -function lpeg.finder(lst,makefunction,isutf) - local pattern - if type(lst)=="table" then - pattern=P(false) - if #lst==0 then - for k,v in next,lst do - pattern=pattern+P(k) - end - else - for i=1,#lst do - pattern=pattern+P(lst[i]) - end - end - else - pattern=P(lst) - end - if isutf then - pattern=((utf8char or 1)-pattern)^0*pattern - else - pattern=(1-pattern)^0*pattern - end - if makefunction then - return function(str) - return lpegmatch(pattern,str) - end - else - return pattern - end -end -local splitters_f,splitters_s={},{} -function lpeg.firstofsplit(separator) - local splitter=splitters_f[separator] - if not splitter then - local pattern=P(separator) - splitter=C((1-pattern)^0) - splitters_f[separator]=splitter - end - return splitter -end -function lpeg.secondofsplit(separator) - local splitter=splitters_s[separator] - if not splitter then - local pattern=P(separator) - splitter=(1-pattern)^0*pattern*C(anything^0) - splitters_s[separator]=splitter - end - return splitter -end -local splitters_s,splitters_p={},{} -function lpeg.beforesuffix(separator) - local splitter=splitters_s[separator] - if not splitter then - local pattern=P(separator) - splitter=C((1-pattern)^0)*pattern*endofstring - splitters_s[separator]=splitter - end - return splitter -end -function lpeg.afterprefix(separator) - local splitter=splitters_p[separator] - if not splitter then - local pattern=P(separator) - splitter=pattern*C(anything^0) - splitters_p[separator]=splitter - end - return splitter -end -function lpeg.balancer(left,right) - left,right=P(left),P(right) - return P { left*((1-left-right)+V(1))^0*right } -end -local nany=utf8char/"" -function lpeg.counter(pattern) - pattern=Cs((P(pattern)/" "+nany)^0) - return function(str) - return #lpegmatch(pattern,str) - end -end -utf=utf or (unicode and unicode.utf8) or {} -local utfcharacters=utf and utf.characters or string.utfcharacters -local utfgmatch=utf and utf.gmatch -local utfchar=utf and utf.char -lpeg.UP=lpeg.P -if utfcharacters then - function lpeg.US(str) - local p=P(false) - for uc in utfcharacters(str) do - p=p+P(uc) - end - return p - end -elseif utfgmatch then - function lpeg.US(str) - local p=P(false) - for uc in utfgmatch(str,".") do - p=p+P(uc) - end - return p - end -else - function lpeg.US(str) - local p=P(false) - local f=function(uc) - p=p+P(uc) - end - lpegmatch((utf8char/f)^0,str) - return p - end -end -local range=utf8byte*utf8byte+Cc(false) -function lpeg.UR(str,more) - local first,last - if type(str)=="number" then - first=str - last=more or first - else - first,last=lpegmatch(range,str) - if not last then - return P(str) - end - end - if first==last then - return P(str) - elseif utfchar and (last-first<8) then - local p=P(false) - for i=first,last do - p=p+P(utfchar(i)) - end - return p - else - local f=function(b) - return b>=first and b<=last - end - return utf8byte/f - end -end -function lpeg.is_lpeg(p) - return p and lpegtype(p)=="pattern" -end -function lpeg.oneof(list,...) - if type(list)~="table" then - list={ list,... } - end - local p=P(list[1]) - for l=2,#list do - p=p+P(list[l]) - end - return p -end -local sort=table.sort -local function copyindexed(old) - local new={} - for i=1,#old do - new[i]=old - end - return new -end -local function sortedkeys(tab) - local keys,s={},0 - for key,_ in next,tab do - s=s+1 - keys[s]=key - end - sort(keys) - return keys -end -function lpeg.append(list,pp,delayed,checked) - local p=pp - if #list>0 then - local keys=copyindexed(list) - sort(keys) - for i=#keys,1,-1 do - local k=keys[i] - if p then - p=P(k)+p - else - p=P(k) - end - end - elseif delayed then - local keys=sortedkeys(list) - if p then - for i=1,#keys,1 do - local k=keys[i] - local v=list[k] - p=P(k)/list+p - end - else - for i=1,#keys do - local k=keys[i] - local v=list[k] - if p then - p=P(k)+p - else - p=P(k) - end - end - if p then - p=p/list - end - end - elseif checked then - local keys=sortedkeys(list) - for i=1,#keys do - local k=keys[i] - local v=list[k] - if p then - if k==v then - p=P(k)+p - else - p=P(k)/v+p - end - else - if k==v then - p=P(k) - else - p=P(k)/v - end - end - end - else - local keys=sortedkeys(list) - for i=1,#keys do - local k=keys[i] - local v=list[k] - if p then - p=P(k)/v+p - else - p=P(k)/v - end - end - end - return p -end -local function make(t) - local p - local keys=sortedkeys(t) - for i=1,#keys do - local k=keys[i] - local v=t[k] - if not p then - if next(v) then - p=P(k)*make(v) - else - p=P(k) - end - else - if next(v) then - p=p+P(k)*make(v) - else - p=p+P(k) - end - end - end - return p -end -function lpeg.utfchartabletopattern(list) - local tree={} - for i=1,#list do - local t=tree - for c in gmatch(list[i],".") do - if not t[c] then - t[c]={} - end - t=t[c] - end - end - return make(tree) -end -patterns.containseol=lpeg.finder(eol) -local function nextstep(n,step,result) - local m=n%step - local d=floor(n/step) - if d>0 then - local v=V(tostring(step)) - local s=result.start - for i=1,d do - if s then - s=v*s - else - s=v - end - end - result.start=s - end - if step>1 and result.start then - local v=V(tostring(step/2)) - result[tostring(step)]=v*v - end - if step>0 then - return nextstep(m,step/2,result) - else - return result - end -end -function lpeg.times(pattern,n) - return P(nextstep(n,2^16,{ "start",["1"]=pattern })) -end -local trailingzeros=zero^0*-digit -local case_1=period*trailingzeros/"" -local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"") -local number=digit^1*(case_1+case_2) -local stripper=Cs((number+1)^0) -lpeg.patterns.stripzeros=stripper - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['l-functions']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -functions=functions or {} -function functions.dummy() end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['l-string']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local string=string -local sub,gmatch,format,char,byte,rep,lower=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower -local lpegmatch,patterns=lpeg.match,lpeg.patterns -local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs -local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote -function string.unquoted(str) - return lpegmatch(unquoted,str) or str -end -function string.quoted(str) - return format("%q",str) -end -function string.count(str,pattern) - local n=0 - for _ in gmatch(str,pattern) do - n=n+1 - end - return n -end -function string.limit(str,n,sentinel) - if #str>n then - sentinel=sentinel or "..." - return sub(str,1,(n-#sentinel))..sentinel - else - return str - end -end -local stripper=patterns.stripper -local collapser=patterns.collapser -local longtostring=patterns.longtostring -function string.strip(str) - return lpegmatch(stripper,str) or "" -end -function string.collapsespaces(str) - return lpegmatch(collapser,str) or "" -end -function string.longtostring(str) - return lpegmatch(longtostring,str) or "" -end -local pattern=P(" ")^0*P(-1) -function string.is_empty(str) - if str=="" then - return true - else - return lpegmatch(pattern,str) and true or false - end -end -local anything=patterns.anything -local allescapes=Cc("%")*S(".-+%?()[]*") -local someescapes=Cc("%")*S(".-+%()[]") -local matchescapes=Cc(".")*S("*?") -local pattern_a=Cs ((allescapes+anything )^0 ) -local pattern_b=Cs ((someescapes+matchescapes+anything )^0 ) -local pattern_c=Cs (Cc("^")*(someescapes+matchescapes+anything )^0*Cc("$") ) -function string.escapedpattern(str,simple) - return lpegmatch(simple and pattern_b or pattern_a,str) -end -function string.topattern(str,lowercase,strict) - if str=="" or type(str)~="string" then - return ".*" - elseif strict then - str=lpegmatch(pattern_c,str) - else - str=lpegmatch(pattern_b,str) - end - if lowercase then - return lower(str) - else - return str - end -end -function string.valid(str,default) - return (type(str)=="string" and str~="" and str) or default or nil -end -string.itself=function(s) return s end -local pattern=Ct(C(1)^0) -function string.totable(str) - return lpegmatch(pattern,str) -end -local replacer=lpeg.replacer("@","%%") -function string.tformat(fmt,...) - return format(lpegmatch(replacer,fmt),...) -end -string.quote=string.quoted -string.unquote=string.unquoted - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['l-table']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local type,next,tostring,tonumber,ipairs,select=type,next,tostring,tonumber,ipairs,select -local table,string=table,string -local concat,sort,insert,remove=table.concat,table.sort,table.insert,table.remove -local format,lower,dump=string.format,string.lower,string.dump -local getmetatable,setmetatable=getmetatable,setmetatable -local getinfo=debug.getinfo -local lpegmatch,patterns=lpeg.match,lpeg.patterns -local floor=math.floor -local stripper=patterns.stripper -function table.strip(tab) - local lst,l={},0 - for i=1,#tab do - local s=lpegmatch(stripper,tab[i]) or "" - if s=="" then - else - l=l+1 - lst[l]=s - end - end - return lst -end -function table.keys(t) - if t then - local keys,k={},0 - for key,_ in next,t do - k=k+1 - keys[k]=key - end - return keys - else - return {} - end -end -local function compare(a,b) - local ta,tb=type(a),type(b) - if ta==tb then - return a0 then - local n=0 - for _,v in next,t do - n=n+1 - end - if n==#t then - local tt,nt={},0 - for i=1,#t do - local v=t[i] - local tv=type(v) - if tv=="number" then - nt=nt+1 - if hexify then - tt[nt]=format("0x%04X",v) - else - tt[nt]=tostring(v) - end - elseif tv=="string" then - nt=nt+1 - tt[nt]=format("%q",v) - elseif tv=="boolean" then - nt=nt+1 - tt[nt]=v and "true" or "false" - else - tt=nil - break - end - end - return tt - end - end - return nil -end -local propername=patterns.propername -local function dummy() end -local function do_serialize(root,name,depth,level,indexed) - if level>0 then - depth=depth.." " - if indexed then - handle(format("%s{",depth)) - else - local tn=type(name) - if tn=="number" then - if hexify then - handle(format("%s[0x%04X]={",depth,name)) - else - handle(format("%s[%s]={",depth,name)) - end - elseif tn=="string" then - if noquotes and not reserved[name] and lpegmatch(propername,name) then - handle(format("%s%s={",depth,name)) - else - handle(format("%s[%q]={",depth,name)) - end - elseif tn=="boolean" then - handle(format("%s[%s]={",depth,name and "true" or "false")) - else - handle(format("%s{",depth)) - end - end - end - if root and next(root) then - local first,last=nil,0 - if compact then - last=#root - for k=1,last do - if root[k]==nil then - last=k-1 - break - end - end - if last>0 then - first=1 - end - end - local sk=sortedkeys(root) - for i=1,#sk do - local k=sk[i] - local v=root[k] - local tv,tk=type(v),type(k) - if compact and first and tk=="number" and k>=first and k<=last then - if tv=="number" then - if hexify then - handle(format("%s 0x%04X,",depth,v)) - else - handle(format("%s %s,",depth,v)) - end - elseif tv=="string" then - if reduce and tonumber(v) then - handle(format("%s %s,",depth,v)) - else - handle(format("%s %q,",depth,v)) - end - elseif tv=="table" then - if not next(v) then - handle(format("%s {},",depth)) - elseif inline then - local st=simple_table(v) - if st then - handle(format("%s { %s },",depth,concat(st,", "))) - else - do_serialize(v,k,depth,level+1,true) - end - else - do_serialize(v,k,depth,level+1,true) - end - elseif tv=="boolean" then - handle(format("%s %s,",depth,v and "true" or "false")) - elseif tv=="function" then - if functions then - handle(format('%s load(%q),',depth,dump(v))) - else - handle(format('%s "function",',depth)) - end - else - handle(format("%s %q,",depth,tostring(v))) - end - elseif k=="__p__" then - if false then - handle(format("%s __p__=nil,",depth)) - end - elseif tv=="number" then - if tk=="number" then - if hexify then - handle(format("%s [0x%04X]=0x%04X,",depth,k,v)) - else - handle(format("%s [%s]=%s,",depth,k,v)) - end - elseif tk=="boolean" then - if hexify then - handle(format("%s [%s]=0x%04X,",depth,k and "true" or "false",v)) - else - handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) - end - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - if hexify then - handle(format("%s %s=0x%04X,",depth,k,v)) - else - handle(format("%s %s=%s,",depth,k,v)) - end - else - if hexify then - handle(format("%s [%q]=0x%04X,",depth,k,v)) - else - handle(format("%s [%q]=%s,",depth,k,v)) - end - end - elseif tv=="string" then - if reduce and tonumber(v) then - if tk=="number" then - if hexify then - handle(format("%s [0x%04X]=%s,",depth,k,v)) - else - handle(format("%s [%s]=%s,",depth,k,v)) - end - elseif tk=="boolean" then - handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%s,",depth,k,v)) - else - handle(format("%s [%q]=%s,",depth,k,v)) - end - else - if tk=="number" then - if hexify then - handle(format("%s [0x%04X]=%q,",depth,k,v)) - else - handle(format("%s [%s]=%q,",depth,k,v)) - end - elseif tk=="boolean" then - handle(format("%s [%s]=%q,",depth,k and "true" or "false",v)) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%q,",depth,k,v)) - else - handle(format("%s [%q]=%q,",depth,k,v)) - end - end - elseif tv=="table" then - if not next(v) then - if tk=="number" then - if hexify then - handle(format("%s [0x%04X]={},",depth,k)) - else - handle(format("%s [%s]={},",depth,k)) - end - elseif tk=="boolean" then - handle(format("%s [%s]={},",depth,k and "true" or "false")) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s={},",depth,k)) - else - handle(format("%s [%q]={},",depth,k)) - end - elseif inline then - local st=simple_table(v) - if st then - if tk=="number" then - if hexify then - handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", "))) - else - handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) - end - elseif tk=="boolean" then - handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", "))) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s={ %s },",depth,k,concat(st,", "))) - else - handle(format("%s [%q]={ %s },",depth,k,concat(st,", "))) - end - else - do_serialize(v,k,depth,level+1) - end - else - do_serialize(v,k,depth,level+1) - end - elseif tv=="boolean" then - if tk=="number" then - if hexify then - handle(format("%s [0x%04X]=%s,",depth,k,v and "true" or "false")) - else - handle(format("%s [%s]=%s,",depth,k,v and "true" or "false")) - end - elseif tk=="boolean" then - handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false")) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%s,",depth,k,v and "true" or "false")) - else - handle(format("%s [%q]=%s,",depth,k,v and "true" or "false")) - end - elseif tv=="function" then - if functions then - local f=getinfo(v).what=="C" and dump(dummy) or dump(v) - if tk=="number" then - if hexify then - handle(format("%s [0x%04X]=load(%q),",depth,k,f)) - else - handle(format("%s [%s]=load(%q),",depth,k,f)) - end - elseif tk=="boolean" then - handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f)) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=load(%q),",depth,k,f)) - else - handle(format("%s [%q]=load(%q),",depth,k,f)) - end - end - else - if tk=="number" then - if hexify then - handle(format("%s [0x%04X]=%q,",depth,k,tostring(v))) - else - handle(format("%s [%s]=%q,",depth,k,tostring(v))) - end - elseif tk=="boolean" then - handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v))) - elseif noquotes and not reserved[k] and lpegmatch(propername,k) then - handle(format("%s %s=%q,",depth,k,tostring(v))) - else - handle(format("%s [%q]=%q,",depth,k,tostring(v))) - end - end - end - end - if level>0 then - handle(format("%s},",depth)) - end -end -local function serialize(_handle,root,name,specification) - local tname=type(name) - if type(specification)=="table" then - noquotes=specification.noquotes - hexify=specification.hexify - handle=_handle or specification.handle or print - reduce=specification.reduce or false - functions=specification.functions - compact=specification.compact - inline=specification.inline and compact - if functions==nil then - functions=true - end - if compact==nil then - compact=true - end - if inline==nil then - inline=compact - end - else - noquotes=false - hexify=false - handle=_handle or print - reduce=false - compact=true - inline=true - functions=true - end - if tname=="string" then - if name=="return" then - handle("return {") - else - handle(name.."={") - end - elseif tname=="number" then - if hexify then - handle(format("[0x%04X]={",name)) - else - handle("["..name.."]={") - end - elseif tname=="boolean" then - if name then - handle("return {") - else - handle("{") - end - else - handle("t={") - end - if root then - if getmetatable(root) then - local dummy=root._w_h_a_t_e_v_e_r_ - root._w_h_a_t_e_v_e_r_=nil - end - if next(root) then - do_serialize(root,name,"",0) - end - end - handle("}") -end -function table.serialize(root,name,specification) - local t,n={},0 - local function flush(s) - n=n+1 - t[n]=s - end - serialize(flush,root,name,specification) - return concat(t,"\n") -end -table.tohandle=serialize -local maxtab=2*1024 -function table.tofile(filename,root,name,specification) - local f=io.open(filename,'w') - if f then - if maxtab>1 then - local t,n={},0 - local function flush(s) - n=n+1 - t[n]=s - if n>maxtab then - f:write(concat(t,"\n"),"\n") - t,n={},0 - end - end - serialize(flush,root,name,specification) - f:write(concat(t,"\n"),"\n") - else - local function flush(s) - f:write(s,"\n") - end - serialize(flush,root,name,specification) - end - f:close() - io.flush() - end -end -local function flattened(t,f,depth) - if f==nil then - f={} - depth=0xFFFF - elseif tonumber(f) then - depth=f - f={} - elseif not depth then - depth=0xFFFF - end - for k,v in next,t do - if type(k)~="number" then - if depth>0 and type(v)=="table" then - flattened(v,f,depth-1) - else - f[#f+1]=v - end - end - end - for k=1,#t do - local v=t[k] - if depth>0 and type(v)=="table" then - flattened(v,f,depth-1) - else - f[#f+1]=v - end - end - return f -end -table.flattened=flattened -local function unnest(t,f) - if not f then - f={} - end - for i=1,#t do - local v=t[i] - if type(v)=="table" then - if type(v[1])=="table" then - unnest(v,f) - else - f[#f+1]=v - end - else - f[#f+1]=v - end - end - return f -end -function table.unnest(t) - return unnest(t) -end -local function are_equal(a,b,n,m) - if a and b and #a==#b then - n=n or 1 - m=m or #a - for i=n,m do - local ai,bi=a[i],b[i] - if ai==bi then - elseif type(ai)=="table" and type(bi)=="table" then - if not are_equal(ai,bi) then - return false - end - else - return false - end - end - return true - else - return false - end -end -local function identical(a,b) - for ka,va in next,a do - local vb=b[ka] - if va==vb then - elseif type(va)=="table" and type(vb)=="table" then - if not identical(va,vb) then - return false - end - else - return false - end - end - return true -end -table.identical=identical -table.are_equal=are_equal -function table.compact(t) - if t then - for k,v in next,t do - if not next(v) then - t[k]=nil - end - end - end -end -function table.contains(t,v) - if t then - for i=1,#t do - if t[i]==v then - return i - end - end - end - return false -end -function table.count(t) - local n=0 - for k,v in next,t do - n=n+1 - end - return n -end -function table.swapped(t,s) - local n={} - if s then - for k,v in next,s do - n[k]=v - end - end - for k,v in next,t do - n[v]=k - end - return n -end -function table.mirrored(t) - local n={} - for k,v in next,t do - n[v]=k - n[k]=v - end - return n -end -function table.reversed(t) - if t then - local tt,tn={},#t - if tn>0 then - local ttn=0 - for i=tn,1,-1 do - ttn=ttn+1 - tt[ttn]=t[i] - end - end - return tt - end -end -function table.reverse(t) - if t then - local n=#t - for i=1,floor(n/2) do - local j=n-i+1 - t[i],t[j]=t[j],t[i] - end - return t - end -end -function table.sequenced(t,sep,simple) - if not t then - return "" - end - local n=#t - local s={} - if n>0 then - for i=1,n do - s[i]=tostring(t[i]) - end - else - n=0 - for k,v in sortedhash(t) do - if simple then - if v==true then - n=n+1 - s[n]=k - elseif v and v~="" then - n=n+1 - s[n]=k.."="..tostring(v) - end - else - n=n+1 - s[n]=k.."="..tostring(v) - end - end - end - return concat(s,sep or " | ") -end -function table.print(t,...) - if type(t)~="table" then - print(tostring(t)) - else - serialize(print,t,...) - end -end -if setinspector then - setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) -end -function table.sub(t,i,j) - return { unpack(t,i,j) } -end -function table.is_empty(t) - return not t or not next(t) -end -function table.has_one_entry(t) - return t and not next(t,next(t)) -end -function table.loweredkeys(t) - local l={} - for k,v in next,t do - l[lower(k)]=v - end - return l -end -function table.unique(old) - local hash={} - local new={} - local n=0 - for i=1,#old do - local oi=old[i] - if not hash[oi] then - n=n+1 - new[n]=oi - hash[oi]=true - end - end - return new -end -function table.sorted(t,...) - sort(t,...) - return t -end -function table.values(t,s) - if t then - local values,keys,v={},{},0 - for key,value in next,t do - if not keys[value] then - v=v+1 - values[v]=value - keys[k]=key - end - end - if s then - sort(values) - end - return values - else - return {} - end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['l-io']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local io=io -local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format -local concat=table.concat -local floor=math.floor -local type=type -if string.find(os.getenv("PATH"),";") then - io.fileseparator,io.pathseparator="\\",";" -else - io.fileseparator,io.pathseparator="/",":" -end -local function readall(f) - return f:read("*all") -end -local function readall(f) - local size=f:seek("end") - if size==0 then - return "" - elseif size<1024*1024 then - f:seek("set",0) - return f:read('*all') - else - local done=f:seek("set",0) - local step - if size<1024*1024 then - step=1024*1024 - elseif size>16*1024*1024 then - step=16*1024*1024 - else - step=floor(size/(1024*1024))*1024*1024/8 - end - local data={} - while true do - local r=f:read(step) - if not r then - return concat(data) - else - data[#data+1]=r - end - end - end -end -io.readall=readall -function io.loaddata(filename,textmode) - local f=io.open(filename,(textmode and 'r') or 'rb') - if f then - local data=readall(f) - f:close() - if #data>0 then - return data - end - end -end -function io.savedata(filename,data,joiner) - local f=io.open(filename,"wb") - if f then - if type(data)=="table" then - f:write(concat(data,joiner or "")) - elseif type(data)=="function" then - data(f) - else - f:write(data or "") - end - f:close() - io.flush() - return true - else - return false - end -end -function io.loadlines(filename,n) - local f=io.open(filename,'r') - if not f then - elseif n then - local lines={} - for i=1,n do - local line=f:read("*lines") - if line then - lines[#lines+1]=line - else - break - end - end - f:close() - lines=concat(lines,"\n") - if #lines>0 then - return lines - end - else - local line=f:read("*line") or "" - f:close() - if #line>0 then - return line - end - end -end -function io.loadchunk(filename,n) - local f=io.open(filename,'rb') - if f then - local data=f:read(n or 1024) - f:close() - if #data>0 then - return data - end - end -end -function io.exists(filename) - local f=io.open(filename) - if f==nil then - return false - else - f:close() - return true - end -end -function io.size(filename) - local f=io.open(filename) - if f==nil then - return 0 - else - local s=f:seek("end") - f:close() - return s - end -end -function io.noflines(f) - if type(f)=="string" then - local f=io.open(filename) - if f then - local n=f and io.noflines(f) or 0 - f:close() - return n - else - return 0 - end - else - local n=0 - for _ in f:lines() do - n=n+1 - end - f:seek('set',0) - return n - end -end -local nextchar={ - [ 4]=function(f) - return f:read(1,1,1,1) - end, - [ 2]=function(f) - return f:read(1,1) - end, - [ 1]=function(f) - return f:read(1) - end, - [-2]=function(f) - local a,b=f:read(1,1) - return b,a - end, - [-4]=function(f) - local a,b,c,d=f:read(1,1,1,1) - return d,c,b,a - end -} -function io.characters(f,n) - if f then - return nextchar[n or 1],f - end -end -local nextbyte={ - [4]=function(f) - local a,b,c,d=f:read(1,1,1,1) - if d then - return byte(a),byte(b),byte(c),byte(d) - end - end, - [3]=function(f) - local a,b,c=f:read(1,1,1) - if b then - return byte(a),byte(b),byte(c) - end - end, - [2]=function(f) - local a,b=f:read(1,1) - if b then - return byte(a),byte(b) - end - end, - [1]=function (f) - local a=f:read(1) - if a then - return byte(a) - end - end, - [-2]=function (f) - local a,b=f:read(1,1) - if b then - return byte(b),byte(a) - end - end, - [-3]=function(f) - local a,b,c=f:read(1,1,1) - if b then - return byte(c),byte(b),byte(a) - end - end, - [-4]=function(f) - local a,b,c,d=f:read(1,1,1,1) - if d then - return byte(d),byte(c),byte(b),byte(a) - end - end -} -function io.bytes(f,n) - if f then - return nextbyte[n or 1],f - else - return nil,nil - end -end -function io.ask(question,default,options) - while true do - io.write(question) - if options then - io.write(format(" [%s]",concat(options,"|"))) - end - if default then - io.write(format(" [%s]",default)) - end - io.write(format(" ")) - io.flush() - local answer=io.read() - answer=gsub(answer,"^%s*(.*)%s*$","%1") - if answer=="" and default then - return default - elseif not options then - return answer - else - for k=1,#options do - if options[k]==answer then - return answer - end - end - local pattern="^"..answer - for k=1,#options do - local v=options[k] - if find(v,pattern) then - return v - end - end - end - end -end -local function readnumber(f,n,m) - if m then - f:seek("set",n) - n=m - end - if n==1 then - return byte(f:read(1)) - elseif n==2 then - local a,b=byte(f:read(2),1,2) - return 256*a+b - elseif n==3 then - local a,b,c=byte(f:read(3),1,3) - return 256*256*a+256*b+c - elseif n==4 then - local a,b,c,d=byte(f:read(4),1,4) - return 256*256*256*a+256*256*b+256*c+d - elseif n==8 then - local a,b=readnumber(f,4),readnumber(f,4) - return 256*a+b - elseif n==12 then - local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4) - return 256*256*a+256*b+c - elseif n==-2 then - local b,a=byte(f:read(2),1,2) - return 256*a+b - elseif n==-3 then - local c,b,a=byte(f:read(3),1,3) - return 256*256*a+256*b+c - elseif n==-4 then - local d,c,b,a=byte(f:read(4),1,4) - return 256*256*256*a+256*256*b+256*c+d - elseif n==-8 then - local h,g,f,e,d,c,b,a=byte(f:read(8),1,8) - return 256*256*256*256*256*256*256*a+256*256*256*256*256*256*b+256*256*256*256*256*c+256*256*256*256*d+256*256*256*e+256*256*f+256*g+h - else - return 0 - end -end -io.readnumber=readnumber -function io.readstring(f,n,m) - if m then - f:seek("set",n) - n=m - end - local str=gsub(f:read(n),"\000","") - return str -end -if not io.i_limiter then function io.i_limiter() end end -if not io.o_limiter then function io.o_limiter() end end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['l-file']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -file=file or {} -local file=file -if not lfs then - lfs=optionalrequire("lfs") -end -if not lfs then - lfs={ - getcurrentdir=function() - return "." - end, - attributes=function() - return nil - end, - isfile=function(name) - local f=io.open(name,'rb') - if f then - f:close() - return true - end - end, - isdir=function(name) - print("you need to load lfs") - return false - end - } -elseif not lfs.isfile then - local attributes=lfs.attributes - function lfs.isdir(name) - return attributes(name,"mode")=="directory" - end - function lfs.isfile(name) - return attributes(name,"mode")=="file" - end -end -local insert,concat=table.insert,table.concat -local match,find,gmatch=string.match,string.find,string.gmatch -local lpegmatch=lpeg.match -local getcurrentdir,attributes=lfs.currentdir,lfs.attributes -local checkedsplit=string.checkedsplit -local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct -local colon=P(":") -local period=P(".") -local periods=P("..") -local fwslash=P("/") -local bwslash=P("\\") -local slashes=S("\\/") -local noperiod=1-period -local noslashes=1-slashes -local name=noperiod^1 -local suffix=period/""*(1-period-slashes)^1*-1 -local pattern=C((1-(slashes^1*noslashes^1*-1))^1)*P(1) -local function pathpart(name,default) - return name and lpegmatch(pattern,name) or default or "" -end -local pattern=(noslashes^0*slashes)^1*C(noslashes^1)*-1 -local function basename(name) - return name and lpegmatch(pattern,name) or name -end -local pattern=(noslashes^0*slashes^1)^0*Cs((1-suffix)^1)*suffix^0 -local function nameonly(name) - return name and lpegmatch(pattern,name) or name -end -local pattern=(noslashes^0*slashes)^0*(noperiod^1*period)^1*C(noperiod^1)*-1 -local function suffixonly(name) - return name and lpegmatch(pattern,name) or "" -end -local pattern=(noslashes^0*slashes)^0*noperiod^1*((period*C(noperiod^1))^1)*-1+Cc("") -local function suffixesonly(name) - if name then - return lpegmatch(pattern,name) - else - return "" - end -end -file.pathpart=pathpart -file.basename=basename -file.nameonly=nameonly -file.suffixonly=suffixonly -file.suffix=suffixonly -file.suffixesonly=suffixesonly -file.suffixes=suffixesonly -file.dirname=pathpart -file.extname=suffixonly -local drive=C(R("az","AZ"))*colon -local path=C((noslashes^0*slashes)^0) -local suffix=period*C(P(1-period)^0*P(-1)) -local base=C((1-suffix)^0) -local rest=C(P(1)^0) -drive=drive+Cc("") -path=path+Cc("") -base=base+Cc("") -suffix=suffix+Cc("") -local pattern_a=drive*path*base*suffix -local pattern_b=path*base*suffix -local pattern_c=C(drive*path)*C(base*suffix) -local pattern_d=path*rest -function file.splitname(str,splitdrive) - if not str then - elseif splitdrive then - return lpegmatch(pattern_a,str) - else - return lpegmatch(pattern_b,str) - end -end -function file.splitbase(str) - if str then - return lpegmatch(pattern_d,str) - else - return "",str - end -end -function file.nametotable(str,splitdrive) - if str then - local path,drive,subpath,name,base,suffix=lpegmatch(pattern_c,str) - if splitdrive then - return { - path=path, - drive=drive, - subpath=subpath, - name=name, - base=base, - suffix=suffix, - } - else - return { - path=path, - name=name, - base=base, - suffix=suffix, - } - end - end -end -local pattern=Cs(((period*(1-period-slashes)^1*-1)/""+1)^1) -function file.removesuffix(name) - return name and lpegmatch(pattern,name) -end -local suffix=period/""*(1-period-slashes)^1*-1 -local pattern=Cs((noslashes^0*slashes^1)^0*((1-suffix)^1))*Cs(suffix) -function file.addsuffix(filename,suffix,criterium) - if not filename or not suffix or suffix=="" then - return filename - elseif criterium==true then - return filename.."."..suffix - elseif not criterium then - local n,s=lpegmatch(pattern,filename) - if not s or s=="" then - return filename.."."..suffix - else - return filename - end - else - local n,s=lpegmatch(pattern,filename) - if s and s~="" then - local t=type(criterium) - if t=="table" then - for i=1,#criterium do - if s==criterium[i] then - return filename - end - end - elseif t=="string" then - if s==criterium then - return filename - end - end - end - return (n or filename).."."..suffix - end -end -local suffix=period*(1-period-slashes)^1*-1 -local pattern=Cs((1-suffix)^0) -function file.replacesuffix(name,suffix) - if name and suffix and suffix~="" then - return lpegmatch(pattern,name).."."..suffix - else - return name - end -end -local reslasher=lpeg.replacer(P("\\"),"/") -function file.reslash(str) - return str and lpegmatch(reslasher,str) -end -function file.is_writable(name) - if not name then - elseif lfs.isdir(name) then - name=name.."/m_t_x_t_e_s_t.tmp" - local f=io.open(name,"wb") - if f then - f:close() - os.remove(name) - return true - end - elseif lfs.isfile(name) then - local f=io.open(name,"ab") - if f then - f:close() - return true - end - else - local f=io.open(name,"ab") - if f then - f:close() - os.remove(name) - return true - end - end - return false -end -local readable=P("r")*Cc(true) -function file.is_readable(name) - if name then - local a=attributes(name) - return a and lpegmatch(readable,a.permissions) or false - else - return false - end -end -file.isreadable=file.is_readable -file.iswritable=file.is_writable -function file.size(name) - if name then - local a=attributes(name) - return a and a.size or 0 - else - return 0 - end -end -function file.splitpath(str,separator) - return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator) -end -function file.joinpath(tab,separator) - return tab and concat(tab,separator or io.pathseparator) -end -local someslash=S("\\/") -local stripper=Cs(P(fwslash)^0/""*reslasher) -local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon -local isroot=fwslash^1*-1 -local hasroot=fwslash^1 -local reslasher=lpeg.replacer(S("\\/"),"/") -local deslasher=lpeg.replacer(S("\\/")^1,"/") -function file.join(...) - local lst={... } - local one=lst[1] - if lpegmatch(isnetwork,one) then - local one=lpegmatch(reslasher,one) - local two=lpegmatch(deslasher,concat(lst,"/",2)) - if lpegmatch(hasroot,two) then - return one..two - else - return one.."/"..two - end - elseif lpegmatch(isroot,one) then - local two=lpegmatch(deslasher,concat(lst,"/",2)) - if lpegmatch(hasroot,two) then - return two - else - return "/"..two - end - elseif one=="" then - return lpegmatch(stripper,concat(lst,"/",2)) - else - return lpegmatch(deslasher,concat(lst,"/")) - end -end -local drivespec=R("az","AZ")^1*colon -local anchors=fwslash+drivespec -local untouched=periods+(1-period)^1*P(-1) -local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0) -local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//") -local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1)) -local absolute=fwslash -function file.collapsepath(str,anchor) - if not str then - return - end - if anchor==true and not lpegmatch(anchors,str) then - str=getcurrentdir().."/"..str - end - if str=="" or str=="." then - return "." - elseif lpegmatch(untouched,str) then - return lpegmatch(reslasher,str) - end - local starter,oldelements=lpegmatch(splitstarter,str) - local newelements={} - local i=#oldelements - while i>0 do - local element=oldelements[i] - if element=='.' then - elseif element=='..' then - local n=i-1 - while n>0 do - local element=oldelements[n] - if element~='..' and element~='.' then - oldelements[n]='.' - break - else - n=n-1 - end - end - if n<1 then - insert(newelements,1,'..') - end - elseif element~="" then - insert(newelements,1,element) - end - i=i-1 - end - if #newelements==0 then - return starter or "." - elseif starter then - return starter..concat(newelements,'/') - elseif lpegmatch(absolute,str) then - return "/"..concat(newelements,'/') - else - newelements=concat(newelements,'/') - if anchor=="." and find(str,"^%./") then - return "./"..newelements - else - return newelements - end - end -end -local validchars=R("az","09","AZ","--","..") -local pattern_a=lpeg.replacer(1-validchars) -local pattern_a=Cs((validchars+P(1)/"-")^1) -local whatever=P("-")^0/"" -local pattern_b=Cs(whatever*(1-whatever*-1)^1) -function file.robustname(str,strict) - if str then - str=lpegmatch(pattern_a,str) or str - if strict then - return lpegmatch(pattern_b,str) or str - else - return str - end - end -end -file.readdata=io.loaddata -file.savedata=io.savedata -function file.copy(oldname,newname) - if oldname and newname then - local data=io.loaddata(oldname) - if data and data~="" then - file.savedata(newname,data) - end - end -end -local letter=R("az","AZ")+S("_-+") -local separator=P("://") -local qualified=period^0*fwslash+letter*colon+letter^1*separator+letter^1*fwslash -local rootbased=fwslash+letter*colon -lpeg.patterns.qualified=qualified -lpeg.patterns.rootbased=rootbased -function file.is_qualified_path(filename) - return filename and lpegmatch(qualified,filename)~=nil -end -function file.is_rootbased_path(filename) - return filename and lpegmatch(rootbased,filename)~=nil -end -function file.strip(name,dir) - if name then - local b,a=match(name,"^(.-)"..dir.."(.*)$") - return a~="" and a or name - end -end -function lfs.mkdirs(path) - local full="" - for sub in gmatch(path,"(/*[^\\/]+)") do - full=full..sub - lfs.mkdir(full) - end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['l-boolean']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local type,tonumber=type,tonumber -boolean=boolean or {} -local boolean=boolean -function boolean.tonumber(b) - if b then return 1 else return 0 end -end -function toboolean(str,tolerant) - if str==nil then - return false - elseif str==false then - return false - elseif str==true then - return true - elseif str=="true" then - return true - elseif str=="false" then - return false - elseif not tolerant then - return false - elseif str==0 then - return false - elseif (tonumber(str) or 0)>0 then - return true - else - return str=="yes" or str=="on" or str=="t" - end -end -string.toboolean=toboolean -function string.booleanstring(str) - if str=="0" then - return false - elseif str=="1" then - return true - elseif str=="" then - return false - elseif str=="false" then - return false - elseif str=="true" then - return true - elseif (tonumber(str) or 0)>0 then - return true - else - return str=="yes" or str=="on" or str=="t" - end -end -function string.is_boolean(str,default) - if type(str)=="string" then - if str=="true" or str=="yes" or str=="on" or str=="t" or str=="1" then - return true - elseif str=="false" or str=="no" or str=="off" or str=="f" or str=="0" then - return false - end - end - return default -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['l-math']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local floor,sin,cos,tan=math.floor,math.sin,math.cos,math.tan -if not math.round then - function math.round(x) return floor(x+0.5) end -end -if not math.div then - function math.div(n,m) return floor(n/m) end -end -if not math.mod then - function math.mod(n,m) return n%m end -end -local pipi=2*math.pi/360 -if not math.sind then - function math.sind(d) return sin(d*pipi) end - function math.cosd(d) return cos(d*pipi) end - function math.tand(d) return tan(d*pipi) end -end -if not math.odd then - function math.odd (n) return n%2~=0 end - function math.even(n) return n%2==0 end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['util-str']={ - version=1.001, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -utilities=utilities or {} -utilities.strings=utilities.strings or {} -local strings=utilities.strings -local format,gsub,rep,sub=string.format,string.gsub,string.rep,string.sub -local load,dump=load,string.dump -local tonumber,type,tostring=tonumber,type,tostring -local unpack,concat=table.unpack,table.concat -local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc -local patterns,lpegmatch=lpeg.patterns,lpeg.match -local utfchar,utfbyte=utf.char,utf.byte -local loadstripped=function(str,shortcuts) - if shortcuts then - return load(dump(load(str),true),nil,nil,shortcuts) - else - return load(dump(load(str),true)) - end -end -if not number then number={} end -local stripper=patterns.stripzeros -local function points(n) - return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) -end -local function basepoints(n) - return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536)) -end -number.points=points -number.basepoints=basepoints -local rubish=patterns.spaceortab^0*patterns.newline -local anyrubish=patterns.spaceortab+patterns.newline -local anything=patterns.anything -local stripped=(patterns.spaceortab^1/"")*patterns.newline -local leading=rubish^0/"" -local trailing=(anyrubish^1*patterns.endofstring)/"" -local redundant=rubish^3/"\n" -local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0) -function strings.collapsecrlf(str) - return lpegmatch(pattern,str) -end -local repeaters={} -function strings.newrepeater(str,offset) - offset=offset or 0 - local s=repeaters[str] - if not s then - s={} - repeaters[str]=s - end - local t=s[offset] - if t then - return t - end - t={} - setmetatable(t,{ __index=function(t,k) - if not k then - return "" - end - local n=k+offset - local s=n>0 and rep(str,n) or "" - t[k]=s - return s - end }) - s[offset]=t - return t -end -local extra,tab,start=0,0,4,0 -local nspaces=strings.newrepeater(" ") -string.nspaces=nspaces -local pattern=Carg(1)/function(t) - extra,tab,start=0,t or 7,1 - end*Cs(( - Cp()*patterns.tab/function(position) - local current=(position-start+1)+extra - local spaces=tab-(current-1)%tab - if spaces>0 then - extra=extra+spaces-1 - return nspaces[spaces] - else - return "" - end - end+patterns.newline*Cp()/function(position) - extra,start=0,position - end+patterns.anything - )^1) -function strings.tabtospace(str,tab) - return lpegmatch(pattern,str,1,tab or 7) -end -function strings.striplong(str) - str=gsub(str,"^%s*","") - str=gsub(str,"[\n\r]+ *","\n") - return str -end -function strings.nice(str) - str=gsub(str,"[:%-+_]+"," ") - return str -end -local n=0 -local sequenced=table.sequenced -function string.autodouble(s,sep) - if s==nil then - return '""' - end - local t=type(s) - if t=="number" then - return tostring(s) - end - if t=="table" then - return ('"'..sequenced(s,sep or ",")..'"') - end - return ('"'..tostring(s)..'"') -end -function string.autosingle(s,sep) - if s==nil then - return "''" - end - local t=type(s) - if t=="number" then - return tostring(s) - end - if t=="table" then - return ("'"..sequenced(s,sep or ",").."'") - end - return ("'"..tostring(s).."'") -end -local tracedchars={} -string.tracedchars=tracedchars -strings.tracers=tracedchars -function string.tracedchar(b) - if type(b)=="number" then - return tracedchars[b] or (utfchar(b).." (U+"..format('%05X',b)..")") - else - local c=utfbyte(b) - return tracedchars[c] or (b.." (U+"..format('%05X',c)..")") - end -end -function number.signed(i) - if i>0 then - return "+",i - else - return "-",-i - end -end -local zero=P("0")^1/"" -local plus=P("+")/"" -local minus=P("-") -local separator=S(".") -local digit=R("09") -local trailing=zero^1*#S("eE") -local exponent=(S("eE")*(plus+Cs((minus*zero^0*P(-1))/"")+minus)*zero^0*(P(-1)*Cc("0")+P(1)^1)) -local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent) -local pattern_b=Cs((exponent+P(1))^0) -function number.sparseexponent(f,n) - if not n then - n=f - f="%e" - end - local tn=type(n) - if tn=="string" then - local m=tonumber(n) - if m then - return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m)) - end - elseif tn=="number" then - return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n)) - end - return tostring(n) -end -local template=[[ -%s -%s -return function(%s) return %s end -]] -local environment={ - global=global or _G, - lpeg=lpeg, - type=type, - tostring=tostring, - tonumber=tonumber, - format=string.format, - concat=table.concat, - signed=number.signed, - points=number.points, - basepoints=number.basepoints, - utfchar=utf.char, - utfbyte=utf.byte, - lpegmatch=lpeg.match, - nspaces=string.nspaces, - tracedchar=string.tracedchar, - autosingle=string.autosingle, - autodouble=string.autodouble, - sequenced=table.sequenced, - formattednumber=number.formatted, - sparseexponent=number.sparseexponent, -} -local preamble="" -local arguments={ "a1" } -setmetatable(arguments,{ __index=function(t,k) - local v=t[k-1]..",a"..k - t[k]=v - return v - end -}) -local prefix_any=C((S("+- .")+R("09"))^0) -local prefix_tab=P("{")*C((1-P("}"))^0)*P("}")+C((1-R("az","AZ","09","%%"))^0) -local format_s=function(f) - n=n+1 - if f and f~="" then - return format("format('%%%ss',a%s)",f,n) - else - return format("(a%s or '')",n) - end -end -local format_S=function(f) - n=n+1 - if f and f~="" then - return format("format('%%%ss',tostring(a%s))",f,n) - else - return format("tostring(a%s)",n) - end -end -local format_q=function() - n=n+1 - return format("(a%s and format('%%q',a%s) or '')",n,n) -end -local format_Q=function() - n=n+1 - return format("format('%%q',tostring(a%s))",n) -end -local format_i=function(f) - n=n+1 - if f and f~="" then - return format("format('%%%si',a%s)",f,n) - else - return format("format('%%i',a%s)",n) - end -end -local format_d=format_i -local format_I=function(f) - n=n+1 - return format("format('%%s%%%si',signed(a%s))",f,n) -end -local format_f=function(f) - n=n+1 - return format("format('%%%sf',a%s)",f,n) -end -local format_g=function(f) - n=n+1 - return format("format('%%%sg',a%s)",f,n) -end -local format_G=function(f) - n=n+1 - return format("format('%%%sG',a%s)",f,n) -end -local format_e=function(f) - n=n+1 - return format("format('%%%se',a%s)",f,n) -end -local format_E=function(f) - n=n+1 - return format("format('%%%sE',a%s)",f,n) -end -local format_j=function(f) - n=n+1 - return format("sparseexponent('%%%se',a%s)",f,n) -end -local format_J=function(f) - n=n+1 - return format("sparseexponent('%%%sE',a%s)",f,n) -end -local format_x=function(f) - n=n+1 - return format("format('%%%sx',a%s)",f,n) -end -local format_X=function(f) - n=n+1 - return format("format('%%%sX',a%s)",f,n) -end -local format_o=function(f) - n=n+1 - return format("format('%%%so',a%s)",f,n) -end -local format_c=function() - n=n+1 - return format("utfchar(a%s)",n) -end -local format_C=function() - n=n+1 - return format("tracedchar(a%s)",n) -end -local format_r=function(f) - n=n+1 - return format("format('%%%s.0f',a%s)",f,n) -end -local format_h=function(f) - n=n+1 - if f=="-" then - f=sub(f,2) - return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) - else - return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) - end -end -local format_H=function(f) - n=n+1 - if f=="-" then - f=sub(f,2) - return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) - else - return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) - end -end -local format_u=function(f) - n=n+1 - if f=="-" then - f=sub(f,2) - return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) - else - return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) - end -end -local format_U=function(f) - n=n+1 - if f=="-" then - f=sub(f,2) - return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) - else - return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) - end -end -local format_p=function() - n=n+1 - return format("points(a%s)",n) -end -local format_b=function() - n=n+1 - return format("basepoints(a%s)",n) -end -local format_t=function(f) - n=n+1 - if f and f~="" then - return format("concat(a%s,%q)",n,f) - else - return format("concat(a%s)",n) - end -end -local format_T=function(f) - n=n+1 - if f and f~="" then - return format("sequenced(a%s,%q)",n,f) - else - return format("sequenced(a%s)",n) - end -end -local format_l=function() - n=n+1 - return format("(a%s and 'true' or 'false')",n) -end -local format_L=function() - n=n+1 - return format("(a%s and 'TRUE' or 'FALSE')",n) -end -local format_N=function() - n=n+1 - return format("tostring(tonumber(a%s) or a%s)",n,n) -end -local format_a=function(f) - n=n+1 - if f and f~="" then - return format("autosingle(a%s,%q)",n,f) - else - return format("autosingle(a%s)",n) - end -end -local format_A=function(f) - n=n+1 - if f and f~="" then - return format("autodouble(a%s,%q)",n,f) - else - return format("autodouble(a%s)",n) - end -end -local format_w=function(f) - n=n+1 - f=tonumber(f) - if f then - return format("nspaces[%s+a%s]",f,n) - else - return format("nspaces[a%s]",n) - end -end -local format_W=function(f) - return format("nspaces[%s]",tonumber(f) or 0) -end -local digit=patterns.digit -local period=patterns.period -local three=digit*digit*digit -local splitter=Cs ( - (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2) -) -patterns.formattednumber=splitter -function number.formatted(n,sep1,sep2) - local s=type(s)=="string" and n or format("%0.2f",n) - if sep1==true then - return lpegmatch(splitter,s,1,".",",") - elseif sep1=="." then - return lpegmatch(splitter,s,1,sep1,sep2 or ",") - elseif sep1=="," then - return lpegmatch(splitter,s,1,sep1,sep2 or ".") - else - return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") - end -end -local format_m=function(f) - n=n+1 - if not f or f=="" then - f="," - end - return format([[formattednumber(a%s,%q,".")]],n,f) -end -local format_M=function(f) - n=n+1 - if not f or f=="" then - f="." - end - return format([[formattednumber(a%s,%q,",")]],n,f) -end -local format_z=function(f) - n=n+(tonumber(f) or 1) - return "''" -end -local format_rest=function(s) - return format("%q",s) -end -local format_extension=function(extensions,f,name) - local extension=extensions[name] or "tostring(%s)" - local f=tonumber(f) or 1 - if f==0 then - return extension - elseif f==1 then - n=n+1 - local a="a"..n - return format(extension,a,a) - elseif f<0 then - local a="a"..(n+f+1) - return format(extension,a,a) - else - local t={} - for i=1,f do - n=n+1 - t[#t+1]="a"..n - end - return format(extension,unpack(t)) - end -end -local builder=Cs { "start", - start=( - ( - P("%")/""*( - V("!") -+V("s")+V("q")+V("i")+V("d")+V("f")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") -+V("c")+V("C")+V("S") -+V("Q") -+V("N") -+V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w") -+V("W") -+V("a") -+V("A") -+V("j")+V("J") -+V("m")+V("M") -+V("z") -+V("*") - )+V("*") - )*(P(-1)+Carg(1)) - )^0, - ["s"]=(prefix_any*P("s"))/format_s, - ["q"]=(prefix_any*P("q"))/format_q, - ["i"]=(prefix_any*P("i"))/format_i, - ["d"]=(prefix_any*P("d"))/format_d, - ["f"]=(prefix_any*P("f"))/format_f, - ["g"]=(prefix_any*P("g"))/format_g, - ["G"]=(prefix_any*P("G"))/format_G, - ["e"]=(prefix_any*P("e"))/format_e, - ["E"]=(prefix_any*P("E"))/format_E, - ["x"]=(prefix_any*P("x"))/format_x, - ["X"]=(prefix_any*P("X"))/format_X, - ["o"]=(prefix_any*P("o"))/format_o, - ["S"]=(prefix_any*P("S"))/format_S, - ["Q"]=(prefix_any*P("Q"))/format_S, - ["N"]=(prefix_any*P("N"))/format_N, - ["c"]=(prefix_any*P("c"))/format_c, - ["C"]=(prefix_any*P("C"))/format_C, - ["r"]=(prefix_any*P("r"))/format_r, - ["h"]=(prefix_any*P("h"))/format_h, - ["H"]=(prefix_any*P("H"))/format_H, - ["u"]=(prefix_any*P("u"))/format_u, - ["U"]=(prefix_any*P("U"))/format_U, - ["p"]=(prefix_any*P("p"))/format_p, - ["b"]=(prefix_any*P("b"))/format_b, - ["t"]=(prefix_tab*P("t"))/format_t, - ["T"]=(prefix_tab*P("T"))/format_T, - ["l"]=(prefix_any*P("l"))/format_l, - ["L"]=(prefix_any*P("L"))/format_L, - ["I"]=(prefix_any*P("I"))/format_I, - ["w"]=(prefix_any*P("w"))/format_w, - ["W"]=(prefix_any*P("W"))/format_W, - ["j"]=(prefix_any*P("j"))/format_j, - ["J"]=(prefix_any*P("J"))/format_J, - ["m"]=(prefix_tab*P("m"))/format_m, - ["M"]=(prefix_tab*P("M"))/format_M, - ["z"]=(prefix_any*P("z"))/format_z, - ["a"]=(prefix_any*P("a"))/format_a, - ["A"]=(prefix_any*P("A"))/format_A, - ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, - ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, -} -local direct=Cs ( - P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]] -) -local function make(t,str) - local f - local p - local p=lpegmatch(direct,str) - if p then - f=loadstripped(p)() - else - n=0 - p=lpegmatch(builder,str,1,"..",t._extensions_) - if n>0 then - p=format(template,preamble,t._preamble_,arguments[n],p) - f=loadstripped(p,t._environment_)() - else - f=function() return str end - end - end - t[str]=f - return f -end -local function use(t,fmt,...) - return t[fmt](...) -end -strings.formatters={} -function strings.formatters.new() - local e={} - for k,v in next,environment do - e[k]=v - end - local t={ _extensions_={},_preamble_="",_environment_=e,_type_="formatter" } - setmetatable(t,{ __index=make,__call=use }) - return t -end -local formatters=strings.formatters.new() -string.formatters=formatters -string.formatter=function(str,...) return formatters[str](...) end -local function add(t,name,template,preamble) - if type(t)=="table" and t._type_=="formatter" then - t._extensions_[name]=template or "%s" - if type(preamble)=="string" then - t._preamble_=preamble.."\n"..t._preamble_ - elseif type(preamble)=="table" then - for k,v in next,preamble do - t._environment_[k]=v - end - end - end -end -strings.formatters.add=add -patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"""+P(1))^0) -patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) -patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) -patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) -add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) -add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) -add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luat-basics-gen']={ - version=1.100, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end -local dummyfunction=function() -end -local dummyreporter=function(c) - return function(...) - (texio.reporter or texio.write_nl)(c.." : "..string.formatters(...)) - end -end -statistics={ - register=dummyfunction, - starttiming=dummyfunction, - stoptiming=dummyfunction, - elapsedtime=nil, -} -directives={ - register=dummyfunction, - enable=dummyfunction, - disable=dummyfunction, -} -trackers={ - register=dummyfunction, - enable=dummyfunction, - disable=dummyfunction, -} -experiments={ - register=dummyfunction, - enable=dummyfunction, - disable=dummyfunction, -} -storage={ - register=dummyfunction, - shared={}, -} -logs={ - new=dummyreporter, - reporter=dummyreporter, - messenger=dummyreporter, - report=dummyfunction, -} -callbacks={ - register=function(n,f) return callback.register(n,f) end, -} -utilities={ - storage={ - allocate=function(t) return t or {} end, - mark=function(t) return t or {} end, - }, -} -characters=characters or { - data={} -} -texconfig.kpse_init=true -resolvers=resolvers or {} -local remapper={ - otf="opentype fonts", - ttf="truetype fonts", - ttc="truetype fonts", - dfont="truetype fonts", - cid="cid maps", - cidmap="cid maps", - fea="font feature files", - pfa="type1 fonts", - pfb="type1 fonts", - afm="afm", -} -function resolvers.findfile(name,fileformat) - name=string.gsub(name,"\\","/") - if not fileformat or fileformat=="" then - fileformat=file.suffix(name) - if fileformat=="" then - fileformat="tex" - end - end - fileformat=string.lower(fileformat) - fileformat=remapper[fileformat] or fileformat - local found=kpse.find_file(name,fileformat) - if not found or found=="" then - found=kpse.find_file(name,"other text files") - end - return found -end -resolvers.findbinfile=resolvers.findfile -function resolvers.loadbinfile(filename,filetype) - local data=io.loaddata(filename) - return true,data,#data -end -function resolvers.resolve(s) - return s -end -function resolvers.unresolve(s) - return s -end -caches={} -local writable=nil -local readables={} -local usingjit=jit -if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then - caches.namespace='generic' -end -do - local cachepaths=kpse.expand_var('$TEXMFCACHE') or "" - if cachepaths=="" or cachepaths=="$TEXMFCACHE" then - cachepaths=kpse.expand_var('$TEXMFVAR') or "" - end - if cachepaths=="" or cachepaths=="$TEXMFVAR" then - cachepaths=kpse.expand_var('$VARTEXMF') or "" - end - if cachepaths=="" then - local fallbacks={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" } - for i=1,#fallbacks do - cachepaths=os.getenv(fallbacks[i]) or "" - if cachepath~="" and lfs.isdir(cachepath) then - break - end - end - end - if cachepaths=="" then - cachepaths="." - end - cachepaths=string.split(cachepaths,os.type=="windows" and ";" or ":") - for i=1,#cachepaths do - local cachepath=cachepaths[i] - if not lfs.isdir(cachepath) then - lfs.mkdirs(cachepath) - if lfs.isdir(cachepath) then - texio.write(string.format("(created cache path: %s)",cachepath)) - end - end - if file.is_writable(cachepath) then - writable=file.join(cachepath,"luatex-cache") - lfs.mkdir(writable) - writable=file.join(writable,caches.namespace) - lfs.mkdir(writable) - break - end - end - for i=1,#cachepaths do - if file.is_readable(cachepaths[i]) then - readables[#readables+1]=file.join(cachepaths[i],"luatex-cache",caches.namespace) - end - end - if not writable then - texio.write_nl("quiting: fix your writable cache path") - os.exit() - elseif #readables==0 then - texio.write_nl("quiting: fix your readable cache path") - os.exit() - elseif #readables==1 and readables[1]==writable then - texio.write(string.format("(using cache: %s)",writable)) - else - texio.write(string.format("(using write cache: %s)",writable)) - texio.write(string.format("(using read cache: %s)",table.concat(readables," "))) - end -end -function caches.getwritablepath(category,subcategory) - local path=file.join(writable,category) - lfs.mkdir(path) - path=file.join(path,subcategory) - lfs.mkdir(path) - return path -end -function caches.getreadablepaths(category,subcategory) - local t={} - for i=1,#readables do - t[i]=file.join(readables[i],category,subcategory) - end - return t -end -local function makefullname(path,name) - if path and path~="" then - return file.addsuffix(file.join(path,name),"lua"),file.addsuffix(file.join(path,name),usingjit and "lub" or "luc") - end -end -function caches.is_writable(path,name) - local fullname=makefullname(path,name) - return fullname and file.is_writable(fullname) -end -function caches.loaddata(paths,name) - for i=1,#paths do - local data=false - local luaname,lucname=makefullname(paths[i],name) - if lucname and lfs.isfile(lucname) then - texio.write(string.format("(load luc: %s)",lucname)) - data=loadfile(lucname) - if data then - data=data() - end - if data then - return data - else - texio.write(string.format("(loading failed: %s)",lucname)) - end - end - if luaname and lfs.isfile(luaname) then - texio.write(string.format("(load lua: %s)",luaname)) - data=loadfile(luaname) - if data then - data=data() - end - if data then - return data - end - end - end -end -function caches.savedata(path,name,data) - local luaname,lucname=makefullname(path,name) - if luaname then - texio.write(string.format("(save: %s)",luaname)) - table.tofile(luaname,data,true) - if lucname and type(caches.compile)=="function" then - os.remove(lucname) - texio.write(string.format("(save: %s)",lucname)) - caches.compile(data,luaname,lucname) - end - end -end -function caches.compile(data,luaname,lucname) - local d=io.loaddata(luaname) - if not d or d=="" then - d=table.serialize(data,true) - end - if d and d~="" then - local f=io.open(lucname,'wb') - if f then - local s=loadstring(d) - if s then - f:write(string.dump(s,true)) - end - f:close() - end - end -end -function table.setmetatableindex(t,f) - setmetatable(t,{ __index=f }) -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['data-con']={ - version=1.100, - comment="companion to luat-lib.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local format,lower,gsub=string.format,string.lower,string.gsub -local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end) -local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end) -local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end) -containers=containers or {} -local containers=containers -containers.usecache=true -local report_containers=logs.reporter("resolvers","containers") -local allocated={} -local mt={ - __index=function(t,k) - if k=="writable" then - local writable=caches.getwritablepath(t.category,t.subcategory) or { "." } - t.writable=writable - return writable - elseif k=="readables" then - local readables=caches.getreadablepaths(t.category,t.subcategory) or { "." } - t.readables=readables - return readables - end - end, - __storage__=true -} -function containers.define(category,subcategory,version,enabled) - if category and subcategory then - local c=allocated[category] - if not c then - c={} - allocated[category]=c - end - local s=c[subcategory] - if not s then - s={ - category=category, - subcategory=subcategory, - storage={}, - enabled=enabled, - version=version or math.pi, - trace=false, - } - setmetatable(s,mt) - c[subcategory]=s - end - return s - end -end -function containers.is_usable(container,name) - return container.enabled and caches and caches.is_writable(container.writable,name) -end -function containers.is_valid(container,name) - if name and name~="" then - local storage=container.storage[name] - return storage and storage.cache_version==container.version - else - return false - end -end -function containers.read(container,name) - local storage=container.storage - local stored=storage[name] - if not stored and container.enabled and caches and containers.usecache then - stored=caches.loaddata(container.readables,name) - if stored and stored.cache_version==container.version then - if trace_cache or trace_containers then - report_containers("action %a, category %a, name %a","load",container.subcategory,name) - end - else - stored=nil - end - storage[name]=stored - elseif stored then - if trace_cache or trace_containers then - report_containers("action %a, category %a, name %a","reuse",container.subcategory,name) - end - end - return stored -end -function containers.write(container,name,data) - if data then - data.cache_version=container.version - if container.enabled and caches then - local unique,shared=data.unique,data.shared - data.unique,data.shared=nil,nil - caches.savedata(container.writable,name,data) - if trace_cache or trace_containers then - report_containers("action %a, category %a, name %a","save",container.subcategory,name) - end - data.unique,data.shared=unique,shared - end - if trace_cache or trace_containers then - report_containers("action %a, category %a, name %a","store",container.subcategory,name) - end - container.storage[name]=data - end - return data -end -function containers.content(container,name) - return container.storage[name] -end -function containers.cleanname(name) - return (gsub(lower(name),"[^%w\128-\255]+","-")) -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luatex-fonts-nod']={ - version=1.001, - comment="companion to luatex-fonts.lua", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end -if tex.attribute[0]~=0 then - texio.write_nl("log","!") - texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") - texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") - texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.") - texio.write_nl("log","!") - tex.attribute[0]=0 -end -attributes=attributes or {} -attributes.unsetvalue=-0x7FFFFFFF -local numbers,last={},127 -attributes.private=attributes.private or function(name) - local number=numbers[name] - if not number then - if last<255 then - last=last+1 - end - number=last - numbers[name]=number - end - return number -end -nodes={} -nodes.pool={} -nodes.handlers={} -local nodecodes={} for k,v in next,node.types () do nodecodes[string.gsub(v,"_","")]=k end -local whatcodes={} for k,v in next,node.whatsits() do whatcodes[string.gsub(v,"_","")]=k end -local glyphcodes={ [0]="character","glyph","ligature","ghost","left","right" } -local disccodes={ [0]="discretionary","explicit","automatic","regular","first","second" } -nodes.nodecodes=nodecodes -nodes.whatcodes=whatcodes -nodes.whatsitcodes=whatcodes -nodes.glyphcodes=glyphcodes -nodes.disccodes=disccodes -local free_node=node.free -local remove_node=node.remove -local new_node=node.new -local traverse_id=node.traverse_id -local math_code=nodecodes.math -nodes.handlers.protectglyphs=node.protect_glyphs -nodes.handlers.unprotectglyphs=node.unprotect_glyphs -function nodes.remove(head,current,free_too) - local t=current - head,current=remove_node(head,current) - if t then - if free_too then - free_node(t) - t=nil - else - t.next,t.prev=nil,nil - end - end - return head,current,t -end -function nodes.delete(head,current) - return nodes.remove(head,current,true) -end -function nodes.pool.kern(k) - local n=new_node("kern",1) - n.kern=k - return n -end -local getfield=node.getfield or function(n,tag) return n[tag] end -local setfield=node.setfield or function(n,tag,value) n[tag]=value end -nodes.getfield=getfield -nodes.setfield=setfield -nodes.getattr=getfield -nodes.setattr=setfield -if node.getid then nodes.getid=node.getid else function nodes.getid (n) return getfield(n,"id") end end -if node.getsubtype then nodes.getsubtype=node.getsubtype else function nodes.getsubtype(n) return getfield(n,"subtype") end end -if node.getnext then nodes.getnext=node.getnext else function nodes.getnext (n) return getfield(n,"next") end end -if node.getprev then nodes.getprev=node.getprev else function nodes.getprev (n) return getfield(n,"prev") end end -if node.getchar then nodes.getchar=node.getchar else function nodes.getchar (n) return getfield(n,"char") end end -if node.getfont then nodes.getfont=node.getfont else function nodes.getfont (n) return getfield(n,"font") end end -if node.getlist then nodes.getlist=node.getlist else function nodes.getlist (n) return getfield(n,"list") end end -function nodes.tonut (n) return n end -function nodes.tonode(n) return n end -nodes.tostring=node.tostring or tostring -nodes.copy=node.copy -nodes.copy_list=node.copy_list -nodes.delete=node.delete -nodes.dimensions=node.dimensions -nodes.end_of_math=node.end_of_math -nodes.flush_list=node.flush_list -nodes.flush_node=node.flush_node -nodes.free=node.free -nodes.insert_after=node.insert_after -nodes.insert_before=node.insert_before -nodes.hpack=node.hpack -nodes.new=node.new -nodes.tail=node.tail -nodes.traverse=node.traverse -nodes.traverse_id=node.traverse_id -nodes.slide=node.slide -nodes.vpack=node.vpack -nodes.first_glyph=node.first_glyph -nodes.first_character=node.first_character -nodes.has_glyph=node.has_glyph or node.first_glyph -nodes.current_attr=node.current_attr -nodes.do_ligature_n=node.do_ligature_n -nodes.has_field=node.has_field -nodes.last_node=node.last_node -nodes.usedlist=node.usedlist -nodes.protrusion_skippable=node.protrusion_skippable -nodes.write=node.write -nodes.has_attribute=node.has_attribute -nodes.set_attribute=node.set_attribute -nodes.unset_attribute=node.unset_attribute -nodes.protect_glyphs=node.protect_glyphs -nodes.unprotect_glyphs=node.unprotect_glyphs -nodes.kerning=node.kerning -nodes.ligaturing=node.ligaturing -nodes.mlist_to_hlist=node.mlist_to_hlist -nodes.nuts=nodes - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-ini']={ - 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 allocate=utilities.storage.allocate -local report_defining=logs.reporter("fonts","defining") -fonts=fonts or {} -local fonts=fonts -fonts.hashes={ identifiers=allocate() } -fonts.tables=fonts.tables or {} -fonts.helpers=fonts.helpers or {} -fonts.tracers=fonts.tracers or {} -fonts.specifiers=fonts.specifiers or {} -fonts.analyzers={} -fonts.readers={} -fonts.definers={ methods={} } -fonts.loggers={ register=function() end } -fontloader.totable=fontloader.to_table - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-con']={ - 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 next,tostring,rawget=next,tostring,rawget -local format,match,lower,gsub=string.format,string.match,string.lower,string.gsub -local utfbyte=utf.byte -local sort,insert,concat,sortedkeys,serialize,fastcopy=table.sort,table.insert,table.concat,table.sortedkeys,table.serialize,table.fastcopy -local derivetable=table.derive -local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) -local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end) -local report_defining=logs.reporter("fonts","defining") -local fonts=fonts -local constructors=fonts.constructors or {} -fonts.constructors=constructors -local handlers=fonts.handlers or {} -fonts.handlers=handlers -local allocate=utilities.storage.allocate -local setmetatableindex=table.setmetatableindex -constructors.dontembed=allocate() -constructors.autocleanup=true -constructors.namemode="fullpath" -constructors.version=1.01 -constructors.cache=containers.define("fonts","constructors",constructors.version,false) -constructors.privateoffset=0xF0000 -constructors.keys={ - properties={ - encodingbytes="number", - embedding="number", - cidinfo={}, - format="string", - fontname="string", - fullname="string", - filename="filename", - psname="string", - name="string", - virtualized="boolean", - hasitalics="boolean", - autoitalicamount="basepoints", - nostackmath="boolean", - noglyphnames="boolean", - mode="string", - hasmath="boolean", - mathitalics="boolean", - textitalics="boolean", - finalized="boolean", - }, - parameters={ - mathsize="number", - scriptpercentage="float", - scriptscriptpercentage="float", - units="cardinal", - designsize="scaledpoints", - expansion={ - stretch="integerscale", - shrink="integerscale", - step="integerscale", - auto="boolean", - }, - protrusion={ - auto="boolean", - }, - slantfactor="float", - extendfactor="float", - factor="float", - hfactor="float", - vfactor="float", - size="scaledpoints", - units="scaledpoints", - scaledpoints="scaledpoints", - slantperpoint="scaledpoints", - spacing={ - width="scaledpoints", - stretch="scaledpoints", - shrink="scaledpoints", - extra="scaledpoints", - }, - xheight="scaledpoints", - quad="scaledpoints", - ascender="scaledpoints", - descender="scaledpoints", - synonyms={ - space="spacing.width", - spacestretch="spacing.stretch", - spaceshrink="spacing.shrink", - extraspace="spacing.extra", - x_height="xheight", - space_stretch="spacing.stretch", - space_shrink="spacing.shrink", - extra_space="spacing.extra", - em="quad", - ex="xheight", - slant="slantperpoint", - }, - }, - description={ - width="basepoints", - height="basepoints", - depth="basepoints", - boundingbox={}, - }, - character={ - width="scaledpoints", - height="scaledpoints", - depth="scaledpoints", - italic="scaledpoints", - }, -} -local designsizes=allocate() -constructors.designsizes=designsizes -local loadedfonts=allocate() -constructors.loadedfonts=loadedfonts -local factors={ - pt=65536.0, - bp=65781.8, -} -function constructors.setfactor(f) - constructors.factor=factors[f or 'pt'] or factors.pt -end -constructors.setfactor() -function constructors.scaled(scaledpoints,designsize) - if scaledpoints<0 then - if designsize then - local factor=constructors.factor - if designsize>factor then - return (- scaledpoints/1000)*designsize - else - return (- scaledpoints/1000)*designsize*factor - end - else - return (- scaledpoints/1000)*10*factor - end - else - return scaledpoints - end -end -function constructors.cleanuptable(tfmdata) - if constructors.autocleanup and tfmdata.properties.virtualized then - for k,v in next,tfmdata.characters do - if v.commands then v.commands=nil end - end - end -end -function constructors.calculatescale(tfmdata,scaledpoints) - local parameters=tfmdata.parameters - if scaledpoints<0 then - scaledpoints=(- scaledpoints/1000)*(tfmdata.designsize or parameters.designsize) - end - return scaledpoints,scaledpoints/(parameters.units or 1000) -end -local unscaled={ - ScriptPercentScaleDown=true, - ScriptScriptPercentScaleDown=true, - RadicalDegreeBottomRaisePercent=true -} -function constructors.assignmathparameters(target,original) - local mathparameters=original.mathparameters - if mathparameters and next(mathparameters) then - local targetparameters=target.parameters - local targetproperties=target.properties - local targetmathparameters={} - local factor=targetproperties.math_is_scaled and 1 or targetparameters.factor - for name,value in next,mathparameters do - if unscaled[name] then - targetmathparameters[name]=value - else - targetmathparameters[name]=value*factor - end - end - if not targetmathparameters.FractionDelimiterSize then - targetmathparameters.FractionDelimiterSize=1.01*targetparameters.size - end - if not mathparameters.FractionDelimiterDisplayStyleSize then - targetmathparameters.FractionDelimiterDisplayStyleSize=2.40*targetparameters.size - end - target.mathparameters=targetmathparameters - end -end -function constructors.beforecopyingcharacters(target,original) -end -function constructors.aftercopyingcharacters(target,original) -end -constructors.sharefonts=false -constructors.nofsharedfonts=0 -local sharednames={} -function constructors.trytosharefont(target,tfmdata) - if constructors.sharefonts then - local characters=target.characters - local n=1 - local t={ target.psname } - local u=sortedkeys(characters) - for i=1,#u do - n=n+1;t[n]=k - n=n+1;t[n]=characters[u[i]].index or k - end - local h=md5.HEX(concat(t," ")) - local s=sharednames[h] - if s then - if trace_defining then - report_defining("font %a uses backend resources of font %a",target.fullname,s) - end - target.fullname=s - constructors.nofsharedfonts=constructors.nofsharedfonts+1 - target.properties.sharedwith=s - else - sharednames[h]=target.fullname - end - end -end -function constructors.enhanceparameters(parameters) - local xheight=parameters.x_height - local quad=parameters.quad - local space=parameters.space - local stretch=parameters.space_stretch - local shrink=parameters.space_shrink - local extra=parameters.extra_space - local slant=parameters.slant - parameters.xheight=xheight - parameters.spacestretch=stretch - parameters.spaceshrink=shrink - parameters.extraspace=extra - parameters.em=quad - parameters.ex=xheight - parameters.slantperpoint=slant - parameters.spacing={ - width=space, - stretch=stretch, - shrink=shrink, - extra=extra, - } -end -function constructors.scale(tfmdata,specification) - local target={} - if tonumber(specification) then - specification={ size=specification } - end - target.specification=specification - local scaledpoints=specification.size - local relativeid=specification.relativeid - local properties=tfmdata.properties or {} - local goodies=tfmdata.goodies or {} - local resources=tfmdata.resources or {} - local descriptions=tfmdata.descriptions or {} - local characters=tfmdata.characters or {} - local changed=tfmdata.changed or {} - local shared=tfmdata.shared or {} - local parameters=tfmdata.parameters or {} - local mathparameters=tfmdata.mathparameters or {} - local targetcharacters={} - local targetdescriptions=derivetable(descriptions) - local targetparameters=derivetable(parameters) - local targetproperties=derivetable(properties) - local targetgoodies=goodies - target.characters=targetcharacters - target.descriptions=targetdescriptions - target.parameters=targetparameters - target.properties=targetproperties - target.goodies=targetgoodies - target.shared=shared - target.resources=resources - target.unscaled=tfmdata - local mathsize=tonumber(specification.mathsize) or 0 - local textsize=tonumber(specification.textsize) or scaledpoints - local forcedsize=tonumber(parameters.mathsize ) or 0 - local extrafactor=tonumber(specification.factor ) or 1 - if (mathsize==2 or forcedsize==2) and parameters.scriptpercentage then - scaledpoints=parameters.scriptpercentage*textsize/100 - elseif (mathsize==3 or forcedsize==3) and parameters.scriptscriptpercentage then - scaledpoints=parameters.scriptscriptpercentage*textsize/100 - elseif forcedsize>1000 then - scaledpoints=forcedsize - end - targetparameters.mathsize=mathsize - targetparameters.textsize=textsize - targetparameters.forcedsize=forcedsize - targetparameters.extrafactor=extrafactor - local tounicode=resources.tounicode - local defaultwidth=resources.defaultwidth or 0 - local defaultheight=resources.defaultheight or 0 - local defaultdepth=resources.defaultdepth or 0 - local units=parameters.units or 1000 - if target.fonts then - target.fonts=fastcopy(target.fonts) - end - targetproperties.language=properties.language or "dflt" - targetproperties.script=properties.script or "dflt" - targetproperties.mode=properties.mode or "base" - local askedscaledpoints=scaledpoints - local scaledpoints,delta=constructors.calculatescale(tfmdata,scaledpoints,nil,specification) - local hdelta=delta - local vdelta=delta - target.designsize=parameters.designsize - target.units_per_em=units - local direction=properties.direction or tfmdata.direction or 0 - target.direction=direction - properties.direction=direction - target.size=scaledpoints - target.encodingbytes=properties.encodingbytes or 1 - target.embedding=properties.embedding or "subset" - target.tounicode=1 - target.cidinfo=properties.cidinfo - target.format=properties.format - local fontname=properties.fontname or tfmdata.fontname - local fullname=properties.fullname or tfmdata.fullname - local filename=properties.filename or tfmdata.filename - local psname=properties.psname or tfmdata.psname - local name=properties.name or tfmdata.name - if not psname or psname=="" then - psname=fontname or (fullname and fonts.names.cleanname(fullname)) - end - target.fontname=fontname - target.fullname=fullname - target.filename=filename - target.psname=psname - target.name=name - properties.fontname=fontname - properties.fullname=fullname - properties.filename=filename - properties.psname=psname - properties.name=name - local expansion=parameters.expansion - if expansion then - target.stretch=expansion.stretch - target.shrink=expansion.shrink - target.step=expansion.step - target.auto_expand=expansion.auto - end - local protrusion=parameters.protrusion - if protrusion then - target.auto_protrude=protrusion.auto - end - local extendfactor=parameters.extendfactor or 0 - if extendfactor~=0 and extendfactor~=1 then - hdelta=hdelta*extendfactor - target.extend=extendfactor*1000 - else - target.extend=1000 - end - local slantfactor=parameters.slantfactor or 0 - if slantfactor~=0 then - target.slant=slantfactor*1000 - else - target.slant=0 - end - targetparameters.factor=delta - targetparameters.hfactor=hdelta - targetparameters.vfactor=vdelta - targetparameters.size=scaledpoints - targetparameters.units=units - targetparameters.scaledpoints=askedscaledpoints - local isvirtual=properties.virtualized or tfmdata.type=="virtual" - local hasquality=target.auto_expand or target.auto_protrude - local hasitalics=properties.hasitalics - local autoitalicamount=properties.autoitalicamount - local stackmath=not properties.nostackmath - local nonames=properties.noglyphnames - local nodemode=properties.mode=="node" - if changed and not next(changed) then - changed=false - end - target.type=isvirtual and "virtual" or "real" - target.postprocessors=tfmdata.postprocessors - local targetslant=(parameters.slant or parameters[1] or 0)*factors.pt - local targetspace=(parameters.space or parameters[2] or 0)*hdelta - local targetspace_stretch=(parameters.space_stretch or parameters[3] or 0)*hdelta - local targetspace_shrink=(parameters.space_shrink or parameters[4] or 0)*hdelta - local targetx_height=(parameters.x_height or parameters[5] or 0)*vdelta - local targetquad=(parameters.quad or parameters[6] or 0)*hdelta - local targetextra_space=(parameters.extra_space or parameters[7] or 0)*hdelta - targetparameters.slant=targetslant - targetparameters.space=targetspace - targetparameters.space_stretch=targetspace_stretch - targetparameters.space_shrink=targetspace_shrink - targetparameters.x_height=targetx_height - targetparameters.quad=targetquad - targetparameters.extra_space=targetextra_space - local ascender=parameters.ascender - if ascender then - targetparameters.ascender=delta*ascender - end - local descender=parameters.descender - if descender then - targetparameters.descender=delta*descender - end - constructors.enhanceparameters(targetparameters) - local protrusionfactor=(targetquad~=0 and 1000/targetquad) or 0 - local scaledwidth=defaultwidth*hdelta - local scaledheight=defaultheight*vdelta - local scaleddepth=defaultdepth*vdelta - local hasmath=(properties.hasmath or next(mathparameters)) and true - if hasmath then - constructors.assignmathparameters(target,tfmdata) - properties.hasmath=true - target.nomath=false - target.MathConstants=target.mathparameters - else - properties.hasmath=false - target.nomath=true - target.mathparameters=nil - end - local italickey="italic" - local useitalics=true - if hasmath then - autoitalicamount=false - elseif properties.textitalics then - italickey="italic_correction" - useitalics=false - if properties.delaytextitalics then - autoitalicamount=false - end - end - if trace_defining then - report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a", - name,fullname,filename,hdelta,vdelta, - hasmath and "enabled" or "disabled",useitalics and "enabled" or "disabled") - end - constructors.beforecopyingcharacters(target,tfmdata) - local sharedkerns={} - for unicode,character in next,characters do - local chr,description,index,touni - if changed then - local c=changed[unicode] - if c then - description=descriptions[c] or descriptions[unicode] or character - character=characters[c] or character - index=description.index or c - if tounicode then - touni=tounicode[index] - if not touni then - local d=descriptions[unicode] or characters[unicode] - local i=d.index or unicode - touni=tounicode[i] - end - end - else - description=descriptions[unicode] or character - index=description.index or unicode - if tounicode then - touni=tounicode[index] - end - end - else - description=descriptions[unicode] or character - index=description.index or unicode - if tounicode then - touni=tounicode[index] - end - end - local width=description.width - local height=description.height - local depth=description.depth - if width then width=hdelta*width else width=scaledwidth end - if height then height=vdelta*height else height=scaledheight end - if depth and depth~=0 then - depth=delta*depth - if nonames then - chr={ - index=index, - height=height, - depth=depth, - width=width, - } - else - chr={ - name=description.name, - index=index, - height=height, - depth=depth, - width=width, - } - end - else - if nonames then - chr={ - index=index, - height=height, - width=width, - } - else - chr={ - name=description.name, - index=index, - height=height, - width=width, - } - end - end - if touni then - chr.tounicode=touni - end - if hasquality then - local ve=character.expansion_factor - if ve then - chr.expansion_factor=ve*1000 - end - local vl=character.left_protruding - if vl then - chr.left_protruding=protrusionfactor*width*vl - end - local vr=character.right_protruding - if vr then - chr.right_protruding=protrusionfactor*width*vr - end - end - if autoitalicamount then - local vi=description.italic - if not vi then - local vi=description.boundingbox[3]-description.width+autoitalicamount - if vi>0 then - chr[italickey]=vi*hdelta - end - elseif vi~=0 then - chr[italickey]=vi*hdelta - end - elseif hasitalics then - local vi=description.italic - if vi and vi~=0 then - chr[italickey]=vi*hdelta - end - end - if hasmath then - local vn=character.next - if vn then - chr.next=vn - else - local vv=character.vert_variants - if vv then - local t={} - for i=1,#vv do - local vvi=vv[i] - t[i]={ - ["start"]=(vvi["start"] or 0)*vdelta, - ["end"]=(vvi["end"] or 0)*vdelta, - ["advance"]=(vvi["advance"] or 0)*vdelta, - ["extender"]=vvi["extender"], - ["glyph"]=vvi["glyph"], - } - end - chr.vert_variants=t - else - local hv=character.horiz_variants - if hv then - local t={} - for i=1,#hv do - local hvi=hv[i] - t[i]={ - ["start"]=(hvi["start"] or 0)*hdelta, - ["end"]=(hvi["end"] or 0)*hdelta, - ["advance"]=(hvi["advance"] or 0)*hdelta, - ["extender"]=hvi["extender"], - ["glyph"]=hvi["glyph"], - } - end - chr.horiz_variants=t - end - end - end - local va=character.top_accent - if va then - chr.top_accent=vdelta*va - end - if stackmath then - local mk=character.mathkerns - if mk then - local kerns={} - local v=mk.top_right if v then local k={} for i=1,#v do local vi=v[i] - k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } - end kerns.top_right=k end - local v=mk.top_left if v then local k={} for i=1,#v do local vi=v[i] - k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } - end kerns.top_left=k end - local v=mk.bottom_left if v then local k={} for i=1,#v do local vi=v[i] - k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } - end kerns.bottom_left=k end - local v=mk.bottom_right if v then local k={} for i=1,#v do local vi=v[i] - k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } - end kerns.bottom_right=k end - chr.mathkern=kerns - end - end - end - if not nodemode then - local vk=character.kerns - if vk then - local s=sharedkerns[vk] - if not s then - s={} - for k,v in next,vk do s[k]=v*hdelta end - sharedkerns[vk]=s - end - chr.kerns=s - end - local vl=character.ligatures - if vl then - if true then - chr.ligatures=vl - else - local tt={} - for i,l in next,vl do - tt[i]=l - end - chr.ligatures=tt - end - end - end - if isvirtual then - local vc=character.commands - if vc then - local ok=false - for i=1,#vc do - local key=vc[i][1] - if key=="right" or key=="down" then - ok=true - break - end - end - if ok then - local tt={} - for i=1,#vc do - local ivc=vc[i] - local key=ivc[1] - if key=="right" then - tt[i]={ key,ivc[2]*hdelta } - elseif key=="down" then - tt[i]={ key,ivc[2]*vdelta } - elseif key=="rule" then - tt[i]={ key,ivc[2]*vdelta,ivc[3]*hdelta } - else - tt[i]=ivc - end - end - chr.commands=tt - else - chr.commands=vc - end - chr.index=nil - end - end - targetcharacters[unicode]=chr - end - constructors.aftercopyingcharacters(target,tfmdata) - constructors.trytosharefont(target,tfmdata) - return target -end -function constructors.finalize(tfmdata) - if tfmdata.properties and tfmdata.properties.finalized then - return - end - if not tfmdata.characters then - return nil - end - if not tfmdata.goodies then - tfmdata.goodies={} - end - local parameters=tfmdata.parameters - if not parameters then - return nil - end - if not parameters.expansion then - parameters.expansion={ - stretch=tfmdata.stretch or 0, - shrink=tfmdata.shrink or 0, - step=tfmdata.step or 0, - auto=tfmdata.auto_expand or false, - } - end - if not parameters.protrusion then - parameters.protrusion={ - auto=auto_protrude - } - end - if not parameters.size then - parameters.size=tfmdata.size - end - if not parameters.extendfactor then - parameters.extendfactor=tfmdata.extend or 0 - end - if not parameters.slantfactor then - parameters.slantfactor=tfmdata.slant or 0 - end - if not parameters.designsize then - parameters.designsize=tfmdata.designsize or (factors.pt*10) - end - if not parameters.units then - parameters.units=tfmdata.units_per_em or 1000 - end - if not tfmdata.descriptions then - local descriptions={} - setmetatableindex(descriptions,function(t,k) local v={} t[k]=v return v end) - tfmdata.descriptions=descriptions - end - local properties=tfmdata.properties - if not properties then - properties={} - tfmdata.properties=properties - end - if not properties.virtualized then - properties.virtualized=tfmdata.type=="virtual" - end - if not tfmdata.properties then - tfmdata.properties={ - fontname=tfmdata.fontname, - filename=tfmdata.filename, - fullname=tfmdata.fullname, - name=tfmdata.name, - psname=tfmdata.psname, - encodingbytes=tfmdata.encodingbytes or 1, - embedding=tfmdata.embedding or "subset", - tounicode=tfmdata.tounicode or 1, - cidinfo=tfmdata.cidinfo or nil, - format=tfmdata.format or "type1", - direction=tfmdata.direction or 0, - } - end - if not tfmdata.resources then - tfmdata.resources={} - end - if not tfmdata.shared then - tfmdata.shared={} - end - if not properties.hasmath then - properties.hasmath=not tfmdata.nomath - end - tfmdata.MathConstants=nil - tfmdata.postprocessors=nil - tfmdata.fontname=nil - tfmdata.filename=nil - tfmdata.fullname=nil - tfmdata.name=nil - tfmdata.psname=nil - tfmdata.encodingbytes=nil - tfmdata.embedding=nil - tfmdata.tounicode=nil - tfmdata.cidinfo=nil - tfmdata.format=nil - tfmdata.direction=nil - tfmdata.type=nil - tfmdata.nomath=nil - tfmdata.designsize=nil - tfmdata.size=nil - tfmdata.stretch=nil - tfmdata.shrink=nil - tfmdata.step=nil - tfmdata.auto_expand=nil - tfmdata.auto_protrude=nil - tfmdata.extend=nil - tfmdata.slant=nil - tfmdata.units_per_em=nil - properties.finalized=true - return tfmdata -end -local hashmethods={} -constructors.hashmethods=hashmethods -function constructors.hashfeatures(specification) - local features=specification.features - if features then - local t,tn={},0 - for category,list in next,features do - if next(list) then - local hasher=hashmethods[category] - if hasher then - local hash=hasher(list) - if hash then - tn=tn+1 - t[tn]=category..":"..hash - end - end - end - end - if tn>0 then - return concat(t," & ") - end - end - return "unknown" -end -hashmethods.normal=function(list) - local s={} - local n=0 - for k,v in next,list do - if not k then - elseif k=="number" or k=="features" then - else - n=n+1 - s[n]=k - end - end - if n>0 then - sort(s) - for i=1,n do - local k=s[i] - s[i]=k..'='..tostring(list[k]) - end - return concat(s,"+") - end -end -function constructors.hashinstance(specification,force) - local hash,size,fallbacks=specification.hash,specification.size,specification.fallbacks - if force or not hash then - hash=constructors.hashfeatures(specification) - specification.hash=hash - end - if size<1000 and designsizes[hash] then - size=math.round(constructors.scaled(size,designsizes[hash])) - specification.size=size - end - if fallbacks then - return hash..' @ '..tostring(size)..' @ '..fallbacks - else - return hash..' @ '..tostring(size) - end -end -function constructors.setname(tfmdata,specification) - if constructors.namemode=="specification" then - local specname=specification.specification - if specname then - tfmdata.properties.name=specname - if trace_defining then - report_otf("overloaded fontname %a",specname) - end - end - end -end -function constructors.checkedfilename(data) - local foundfilename=data.foundfilename - if not foundfilename then - local askedfilename=data.filename or "" - if askedfilename~="" then - askedfilename=resolvers.resolve(askedfilename) - foundfilename=resolvers.findbinfile(askedfilename,"") or "" - if foundfilename=="" then - report_defining("source file %a is not found",askedfilename) - foundfilename=resolvers.findbinfile(file.basename(askedfilename),"") or "" - if foundfilename~="" then - report_defining("using source file %a due to cache mismatch",foundfilename) - end - end - end - data.foundfilename=foundfilename - end - return foundfilename -end -local formats=allocate() -fonts.formats=formats -setmetatableindex(formats,function(t,k) - local l=lower(k) - if rawget(t,k) then - t[k]=l - return l - end - return rawget(t,file.suffix(l)) -end) -local locations={} -local function setindeed(mode,target,group,name,action,position) - local t=target[mode] - if not t then - report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode) - os.exit() - elseif position then - insert(t,position,{ name=name,action=action }) - else - for i=1,#t do - local ti=t[i] - if ti.name==name then - ti.action=action - return - end - end - insert(t,{ name=name,action=action }) - end -end -local function set(group,name,target,source) - target=target[group] - if not target then - report_defining("fatal target error in setting feature %a, group %a",name,group) - os.exit() - end - local source=source[group] - if not source then - report_defining("fatal source error in setting feature %a, group %a",name,group) - os.exit() - end - local node=source.node - local base=source.base - local position=source.position - if node then - setindeed("node",target,group,name,node,position) - end - if base then - setindeed("base",target,group,name,base,position) - end -end -local function register(where,specification) - local name=specification.name - if name and name~="" then - local default=specification.default - local description=specification.description - local initializers=specification.initializers - local processors=specification.processors - local manipulators=specification.manipulators - local modechecker=specification.modechecker - if default then - where.defaults[name]=default - end - if description and description~="" then - where.descriptions[name]=description - end - if initializers then - set('initializers',name,where,specification) - end - if processors then - set('processors',name,where,specification) - end - if manipulators then - set('manipulators',name,where,specification) - end - if modechecker then - where.modechecker=modechecker - end - end -end -constructors.registerfeature=register -function constructors.getfeatureaction(what,where,mode,name) - what=handlers[what].features - if what then - where=what[where] - if where then - mode=where[mode] - if mode then - for i=1,#mode do - local m=mode[i] - if m.name==name then - return m.action - end - end - end - end - end -end -function constructors.newhandler(what) - local handler=handlers[what] - if not handler then - handler={} - handlers[what]=handler - end - return handler -end -function constructors.newfeatures(what) - local handler=handlers[what] - local features=handler.features - if not features then - local tables=handler.tables - local statistics=handler.statistics - features=allocate { - defaults={}, - descriptions=tables and tables.features or {}, - used=statistics and statistics.usedfeatures or {}, - initializers={ base={},node={} }, - processors={ base={},node={} }, - manipulators={ base={},node={} }, - } - features.register=function(specification) return register(features,specification) end - handler.features=features - end - return features -end -function constructors.checkedfeatures(what,features) - local defaults=handlers[what].features.defaults - if features and next(features) then - features=fastcopy(features) - for key,value in next,defaults do - if features[key]==nil then - features[key]=value - end - end - return features - else - return fastcopy(defaults) - end -end -function constructors.initializefeatures(what,tfmdata,features,trace,report) - if features and next(features) then - local properties=tfmdata.properties or {} - local whathandler=handlers[what] - local whatfeatures=whathandler.features - local whatinitializers=whatfeatures.initializers - local whatmodechecker=whatfeatures.modechecker - local mode=properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base" - properties.mode=mode - features.mode=mode - local done={} - while true do - local redo=false - local initializers=whatfeatures.initializers[mode] - if initializers then - for i=1,#initializers do - local step=initializers[i] - local feature=step.name - local value=features[feature] - if not value then - elseif done[feature] then - else - local action=step.action - if trace then - report("initializing feature %a to %a for mode %a for font %a",feature, - value,mode,tfmdata.properties.fullname) - end - action(tfmdata,value,features) - if mode~=properties.mode or mode~=features.mode then - if whatmodechecker then - properties.mode=whatmodechecker(tfmdata,features,properties.mode) - features.mode=properties.mode - end - if mode~=properties.mode then - mode=properties.mode - redo=true - end - end - done[feature]=true - end - if redo then - break - end - end - if not redo then - break - end - else - break - end - end - properties.mode=mode - return true - else - return false - end -end -function constructors.collectprocessors(what,tfmdata,features,trace,report) - local processes,nofprocesses={},0 - if features and next(features) then - local properties=tfmdata.properties - local whathandler=handlers[what] - local whatfeatures=whathandler.features - local whatprocessors=whatfeatures.processors - local mode=properties.mode - local processors=whatprocessors[mode] - if processors then - for i=1,#processors do - local step=processors[i] - local feature=step.name - if features[feature] then - local action=step.action - if trace then - report("installing feature processor %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname) - end - if action then - nofprocesses=nofprocesses+1 - processes[nofprocesses]=action - end - end - end - elseif trace then - report("no feature processors for mode %a for font %a",mode,properties.fullname) - end - end - return processes -end -function constructors.applymanipulators(what,tfmdata,features,trace,report) - if features and next(features) then - local properties=tfmdata.properties - local whathandler=handlers[what] - local whatfeatures=whathandler.features - local whatmanipulators=whatfeatures.manipulators - local mode=properties.mode - local manipulators=whatmanipulators[mode] - if manipulators then - for i=1,#manipulators do - local step=manipulators[i] - local feature=step.name - local value=features[feature] - if value then - local action=step.action - if trace then - report("applying feature manipulator %a for mode %a for font %a",feature,mode,properties.fullname) - end - if action then - action(tfmdata,feature,value) - end - end - end - end - end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luatex-font-enc']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end -local fonts=fonts -fonts.encodings={} -fonts.encodings.agl={} -setmetatable(fonts.encodings.agl,{ __index=function(t,k) - if k=="unicodes" then - texio.write(" ") - local unicodes=dofile(resolvers.findfile("font-age.lua")) - fonts.encodings.agl={ unicodes=unicodes } - return unicodes - else - return nil - end -end }) - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-cid']={ - version=1.001, - comment="companion to font-otf.lua (cidmaps)", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local format,match,lower=string.format,string.match,string.lower -local tonumber=tonumber -local P,S,R,C,V,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.match -local fonts,logs,trackers=fonts,logs,trackers -local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) -local report_otf=logs.reporter("fonts","otf loading") -local cid={} -fonts.cid=cid -local cidmap={} -local cidmax=10 -local number=C(R("09","af","AF")^1) -local space=S(" \n\r\t") -local spaces=space^0 -local period=P(".") -local periods=period*period -local name=P("/")*C((1-space)^1) -local unicodes,names={},{} -local function do_one(a,b) - unicodes[tonumber(a)]=tonumber(b,16) -end -local function do_range(a,b,c) - c=tonumber(c,16) - for i=tonumber(a),tonumber(b) do - unicodes[i]=c - c=c+1 - end -end -local function do_name(a,b) - names[tonumber(a)]=b -end -local grammar=P { "start", - start=number*spaces*number*V("series"), - series=(spaces*(V("one")+V("range")+V("named")))^1, - one=(number*spaces*number)/do_one, - range=(number*periods*number*spaces*number)/do_range, - named=(number*spaces*name)/do_name -} -local function loadcidfile(filename) - local data=io.loaddata(filename) - if data then - unicodes,names={},{} - lpegmatch(grammar,data) - local supplement,registry,ordering=match(filename,"^(.-)%-(.-)%-()%.(.-)$") - return { - supplement=supplement, - registry=registry, - ordering=ordering, - filename=filename, - unicodes=unicodes, - names=names - } - end -end -cid.loadfile=loadcidfile -local template="%s-%s-%s.cidmap" -local function locate(registry,ordering,supplement) - local filename=format(template,registry,ordering,supplement) - local hashname=lower(filename) - local found=cidmap[hashname] - if not found then - if trace_loading then - report_otf("checking cidmap, registry %a, ordering %a, supplement %a, filename %a",registry,ordering,supplement,filename) - end - local fullname=resolvers.findfile(filename,'cid') or "" - if fullname~="" then - found=loadcidfile(fullname) - if found then - if trace_loading then - report_otf("using cidmap file %a",filename) - end - cidmap[hashname]=found - found.usedname=file.basename(filename) - end - end - end - return found -end -function cid.getmap(specification) - if not specification then - report_otf("invalid cidinfo specification, table expected") - return - end - local registry=specification.registry - local ordering=specification.ordering - local supplement=specification.supplement - local filename=format(registry,ordering,supplement) - local found=cidmap[lower(filename)] - if found then - return found - end - if trace_loading then - report_otf("cidmap needed, registry %a, ordering %a, supplement %a",registry,ordering,supplement) - end - found=locate(registry,ordering,supplement) - if not found then - local supnum=tonumber(supplement) - local cidnum=nil - if supnum0 then - for s=supnum-1,0,-1 do - local c=locate(registry,ordering,s) - if c then - found,cidnum=c,s - break - end - end - end - registry=lower(registry) - ordering=lower(ordering) - if found and cidnum>0 then - for s=0,cidnum-1 do - local filename=format(template,registry,ordering,s) - if not cidmap[filename] then - cidmap[filename]=found - end - end - end - end - return found -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-map']={ - 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=tonumber -local match,format,find,concat,gsub,lower=string.match,string.format,string.find,table.concat,string.gsub,string.lower -local P,R,S,C,Ct,Cc,lpegmatch=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.match -local utfbyte=utf.byte -local floor=math.floor -local trace_loading=false trackers.register("fonts.loading",function(v) trace_loading=v end) -local trace_mapping=false trackers.register("fonts.mapping",function(v) trace_unimapping=v end) -local report_fonts=logs.reporter("fonts","loading") -local fonts=fonts or {} -local mappings=fonts.mappings or {} -fonts.mappings=mappings -local function loadlumtable(filename) - local lumname=file.replacesuffix(file.basename(filename),"lum") - local lumfile=resolvers.findfile(lumname,"map") or "" - if lumfile~="" and lfs.isfile(lumfile) then - if trace_loading or trace_mapping then - report_fonts("loading map table %a",lumfile) - end - lumunic=dofile(lumfile) - return lumunic,lumfile - end -end -local hex=R("AF","09") -local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end -local hexsix=(hex*hex*hex*hex*hex*hex)/function(s) return tonumber(s,16) end -local dec=(R("09")^1)/tonumber -local period=P(".") -local unicode=P("uni")*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true)) -local ucode=P("u")*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true)) -local index=P("index")*dec*Cc(false) -local parser=unicode+ucode+index -local parsers={} -local function makenameparser(str) - if not str or str=="" then - return parser - else - local p=parsers[str] - if not p then - p=P(str)*period*dec*Cc(false) - parsers[str]=p - end - return p - end -end -local function tounicode16(unicode,name) - if unicode<0x10000 then - return format("%04X",unicode) - elseif unicode<0x1FFFFFFFFF then - return format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00) - else - report_fonts("can't convert %a in %a into tounicode",unicode,name) - end -end -local function tounicode16sequence(unicodes,name) - local t={} - for l=1,#unicodes do - local unicode=unicodes[l] - if unicode<0x10000 then - t[l]=format("%04X",unicode) - elseif unicode<0x1FFFFFFFFF then - t[l]=format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00) - else - report_fonts ("can't convert %a in %a into tounicode",unicode,name) - end - end - return concat(t) -end -local function fromunicode16(str) - if #str==4 then - return tonumber(str,16) - else - local l,r=match(str,"(....)(....)") - return (tonumber(l,16))*0x400+tonumber(r,16)-0xDC00 - end -end -mappings.loadlumtable=loadlumtable -mappings.makenameparser=makenameparser -mappings.tounicode16=tounicode16 -mappings.tounicode16sequence=tounicode16sequence -mappings.fromunicode16=fromunicode16 -local ligseparator=P("_") -local varseparator=P(".") -local namesplitter=Ct(C((1-ligseparator-varseparator)^1)*(ligseparator*C((1-ligseparator-varseparator)^1))^0) -function mappings.addtounicode(data,filename) - local resources=data.resources - local properties=data.properties - local descriptions=data.descriptions - local unicodes=resources.unicodes - if not unicodes then - return - end - unicodes['space']=unicodes['space'] or 32 - unicodes['hyphen']=unicodes['hyphen'] or 45 - unicodes['zwj']=unicodes['zwj'] or 0x200D - unicodes['zwnj']=unicodes['zwnj'] or 0x200C - local private=fonts.constructors.privateoffset - local unknown=format("%04X",utfbyte("?")) - local unicodevector=fonts.encodings.agl.unicodes - local tounicode={} - local originals={} - resources.tounicode=tounicode - resources.originals=originals - local lumunic,uparser,oparser - local cidinfo,cidnames,cidcodes,usedmap - if false then - lumunic=loadlumtable(filename) - lumunic=lumunic and lumunic.tounicode - end - cidinfo=properties.cidinfo - usedmap=cidinfo and fonts.cid.getmap(cidinfo) - if usedmap then - oparser=usedmap and makenameparser(cidinfo.ordering) - cidnames=usedmap.names - cidcodes=usedmap.unicodes - end - uparser=makenameparser() - local ns,nl=0,0 - for unic,glyph in next,descriptions do - local index=glyph.index - local name=glyph.name - if unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then - local unicode=lumunic and lumunic[name] or unicodevector[name] - if unicode then - originals[index]=unicode - tounicode[index]=tounicode16(unicode,name) - ns=ns+1 - end - if (not unicode) and usedmap then - local foundindex=lpegmatch(oparser,name) - if foundindex then - unicode=cidcodes[foundindex] - if unicode then - originals[index]=unicode - tounicode[index]=tounicode16(unicode,name) - ns=ns+1 - else - local reference=cidnames[foundindex] - if reference then - local foundindex=lpegmatch(oparser,reference) - if foundindex then - unicode=cidcodes[foundindex] - if unicode then - originals[index]=unicode - tounicode[index]=tounicode16(unicode,name) - ns=ns+1 - end - end - if not unicode or unicode=="" then - local foundcodes,multiple=lpegmatch(uparser,reference) - if foundcodes then - originals[index]=foundcodes - if multiple then - tounicode[index]=tounicode16sequence(foundcodes) - nl=nl+1 - unicode=true - else - tounicode[index]=tounicode16(foundcodes,name) - ns=ns+1 - unicode=foundcodes - end - end - end - end - end - end - end - if not unicode or unicode=="" then - local split=lpegmatch(namesplitter,name) - local nsplit=split and #split or 0 - local t,n={},0 - unicode=true - for l=1,nsplit do - local base=split[l] - local u=unicodes[base] or unicodevector[base] - if not u then - break - elseif type(u)=="table" then - if u[1]>=private then - unicode=false - break - end - n=n+1 - t[n]=u[1] - else - if u>=private then - unicode=false - break - end - n=n+1 - t[n]=u - end - end - if n==0 then - elseif n==1 then - originals[index]=t[1] - tounicode[index]=tounicode16(t[1],name) - else - originals[index]=t - tounicode[index]=tounicode16sequence(t) - end - nl=nl+1 - end - if not unicode or unicode=="" then - local foundcodes,multiple=lpegmatch(uparser,name) - if foundcodes then - if multiple then - originals[index]=foundcodes - tounicode[index]=tounicode16sequence(foundcodes,name) - nl=nl+1 - unicode=true - else - originals[index]=foundcodes - tounicode[index]=tounicode16(foundcodes,name) - ns=ns+1 - unicode=foundcodes - end - end - end - end - end - if trace_mapping then - for unic,glyph in table.sortedhash(descriptions) do - local name=glyph.name - local index=glyph.index - local toun=tounicode[index] - if toun then - report_fonts("internal slot %U, name %a, unicode %U, tounicode %a",index,name,unic,toun) - else - report_fonts("internal slot %U, name %a, unicode %U",index,name,unic) - end - end - end - if trace_loading and (ns>0 or nl>0) then - report_fonts("%s tounicode entries added, ligatures %s",nl+ns,ns) - end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luatex-fonts-syn']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end -local fonts=fonts -fonts.names=fonts.names or {} -fonts.names.version=1.001 -fonts.names.basename="luatex-fonts-names" -fonts.names.new_to_old={} -fonts.names.old_to_new={} -fonts.names.cache=containers.define("fonts","data",fonts.names.version,true) -local data,loaded=nil,false -local fileformats={ "lua","tex","other text files" } -function fonts.names.reportmissingbase() - texio.write("") - fonts.names.reportmissingbase=nil -end -function fonts.names.reportmissingname() - texio.write("") - fonts.names.reportmissingname=nil -end -function fonts.names.resolve(name,sub) - if not loaded then - local basename=fonts.names.basename - if basename and basename~="" then - data=containers.read(fonts.names.cache,basename) - if not data then - basename=file.addsuffix(basename,"lua") - for i=1,#fileformats do - local format=fileformats[i] - local foundname=resolvers.findfile(basename,format) or "" - if foundname~="" then - data=dofile(foundname) - texio.write("") - break - end - end - end - end - loaded=true - end - if type(data)=="table" and data.version==fonts.names.version then - local condensed=string.gsub(string.lower(name),"[^%a%d]","") - local found=data.mappings and data.mappings[condensed] - if found then - local fontname,filename,subfont=found[1],found[2],found[3] - if subfont then - return filename,fontname - else - return filename,false - end - elseif fonts.names.reportmissingname then - fonts.names.reportmissingname() - return name,false - end - elseif fonts.names.reportmissingbase then - fonts.names.reportmissingbase() - end -end -fonts.names.resolvespec=fonts.names.resolve -function fonts.names.getfilename(askedname,suffix) - return "" -end -function fonts.names.ignoredfile(filename) - return false -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-tfm']={ - 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 next=next -local match=string.match -local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) -local trace_features=false trackers.register("tfm.features",function(v) trace_features=v end) -local report_defining=logs.reporter("fonts","defining") -local report_tfm=logs.reporter("fonts","tfm loading") -local findbinfile=resolvers.findbinfile -local fonts=fonts -local handlers=fonts.handlers -local readers=fonts.readers -local constructors=fonts.constructors -local encodings=fonts.encodings -local tfm=constructors.newhandler("tfm") -local tfmfeatures=constructors.newfeatures("tfm") -local registertfmfeature=tfmfeatures.register -constructors.resolvevirtualtoo=false -fonts.formats.tfm="type1" -function tfm.setfeatures(tfmdata,features) - local okay=constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm) - if okay then - return constructors.collectprocessors("tfm",tfmdata,features,trace_features,report_tfm) - else - return {} - end -end -local function read_from_tfm(specification) - local filename=specification.filename - local size=specification.size - if trace_defining then - report_defining("loading tfm file %a at size %s",filename,size) - end - local tfmdata=font.read_tfm(filename,size) - if tfmdata then - local features=specification.features and specification.features.normal or {} - local resources=tfmdata.resources or {} - local properties=tfmdata.properties or {} - local parameters=tfmdata.parameters or {} - local shared=tfmdata.shared or {} - properties.name=tfmdata.name - properties.fontname=tfmdata.fontname - properties.psname=tfmdata.psname - properties.filename=specification.filename - parameters.size=size - shared.rawdata={} - shared.features=features - shared.processes=next(features) and tfm.setfeatures(tfmdata,features) or nil - tfmdata.properties=properties - tfmdata.resources=resources - tfmdata.parameters=parameters - tfmdata.shared=shared - parameters.slant=parameters.slant or parameters[1] or 0 - parameters.space=parameters.space or parameters[2] or 0 - parameters.space_stretch=parameters.space_stretch or parameters[3] or 0 - parameters.space_shrink=parameters.space_shrink or parameters[4] or 0 - parameters.x_height=parameters.x_height or parameters[5] or 0 - parameters.quad=parameters.quad or parameters[6] or 0 - parameters.extra_space=parameters.extra_space or parameters[7] or 0 - constructors.enhanceparameters(parameters) - if constructors.resolvevirtualtoo then - fonts.loggers.register(tfmdata,file.suffix(filename),specification) - local vfname=findbinfile(specification.name,'ovf') - if vfname and vfname~="" then - local vfdata=font.read_vf(vfname,size) - if vfdata then - local chars=tfmdata.characters - for k,v in next,vfdata.characters do - chars[k].commands=v.commands - end - properties.virtualized=true - tfmdata.fonts=vfdata.fonts - end - end - end - local allfeatures=tfmdata.shared.features or specification.features.normal - constructors.applymanipulators("tfm",tfmdata,allfeatures.normal,trace_features,report_tfm) - if not features.encoding then - local encoding,filename=match(properties.filename,"^(.-)%-(.*)$") - if filename and encoding and encodings.known and encodings.known[encoding] then - features.encoding=encoding - end - end - return tfmdata - end -end -local function check_tfm(specification,fullname) - local foundname=findbinfile(fullname,'tfm') or "" - if foundname=="" then - foundname=findbinfile(fullname,'ofm') or "" - end - if foundname=="" then - foundname=fonts.names.getfilename(fullname,"tfm") or "" - end - if foundname~="" then - specification.filename=foundname - specification.format="ofm" - return read_from_tfm(specification) - elseif trace_defining then - report_defining("loading tfm with name %a fails",specification.name) - end -end -readers.check_tfm=check_tfm -function readers.tfm(specification) - local fullname=specification.filename or "" - if fullname=="" then - local forced=specification.forced or "" - if forced~="" then - fullname=specification.name.."."..forced - else - fullname=specification.name - end - end - return check_tfm(specification,fullname) -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-afm']={ - 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 fonts,logs,trackers,containers,resolvers=fonts,logs,trackers,containers,resolvers -local next,type,tonumber=next,type,tonumber -local format,match,gmatch,lower,gsub,strip=string.format,string.match,string.gmatch,string.lower,string.gsub,string.strip -local abs=math.abs -local P,S,C,R,lpegmatch,patterns=lpeg.P,lpeg.S,lpeg.C,lpeg.R,lpeg.match,lpeg.patterns -local derivetable=table.derive -local trace_features=false trackers.register("afm.features",function(v) trace_features=v end) -local trace_indexing=false trackers.register("afm.indexing",function(v) trace_indexing=v end) -local trace_loading=false trackers.register("afm.loading",function(v) trace_loading=v end) -local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) -local report_afm=logs.reporter("fonts","afm loading") -local findbinfile=resolvers.findbinfile -local definers=fonts.definers -local readers=fonts.readers -local constructors=fonts.constructors -local afm=constructors.newhandler("afm") -local pfb=constructors.newhandler("pfb") -local afmfeatures=constructors.newfeatures("afm") -local registerafmfeature=afmfeatures.register -afm.version=1.410 -afm.cache=containers.define("fonts","afm",afm.version,true) -afm.autoprefixed=true -afm.helpdata={} -afm.syncspace=true -afm.addligatures=true -afm.addtexligatures=true -afm.addkerns=true -local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes -local function setmode(tfmdata,value) - if value then - tfmdata.properties.mode=lower(value) - end -end -registerafmfeature { - name="mode", - description="mode", - initializers={ - base=setmode, - node=setmode, - } -} -local comment=P("Comment") -local spacing=patterns.spacer -local lineend=patterns.newline -local words=C((1-lineend)^1) -local number=C((R("09")+S("."))^1)/tonumber*spacing^0 -local data=lpeg.Carg(1) -local pattern=( - comment*spacing*( - data*( - ("CODINGSCHEME"*spacing*words )/function(fd,a) end+("DESIGNSIZE"*spacing*number*words )/function(fd,a) fd[ 1]=a end+("CHECKSUM"*spacing*number*words )/function(fd,a) fd[ 2]=a end+("SPACE"*spacing*number*"plus"*number*"minus"*number)/function(fd,a,b,c) fd[ 3],fd[ 4],fd[ 5]=a,b,c end+("QUAD"*spacing*number )/function(fd,a) fd[ 6]=a end+("EXTRASPACE"*spacing*number )/function(fd,a) fd[ 7]=a end+("NUM"*spacing*number*number*number )/function(fd,a,b,c) fd[ 8],fd[ 9],fd[10]=a,b,c end+("DENOM"*spacing*number*number )/function(fd,a,b ) fd[11],fd[12]=a,b end+("SUP"*spacing*number*number*number )/function(fd,a,b,c) fd[13],fd[14],fd[15]=a,b,c end+("SUB"*spacing*number*number )/function(fd,a,b) fd[16],fd[17]=a,b end+("SUPDROP"*spacing*number )/function(fd,a) fd[18]=a end+("SUBDROP"*spacing*number )/function(fd,a) fd[19]=a end+("DELIM"*spacing*number*number )/function(fd,a,b) fd[20],fd[21]=a,b end+("AXISHEIGHT"*spacing*number )/function(fd,a) fd[22]=a end - )+(1-lineend)^0 - )+(1-comment)^1 -)^0 -local function scan_comment(str) - local fd={} - lpegmatch(pattern,str,1,fd) - return fd -end -local keys={} -function keys.FontName (data,line) data.metadata.fontname=strip (line) - data.metadata.fullname=strip (line) end -function keys.ItalicAngle (data,line) data.metadata.italicangle=tonumber (line) end -function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch=toboolean(line,true) end -function keys.CharWidth (data,line) data.metadata.charwidth=tonumber (line) end -function keys.XHeight (data,line) data.metadata.xheight=tonumber (line) end -function keys.Descender (data,line) data.metadata.descender=tonumber (line) end -function keys.Ascender (data,line) data.metadata.ascender=tonumber (line) end -function keys.Comment (data,line) - line=lower(line) - local designsize=match(line,"designsize[^%d]*(%d+)") - if designsize then data.metadata.designsize=tonumber(designsize) end -end -local function get_charmetrics(data,charmetrics,vector) - local characters=data.characters - local chr,ind={},0 - for k,v in gmatch(charmetrics,"([%a]+) +(.-) *;") do - if k=='C' then - v=tonumber(v) - if v<0 then - ind=ind+1 - else - ind=v - end - chr={ - index=ind - } - elseif k=='WX' then - chr.width=tonumber(v) - elseif k=='N' then - characters[v]=chr - elseif k=='B' then - local llx,lly,urx,ury=match(v,"^ *(.-) +(.-) +(.-) +(.-)$") - chr.boundingbox={ tonumber(llx),tonumber(lly),tonumber(urx),tonumber(ury) } - elseif k=='L' then - local plus,becomes=match(v,"^(.-) +(.-)$") - local ligatures=chr.ligatures - if ligatures then - ligatures[plus]=becomes - else - chr.ligatures={ [plus]=becomes } - end - end - end -end -local function get_kernpairs(data,kernpairs) - local characters=data.characters - for one,two,value in gmatch(kernpairs,"KPX +(.-) +(.-) +(.-)\n") do - local chr=characters[one] - if chr then - local kerns=chr.kerns - if kerns then - kerns[two]=tonumber(value) - else - chr.kerns={ [two]=tonumber(value) } - end - end - end -end -local function get_variables(data,fontmetrics) - for key,rest in gmatch(fontmetrics,"(%a+) *(.-)[\n\r]") do - local keyhandler=keys[key] - if keyhandler then - keyhandler(data,rest) - end - end -end -local function get_indexes(data,pfbname) - data.resources.filename=resolvers.unresolve(pfbname) - local pfbblob=fontloader.open(pfbname) - if pfbblob then - local characters=data.characters - local pfbdata=fontloader.to_table(pfbblob) - if pfbdata then - local glyphs=pfbdata.glyphs - if glyphs then - if trace_loading then - report_afm("getting index data from %a",pfbname) - end - for index,glyph in next,glyphs do - local name=glyph.name - if name then - local char=characters[name] - if char then - if trace_indexing then - report_afm("glyph %a has index %a",name,index) - end - char.index=index - end - end - end - elseif trace_loading then - report_afm("no glyph data in pfb file %a",pfbname) - end - elseif trace_loading then - report_afm("no data in pfb file %a",pfbname) - end - fontloader.close(pfbblob) - elseif trace_loading then - report_afm("invalid pfb file %a",pfbname) - end -end -local function readafm(filename) - local ok,afmblob,size=resolvers.loadbinfile(filename) - if ok and afmblob then - local data={ - resources={ - filename=resolvers.unresolve(filename), - version=afm.version, - creator="context mkiv", - }, - properties={ - hasitalics=false, - }, - goodies={}, - metadata={ - filename=file.removesuffix(file.basename(filename)) - }, - characters={ - }, - descriptions={ - }, - } - afmblob=gsub(afmblob,"StartCharMetrics(.-)EndCharMetrics",function(charmetrics) - if trace_loading then - report_afm("loading char metrics") - end - get_charmetrics(data,charmetrics,vector) - return "" - end) - afmblob=gsub(afmblob,"StartKernPairs(.-)EndKernPairs",function(kernpairs) - if trace_loading then - report_afm("loading kern pairs") - end - get_kernpairs(data,kernpairs) - return "" - end) - afmblob=gsub(afmblob,"StartFontMetrics%s+([%d%.]+)(.-)EndFontMetrics",function(version,fontmetrics) - if trace_loading then - report_afm("loading variables") - end - data.afmversion=version - get_variables(data,fontmetrics) - data.fontdimens=scan_comment(fontmetrics) - return "" - end) - return data - else - if trace_loading then - report_afm("no valid afm file %a",filename) - end - return nil - end -end -local addkerns,addligatures,addtexligatures,unify,normalize -function afm.load(filename) - filename=resolvers.findfile(filename,'afm') or "" - if filename~="" and not fonts.names.ignoredfile(filename) then - local name=file.removesuffix(file.basename(filename)) - local data=containers.read(afm.cache,name) - local attr=lfs.attributes(filename) - local size,time=attr.size or 0,attr.modification or 0 - local pfbfile=file.replacesuffix(name,"pfb") - local pfbname=resolvers.findfile(pfbfile,"pfb") or "" - if pfbname=="" then - pfbname=resolvers.findfile(file.basename(pfbfile),"pfb") or "" - end - local pfbsize,pfbtime=0,0 - if pfbname~="" then - local attr=lfs.attributes(pfbname) - pfbsize=attr.size or 0 - pfbtime=attr.modification or 0 - end - if not data or data.size~=size or data.time~=time or data.pfbsize~=pfbsize or data.pfbtime~=pfbtime then - report_afm("reading %a",filename) - data=readafm(filename) - if data then - if pfbname~="" then - get_indexes(data,pfbname) - elseif trace_loading then - report_afm("no pfb file for %a",filename) - end - report_afm("unifying %a",filename) - unify(data,filename) - if afm.addligatures then - report_afm("add ligatures") - addligatures(data) - end - if afm.addtexligatures then - report_afm("add tex ligatures") - addtexligatures(data) - end - if afm.addkerns then - report_afm("add extra kerns") - addkerns(data) - end - normalize(data) - report_afm("add tounicode data") - fonts.mappings.addtounicode(data,filename) - data.size=size - data.time=time - data.pfbsize=pfbsize - data.pfbtime=pfbtime - report_afm("saving %a in cache",name) - data=containers.write(afm.cache,name,data) - data=containers.read(afm.cache,name) - end - if applyruntimefixes and data then - applyruntimefixes(filename,data) - end - end - return data - else - return nil - end -end -local uparser=fonts.mappings.makenameparser() -unify=function(data,filename) - local unicodevector=fonts.encodings.agl.unicodes - local unicodes,names={},{} - local private=constructors.privateoffset - local descriptions=data.descriptions - for name,blob in next,data.characters do - local code=unicodevector[name] - if not code then - code=lpegmatch(uparser,name) - if not code then - code=private - private=private+1 - report_afm("assigning private slot %U for unknown glyph name %a",code,name) - end - end - local index=blob.index - unicodes[name]=code - names[name]=index - blob.name=name - descriptions[code]={ - boundingbox=blob.boundingbox, - width=blob.width, - kerns=blob.kerns, - index=index, - name=name, - } - end - for unicode,description in next,descriptions do - local kerns=description.kerns - if kerns then - local krn={} - for name,kern in next,kerns do - local unicode=unicodes[name] - if unicode then - krn[unicode]=kern - else - print(unicode,name) - end - end - description.kerns=krn - end - end - data.characters=nil - local resources=data.resources - local filename=resources.filename or file.removesuffix(file.basename(filename)) - resources.filename=resolvers.unresolve(filename) - resources.unicodes=unicodes - resources.marks={} - resources.names=names - resources.private=private -end -normalize=function(data) -end -local addthem=function(rawdata,ligatures) - if ligatures then - local descriptions=rawdata.descriptions - local resources=rawdata.resources - local unicodes=resources.unicodes - local names=resources.names - for ligname,ligdata in next,ligatures do - local one=descriptions[unicodes[ligname]] - if one then - for _,pair in next,ligdata do - local two,three=unicodes[pair[1]],unicodes[pair[2]] - if two and three then - local ol=one.ligatures - if ol then - if not ol[two] then - ol[two]=three - end - else - one.ligatures={ [two]=three } - end - end - end - end - end - end -end -addligatures=function(rawdata) addthem(rawdata,afm.helpdata.ligatures ) end -addtexligatures=function(rawdata) addthem(rawdata,afm.helpdata.texligatures) end -addkerns=function(rawdata) - local descriptions=rawdata.descriptions - local resources=rawdata.resources - local unicodes=resources.unicodes - local function do_it_left(what) - if what then - for unicode,description in next,descriptions do - local kerns=description.kerns - if kerns then - local extrakerns - for complex,simple in next,what do - complex=unicodes[complex] - simple=unicodes[simple] - if complex and simple then - local ks=kerns[simple] - if ks and not kerns[complex] then - if extrakerns then - extrakerns[complex]=ks - else - extrakerns={ [complex]=ks } - end - end - end - end - if extrakerns then - description.extrakerns=extrakerns - end - end - end - end - end - local function do_it_copy(what) - if what then - for complex,simple in next,what do - complex=unicodes[complex] - simple=unicodes[simple] - if complex and simple then - local complexdescription=descriptions[complex] - if complexdescription then - local simpledescription=descriptions[complex] - if simpledescription then - local extrakerns - local kerns=simpledescription.kerns - if kerns then - for unicode,kern in next,kerns do - if extrakerns then - extrakerns[unicode]=kern - else - extrakerns={ [unicode]=kern } - end - end - end - local extrakerns=simpledescription.extrakerns - if extrakerns then - for unicode,kern in next,extrakerns do - if extrakerns then - extrakerns[unicode]=kern - else - extrakerns={ [unicode]=kern } - end - end - end - if extrakerns then - complexdescription.extrakerns=extrakerns - end - end - end - end - end - end - end - do_it_left(afm.helpdata.leftkerned) - do_it_left(afm.helpdata.bothkerned) - do_it_copy(afm.helpdata.bothkerned) - do_it_copy(afm.helpdata.rightkerned) -end -local function adddimensions(data) - if data then - for unicode,description in next,data.descriptions do - local bb=description.boundingbox - if bb then - local ht,dp=bb[4],-bb[2] - if ht==0 or ht<0 then - else - description.height=ht - end - if dp==0 or dp<0 then - else - description.depth=dp - end - end - end - end -end -local function copytotfm(data) - if data and data.descriptions then - local metadata=data.metadata - local resources=data.resources - local properties=derivetable(data.properties) - local descriptions=derivetable(data.descriptions) - local goodies=derivetable(data.goodies) - local characters={} - local parameters={} - local unicodes=resources.unicodes - for unicode,description in next,data.descriptions do - characters[unicode]={} - end - local filename=constructors.checkedfilename(resources) - local fontname=metadata.fontname or metadata.fullname - local fullname=metadata.fullname or metadata.fontname - local endash=unicodes['space'] - local emdash=unicodes['emdash'] - local spacer="space" - local spaceunits=500 - local monospaced=metadata.isfixedpitch - local charwidth=metadata.charwidth - local italicangle=metadata.italicangle - local charxheight=metadata.xheight and metadata.xheight>0 and metadata.xheight - properties.monospaced=monospaced - parameters.italicangle=italicangle - parameters.charwidth=charwidth - parameters.charxheight=charxheight - if properties.monospaced then - if descriptions[endash] then - spaceunits,spacer=descriptions[endash].width,"space" - end - if not spaceunits and descriptions[emdash] then - spaceunits,spacer=descriptions[emdash].width,"emdash" - end - if not spaceunits and charwidth then - spaceunits,spacer=charwidth,"charwidth" - end - else - if descriptions[endash] then - spaceunits,spacer=descriptions[endash].width,"space" - end - if not spaceunits and charwidth then - spaceunits,spacer=charwidth,"charwidth" - end - end - spaceunits=tonumber(spaceunits) - if spaceunits<200 then - end - parameters.slant=0 - parameters.space=spaceunits - parameters.space_stretch=500 - parameters.space_shrink=333 - parameters.x_height=400 - parameters.quad=1000 - if italicangle and italicangle~=0 then - parameters.italicangle=italicangle - parameters.italicfactor=math.cos(math.rad(90+italicangle)) - parameters.slant=- math.tan(italicangle*math.pi/180) - end - if monospaced then - parameters.space_stretch=0 - parameters.space_shrink=0 - elseif afm.syncspace then - parameters.space_stretch=spaceunits/2 - parameters.space_shrink=spaceunits/3 - end - parameters.extra_space=parameters.space_shrink - if charxheight then - parameters.x_height=charxheight - else - local x=unicodes['x'] - if x then - local x=descriptions[x] - if x then - parameters.x_height=x.height - end - end - end - local fd=data.fontdimens - if fd and fd[8] and fd[9] and fd[10] then - for k,v in next,fd do - parameters[k]=v - end - end - parameters.designsize=(metadata.designsize or 10)*65536 - parameters.ascender=abs(metadata.ascender or 0) - parameters.descender=abs(metadata.descender or 0) - parameters.units=1000 - properties.spacer=spacer - properties.encodingbytes=2 - properties.format=fonts.formats[filename] or "type1" - properties.filename=filename - properties.fontname=fontname - properties.fullname=fullname - properties.psname=fullname - properties.name=filename or fullname or fontname - if next(characters) then - return { - characters=characters, - descriptions=descriptions, - parameters=parameters, - resources=resources, - properties=properties, - goodies=goodies, - } - end - end - return nil -end -function afm.setfeatures(tfmdata,features) - local okay=constructors.initializefeatures("afm",tfmdata,features,trace_features,report_afm) - if okay then - return constructors.collectprocessors("afm",tfmdata,features,trace_features,report_afm) - else - return {} - end -end -local function checkfeatures(specification) -end -local function afmtotfm(specification) - local afmname=specification.filename or specification.name - if specification.forced=="afm" or specification.format=="afm" then - if trace_loading then - report_afm("forcing afm format for %a",afmname) - end - else - local tfmname=findbinfile(afmname,"ofm") or "" - if tfmname~="" then - if trace_loading then - report_afm("fallback from afm to tfm for %a",afmname) - end - return - end - end - if afmname~="" then - local features=constructors.checkedfeatures("afm",specification.features.normal) - specification.features.normal=features - constructors.hashinstance(specification,true) - specification=definers.resolve(specification) - local cache_id=specification.hash - local tfmdata=containers.read(constructors.cache,cache_id) - if not tfmdata then - local rawdata=afm.load(afmname) - if rawdata and next(rawdata) then - adddimensions(rawdata) - tfmdata=copytotfm(rawdata) - if tfmdata and next(tfmdata) then - local shared=tfmdata.shared - if not shared then - shared={} - tfmdata.shared=shared - end - shared.rawdata=rawdata - shared.features=features - shared.processes=afm.setfeatures(tfmdata,features) - end - elseif trace_loading then - report_afm("no (valid) afm file found with name %a",afmname) - end - tfmdata=containers.write(constructors.cache,cache_id,tfmdata) - end - return tfmdata - end -end -local function read_from_afm(specification) - local tfmdata=afmtotfm(specification) - if tfmdata then - tfmdata.properties.name=specification.name - tfmdata=constructors.scale(tfmdata,specification) - local allfeatures=tfmdata.shared.features or specification.features.normal - constructors.applymanipulators("afm",tfmdata,allfeatures,trace_features,report_afm) - fonts.loggers.register(tfmdata,'afm',specification) - end - return tfmdata -end -local function prepareligatures(tfmdata,ligatures,value) - if value then - local descriptions=tfmdata.descriptions - for unicode,character in next,tfmdata.characters do - local description=descriptions[unicode] - local dligatures=description.ligatures - if dligatures then - local cligatures=character.ligatures - if not cligatures then - cligatures={} - character.ligatures=cligatures - end - for unicode,ligature in next,dligatures do - cligatures[unicode]={ - char=ligature, - type=0 - } - end - end - end - end -end -local function preparekerns(tfmdata,kerns,value) - if value then - local rawdata=tfmdata.shared.rawdata - local resources=rawdata.resources - local unicodes=resources.unicodes - local descriptions=tfmdata.descriptions - for u,chr in next,tfmdata.characters do - local d=descriptions[u] - local newkerns=d[kerns] - if newkerns then - local kerns=chr.kerns - if not kerns then - kerns={} - chr.kerns=kerns - end - for k,v in next,newkerns do - local uk=unicodes[k] - if uk then - kerns[uk]=v - end - end - end - end - end -end -local list={ - [0x0027]=0x2019, -} -local function texreplacements(tfmdata,value) - local descriptions=tfmdata.descriptions - local characters=tfmdata.characters - for k,v in next,list do - characters [k]=characters [v] - descriptions[k]=descriptions[v] - end -end -local function ligatures (tfmdata,value) prepareligatures(tfmdata,'ligatures',value) end -local function texligatures(tfmdata,value) prepareligatures(tfmdata,'texligatures',value) end -local function kerns (tfmdata,value) preparekerns (tfmdata,'kerns',value) end -local function extrakerns (tfmdata,value) preparekerns (tfmdata,'extrakerns',value) end -registerafmfeature { - name="liga", - description="traditional ligatures", - initializers={ - base=ligatures, - node=ligatures, - } -} -registerafmfeature { - name="kern", - description="intercharacter kerning", - initializers={ - base=kerns, - node=kerns, - } -} -registerafmfeature { - name="extrakerns", - description="additional intercharacter kerning", - initializers={ - base=extrakerns, - node=extrakerns, - } -} -registerafmfeature { - name='tlig', - description='tex ligatures', - initializers={ - base=texligatures, - node=texligatures, - } -} -registerafmfeature { - name='trep', - description='tex replacements', - initializers={ - base=texreplacements, - node=texreplacements, - } -} -local check_tfm=readers.check_tfm -fonts.formats.afm="type1" -fonts.formats.pfb="type1" -local function check_afm(specification,fullname) - local foundname=findbinfile(fullname,'afm') or "" - if foundname=="" then - foundname=fonts.names.getfilename(fullname,"afm") or "" - end - if foundname=="" and afm.autoprefixed then - local encoding,shortname=match(fullname,"^(.-)%-(.*)$") - if encoding and shortname and fonts.encodings.known[encoding] then - shortname=findbinfile(shortname,'afm') or "" - if shortname~="" then - foundname=shortname - if trace_defining then - report_afm("stripping encoding prefix from filename %a",afmname) - end - end - end - end - if foundname~="" then - specification.filename=foundname - specification.format="afm" - return read_from_afm(specification) - end -end -function readers.afm(specification,method) - local fullname,tfmdata=specification.filename or "",nil - if fullname=="" then - local forced=specification.forced or "" - if forced~="" then - tfmdata=check_afm(specification,specification.name.."."..forced) - end - if not tfmdata then - method=method or definers.method or "afm or tfm" - if method=="tfm" then - tfmdata=check_tfm(specification,specification.name) - elseif method=="afm" then - tfmdata=check_afm(specification,specification.name) - elseif method=="tfm or afm" then - tfmdata=check_tfm(specification,specification.name) or check_afm(specification,specification.name) - else - tfmdata=check_afm(specification,specification.name) or check_tfm(specification,specification.name) - end - end - else - tfmdata=check_afm(specification,fullname) - end - return tfmdata -end -function readers.pfb(specification,method) - local original=specification.specification - if trace_defining then - report_afm("using afm reader for %a",original) - end - specification.specification=gsub(original,"%.pfb",".afm") - specification.forced="afm" - return readers.afm(specification,method) -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-afk']={ - version=1.001, - comment="companion to font-afm.lua", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files", - dataonly=true, -} -local allocate=utilities.storage.allocate -fonts.handlers.afm.helpdata={ - ligatures=allocate { - ['f']={ - { 'f','ff' }, - { 'i','fi' }, - { 'l','fl' }, - }, - ['ff']={ - { 'i','ffi' } - }, - ['fi']={ - { 'i','fii' } - }, - ['fl']={ - { 'i','fli' } - }, - ['s']={ - { 't','st' } - }, - ['i']={ - { 'j','ij' } - }, - }, - texligatures=allocate { - ['quoteleft']={ - { 'quoteleft','quotedblleft' } - }, - ['quoteright']={ - { 'quoteright','quotedblright' } - }, - ['hyphen']={ - { 'hyphen','endash' } - }, - ['endash']={ - { 'hyphen','emdash' } - } - }, - leftkerned=allocate { - AEligature="A",aeligature="a", - OEligature="O",oeligature="o", - IJligature="I",ijligature="i", - AE="A",ae="a", - OE="O",oe="o", - IJ="I",ij="i", - Ssharp="S",ssharp="s", - }, - rightkerned=allocate { - AEligature="E",aeligature="e", - OEligature="E",oeligature="e", - IJligature="J",ijligature="j", - AE="E",ae="e", - OE="E",oe="e", - IJ="J",ij="j", - Ssharp="S",ssharp="s", - }, - bothkerned=allocate { - Acircumflex="A",acircumflex="a", - Ccircumflex="C",ccircumflex="c", - Ecircumflex="E",ecircumflex="e", - Gcircumflex="G",gcircumflex="g", - Hcircumflex="H",hcircumflex="h", - Icircumflex="I",icircumflex="i", - Jcircumflex="J",jcircumflex="j", - Ocircumflex="O",ocircumflex="o", - Scircumflex="S",scircumflex="s", - Ucircumflex="U",ucircumflex="u", - Wcircumflex="W",wcircumflex="w", - Ycircumflex="Y",ycircumflex="y", - Agrave="A",agrave="a", - Egrave="E",egrave="e", - Igrave="I",igrave="i", - Ograve="O",ograve="o", - Ugrave="U",ugrave="u", - Ygrave="Y",ygrave="y", - Atilde="A",atilde="a", - Itilde="I",itilde="i", - Otilde="O",otilde="o", - Utilde="U",utilde="u", - Ntilde="N",ntilde="n", - Adiaeresis="A",adiaeresis="a",Adieresis="A",adieresis="a", - Ediaeresis="E",ediaeresis="e",Edieresis="E",edieresis="e", - Idiaeresis="I",idiaeresis="i",Idieresis="I",idieresis="i", - Odiaeresis="O",odiaeresis="o",Odieresis="O",odieresis="o", - Udiaeresis="U",udiaeresis="u",Udieresis="U",udieresis="u", - Ydiaeresis="Y",ydiaeresis="y",Ydieresis="Y",ydieresis="y", - Aacute="A",aacute="a", - Cacute="C",cacute="c", - Eacute="E",eacute="e", - Iacute="I",iacute="i", - Lacute="L",lacute="l", - Nacute="N",nacute="n", - Oacute="O",oacute="o", - Racute="R",racute="r", - Sacute="S",sacute="s", - Uacute="U",uacute="u", - Yacute="Y",yacute="y", - Zacute="Z",zacute="z", - Dstroke="D",dstroke="d", - Hstroke="H",hstroke="h", - Tstroke="T",tstroke="t", - Cdotaccent="C",cdotaccent="c", - Edotaccent="E",edotaccent="e", - Gdotaccent="G",gdotaccent="g", - Idotaccent="I",idotaccent="i", - Zdotaccent="Z",zdotaccent="z", - Amacron="A",amacron="a", - Emacron="E",emacron="e", - Imacron="I",imacron="i", - Omacron="O",omacron="o", - Umacron="U",umacron="u", - Ccedilla="C",ccedilla="c", - Kcedilla="K",kcedilla="k", - Lcedilla="L",lcedilla="l", - Ncedilla="N",ncedilla="n", - Rcedilla="R",rcedilla="r", - Scedilla="S",scedilla="s", - Tcedilla="T",tcedilla="t", - Ohungarumlaut="O",ohungarumlaut="o", - Uhungarumlaut="U",uhungarumlaut="u", - Aogonek="A",aogonek="a", - Eogonek="E",eogonek="e", - Iogonek="I",iogonek="i", - Uogonek="U",uogonek="u", - Aring="A",aring="a", - Uring="U",uring="u", - Abreve="A",abreve="a", - Ebreve="E",ebreve="e", - Gbreve="G",gbreve="g", - Ibreve="I",ibreve="i", - Obreve="O",obreve="o", - Ubreve="U",ubreve="u", - Ccaron="C",ccaron="c", - Dcaron="D",dcaron="d", - Ecaron="E",ecaron="e", - Lcaron="L",lcaron="l", - Ncaron="N",ncaron="n", - Rcaron="R",rcaron="r", - Scaron="S",scaron="s", - Tcaron="T",tcaron="t", - Zcaron="Z",zcaron="z", - dotlessI="I",dotlessi="i", - dotlessJ="J",dotlessj="j", - AEligature="AE",aeligature="ae",AE="AE",ae="ae", - OEligature="OE",oeligature="oe",OE="OE",oe="oe", - IJligature="IJ",ijligature="ij",IJ="IJ",ij="ij", - Lstroke="L",lstroke="l",Lslash="L",lslash="l", - Ostroke="O",ostroke="o",Oslash="O",oslash="o", - Ssharp="SS",ssharp="ss", - Aumlaut="A",aumlaut="a", - Eumlaut="E",eumlaut="e", - Iumlaut="I",iumlaut="i", - Oumlaut="O",oumlaut="o", - Uumlaut="U",uumlaut="u", - } -} - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luatex-fonts-tfm']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end -local fonts=fonts -local tfm={} -fonts.handlers.tfm=tfm -fonts.formats.tfm="type1" -function fonts.readers.tfm(specification) - local fullname=specification.filename or "" - if fullname=="" then - local forced=specification.forced or "" - if forced~="" then - fullname=specification.name.."."..forced - else - fullname=specification.name - end - end - local foundname=resolvers.findbinfile(fullname,'tfm') or "" - if foundname=="" then - foundname=resolvers.findbinfile(fullname,'ofm') or "" - end - if foundname~="" then - specification.filename=foundname - specification.format="ofm" - return font.read_tfm(specification.filename,specification.size) - end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-oti']={ - 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 lower=string.lower -local fonts=fonts -local constructors=fonts.constructors -local otf=constructors.newhandler("otf") -local otffeatures=constructors.newfeatures("otf") -local otftables=otf.tables -local registerotffeature=otffeatures.register -local allocate=utilities.storage.allocate -registerotffeature { - name="features", - description="initialization of feature handler", - default=true, -} -local function setmode(tfmdata,value) - if value then - tfmdata.properties.mode=lower(value) - end -end -local function setlanguage(tfmdata,value) - if value then - local cleanvalue=lower(value) - local languages=otftables and otftables.languages - local properties=tfmdata.properties - if not languages then - properties.language=cleanvalue - elseif languages[value] then - properties.language=cleanvalue - else - properties.language="dflt" - end - end -end -local function setscript(tfmdata,value) - if value then - local cleanvalue=lower(value) - local scripts=otftables and otftables.scripts - local properties=tfmdata.properties - if not scripts then - properties.script=cleanvalue - elseif scripts[value] then - properties.script=cleanvalue - else - properties.script="dflt" - end - end -end -registerotffeature { - name="mode", - description="mode", - initializers={ - base=setmode, - node=setmode, - } -} -registerotffeature { - name="language", - description="language", - initializers={ - base=setlanguage, - node=setlanguage, - } -} -registerotffeature { - name="script", - description="script", - initializers={ - base=setscript, - node=setscript, - } -} - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-otf']={ - 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 utfbyte=utf.byte -local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip -local type,next,tonumber,tostring=type,next,tonumber,tostring -local abs=math.abs -local insert=table.insert -local lpegmatch=lpeg.match -local reversed,concat,remove,sortedkeys=table.reversed,table.concat,table.remove,table.sortedkeys -local ioflush=io.flush -local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive -local formatters=string.formatters -local allocate=utilities.storage.allocate -local registertracker=trackers.register -local registerdirective=directives.register -local starttiming=statistics.starttiming -local stoptiming=statistics.stoptiming -local elapsedtime=statistics.elapsedtime -local findbinfile=resolvers.findbinfile -local trace_private=false registertracker("otf.private",function(v) trace_private=v end) -local trace_loading=false registertracker("otf.loading",function(v) trace_loading=v end) -local trace_features=false registertracker("otf.features",function(v) trace_features=v end) -local trace_dynamics=false registertracker("otf.dynamics",function(v) trace_dynamics=v end) -local trace_sequences=false registertracker("otf.sequences",function(v) trace_sequences=v end) -local trace_markwidth=false registertracker("otf.markwidth",function(v) trace_markwidth=v end) -local trace_defining=false registertracker("fonts.defining",function(v) trace_defining=v end) -local report_otf=logs.reporter("fonts","otf loading") -local fonts=fonts -local otf=fonts.handlers.otf -otf.glists={ "gsub","gpos" } -otf.version=2.751 -otf.cache=containers.define("fonts","otf",otf.version,true) -local fontdata=fonts.hashes.identifiers -local chardata=characters and characters.data -local otffeatures=fonts.constructors.newfeatures("otf") -local registerotffeature=otffeatures.register -local enhancers=allocate() -otf.enhancers=enhancers -local patches={} -enhancers.patches=patches -local definers=fonts.definers -local readers=fonts.readers -local constructors=fonts.constructors -local forceload=false -local cleanup=0 -local usemetatables=false -local packdata=true -local syncspace=true -local forcenotdef=false -local includesubfonts=false -local overloadkerns=false -local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes -local wildcard="*" -local default="dflt" -local fontloaderfields=fontloader.fields -local mainfields=nil -local glyphfields=nil -local formats=fonts.formats -formats.otf="opentype" -formats.ttf="truetype" -formats.ttc="truetype" -formats.dfont="truetype" -registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or (v and 1) or 0 end) -registerdirective("fonts.otf.loader.force",function(v) forceload=v end) -registerdirective("fonts.otf.loader.usemetatables",function(v) usemetatables=v end) -registerdirective("fonts.otf.loader.pack",function(v) packdata=v end) -registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end) -registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end) -registerdirective("fonts.otf.loader.overloadkerns",function(v) overloadkerns=v end) -function otf.fileformat(filename) - local leader=lower(io.loadchunk(filename,4)) - local suffix=lower(file.suffix(filename)) - if leader=="otto" then - return formats.otf,suffix=="otf" - elseif leader=="ttcf" then - return formats.ttc,suffix=="ttc" - elseif suffix=="ttc" then - return formats.ttc,true - elseif suffix=="dfont" then - return formats.dfont,true - else - return formats.ttf,suffix=="ttf" - end -end -local function otf_format(filename) - local format,okay=otf.fileformat(filename) - if not okay then - report_otf("font %a is actually an %a file",filename,format) - end - return format -end -local function load_featurefile(raw,featurefile) - if featurefile and featurefile~="" then - if trace_loading then - report_otf("using featurefile %a",featurefile) - end - fontloader.apply_featurefile(raw,featurefile) - end -end -local function showfeatureorder(rawdata,filename) - local sequences=rawdata.resources.sequences - if sequences and #sequences>0 then - if trace_loading then - report_otf("font %a has %s sequences",filename,#sequences) - report_otf(" ") - end - for nos=1,#sequences do - local sequence=sequences[nos] - local typ=sequence.type or "no-type" - local name=sequence.name or "no-name" - local subtables=sequence.subtables or { "no-subtables" } - local features=sequence.features - if trace_loading then - report_otf("%3i %-15s %-20s [% t]",nos,name,typ,subtables) - end - if features then - for feature,scripts in next,features do - local tt={} - if type(scripts)=="table" then - for script,languages in next,scripts do - local ttt={} - for language,_ in next,languages do - ttt[#ttt+1]=language - end - tt[#tt+1]=formatters["[%s: % t]"](script,ttt) - end - if trace_loading then - report_otf(" %s: % t",feature,tt) - end - else - if trace_loading then - report_otf(" %s: %S",feature,scripts) - end - end - end - end - end - if trace_loading then - report_otf("\n") - end - elseif trace_loading then - report_otf("font %a has no sequences",filename) - end -end -local valid_fields=table.tohash { - "ascent", - "cidinfo", - "copyright", - "descent", - "design_range_bottom", - "design_range_top", - "design_size", - "encodingchanged", - "extrema_bound", - "familyname", - "fontname", - "fontname", - "fontstyle_id", - "fontstyle_name", - "fullname", - "hasvmetrics", - "horiz_base", - "issans", - "isserif", - "italicangle", - "macstyle", - "onlybitmaps", - "origname", - "os2_version", - "pfminfo", - "serifcheck", - "sfd_version", - "strokedfont", - "strokewidth", - "table_version", - "ttf_tables", - "uni_interp", - "uniqueid", - "units_per_em", - "upos", - "use_typo_metrics", - "uwidth", - "validation_state", - "version", - "vert_base", - "weight", - "weight_width_slope_only", -} -local ordered_enhancers={ - "prepare tables", - "prepare glyphs", - "prepare lookups", - "analyze glyphs", - "analyze math", - "prepare tounicode", - "reorganize lookups", - "reorganize mark classes", - "reorganize anchor classes", - "reorganize glyph kerns", - "reorganize glyph lookups", - "reorganize glyph anchors", - "merge kern classes", - "reorganize features", - "reorganize subtables", - "check glyphs", - "check metadata", - "check extra features", - "check encoding", - "add duplicates", - "cleanup tables", -} -local actions=allocate() -local before=allocate() -local after=allocate() -patches.before=before -patches.after=after -local function enhance(name,data,filename,raw) - local enhancer=actions[name] - if enhancer then - if trace_loading then - report_otf("apply enhancement %a to file %a",name,filename) - ioflush() - end - enhancer(data,filename,raw) - else - end -end -function enhancers.apply(data,filename,raw) - local basename=file.basename(lower(filename)) - if trace_loading then - report_otf("%s enhancing file %a","start",filename) - end - ioflush() - for e=1,#ordered_enhancers do - local enhancer=ordered_enhancers[e] - local b=before[enhancer] - if b then - for pattern,action in next,b do - if find(basename,pattern) then - action(data,filename,raw) - end - end - end - enhance(enhancer,data,filename,raw) - local a=after[enhancer] - if a then - for pattern,action in next,a do - if find(basename,pattern) then - action(data,filename,raw) - end - end - end - ioflush() - end - if trace_loading then - report_otf("%s enhancing file %a","stop",filename) - end - ioflush() -end -function patches.register(what,where,pattern,action) - local pw=patches[what] - if pw then - local ww=pw[where] - if ww then - ww[pattern]=action - else - pw[where]={ [pattern]=action} - end - end -end -function patches.report(fmt,...) - if trace_loading then - report_otf("patching: %s",formatters[fmt](...)) - end -end -function enhancers.register(what,action) - actions[what]=action -end -function otf.load(filename,sub,featurefile) - local base=file.basename(file.removesuffix(filename)) - local name=file.removesuffix(base) - local attr=lfs.attributes(filename) - local size=attr and attr.size or 0 - local time=attr and attr.modification or 0 - if featurefile then - name=name.."@"..file.removesuffix(file.basename(featurefile)) - end - if sub=="" then - sub=false - end - local hash=name - if sub then - hash=hash.."-"..sub - end - hash=containers.cleanname(hash) - local featurefiles - if featurefile then - featurefiles={} - for s in gmatch(featurefile,"[^,]+") do - local name=resolvers.findfile(file.addsuffix(s,'fea'),'fea') or "" - if name=="" then - report_otf("loading error, no featurefile %a",s) - else - local attr=lfs.attributes(name) - featurefiles[#featurefiles+1]={ - name=name, - size=attr and attr.size or 0, - time=attr and attr.modification or 0, - } - end - end - if #featurefiles==0 then - featurefiles=nil - end - end - local data=containers.read(otf.cache,hash) - local reload=not data or data.size~=size or data.time~=time - if forceload then - report_otf("forced reload of %a due to hard coded flag",filename) - reload=true - end - if not reload then - local featuredata=data.featuredata - if featurefiles then - if not featuredata or #featuredata~=#featurefiles then - reload=true - else - for i=1,#featurefiles do - local fi,fd=featurefiles[i],featuredata[i] - if fi.name~=fd.name or fi.size~=fd.size or fi.time~=fd.time then - reload=true - break - end - end - end - elseif featuredata then - reload=true - end - if reload then - report_otf("loading: forced reload due to changed featurefile specification %a",featurefile) - end - end - if reload then - report_otf("loading %a, hash %a",filename,hash) - local fontdata,messages - if sub then - fontdata,messages=fontloader.open(filename,sub) - else - fontdata,messages=fontloader.open(filename) - end - if fontdata then - mainfields=mainfields or (fontloaderfields and fontloaderfields(fontdata)) - end - if trace_loading and messages and #messages>0 then - if type(messages)=="string" then - report_otf("warning: %s",messages) - else - for m=1,#messages do - report_otf("warning: %S",messages[m]) - end - end - else - report_otf("loading done") - end - if fontdata then - if featurefiles then - for i=1,#featurefiles do - load_featurefile(fontdata,featurefiles[i].name) - end - end - local unicodes={ - } - local splitter=lpeg.splitter(" ",unicodes) - data={ - size=size, - time=time, - format=otf_format(filename), - featuredata=featurefiles, - resources={ - filename=resolvers.unresolve(filename), - version=otf.version, - creator="context mkiv", - unicodes=unicodes, - indices={ - }, - duplicates={ - }, - variants={ - }, - lookuptypes={}, - }, - metadata={ - }, - properties={ - }, - descriptions={}, - goodies={}, - helpers={ - tounicodelist=splitter, - tounicodetable=lpeg.Ct(splitter), - }, - } - starttiming(data) - report_otf("file size: %s",size) - enhancers.apply(data,filename,fontdata) - local packtime={} - if packdata then - if cleanup>0 then - collectgarbage("collect") - end - starttiming(packtime) - enhance("pack",data,filename,nil) - stoptiming(packtime) - end - report_otf("saving %a in cache",filename) - data=containers.write(otf.cache,hash,data) - if cleanup>1 then - collectgarbage("collect") - end - stoptiming(data) - if elapsedtime then - report_otf("preprocessing and caching time %s, packtime %s", - elapsedtime(data),packdata and elapsedtime(packtime) or 0) - end - fontloader.close(fontdata) - if cleanup>3 then - collectgarbage("collect") - end - data=containers.read(otf.cache,hash) - if cleanup>2 then - collectgarbage("collect") - end - else - data=nil - report_otf("loading failed due to read error") - end - end - if data then - if trace_defining then - report_otf("loading from cache using hash %a",hash) - end - enhance("unpack",data,filename,nil,false) - if applyruntimefixes then - applyruntimefixes(filename,data) - end - enhance("add dimensions",data,filename,nil,false) - if trace_sequences then - showfeatureorder(data,filename) - end - end - return data -end -local mt={ - __index=function(t,k) - if k=="height" then - local ht=t.boundingbox[4] - return ht<0 and 0 or ht - elseif k=="depth" then - local dp=-t.boundingbox[2] - return dp<0 and 0 or dp - elseif k=="width" then - return 0 - elseif k=="name" then - return forcenotdef and ".notdef" - end - end -} -actions["prepare tables"]=function(data,filename,raw) - data.properties.hasitalics=false -end -actions["add dimensions"]=function(data,filename) - if data then - local descriptions=data.descriptions - local resources=data.resources - local defaultwidth=resources.defaultwidth or 0 - local defaultheight=resources.defaultheight or 0 - local defaultdepth=resources.defaultdepth or 0 - local basename=trace_markwidth and file.basename(filename) - if usemetatables then - for _,d in next,descriptions do - local wd=d.width - if not wd then - d.width=defaultwidth - elseif trace_markwidth and wd~=0 and d.class=="mark" then - report_otf("mark %a with width %b found in %a",d.name or "",wd,basename) - end - setmetatable(d,mt) - end - else - for _,d in next,descriptions do - local bb,wd=d.boundingbox,d.width - if not wd then - d.width=defaultwidth - elseif trace_markwidth and wd~=0 and d.class=="mark" then - report_otf("mark %a with width %b found in %a",d.name or "",wd,basename) - end - if bb then - local ht,dp=bb[4],-bb[2] - if ht==0 or ht<0 then - else - d.height=ht - end - if dp==0 or dp<0 then - else - d.depth=dp - end - end - end - end - end -end -local function somecopy(old) - if old then - local new={} - if type(old)=="table" then - for k,v in next,old do - if k=="glyphs" then - elseif type(v)=="table" then - new[k]=somecopy(v) - else - new[k]=v - end - end - else - for i=1,#mainfields do - local k=mainfields[i] - local v=old[k] - if k=="glyphs" then - elseif type(v)=="table" then - new[k]=somecopy(v) - else - new[k]=v - end - end - end - return new - else - return {} - end -end -actions["prepare glyphs"]=function(data,filename,raw) - local rawglyphs=raw.glyphs - local rawsubfonts=raw.subfonts - local rawcidinfo=raw.cidinfo - local criterium=constructors.privateoffset - local private=criterium - local resources=data.resources - local metadata=data.metadata - local properties=data.properties - local descriptions=data.descriptions - local unicodes=resources.unicodes - local indices=resources.indices - local duplicates=resources.duplicates - local variants=resources.variants - if rawsubfonts then - metadata.subfonts=includesubfonts and {} - properties.cidinfo=rawcidinfo - if rawcidinfo.registry then - local cidmap=fonts.cid.getmap(rawcidinfo) - if cidmap then - rawcidinfo.usedname=cidmap.usedname - local nofnames,nofunicodes=0,0 - local cidunicodes,cidnames=cidmap.unicodes,cidmap.names - for cidindex=1,#rawsubfonts do - local subfont=rawsubfonts[cidindex] - local cidglyphs=subfont.glyphs - if includesubfonts then - metadata.subfonts[cidindex]=somecopy(subfont) - end - for index=0,subfont.glyphcnt-1 do - local glyph=cidglyphs[index] - if glyph then - local unicode=glyph.unicode - local name=glyph.name or cidnames[index] - if not unicode or unicode==-1 or unicode>=criterium then - unicode=cidunicodes[index] - end - if unicode and descriptions[unicode] then - report_otf("preventing glyph %a at index %H to overload unicode %U",name or "noname",index,unicode) - unicode=-1 - end - if not unicode or unicode==-1 or unicode>=criterium then - if not name then - name=format("u%06X",private) - end - unicode=private - unicodes[name]=private - if trace_private then - report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private) - end - private=private+1 - nofnames=nofnames+1 - else - if not name then - name=format("u%06X",unicode) - end - unicodes[name]=unicode - nofunicodes=nofunicodes+1 - end - indices[index]=unicode - local description={ - boundingbox=glyph.boundingbox, - name=glyph.name or name or "unknown", - cidindex=cidindex, - index=index, - glyph=glyph, - } - descriptions[unicode]=description - else - end - end - end - if trace_loading then - report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes,nofnames,nofunicodes+nofnames) - end - elseif trace_loading then - report_otf("unable to remap cid font, missing cid file for %a",filename) - end - elseif trace_loading then - report_otf("font %a has no glyphs",filename) - end - else - for index=0,raw.glyphcnt-1 do - local glyph=rawglyphs[index] - if glyph then - local unicode=glyph.unicode - local name=glyph.name - if not unicode or unicode==-1 or unicode>=criterium then - unicode=private - unicodes[name]=private - if trace_private then - report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private) - end - private=private+1 - else - unicodes[name]=unicode - end - indices[index]=unicode - if not name then - name=format("u%06X",unicode) - end - descriptions[unicode]={ - boundingbox=glyph.boundingbox, - name=name, - index=index, - glyph=glyph, - } - local altuni=glyph.altuni - if altuni then - for i=1,#altuni do - local a=altuni[i] - local u=a.unicode - local v=a.variant - if v then - local vv=variants[v] - if vv then - vv[u]=unicode - else - vv={ [u]=unicode } - variants[v]=vv - end - end - end - end - else - report_otf("potential problem: glyph %U is used but empty",index) - end - end - end - resources.private=private -end -actions["check encoding"]=function(data,filename,raw) - local descriptions=data.descriptions - local resources=data.resources - local properties=data.properties - local unicodes=resources.unicodes - local indices=resources.indices - local duplicates=resources.duplicates - local mapdata=raw.map or {} - local unicodetoindex=mapdata and mapdata.map or {} - local indextounicode=mapdata and mapdata.backmap or {} - local encname=lower(data.enc_name or mapdata.enc_name or "") - local criterium=0xFFFF - if find(encname,"unicode") then - if trace_loading then - report_otf("checking embedded unicode map %a",encname) - end - local hash={} - for index,unicode in next,indices do - hash[index]=descriptions[unicode] - end - local reported={} - for unicode,index in next,unicodetoindex do - if not descriptions[unicode] then - local d=hash[index] - if d then - if d.unicode~=unicode then - local c=d.copies - if c then - c[unicode]=true - else - d.copies={ [unicode]=true } - end - end - elseif not reported[i] then - report_otf("missing index %i",index) - reported[i]=true - end - end - end - for index,data in next,hash do - data.copies=sortedkeys(data.copies) - end - for index,unicode in next,indices do - local description=hash[index] - local copies=description.copies - if copies then - duplicates[unicode]=copies - description.copies=nil - else - report_otf("copies but no unicode parent %U",unicode) - end - end - elseif properties.cidinfo then - report_otf("warning: no unicode map, used cidmap %a",properties.cidinfo.usedname) - else - report_otf("warning: non unicode map %a, only using glyph unicode data",encname or "whatever") - end - if mapdata then - mapdata.map={} - mapdata.backmap={} - end -end -actions["add duplicates"]=function(data,filename,raw) - local descriptions=data.descriptions - local resources=data.resources - local properties=data.properties - local unicodes=resources.unicodes - local indices=resources.indices - local duplicates=resources.duplicates - for unicode,d in next,duplicates do - local nofduplicates=#d - if nofduplicates>4 then - if trace_loading then - report_otf("ignoring excessive duplicates of %U (n=%s)",unicode,nofduplicates) - end - else - for i=1,nofduplicates do - local u=d[i] - if not descriptions[u] then - local description=descriptions[unicode] - local n=0 - for _,description in next,descriptions do - if kerns then - local kerns=description.kerns - for _,k in next,kerns do - local ku=k[unicode] - if ku then - k[u]=ku - n=n+1 - end - end - end - end - if u>0 then - local duplicate=table.copy(description) - duplicate.comment=format("copy of U+%05X",unicode) - descriptions[u]=duplicate - if trace_loading then - report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n) - end - end - end - end - end - end -end -actions["analyze glyphs"]=function(data,filename,raw) - local descriptions=data.descriptions - local resources=data.resources - local metadata=data.metadata - local properties=data.properties - local hasitalics=false - local widths={} - local marks={} - for unicode,description in next,descriptions do - local glyph=description.glyph - local italic=glyph.italic_correction - if not italic then - elseif italic==0 then - else - description.italic=italic - hasitalics=true - end - local width=glyph.width - widths[width]=(widths[width] or 0)+1 - local class=glyph.class - if class then - if class=="mark" then - marks[unicode]=true - end - description.class=class - end - end - properties.hasitalics=hasitalics - resources.marks=marks - local wd,most=0,1 - for k,v in next,widths do - if v>most then - wd,most=k,v - end - end - if most>1000 then - if trace_loading then - report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most) - end - for unicode,description in next,descriptions do - if description.width==wd then - else - description.width=description.glyph.width - end - end - resources.defaultwidth=wd - else - for unicode,description in next,descriptions do - description.width=description.glyph.width - end - end -end -actions["reorganize mark classes"]=function(data,filename,raw) - local mark_classes=raw.mark_classes - if mark_classes then - local resources=data.resources - local unicodes=resources.unicodes - local markclasses={} - resources.markclasses=markclasses - for name,class in next,mark_classes do - local t={} - for s in gmatch(class,"[^ ]+") do - t[unicodes[s]]=true - end - markclasses[name]=t - end - end -end -actions["reorganize features"]=function(data,filename,raw) - local features={} - data.resources.features=features - for k,what in next,otf.glists do - local dw=raw[what] - if dw then - local f={} - features[what]=f - for i=1,#dw do - local d=dw[i] - local dfeatures=d.features - if dfeatures then - for i=1,#dfeatures do - local df=dfeatures[i] - local tag=strip(lower(df.tag)) - local ft=f[tag] - if not ft then - ft={} - f[tag]=ft - end - local dscripts=df.scripts - for i=1,#dscripts do - local d=dscripts[i] - local languages=d.langs - local script=strip(lower(d.script)) - local fts=ft[script] if not fts then fts={} ft[script]=fts end - for i=1,#languages do - fts[strip(lower(languages[i]))]=true - end - end - end - end - end - end - end -end -actions["reorganize anchor classes"]=function(data,filename,raw) - local resources=data.resources - local anchor_to_lookup={} - local lookup_to_anchor={} - resources.anchor_to_lookup=anchor_to_lookup - resources.lookup_to_anchor=lookup_to_anchor - local classes=raw.anchor_classes - if classes then - for c=1,#classes do - local class=classes[c] - local anchor=class.name - local lookups=class.lookup - if type(lookups)~="table" then - lookups={ lookups } - end - local a=anchor_to_lookup[anchor] - if not a then - a={} - anchor_to_lookup[anchor]=a - end - for l=1,#lookups do - local lookup=lookups[l] - local l=lookup_to_anchor[lookup] - if l then - l[anchor]=true - else - l={ [anchor]=true } - lookup_to_anchor[lookup]=l - end - a[lookup]=true - end - end - end -end -actions["prepare tounicode"]=function(data,filename,raw) - fonts.mappings.addtounicode(data,filename) -end -local g_directions={ - gsub_contextchain=1, - gpos_contextchain=1, - gsub_reversecontextchain=-1, - gpos_reversecontextchain=-1, -} -actions["reorganize subtables"]=function(data,filename,raw) - local resources=data.resources - local sequences={} - local lookups={} - local chainedfeatures={} - resources.sequences=sequences - resources.lookups=lookups - for _,what in next,otf.glists do - local dw=raw[what] - if dw then - for k=1,#dw do - local gk=dw[k] - local features=gk.features - local typ=gk.type - local chain=g_directions[typ] or 0 - local subtables=gk.subtables - if subtables then - local t={} - for s=1,#subtables do - t[s]=subtables[s].name - end - subtables=t - end - local flags,markclass=gk.flags,nil - if flags then - local t={ - (flags.ignorecombiningmarks and "mark") or false, - (flags.ignoreligatures and "ligature") or false, - (flags.ignorebaseglyphs and "base") or false, - flags.r2l or false, - } - markclass=flags.mark_class - if markclass then - markclass=resources.markclasses[markclass] - end - flags=t - end - local name=gk.name - if not name then - report_otf("skipping weird lookup number %s",k) - elseif features then - local f={} - for i=1,#features do - local df=features[i] - local tag=strip(lower(df.tag)) - local ft=f[tag] if not ft then ft={} f[tag]=ft end - local dscripts=df.scripts - for i=1,#dscripts do - local d=dscripts[i] - local languages=d.langs - local script=strip(lower(d.script)) - local fts=ft[script] if not fts then fts={} ft[script]=fts end - for i=1,#languages do - fts[strip(lower(languages[i]))]=true - end - end - end - sequences[#sequences+1]={ - type=typ, - chain=chain, - flags=flags, - name=name, - subtables=subtables, - markclass=markclass, - features=f, - } - else - lookups[name]={ - type=typ, - chain=chain, - flags=flags, - subtables=subtables, - markclass=markclass, - } - end - end - end - end -end -actions["prepare lookups"]=function(data,filename,raw) - local lookups=raw.lookups - if lookups then - data.lookups=lookups - end -end -local function t_uncover(splitter,cache,covers) - local result={} - for n=1,#covers do - local cover=covers[n] - local uncovered=cache[cover] - if not uncovered then - uncovered=lpegmatch(splitter,cover) - cache[cover]=uncovered - end - result[n]=uncovered - end - return result -end -local function s_uncover(splitter,cache,cover) - if cover=="" then - return nil - else - local uncovered=cache[cover] - if not uncovered then - uncovered=lpegmatch(splitter,cover) - cache[cover]=uncovered - end - return { uncovered } - end -end -local function t_hashed(t,cache) - if t then - local ht={} - for i=1,#t do - local ti=t[i] - local tih=cache[ti] - if not tih then - tih={} - for i=1,#ti do - tih[ti[i]]=true - end - cache[ti]=tih - end - ht[i]=tih - end - return ht - else - return nil - end -end -local function s_hashed(t,cache) - if t then - local ht={} - local tf=t[1] - for i=1,#tf do - ht[i]={ [tf[i]]=true } - end - return ht - else - return nil - end -end -local function r_uncover(splitter,cache,cover,replacements) - if cover=="" then - return nil - else - local uncovered=cover[1] - local replaced=cache[replacements] - if not replaced then - replaced=lpegmatch(splitter,replacements) - cache[replacements]=replaced - end - local nu,nr=#uncovered,#replaced - local r={} - if nu==nr then - for i=1,nu do - r[uncovered[i]]=replaced[i] - end - end - return r - end -end -actions["reorganize lookups"]=function(data,filename,raw) - if data.lookups then - local splitter=data.helpers.tounicodetable - local t_u_cache={} - local s_u_cache=t_u_cache - local t_h_cache={} - local s_h_cache=t_h_cache - local r_u_cache={} - for _,lookup in next,data.lookups do - local rules=lookup.rules - if rules then - local format=lookup.format - if format=="class" then - local before_class=lookup.before_class - if before_class then - before_class=t_uncover(splitter,t_u_cache,reversed(before_class)) - end - local current_class=lookup.current_class - if current_class then - current_class=t_uncover(splitter,t_u_cache,current_class) - end - local after_class=lookup.after_class - if after_class then - after_class=t_uncover(splitter,t_u_cache,after_class) - end - for i=1,#rules do - local rule=rules[i] - local class=rule.class - local before=class.before - if before then - for i=1,#before do - before[i]=before_class[before[i]] or {} - end - rule.before=t_hashed(before,t_h_cache) - end - local current=class.current - local lookups=rule.lookups - if current then - for i=1,#current do - current[i]=current_class[current[i]] or {} - if lookups and not lookups[i] then - lookups[i]="" - end - end - rule.current=t_hashed(current,t_h_cache) - end - local after=class.after - if after then - for i=1,#after do - after[i]=after_class[after[i]] or {} - end - rule.after=t_hashed(after,t_h_cache) - end - rule.class=nil - end - lookup.before_class=nil - lookup.current_class=nil - lookup.after_class=nil - lookup.format="coverage" - elseif format=="coverage" then - for i=1,#rules do - local rule=rules[i] - local coverage=rule.coverage - if coverage then - local before=coverage.before - if before then - before=t_uncover(splitter,t_u_cache,reversed(before)) - rule.before=t_hashed(before,t_h_cache) - end - local current=coverage.current - if current then - current=t_uncover(splitter,t_u_cache,current) - local lookups=rule.lookups - if lookups then - for i=1,#current do - if not lookups[i] then - lookups[i]="" - end - end - end - rule.current=t_hashed(current,t_h_cache) - end - local after=coverage.after - if after then - after=t_uncover(splitter,t_u_cache,after) - rule.after=t_hashed(after,t_h_cache) - end - rule.coverage=nil - end - end - elseif format=="reversecoverage" then - for i=1,#rules do - local rule=rules[i] - local reversecoverage=rule.reversecoverage - if reversecoverage then - local before=reversecoverage.before - if before then - before=t_uncover(splitter,t_u_cache,reversed(before)) - rule.before=t_hashed(before,t_h_cache) - end - local current=reversecoverage.current - if current then - current=t_uncover(splitter,t_u_cache,current) - rule.current=t_hashed(current,t_h_cache) - end - local after=reversecoverage.after - if after then - after=t_uncover(splitter,t_u_cache,after) - rule.after=t_hashed(after,t_h_cache) - end - local replacements=reversecoverage.replacements - if replacements then - rule.replacements=r_uncover(splitter,r_u_cache,current,replacements) - end - rule.reversecoverage=nil - end - end - elseif format=="glyphs" then - for i=1,#rules do - local rule=rules[i] - local glyphs=rule.glyphs - if glyphs then - local fore=glyphs.fore - if fore and fore~="" then - fore=s_uncover(splitter,s_u_cache,fore) - rule.before=s_hashed(fore,s_h_cache) - end - local back=glyphs.back - if back then - back=s_uncover(splitter,s_u_cache,back) - rule.after=s_hashed(back,s_h_cache) - end - local names=glyphs.names - if names then - names=s_uncover(splitter,s_u_cache,names) - rule.current=s_hashed(names,s_h_cache) - end - rule.glyphs=nil - end - end - end - end - end - end -end -local function check_variants(unicode,the_variants,splitter,unicodes) - local variants=the_variants.variants - if variants then - local glyphs=lpegmatch(splitter,variants) - local done={ [unicode]=true } - local n=0 - for i=1,#glyphs do - local g=glyphs[i] - if done[g] then - report_otf("skipping cyclic reference %U in math variant %U",g,unicode) - else - if n==0 then - n=1 - variants={ g } - else - n=n+1 - variants[n]=g - end - done[g]=true - end - end - if n==0 then - variants=nil - end - end - local parts=the_variants.parts - if parts then - local p=#parts - if p>0 then - for i=1,p do - local pi=parts[i] - pi.glyph=unicodes[pi.component] or 0 - pi.component=nil - end - else - parts=nil - end - end - local italic_correction=the_variants.italic_correction - if italic_correction and italic_correction==0 then - italic_correction=nil - end - return variants,parts,italic_correction -end -actions["analyze math"]=function(data,filename,raw) - if raw.math then - data.metadata.math=raw.math - local unicodes=data.resources.unicodes - local splitter=data.helpers.tounicodetable - for unicode,description in next,data.descriptions do - local glyph=description.glyph - local mathkerns=glyph.mathkern - local horiz_variants=glyph.horiz_variants - local vert_variants=glyph.vert_variants - local top_accent=glyph.top_accent - if mathkerns or horiz_variants or vert_variants or top_accent then - local math={} - if top_accent then - math.top_accent=top_accent - end - if mathkerns then - for k,v in next,mathkerns do - if not next(v) then - mathkerns[k]=nil - else - for k,v in next,v do - if v==0 then - k[v]=nil - end - end - end - end - math.kerns=mathkerns - end - if horiz_variants then - math.horiz_variants,math.horiz_parts,math.horiz_italic_correction=check_variants(unicode,horiz_variants,splitter,unicodes) - end - if vert_variants then - math.vert_variants,math.vert_parts,math.vert_italic_correction=check_variants(unicode,vert_variants,splitter,unicodes) - end - local italic_correction=description.italic - if italic_correction and italic_correction~=0 then - math.italic_correction=italic_correction - end - description.math=math - end - end - end -end -actions["reorganize glyph kerns"]=function(data,filename,raw) - local descriptions=data.descriptions - local resources=data.resources - local unicodes=resources.unicodes - for unicode,description in next,descriptions do - local kerns=description.glyph.kerns - if kerns then - local newkerns={} - for k,kern in next,kerns do - local name=kern.char - local offset=kern.off - local lookup=kern.lookup - if name and offset and lookup then - local unicode=unicodes[name] - if unicode then - if type(lookup)=="table" then - for l=1,#lookup do - local lookup=lookup[l] - local lookupkerns=newkerns[lookup] - if lookupkerns then - lookupkerns[unicode]=offset - else - newkerns[lookup]={ [unicode]=offset } - end - end - else - local lookupkerns=newkerns[lookup] - if lookupkerns then - lookupkerns[unicode]=offset - else - newkerns[lookup]={ [unicode]=offset } - end - end - elseif trace_loading then - report_otf("problems with unicode %a of kern %a of glyph %U",name,k,unicode) - end - end - end - description.kerns=newkerns - end - end -end -actions["merge kern classes"]=function(data,filename,raw) - local gposlist=raw.gpos - if gposlist then - local descriptions=data.descriptions - local resources=data.resources - local unicodes=resources.unicodes - local splitter=data.helpers.tounicodetable - local ignored=0 - local blocked=0 - for gp=1,#gposlist do - local gpos=gposlist[gp] - local subtables=gpos.subtables - if subtables then - local first_done={} - local split={} - for s=1,#subtables do - local subtable=subtables[s] - local kernclass=subtable.kernclass - local lookup=subtable.lookup or subtable.name - if kernclass then - if #kernclass>0 then - kernclass=kernclass[1] - lookup=type(kernclass.lookup)=="string" and kernclass.lookup or lookup - report_otf("fixing kernclass table of lookup %a",lookup) - end - local firsts=kernclass.firsts - local seconds=kernclass.seconds - local offsets=kernclass.offsets - for n,s in next,firsts do - split[s]=split[s] or lpegmatch(splitter,s) - end - local maxseconds=0 - for n,s in next,seconds do - if n>maxseconds then - maxseconds=n - end - split[s]=split[s] or lpegmatch(splitter,s) - end - for fk=1,#firsts do - local fv=firsts[fk] - local splt=split[fv] - if splt then - local extrakerns={} - local baseoffset=(fk-1)*maxseconds - for sk=2,maxseconds do - local sv=seconds[sk] - local splt=split[sv] - if splt then - local offset=offsets[baseoffset+sk] - if offset then - for i=1,#splt do - extrakerns[splt[i]]=offset - end - end - end - end - for i=1,#splt do - local first_unicode=splt[i] - if first_done[first_unicode] then - report_otf("lookup %a: ignoring further kerns of %C",lookup,first_unicode) - blocked=blocked+1 - else - first_done[first_unicode]=true - local description=descriptions[first_unicode] - if description then - local kerns=description.kerns - if not kerns then - kerns={} - description.kerns=kerns - end - local lookupkerns=kerns[lookup] - if not lookupkerns then - lookupkerns={} - kerns[lookup]=lookupkerns - end - if overloadkerns then - for second_unicode,kern in next,extrakerns do - lookupkerns[second_unicode]=kern - end - else - for second_unicode,kern in next,extrakerns do - local k=lookupkerns[second_unicode] - if not k then - lookupkerns[second_unicode]=kern - elseif k~=kern then - if trace_loading then - report_otf("lookup %a: ignoring overload of kern between %C and %C, rejecting %a, keeping %a",lookup,first_unicode,second_unicode,k,kern) - end - ignored=ignored+1 - end - end - end - elseif trace_loading then - report_otf("no glyph data for %U",first_unicode) - end - end - end - end - end - subtable.kernclass={} - end - end - end - end - if ignored>0 then - report_otf("%s kern overloads ignored",ignored) - end - if blocked>0 then - report_otf("%s succesive kerns blocked",blocked) - end - end -end -actions["check glyphs"]=function(data,filename,raw) - for unicode,description in next,data.descriptions do - description.glyph=nil - end -end -actions["check metadata"]=function(data,filename,raw) - local metadata=data.metadata - for _,k in next,mainfields do - if valid_fields[k] then - local v=raw[k] - if not metadata[k] then - metadata[k]=v - end - end - end - local ttftables=metadata.ttf_tables - if ttftables then - for i=1,#ttftables do - ttftables[i].data="deleted" - end - end - if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then - local name=file.nameonly(filename) - metadata.fontname="bad-fontname-"..name - metadata.fullname="bad-fullname-"..name - end -end -actions["cleanup tables"]=function(data,filename,raw) - data.resources.indices=nil - data.helpers=nil -end -actions["reorganize glyph lookups"]=function(data,filename,raw) - local resources=data.resources - local unicodes=resources.unicodes - local descriptions=data.descriptions - local splitter=data.helpers.tounicodelist - local lookuptypes=resources.lookuptypes - for unicode,description in next,descriptions do - local lookups=description.glyph.lookups - if lookups then - for tag,lookuplist in next,lookups do - for l=1,#lookuplist do - local lookup=lookuplist[l] - local specification=lookup.specification - local lookuptype=lookup.type - local lt=lookuptypes[tag] - if not lt then - lookuptypes[tag]=lookuptype - elseif lt~=lookuptype then - report_otf("conflicting lookuptypes, %a points to %a and %a",tag,lt,lookuptype) - end - if lookuptype=="ligature" then - lookuplist[l]={ lpegmatch(splitter,specification.components) } - elseif lookuptype=="alternate" then - lookuplist[l]={ lpegmatch(splitter,specification.components) } - elseif lookuptype=="substitution" then - lookuplist[l]=unicodes[specification.variant] - elseif lookuptype=="multiple" then - lookuplist[l]={ lpegmatch(splitter,specification.components) } - elseif lookuptype=="position" then - lookuplist[l]={ - specification.x or 0, - specification.y or 0, - specification.h or 0, - specification.v or 0 - } - elseif lookuptype=="pair" then - local one=specification.offsets[1] - local two=specification.offsets[2] - local paired=unicodes[specification.paired] - if one then - if two then - lookuplist[l]={ paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 },{ two.x or 0,two.y or 0,two.h or 0,two.v or 0 } } - else - lookuplist[l]={ paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 } } - end - else - if two then - lookuplist[l]={ paired,{},{ two.x or 0,two.y or 0,two.h or 0,two.v or 0} } - else - lookuplist[l]={ paired } - end - end - end - end - end - local slookups,mlookups - for tag,lookuplist in next,lookups do - if #lookuplist==1 then - if slookups then - slookups[tag]=lookuplist[1] - else - slookups={ [tag]=lookuplist[1] } - end - else - if mlookups then - mlookups[tag]=lookuplist - else - mlookups={ [tag]=lookuplist } - end - end - end - if slookups then - description.slookups=slookups - end - if mlookups then - description.mlookups=mlookups - end - end - end -end -actions["reorganize glyph anchors"]=function(data,filename,raw) - local descriptions=data.descriptions - for unicode,description in next,descriptions do - local anchors=description.glyph.anchors - if anchors then - for class,data in next,anchors do - if class=="baselig" then - for tag,specification in next,data do - for i=1,#specification do - local si=specification[i] - specification[i]={ si.x or 0,si.y or 0 } - end - end - else - for tag,specification in next,data do - data[tag]={ specification.x or 0,specification.y or 0 } - end - end - end - description.anchors=anchors - end - end -end -function otf.setfeatures(tfmdata,features) - local okay=constructors.initializefeatures("otf",tfmdata,features,trace_features,report_otf) - if okay then - return constructors.collectprocessors("otf",tfmdata,features,trace_features,report_otf) - else - return {} - end -end -local function copytotfm(data,cache_id) - if data then - local metadata=data.metadata - local resources=data.resources - local properties=derivetable(data.properties) - local descriptions=derivetable(data.descriptions) - local goodies=derivetable(data.goodies) - local characters={} - local parameters={} - local mathparameters={} - local pfminfo=metadata.pfminfo or {} - local resources=data.resources - local unicodes=resources.unicodes - local spaceunits=500 - local spacer="space" - local designsize=metadata.designsize or metadata.design_size or 100 - local mathspecs=metadata.math - if designsize==0 then - designsize=100 - end - if mathspecs then - for name,value in next,mathspecs do - mathparameters[name]=value - end - end - for unicode,_ in next,data.descriptions do - characters[unicode]={} - end - if mathspecs then - for unicode,character in next,characters do - local d=descriptions[unicode] - local m=d.math - if m then - local variants=m.horiz_variants - local parts=m.horiz_parts - if variants then - local c=character - for i=1,#variants do - local un=variants[i] - c.next=un - c=characters[un] - end - c.horiz_variants=parts - elseif parts then - character.horiz_variants=parts - end - local variants=m.vert_variants - local parts=m.vert_parts - if variants then - local c=character - for i=1,#variants do - local un=variants[i] - c.next=un - c=characters[un] - end - c.vert_variants=parts - elseif parts then - character.vert_variants=parts - end - local italic_correction=m.vert_italic_correction - if italic_correction then - character.vert_italic_correction=italic_correction - end - local top_accent=m.top_accent - if top_accent then - character.top_accent=top_accent - end - local kerns=m.kerns - if kerns then - character.mathkerns=kerns - end - end - end - end - local filename=constructors.checkedfilename(resources) - local fontname=metadata.fontname - local fullname=metadata.fullname or fontname - local units=metadata.units_per_em or 1000 - if units==0 then - units=1000 - metadata.units_per_em=1000 - report_otf("changing %a units to %a",0,units) - end - local monospaced=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced") - local charwidth=pfminfo.avgwidth - local charxheight=pfminfo.os2_xheight and pfminfo.os2_xheight>0 and pfminfo.os2_xheight - local italicangle=metadata.italicangle - properties.monospaced=monospaced - parameters.italicangle=italicangle - parameters.charwidth=charwidth - parameters.charxheight=charxheight - local space=0x0020 - local emdash=0x2014 - if monospaced then - if descriptions[space] then - spaceunits,spacer=descriptions[space].width,"space" - end - if not spaceunits and descriptions[emdash] then - spaceunits,spacer=descriptions[emdash].width,"emdash" - end - if not spaceunits and charwidth then - spaceunits,spacer=charwidth,"charwidth" - end - else - if descriptions[space] then - spaceunits,spacer=descriptions[space].width,"space" - end - if not spaceunits and descriptions[emdash] then - spaceunits,spacer=descriptions[emdash].width/2,"emdash/2" - end - if not spaceunits and charwidth then - spaceunits,spacer=charwidth,"charwidth" - end - end - spaceunits=tonumber(spaceunits) or 500 - parameters.slant=0 - parameters.space=spaceunits - parameters.space_stretch=units/2 - parameters.space_shrink=1*units/3 - parameters.x_height=2*units/5 - parameters.quad=units - if spaceunits<2*units/5 then - end - if italicangle and italicangle~=0 then - parameters.italicangle=italicangle - parameters.italicfactor=math.cos(math.rad(90+italicangle)) - parameters.slant=- math.tan(italicangle*math.pi/180) - end - if monospaced then - parameters.space_stretch=0 - parameters.space_shrink=0 - elseif syncspace then - parameters.space_stretch=spaceunits/2 - parameters.space_shrink=spaceunits/3 - end - parameters.extra_space=parameters.space_shrink - if charxheight then - parameters.x_height=charxheight - else - local x=0x78 - if x then - local x=descriptions[x] - if x then - parameters.x_height=x.height - end - end - end - parameters.designsize=(designsize/10)*65536 - parameters.ascender=abs(metadata.ascent or 0) - parameters.descender=abs(metadata.descent or 0) - parameters.units=units - properties.space=spacer - properties.encodingbytes=2 - properties.format=data.format or otf_format(filename) or formats.otf - properties.noglyphnames=true - properties.filename=filename - properties.fontname=fontname - properties.fullname=fullname - properties.psname=fontname or fullname - properties.name=filename or fullname - return { - characters=characters, - descriptions=descriptions, - parameters=parameters, - mathparameters=mathparameters, - resources=resources, - properties=properties, - goodies=goodies, - } - end -end -local function otftotfm(specification) - local cache_id=specification.hash - local tfmdata=containers.read(constructors.cache,cache_id) - if not tfmdata then - local name=specification.name - local sub=specification.sub - local filename=specification.filename - local features=specification.features.normal - local rawdata=otf.load(filename,sub,features and features.featurefile) - if rawdata and next(rawdata) then - local descriptions=rawdata.descriptions - local duplicates=rawdata.resources.duplicates - if duplicates then - local nofduplicates,nofduplicated=0,0 - for parent,list in next,duplicates do - for i=1,#list do - local unicode=list[i] - if not descriptions[unicode] then - descriptions[unicode]=descriptions[parent] - nofduplicated=nofduplicated+1 - end - end - nofduplicates=nofduplicates+#list - end - if trace_otf and nofduplicated~=nofduplicates then - report_otf("%i extra duplicates copied out of %i",nofduplicated,nofduplicates) - end - end - rawdata.lookuphash={} - tfmdata=copytotfm(rawdata,cache_id) - if tfmdata and next(tfmdata) then - local features=constructors.checkedfeatures("otf",features) - local shared=tfmdata.shared - if not shared then - shared={} - tfmdata.shared=shared - end - shared.rawdata=rawdata - shared.dynamics={} - tfmdata.changed={} - shared.features=features - shared.processes=otf.setfeatures(tfmdata,features) - end - end - containers.write(constructors.cache,cache_id,tfmdata) - end - return tfmdata -end -local function read_from_otf(specification) - local tfmdata=otftotfm(specification) - if tfmdata then - tfmdata.properties.name=specification.name - tfmdata.properties.sub=specification.sub - tfmdata=constructors.scale(tfmdata,specification) - local allfeatures=tfmdata.shared.features or specification.features.normal - constructors.applymanipulators("otf",tfmdata,allfeatures,trace_features,report_otf) - constructors.setname(tfmdata,specification) - fonts.loggers.register(tfmdata,file.suffix(specification.filename),specification) - end - return tfmdata -end -local function checkmathsize(tfmdata,mathsize) - local mathdata=tfmdata.shared.rawdata.metadata.math - local mathsize=tonumber(mathsize) - if mathdata then - local parameters=tfmdata.parameters - parameters.scriptpercentage=mathdata.ScriptPercentScaleDown - parameters.scriptscriptpercentage=mathdata.ScriptScriptPercentScaleDown - parameters.mathsize=mathsize - end -end -registerotffeature { - name="mathsize", - description="apply mathsize specified in the font", - initializers={ - base=checkmathsize, - node=checkmathsize, - } -} -function otf.collectlookups(rawdata,kind,script,language) - local sequences=rawdata.resources.sequences - if sequences then - local featuremap,featurelist={},{} - for s=1,#sequences do - local sequence=sequences[s] - local features=sequence.features - features=features and features[kind] - features=features and (features[script] or features[default] or features[wildcard]) - features=features and (features[language] or features[default] or features[wildcard]) - if features then - local subtables=sequence.subtables - if subtables then - for s=1,#subtables do - local ss=subtables[s] - if not featuremap[s] then - featuremap[ss]=true - featurelist[#featurelist+1]=ss - end - end - end - end - end - if #featurelist>0 then - return featuremap,featurelist - end - end - return nil,nil -end -local function check_otf(forced,specification,suffix) - local name=specification.name - if forced then - name=specification.forcedname - end - local fullname=findbinfile(name,suffix) or "" - if fullname=="" then - fullname=fonts.names.getfilename(name,suffix) or "" - end - if fullname~="" and not fonts.names.ignoredfile(fullname) then - specification.filename=fullname - return read_from_otf(specification) - end -end -local function opentypereader(specification,suffix) - local forced=specification.forced or "" - if formats[forced] then - return check_otf(true,specification,forced) - else - return check_otf(false,specification,suffix) - end -end -readers.opentype=opentypereader -function readers.otf (specification) return opentypereader(specification,"otf") end -function readers.ttf (specification) return opentypereader(specification,"ttf") end -function readers.ttc (specification) return opentypereader(specification,"ttf") end -function readers.dfont(specification) return opentypereader(specification,"ttf") end -function otf.scriptandlanguage(tfmdata,attr) - local properties=tfmdata.properties - return properties.script or "dflt",properties.language or "dflt" -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-otb']={ - 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 concat=table.concat -local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip -local type,next,tonumber,tostring=type,next,tonumber,tostring -local lpegmatch=lpeg.match -local utfchar=utf.char -local trace_baseinit=false trackers.register("otf.baseinit",function(v) trace_baseinit=v end) -local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end) -local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end) -local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end) -local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end) -local trace_ligatures_detail=false trackers.register("otf.ligatures.detail",function(v) trace_ligatures_detail=v end) -local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end) -local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end) -local report_prepare=logs.reporter("fonts","otf prepare") -local fonts=fonts -local otf=fonts.handlers.otf -local otffeatures=otf.features -local registerotffeature=otffeatures.register -otf.defaultbasealternate="none" -local wildcard="*" -local default="dflt" -local formatters=string.formatters -local f_unicode=formatters["%U"] -local f_uniname=formatters["%U (%s)"] -local f_unilist=formatters["% t (% t)"] -local function gref(descriptions,n) - if type(n)=="number" then - local name=descriptions[n].name - if name then - return f_uniname(n,name) - else - return f_unicode(n) - end - elseif n then - local num,nam={},{} - for i=2,#n do - local ni=n[i] - if tonumber(ni) then - local di=descriptions[ni] - num[i]=f_unicode(ni) - nam[i]=di and di.name or "-" - end - end - return f_unilist(num,nam) - else - return "" - end -end -local function cref(feature,lookupname) - if lookupname then - return formatters["feature %a, lookup %a"](feature,lookupname) - else - return formatters["feature %a"](feature) - end -end -local function report_alternate(feature,lookupname,descriptions,unicode,replacement,value,comment) - report_prepare("%s: base alternate %s => %s (%S => %S)", - cref(feature,lookupname), - gref(descriptions,unicode), - replacement and gref(descriptions,replacement), - value, - comment) -end -local function report_substitution(feature,lookupname,descriptions,unicode,substitution) - report_prepare("%s: base substitution %s => %S", - cref(feature,lookupname), - gref(descriptions,unicode), - gref(descriptions,substitution)) -end -local function report_ligature(feature,lookupname,descriptions,unicode,ligature) - report_prepare("%s: base ligature %s => %S", - cref(feature,lookupname), - gref(descriptions,ligature), - gref(descriptions,unicode)) -end -local function report_kern(feature,lookupname,descriptions,unicode,otherunicode,value) - report_prepare("%s: base kern %s + %s => %S", - cref(feature,lookupname), - gref(descriptions,unicode), - gref(descriptions,otherunicode), - value) -end -local basemethods={} -local basemethod="" -local function applybasemethod(what,...) - local m=basemethods[basemethod][what] - if m then - return m(...) - end -end -local basehash,basehashes,applied={},1,{} -local function registerbasehash(tfmdata) - local properties=tfmdata.properties - local hash=concat(applied," ") - local base=basehash[hash] - if not base then - basehashes=basehashes+1 - base=basehashes - basehash[hash]=base - end - properties.basehash=base - properties.fullname=properties.fullname.."-"..base - applied={} -end -local function registerbasefeature(feature,value) - applied[#applied+1]=feature.."="..tostring(value) -end -local trace=false -local function finalize_ligatures(tfmdata,ligatures) - local nofligatures=#ligatures - if nofligatures>0 then - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - local resources=tfmdata.resources - local unicodes=resources.unicodes - local private=resources.private - local alldone=false - while not alldone do - local done=0 - for i=1,nofligatures do - local ligature=ligatures[i] - if ligature then - local unicode,lookupdata=ligature[1],ligature[2] - if trace then - trace_ligatures_detail("building % a into %a",lookupdata,unicode) - end - local size=#lookupdata - local firstcode=lookupdata[1] - local firstdata=characters[firstcode] - local okay=false - if firstdata then - local firstname="ctx_"..firstcode - for i=1,size-1 do - local firstdata=characters[firstcode] - if not firstdata then - firstcode=private - if trace then - trace_ligatures_detail("defining %a as %a",firstname,firstcode) - end - unicodes[firstname]=firstcode - firstdata={ intermediate=true,ligatures={} } - characters[firstcode]=firstdata - descriptions[firstcode]={ name=firstname } - private=private+1 - end - local target - local secondcode=lookupdata[i+1] - local secondname=firstname.."_"..secondcode - if i==size-1 then - target=unicode - if not unicodes[secondname] then - unicodes[secondname]=unicode - end - okay=true - else - target=unicodes[secondname] - if not target then - break - end - end - if trace then - trace_ligatures_detail("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target) - end - local firstligs=firstdata.ligatures - if firstligs then - firstligs[secondcode]={ char=target } - else - firstdata.ligatures={ [secondcode]={ char=target } } - end - firstcode=target - firstname=secondname - end - end - if okay then - ligatures[i]=false - done=done+1 - end - end - end - alldone=done==0 - end - if trace then - for k,v in next,characters do - if v.ligatures then table.print(v,k) end - end - end - tfmdata.resources.private=private - end -end -local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - local resources=tfmdata.resources - local changed=tfmdata.changed - local unicodes=resources.unicodes - local lookuphash=resources.lookuphash - local lookuptypes=resources.lookuptypes - local ligatures={} - local alternate=tonumber(value) - local defaultalt=otf.defaultbasealternate - local trace_singles=trace_baseinit and trace_singles - local trace_alternatives=trace_baseinit and trace_alternatives - local trace_ligatures=trace_baseinit and trace_ligatures - local actions={ - substitution=function(lookupdata,lookupname,description,unicode) - if trace_singles then - report_substitution(feature,lookupname,descriptions,unicode,lookupdata) - end - changed[unicode]=lookupdata - end, - alternate=function(lookupdata,lookupname,description,unicode) - local replacement=lookupdata[alternate] - if replacement then - changed[unicode]=replacement - if trace_alternatives then - report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal") - end - elseif defaultalt=="first" then - replacement=lookupdata[1] - changed[unicode]=replacement - if trace_alternatives then - report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) - end - elseif defaultalt=="last" then - replacement=lookupdata[#data] - if trace_alternatives then - report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) - end - else - if trace_alternatives then - report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown") - end - end - end, - ligature=function(lookupdata,lookupname,description,unicode) - if trace_ligatures then - report_ligature(feature,lookupname,descriptions,unicode,lookupdata) - end - ligatures[#ligatures+1]={ unicode,lookupdata } - end, - } - for unicode,character in next,characters do - local description=descriptions[unicode] - local lookups=description.slookups - if lookups then - for l=1,#lookuplist do - local lookupname=lookuplist[l] - local lookupdata=lookups[lookupname] - if lookupdata then - local lookuptype=lookuptypes[lookupname] - local action=actions[lookuptype] - if action then - action(lookupdata,lookupname,description,unicode) - end - end - end - end - local lookups=description.mlookups - if lookups then - for l=1,#lookuplist do - local lookupname=lookuplist[l] - local lookuplist=lookups[lookupname] - if lookuplist then - local lookuptype=lookuptypes[lookupname] - local action=actions[lookuptype] - if action then - for i=1,#lookuplist do - action(lookuplist[i],lookupname,description,unicode) - end - end - end - end - end - end - finalize_ligatures(tfmdata,ligatures) -end -local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - local resources=tfmdata.resources - local unicodes=resources.unicodes - local sharedkerns={} - local traceindeed=trace_baseinit and trace_kerns - for unicode,character in next,characters do - local description=descriptions[unicode] - local rawkerns=description.kerns - if rawkerns then - local s=sharedkerns[rawkerns] - if s==false then - elseif s then - character.kerns=s - else - local newkerns=character.kerns - local done=false - for l=1,#lookuplist do - local lookup=lookuplist[l] - local kerns=rawkerns[lookup] - if kerns then - for otherunicode,value in next,kerns do - if value==0 then - elseif not newkerns then - newkerns={ [otherunicode]=value } - done=true - if traceindeed then - report_kern(feature,lookup,descriptions,unicode,otherunicode,value) - end - elseif not newkerns[otherunicode] then - newkerns[otherunicode]=value - done=true - if traceindeed then - report_kern(feature,lookup,descriptions,unicode,otherunicode,value) - end - end - end - end - end - if done then - sharedkerns[rawkerns]=newkerns - character.kerns=newkerns - else - sharedkerns[rawkerns]=false - end - end - end - end -end -basemethods.independent={ - preparesubstitutions=preparesubstitutions, - preparepositionings=preparepositionings, -} -local function makefake(tfmdata,name,present) - local resources=tfmdata.resources - local private=resources.private - local character={ intermediate=true,ligatures={} } - resources.unicodes[name]=private - tfmdata.characters[private]=character - tfmdata.descriptions[private]={ name=name } - resources.private=private+1 - present[name]=private - return character -end -local function make_1(present,tree,name) - for k,v in next,tree do - if k=="ligature" then - present[name]=v - else - make_1(present,v,name.."_"..k) - end - end -end -local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done,lookupname) - for k,v in next,tree do - if k=="ligature" then - local character=characters[preceding] - if not character then - if trace_baseinit then - report_prepare("weird ligature in lookup %a, current %C, preceding %C",lookupname,v,preceding) - end - character=makefake(tfmdata,name,present) - end - local ligatures=character.ligatures - if ligatures then - ligatures[unicode]={ char=v } - else - character.ligatures={ [unicode]={ char=v } } - end - if done then - local d=done[lookupname] - if not d then - done[lookupname]={ "dummy",v } - else - d[#d+1]=v - end - end - else - local code=present[name] or unicode - local name=name.."_"..k - make_2(present,tfmdata,characters,v,name,code,k,done,lookupname) - end - end -end -local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - local resources=tfmdata.resources - local changed=tfmdata.changed - local lookuphash=resources.lookuphash - local lookuptypes=resources.lookuptypes - local ligatures={} - local alternate=tonumber(value) - local defaultalt=otf.defaultbasealternate - local trace_singles=trace_baseinit and trace_singles - local trace_alternatives=trace_baseinit and trace_alternatives - local trace_ligatures=trace_baseinit and trace_ligatures - for l=1,#lookuplist do - local lookupname=lookuplist[l] - local lookupdata=lookuphash[lookupname] - local lookuptype=lookuptypes[lookupname] - for unicode,data in next,lookupdata do - if lookuptype=="substitution" then - if trace_singles then - report_substitution(feature,lookupname,descriptions,unicode,data) - end - changed[unicode]=data - elseif lookuptype=="alternate" then - local replacement=data[alternate] - if replacement then - changed[unicode]=replacement - if trace_alternatives then - report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal") - end - elseif defaultalt=="first" then - replacement=data[1] - changed[unicode]=replacement - if trace_alternatives then - report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) - end - elseif defaultalt=="last" then - replacement=data[#data] - if trace_alternatives then - report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) - end - else - if trace_alternatives then - report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown") - end - end - elseif lookuptype=="ligature" then - ligatures[#ligatures+1]={ unicode,data,lookupname } - if trace_ligatures then - report_ligature(feature,lookupname,descriptions,unicode,data) - end - end - end - end - local nofligatures=#ligatures - if nofligatures>0 then - local characters=tfmdata.characters - local present={} - local done=trace_baseinit and trace_ligatures and {} - for i=1,nofligatures do - local ligature=ligatures[i] - local unicode,tree=ligature[1],ligature[2] - make_1(present,tree,"ctx_"..unicode) - end - for i=1,nofligatures do - local ligature=ligatures[i] - local unicode,tree,lookupname=ligature[1],ligature[2],ligature[3] - make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,lookupname) - end - end -end -local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - local resources=tfmdata.resources - local lookuphash=resources.lookuphash - local traceindeed=trace_baseinit and trace_kerns - for l=1,#lookuplist do - local lookupname=lookuplist[l] - local lookupdata=lookuphash[lookupname] - for unicode,data in next,lookupdata do - local character=characters[unicode] - local kerns=character.kerns - if not kerns then - kerns={} - character.kerns=kerns - end - if traceindeed then - for otherunicode,kern in next,data do - if not kerns[otherunicode] and kern~=0 then - kerns[otherunicode]=kern - report_kern(feature,lookup,descriptions,unicode,otherunicode,kern) - end - end - else - for otherunicode,kern in next,data do - if not kerns[otherunicode] and kern~=0 then - kerns[otherunicode]=kern - end - end - end - end - end -end -local function initializehashes(tfmdata) - nodeinitializers.features(tfmdata) -end -basemethods.shared={ - initializehashes=initializehashes, - preparesubstitutions=preparesubstitutions, - preparepositionings=preparepositionings, -} -basemethod="independent" -local function featuresinitializer(tfmdata,value) - if true then - local t=trace_preparing and os.clock() - local features=tfmdata.shared.features - if features then - applybasemethod("initializehashes",tfmdata) - local collectlookups=otf.collectlookups - local rawdata=tfmdata.shared.rawdata - local properties=tfmdata.properties - local script=properties.script - local language=properties.language - local basesubstitutions=rawdata.resources.features.gsub - local basepositionings=rawdata.resources.features.gpos - if basesubstitutions then - for feature,data in next,basesubstitutions do - local value=features[feature] - if value then - local validlookups,lookuplist=collectlookups(rawdata,feature,script,language) - if validlookups then - applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist) - registerbasefeature(feature,value) - end - end - end - end - if basepositionings then - for feature,data in next,basepositionings do - local value=features[feature] - if value then - local validlookups,lookuplist=collectlookups(rawdata,feature,script,language) - if validlookups then - applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist) - registerbasefeature(feature,value) - end - end - end - end - registerbasehash(tfmdata) - end - if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %a",os.clock()-t,tfmdata.properties.fullname) - end - end -end -registerotffeature { - name="features", - description="features", - default=true, - initializers={ - base=featuresinitializer, - } -} -directives.register("fonts.otf.loader.basemethod",function(v) - if basemethods[v] then - basemethod=v - end -end) - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['node-inj']={ - version=1.001, - comment="companion to node-ini.mkiv", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files", -} -local next=next -local utfchar=utf.char -local trace_injections=false trackers.register("nodes.injections",function(v) trace_injections=v end) -local report_injections=logs.reporter("nodes","injections") -local attributes,nodes,node=attributes,nodes,node -fonts=fonts -local fontdata=fonts.hashes.identifiers -nodes.injections=nodes.injections or {} -local injections=nodes.injections -local nodecodes=nodes.nodecodes -local glyph_code=nodecodes.glyph -local kern_code=nodecodes.kern -local nodepool=nodes.pool -local newkern=nodepool.kern -local traverse_id=node.traverse_id -local insert_node_before=node.insert_before -local insert_node_after=node.insert_after -local a_kernpair=attributes.private('kernpair') -local a_ligacomp=attributes.private('ligacomp') -local a_markbase=attributes.private('markbase') -local a_markmark=attributes.private('markmark') -local a_markdone=attributes.private('markdone') -local a_cursbase=attributes.private('cursbase') -local a_curscurs=attributes.private('curscurs') -local a_cursdone=attributes.private('cursdone') -function injections.installnewkern(nk) - newkern=nk or newkern -end -local cursives={} -local marks={} -local kerns={} -function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) - local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2]) - local ws,wn=tfmstart.width,tfmnext.width - local bound=#cursives+1 - start[a_cursbase]=bound - nxt[a_curscurs]=bound - cursives[bound]={ rlmode,dx,dy,ws,wn } - return dx,dy,bound -end -function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) - local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4] - if x~=0 or w~=0 or y~=0 or h~=0 then - local bound=current[a_kernpair] - if bound then - local kb=kerns[bound] - kb[2],kb[3],kb[4],kb[5]=(kb[2] or 0)+x,(kb[3] or 0)+y,(kb[4] or 0)+w,(kb[5] or 0)+h - else - bound=#kerns+1 - current[a_kernpair]=bound - kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width } - end - return x,y,w,h,bound - end - return x,y,w,h -end -function injections.setkern(current,factor,rlmode,x,tfmchr) - local dx=factor*x - if dx~=0 then - local bound=#kerns+1 - current[a_kernpair]=bound - kerns[bound]={ rlmode,dx } - return dx,bound - else - return 0,0 - end -end -function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) - local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) - local bound=base[a_markbase] - local index=1 - if bound then - local mb=marks[bound] - if mb then - index=#mb+1 - mb[index]={ dx,dy,rlmode } - start[a_markmark]=bound - start[a_markdone]=index - return dx,dy,bound - else - report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound) - end - end - index=index or 1 - bound=#marks+1 - base[a_markbase]=bound - start[a_markmark]=bound - start[a_markdone]=index - marks[bound]={ [index]={ dx,dy,rlmode,baseismark } } - return dx,dy,bound -end -local function dir(n) - return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" -end -local function trace(head) - report_injections("begin run") - for n in traverse_id(glyph_code,head) do - if n.subtype<256 then - local kp=n[a_kernpair] - local mb=n[a_markbase] - local mm=n[a_markmark] - local md=n[a_markdone] - local cb=n[a_cursbase] - local cc=n[a_curscurs] - local char=n.char - report_injections("font %s, char %U, glyph %c",n.font,char,char) - if kp then - local k=kerns[kp] - if k[3] then - report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5]) - else - report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2]) - end - end - if mb then - report_injections(" markbase: bound %a",mb) - end - if mm then - local m=marks[mm] - if mb then - local m=m[mb] - if m then - report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2]) - else - report_injections(" markmark: bound %a, missing index",mm) - end - else - m=m[1] - report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2]) - end - end - if cb then - report_injections(" cursbase: bound %a",cb) - end - if cc then - local c=cursives[cc] - report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3]) - end - end - end - report_injections("end run") -end -local function show_result(head) - local current=head - local skipping=false - while current do - local id=current.id - if id==glyph_code then - report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset) - skipping=false - elseif id==kern_code then - report_injections("kern: %p",current.kern) - skipping=false - elseif not skipping then - report_injections() - skipping=true - end - current=current.next - end -end -function injections.handler(head,where,keep) - local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns) - if has_marks or has_cursives then - if trace_injections then - trace(head) - end - local done,ky,rl,valid,cx,wx,mk,nofvalid=false,{},{},{},{},{},{},0 - if has_kerns then - local nf,tm=nil,nil - for n in traverse_id(glyph_code,head) do - if n.subtype<256 then - nofvalid=nofvalid+1 - valid[nofvalid]=n - if n.font~=nf then - nf=n.font - tm=fontdata[nf].resources.marks - end - if tm then - mk[n]=tm[n.char] - end - local k=n[a_kernpair] - if k then - local kk=kerns[k] - if kk then - local x,y,w,h=kk[2] or 0,kk[3] or 0,kk[4] or 0,kk[5] or 0 - local dy=y-h - if dy~=0 then - ky[n]=dy - end - if w~=0 or x~=0 then - wx[n]=kk - end - rl[n]=kk[1] - end - end - end - end - else - local nf,tm=nil,nil - for n in traverse_id(glyph_code,head) do - if n.subtype<256 then - nofvalid=nofvalid+1 - valid[nofvalid]=n - if n.font~=nf then - nf=n.font - tm=fontdata[nf].resources.marks - end - if tm then - mk[n]=tm[n.char] - end - end - end - end - if nofvalid>0 then - local cx={} - if has_kerns and next(ky) then - for n,k in next,ky do - n.yoffset=k - end - end - if has_cursives then - local p_cursbase,p=nil,nil - local t,d,maxt={},{},0 - for i=1,nofvalid do - local n=valid[i] - if not mk[n] then - local n_cursbase=n[a_cursbase] - if p_cursbase then - local n_curscurs=n[a_curscurs] - if p_cursbase==n_curscurs then - local c=cursives[n_curscurs] - if c then - local rlmode,dx,dy,ws,wn=c[1],c[2],c[3],c[4],c[5] - if rlmode>=0 then - dx=dx-ws - else - dx=dx+wn - end - if dx~=0 then - cx[n]=dx - rl[n]=rlmode - end - dy=-dy - maxt=maxt+1 - t[maxt]=p - d[maxt]=dy - else - maxt=0 - end - end - elseif maxt>0 then - local ny=n.yoffset - for i=maxt,1,-1 do - ny=ny+d[i] - local ti=t[i] - ti.yoffset=ti.yoffset+ny - end - maxt=0 - end - if not n_cursbase and maxt>0 then - local ny=n.yoffset - for i=maxt,1,-1 do - ny=ny+d[i] - local ti=t[i] - ti.yoffset=ny - end - maxt=0 - end - p_cursbase,p=n_cursbase,n - end - end - if maxt>0 then - local ny=n.yoffset - for i=maxt,1,-1 do - ny=ny+d[i] - local ti=t[i] - ti.yoffset=ny - end - maxt=0 - end - if not keep then - cursives={} - end - end - if has_marks then - for i=1,nofvalid do - local p=valid[i] - local p_markbase=p[a_markbase] - if p_markbase then - local mrks=marks[p_markbase] - local nofmarks=#mrks - for n in traverse_id(glyph_code,p.next) do - local n_markmark=n[a_markmark] - if p_markbase==n_markmark then - local index=n[a_markdone] or 1 - local d=mrks[index] - if d then - local rlmode=d[3] - local k=wx[p] - if k then - local x=k[2] - local w=k[4] - if w then - if rlmode and rlmode>=0 then - n.xoffset=p.xoffset-p.width+d[1]-(w-x) - else - n.xoffset=p.xoffset-d[1]-x - end - else - if rlmode and rlmode>=0 then - n.xoffset=p.xoffset-p.width+d[1] - else - n.xoffset=p.xoffset-d[1]-x - end - end - else - if rlmode and rlmode>=0 then - n.xoffset=p.xoffset-p.width+d[1] - else - n.xoffset=p.xoffset-d[1] - end - local w=n.width - if w~=0 then - insert_node_before(head,n,newkern(-w/2)) - insert_node_after(head,n,newkern(-w/2)) - end - end - if mk[p] then - n.yoffset=p.yoffset+d[2] - else - n.yoffset=n.yoffset+p.yoffset+d[2] - end - if nofmarks==1 then - break - else - nofmarks=nofmarks-1 - end - end - else - end - end - end - end - if not keep then - marks={} - end - end - if next(wx) then - for n,k in next,wx do - local x=k[2] - local w=k[4] - if w then - local rl=k[1] - local wx=w-x - if rl<0 then - if wx~=0 then - insert_node_before(head,n,newkern(wx)) - end - if x~=0 then - insert_node_after (head,n,newkern(x)) - end - else - if x~=0 then - insert_node_before(head,n,newkern(x)) - end - if wx~=0 then - insert_node_after (head,n,newkern(wx)) - end - end - elseif x~=0 then - insert_node_before(head,n,newkern(x)) - end - end - end - if next(cx) then - for n,k in next,cx do - if k~=0 then - local rln=rl[n] - if rln and rln<0 then - insert_node_before(head,n,newkern(-k)) - else - insert_node_before(head,n,newkern(k)) - end - end - end - end - if not keep then - kerns={} - end - return head,true - elseif not keep then - kerns,cursives,marks={},{},{} - end - elseif has_kerns then - if trace_injections then - trace(head) - end - for n in traverse_id(glyph_code,head) do - if n.subtype<256 then - local k=n[a_kernpair] - if k then - local kk=kerns[k] - if kk then - local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4] - if y and y~=0 then - n.yoffset=y - end - if w then - local wx=w-x - if rl<0 then - if wx~=0 then - insert_node_before(head,n,newkern(wx)) - end - if x~=0 then - insert_node_after (head,n,newkern(x)) - end - else - if x~=0 then - insert_node_before(head,n,newkern(x)) - end - if wx~=0 then - insert_node_after(head,n,newkern(wx)) - end - end - else - if x~=0 then - insert_node_before(head,n,newkern(x)) - end - end - end - end - end - end - if not keep then - kerns={} - end - return head,true - else - end - return head,false -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-ota']={ - version=1.001, - comment="companion to font-otf.lua (analysing)", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local type=type -if not trackers then trackers={ register=function() end } end -local fonts,nodes,node=fonts,nodes,node -local allocate=utilities.storage.allocate -local otf=fonts.handlers.otf -local analyzers=fonts.analyzers -local initializers=allocate() -local methods=allocate() -analyzers.initializers=initializers -analyzers.methods=methods -analyzers.useunicodemarks=false -local a_state=attributes.private('state') -local nodecodes=nodes.nodecodes -local glyph_code=nodecodes.glyph -local disc_code=nodecodes.disc -local math_code=nodecodes.math -local traverse_id=node.traverse_id -local traverse_node_list=node.traverse -local end_of_math=node.end_of_math -local fontdata=fonts.hashes.identifiers -local categories=characters and characters.categories or {} -local otffeatures=fonts.constructors.newfeatures("otf") -local registerotffeature=otffeatures.register -local s_init=1 local s_rphf=7 -local s_medi=2 local s_half=8 -local s_fina=3 local s_pref=9 -local s_isol=4 local s_blwf=10 -local s_mark=5 local s_pstf=11 -local s_rest=6 -local states={ - init=s_init, - medi=s_medi, - fina=s_fina, - isol=s_isol, - mark=s_mark, - rest=s_rest, - rphf=s_rphf, - half=s_half, - pref=s_pref, - blwf=s_blwf, - pstf=s_pstf, -} -local features={ - init=s_init, - medi=s_medi, - fina=s_fina, - isol=s_isol, - rphf=s_rphf, - half=s_half, - pref=s_pref, - blwf=s_blwf, - pstf=s_pstf, -} -analyzers.states=states -analyzers.features=features -function analyzers.setstate(head,font) - local useunicodemarks=analyzers.useunicodemarks - local tfmdata=fontdata[font] - local descriptions=tfmdata.descriptions - local first,last,current,n,done=nil,nil,head,0,false - while current do - local id=current.id - if id==glyph_code and current.font==font then - done=true - local char=current.char - local d=descriptions[char] - if d then - if d.class=="mark" or (useunicodemarks and categories[char]=="mn") then - done=true - current[a_state]=s_mark - elseif n==0 then - first,last,n=current,current,1 - current[a_state]=s_init - else - last,n=current,n+1 - current[a_state]=s_medi - end - else - if first and first==last then - last[a_state]=s_isol - elseif last then - last[a_state]=s_fina - end - first,last,n=nil,nil,0 - end - elseif id==disc_code then - current[a_state]=s_medi - last=current - else - if first and first==last then - last[a_state]=s_isol - elseif last then - last[a_state]=s_fina - end - first,last,n=nil,nil,0 - if id==math_code then - current=end_of_math(current) - end - end - current=current.next - end - if first and first==last then - last[a_state]=s_isol - elseif last then - last[a_state]=s_fina - end - return head,done -end -local function analyzeinitializer(tfmdata,value) - local script,language=otf.scriptandlanguage(tfmdata) - local action=initializers[script] - if not action then - elseif type(action)=="function" then - return action(tfmdata,value) - else - local action=action[language] - if action then - return action(tfmdata,value) - end - end -end -local function analyzeprocessor(head,font,attr) - local tfmdata=fontdata[font] - local script,language=otf.scriptandlanguage(tfmdata,attr) - local action=methods[script] - if not action then - elseif type(action)=="function" then - return action(head,font,attr) - else - action=action[language] - if action then - return action(head,font,attr) - end - end - return head,false -end -registerotffeature { - name="analyze", - description="analysis of character classes", - default=true, - initializers={ - node=analyzeinitializer, - }, - processors={ - position=1, - node=analyzeprocessor, - } -} -methods.latn=analyzers.setstate -local tatweel=0x0640 -local zwnj=0x200C -local zwj=0x200D -local isolated={ - [0x0600]=true,[0x0601]=true,[0x0602]=true,[0x0603]=true, - [0x0604]=true, - [0x0608]=true,[0x060B]=true,[0x0621]=true,[0x0674]=true, - [0x06DD]=true, - [0x0856]=true,[0x0858]=true,[0x0857]=true, - [0x07FA]=true, - [zwnj]=true, -} -local final={ - [0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true, - [0x0627]=true,[0x0629]=true,[0x062F]=true,[0x0630]=true, - [0x0631]=true,[0x0632]=true,[0x0648]=true,[0x0671]=true, - [0x0672]=true,[0x0673]=true,[0x0675]=true,[0x0676]=true, - [0x0677]=true,[0x0688]=true,[0x0689]=true,[0x068A]=true, - [0x068B]=true,[0x068C]=true,[0x068D]=true,[0x068E]=true, - [0x068F]=true,[0x0690]=true,[0x0691]=true,[0x0692]=true, - [0x0693]=true,[0x0694]=true,[0x0695]=true,[0x0696]=true, - [0x0697]=true,[0x0698]=true,[0x0699]=true,[0x06C0]=true, - [0x06C3]=true,[0x06C4]=true,[0x06C5]=true,[0x06C6]=true, - [0x06C7]=true,[0x06C8]=true,[0x06C9]=true,[0x06CA]=true, - [0x06CB]=true,[0x06CD]=true,[0x06CF]=true,[0x06D2]=true, - [0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true, - [0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true, - [0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true, - [0x0778]=true,[0x0779]=true, - [0x08AA]=true,[0x08AB]=true,[0x08AC]=true, - [0xFEF5]=true,[0xFEF7]=true,[0xFEF9]=true,[0xFEFB]=true, - [0x0710]=true,[0x0715]=true,[0x0716]=true,[0x0717]=true, - [0x0718]=true,[0x0719]=true,[0x0728]=true,[0x072A]=true, - [0x072C]=true,[0x071E]=true, - [0x072F]=true,[0x074D]=true, - [0x0840]=true,[0x0849]=true,[0x0854]=true,[0x0846]=true, - [0x084F]=true -} -local medial={ - [0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true, - [0x062C]=true,[0x062D]=true,[0x062E]=true,[0x0633]=true, - [0x0634]=true,[0x0635]=true,[0x0636]=true,[0x0637]=true, - [0x0638]=true,[0x0639]=true,[0x063A]=true,[0x063B]=true, - [0x063C]=true,[0x063D]=true,[0x063E]=true,[0x063F]=true, - [0x0641]=true,[0x0642]=true,[0x0643]=true, - [0x0644]=true,[0x0645]=true,[0x0646]=true,[0x0647]=true, - [0x0649]=true,[0x064A]=true,[0x066E]=true,[0x066F]=true, - [0x0678]=true,[0x0679]=true,[0x067A]=true,[0x067B]=true, - [0x067C]=true,[0x067D]=true,[0x067E]=true,[0x067F]=true, - [0x0680]=true,[0x0681]=true,[0x0682]=true,[0x0683]=true, - [0x0684]=true,[0x0685]=true,[0x0686]=true,[0x0687]=true, - [0x069A]=true,[0x069B]=true,[0x069C]=true,[0x069D]=true, - [0x069E]=true,[0x069F]=true,[0x06A0]=true,[0x06A1]=true, - [0x06A2]=true,[0x06A3]=true,[0x06A4]=true,[0x06A5]=true, - [0x06A6]=true,[0x06A7]=true,[0x06A8]=true,[0x06A9]=true, - [0x06AA]=true,[0x06AB]=true,[0x06AC]=true,[0x06AD]=true, - [0x06AE]=true,[0x06AF]=true,[0x06B0]=true,[0x06B1]=true, - [0x06B2]=true,[0x06B3]=true,[0x06B4]=true,[0x06B5]=true, - [0x06B6]=true,[0x06B7]=true,[0x06B8]=true,[0x06B9]=true, - [0x06BA]=true,[0x06BB]=true,[0x06BC]=true,[0x06BD]=true, - [0x06BE]=true,[0x06BF]=true,[0x06C1]=true,[0x06C2]=true, - [0x06CC]=true,[0x06CE]=true,[0x06D0]=true,[0x06D1]=true, - [0x06FA]=true,[0x06FB]=true,[0x06FC]=true,[0x06FF]=true, - [0x0750]=true,[0x0751]=true,[0x0752]=true,[0x0753]=true, - [0x0754]=true,[0x0755]=true,[0x0756]=true,[0x0757]=true, - [0x0758]=true,[0x075C]=true,[0x075D]=true,[0x075E]=true, - [0x075F]=true,[0x0760]=true,[0x0761]=true,[0x0762]=true, - [0x0763]=true,[0x0764]=true,[0x0765]=true,[0x0766]=true, - [0x0767]=true,[0x0768]=true,[0x0769]=true,[0x076A]=true, - [0x076D]=true,[0x076E]=true,[0x076F]=true,[0x0770]=true, - [0x0772]=true,[0x0775]=true,[0x0776]=true,[0x0777]=true, - [0x077A]=true,[0x077B]=true,[0x077C]=true,[0x077D]=true, - [0x077E]=true,[0x077F]=true, - [0x08A0]=true,[0x08A2]=true,[0x08A4]=true,[0x08A5]=true, - [0x08A6]=true,[0x0620]=true,[0x08A8]=true,[0x08A9]=true, - [0x08A7]=true,[0x08A3]=true, - [0x0712]=true,[0x0713]=true,[0x0714]=true,[0x071A]=true, - [0x071B]=true,[0x071C]=true,[0x071D]=true,[0x071F]=true, - [0x0720]=true,[0x0721]=true,[0x0722]=true,[0x0723]=true, - [0x0724]=true,[0x0725]=true,[0x0726]=true,[0x0727]=true, - [0x0729]=true,[0x072B]=true,[0x072D]=true,[0x072E]=true, - [0x074E]=true,[0x074F]=true, - [0x0841]=true,[0x0842]=true,[0x0843]=true,[0x0844]=true, - [0x0845]=true,[0x0847]=true,[0x0848]=true,[0x0855]=true, - [0x0851]=true,[0x084E]=true,[0x084D]=true,[0x084A]=true, - [0x084B]=true,[0x084C]=true,[0x0850]=true,[0x0852]=true, - [0x0853]=true, - [0x07D7]=true,[0x07E8]=true,[0x07D9]=true,[0x07EA]=true, - [0x07CA]=true,[0x07DB]=true,[0x07CC]=true,[0x07DD]=true, - [0x07CE]=true,[0x07DF]=true,[0x07D4]=true,[0x07E5]=true, - [0x07E9]=true,[0x07E7]=true,[0x07E3]=true,[0x07E2]=true, - [0x07E0]=true,[0x07E1]=true,[0x07DE]=true,[0x07DC]=true, - [0x07D1]=true,[0x07DA]=true,[0x07D8]=true,[0x07D6]=true, - [0x07D2]=true,[0x07D0]=true,[0x07CF]=true,[0x07CD]=true, - [0x07CB]=true,[0x07D3]=true,[0x07E4]=true,[0x07D5]=true, - [0x07E6]=true, - [tatweel]=true, - [zwj]=true, -} -local arab_warned={} -local function warning(current,what) - local char=current.char - if not arab_warned[char] then - log.report("analyze","arab: character %C has no %a class",char,what) - arab_warned[char]=true - end -end -local function finish(first,last) - if last then - if first==last then - local fc=first.char - if medial[fc] or final[fc] then - first[a_state]=s_isol - else - warning(first,"isol") - first[a_state]=s_error - end - else - local lc=last.char - if medial[lc] or final[lc] then - last[a_state]=s_fina - else - warning(last,"fina") - last[a_state]=s_error - end - end - first,last=nil,nil - elseif first then - local fc=first.char - if medial[fc] or final[fc] then - first[a_state]=s_isol - else - warning(first,"isol") - first[a_state]=s_error - end - first=nil - end - return first,last -end -function methods.arab(head,font,attr) - local useunicodemarks=analyzers.useunicodemarks - local tfmdata=fontdata[font] - local marks=tfmdata.resources.marks - local first,last,current,done=nil,nil,head,false - while current do - local id=current.id - if id==glyph_code and current.font==font and current.subtype<256 and not current[a_state] then - done=true - local char=current.char - if marks[char] or (useunicodemarks and categories[char]=="mn") then - current[a_state]=s_mark - elseif isolated[char] then - first,last=finish(first,last) - current[a_state]=s_isol - first,last=nil,nil - elseif not first then - if medial[char] then - current[a_state]=s_init - first,last=first or current,current - elseif final[char] then - current[a_state]=s_isol - first,last=nil,nil - else - first,last=finish(first,last) - end - elseif medial[char] then - first,last=first or current,current - current[a_state]=s_medi - elseif final[char] then - if not last[a_state]==s_init then - last[a_state]=s_medi - end - current[a_state]=s_fina - first,last=nil,nil - elseif char>=0x0600 and char<=0x06FF then - current[a_state]=s_rest - first,last=finish(first,last) - else - first,last=finish(first,last) - end - else - if first or last then - first,last=finish(first,last) - end - if id==math_code then - current=end_of_math(current) - end - end - current=current.next - end - if first or last then - finish(first,last) - end - return head,done -end -methods.syrc=methods.arab -methods.mand=methods.arab -methods.nko=methods.arab -directives.register("otf.analyze.useunicodemarks",function(v) - analyzers.useunicodemarks=v -end) - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-otn']={ - 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 concat,insert,remove=table.concat,table.insert,table.remove -local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip -local type,next,tonumber,tostring=type,next,tonumber,tostring -local lpegmatch=lpeg.match -local random=math.random -local formatters=string.formatters -local logs,trackers,nodes,attributes=logs,trackers,nodes,attributes -local registertracker=trackers.register -local fonts=fonts -local otf=fonts.handlers.otf -local trace_lookups=false registertracker("otf.lookups",function(v) trace_lookups=v end) -local trace_singles=false registertracker("otf.singles",function(v) trace_singles=v end) -local trace_multiples=false registertracker("otf.multiples",function(v) trace_multiples=v end) -local trace_alternatives=false registertracker("otf.alternatives",function(v) trace_alternatives=v end) -local trace_ligatures=false registertracker("otf.ligatures",function(v) trace_ligatures=v end) -local trace_contexts=false registertracker("otf.contexts",function(v) trace_contexts=v end) -local trace_marks=false registertracker("otf.marks",function(v) trace_marks=v end) -local trace_kerns=false registertracker("otf.kerns",function(v) trace_kerns=v end) -local trace_cursive=false registertracker("otf.cursive",function(v) trace_cursive=v end) -local trace_preparing=false registertracker("otf.preparing",function(v) trace_preparing=v end) -local trace_bugs=false registertracker("otf.bugs",function(v) trace_bugs=v end) -local trace_details=false registertracker("otf.details",function(v) trace_details=v end) -local trace_applied=false registertracker("otf.applied",function(v) trace_applied=v end) -local trace_steps=false registertracker("otf.steps",function(v) trace_steps=v end) -local trace_skips=false registertracker("otf.skips",function(v) trace_skips=v end) -local trace_directions=false registertracker("otf.directions",function(v) trace_directions=v end) -local report_direct=logs.reporter("fonts","otf direct") -local report_subchain=logs.reporter("fonts","otf subchain") -local report_chain=logs.reporter("fonts","otf chain") -local report_process=logs.reporter("fonts","otf process") -local report_prepare=logs.reporter("fonts","otf prepare") -local report_warning=logs.reporter("fonts","otf warning") -registertracker("otf.verbose_chain",function(v) otf.setcontextchain(v and "verbose") end) -registertracker("otf.normal_chain",function(v) otf.setcontextchain(v and "normal") end) -registertracker("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures") -registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") -registertracker("otf.actions","otf.replacements,otf.positions") -registertracker("otf.injections","nodes.injections") -registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") -local insert_node_after=node.insert_after -local delete_node=nodes.delete -local copy_node=node.copy -local find_node_tail=node.tail or node.slide -local flush_node_list=node.flush_list -local end_of_math=node.end_of_math -local setmetatableindex=table.setmetatableindex -local zwnj=0x200C -local zwj=0x200D -local wildcard="*" -local default="dflt" -local nodecodes=nodes.nodecodes -local whatcodes=nodes.whatcodes -local glyphcodes=nodes.glyphcodes -local disccodes=nodes.disccodes -local glyph_code=nodecodes.glyph -local glue_code=nodecodes.glue -local disc_code=nodecodes.disc -local whatsit_code=nodecodes.whatsit -local math_code=nodecodes.math -local dir_code=whatcodes.dir -local localpar_code=whatcodes.localpar -local discretionary_code=disccodes.discretionary -local ligature_code=glyphcodes.ligature -local privateattribute=attributes.private -local a_state=privateattribute('state') -local a_markbase=privateattribute('markbase') -local a_markmark=privateattribute('markmark') -local a_markdone=privateattribute('markdone') -local a_cursbase=privateattribute('cursbase') -local a_curscurs=privateattribute('curscurs') -local a_cursdone=privateattribute('cursdone') -local a_kernpair=privateattribute('kernpair') -local a_ligacomp=privateattribute('ligacomp') -local injections=nodes.injections -local setmark=injections.setmark -local setcursive=injections.setcursive -local setkern=injections.setkern -local setpair=injections.setpair -local markonce=true -local cursonce=true -local kernonce=true -local fonthashes=fonts.hashes -local fontdata=fonthashes.identifiers -local otffeatures=fonts.constructors.newfeatures("otf") -local registerotffeature=otffeatures.register -local onetimemessage=fonts.loggers.onetimemessage or function() end -otf.defaultnodealternate="none" -local tfmdata=false -local characters=false -local descriptions=false -local resources=false -local marks=false -local currentfont=false -local lookuptable=false -local anchorlookups=false -local lookuptypes=false -local handlers={} -local rlmode=0 -local featurevalue=false -local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end -local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end -local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end -local function logprocess(...) - if trace_steps then - registermessage(...) - end - report_direct(...) -end -local function logwarning(...) - report_direct(...) -end -local f_unicode=formatters["%U"] -local f_uniname=formatters["%U (%s)"] -local f_unilist=formatters["% t (% t)"] -local function gref(n) - if type(n)=="number" then - local description=descriptions[n] - local name=description and description.name - if name then - return f_uniname(n,name) - else - return f_unicode(n) - end - elseif n then - local num,nam={},{} - for i=1,#n do - local ni=n[i] - if tonumber(ni) then - local di=descriptions[ni] - num[i]=f_unicode(ni) - nam[i]=di and di.name or "-" - end - end - return f_unilist(num,nam) - else - return "" - end -end -local function cref(kind,chainname,chainlookupname,lookupname,index) - if index then - return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookupname,index) - elseif lookupname then - return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookupname) - elseif chainlookupname then - return formatters["feature %a, chain %a, sub %a"](kind,chainname,chainlookupname) - elseif chainname then - return formatters["feature %a, chain %a"](kind,chainname) - else - return formatters["feature %a"](kind) - end -end -local function pref(kind,lookupname) - return formatters["feature %a, lookup %a"](kind,lookupname) -end -local function copy_glyph(g) - local components=g.components - if components then - g.components=nil - local n=copy_node(g) - g.components=components - return n - else - return copy_node(g) - end -end -local function markstoligature(kind,lookupname,head,start,stop,char) - if start==stop and start.char==char then - return head,start - else - local prev=start.prev - local next=stop.next - start.prev=nil - stop.next=nil - local base=copy_glyph(start) - if head==start then - head=base - end - base.char=char - base.subtype=ligature_code - base.components=start - if prev then - prev.next=base - end - if next then - next.prev=base - end - base.next=next - base.prev=prev - return head,base - end -end -local function getcomponentindex(start) - if start.id~=glyph_code then - return 0 - elseif start.subtype==ligature_code then - local i=0 - local components=start.components - while components do - i=i+getcomponentindex(components) - components=components.next - end - return i - elseif not marks[start.char] then - return 1 - else - return 0 - end -end -local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) - if start==stop and start.char==char then - start.char=char - return head,start - end - local prev=start.prev - local next=stop.next - start.prev=nil - stop.next=nil - local base=copy_glyph(start) - if start==head then - head=base - end - base.char=char - base.subtype=ligature_code - base.components=start - if prev then - prev.next=base - end - if next then - next.prev=base - end - base.next=next - base.prev=prev - if not discfound then - local deletemarks=markflag~="mark" - local components=start - local baseindex=0 - local componentindex=0 - local head=base - local current=base - while start do - local char=start.char - if not marks[char] then - baseindex=baseindex+componentindex - componentindex=getcomponentindex(start) - elseif not deletemarks then - start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) - end - head,current=insert_node_after(head,current,copy_node(start)) - elseif trace_marks then - logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char)) - end - start=start.next - end - local start=current.next - while start and start.id==glyph_code do - local char=start.char - if marks[char] then - start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex) - if trace_marks then - logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) - end - else - break - end - start=start.next - end - end - return head,base -end -function handlers.gsub_single(head,start,kind,lookupname,replacement) - if trace_singles then - logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) - end - start.char=replacement - return head,start,true -end -local function get_alternative_glyph(start,alternatives,value,trace_alternatives) - local n=#alternatives - if value=="random" then - local r=random(1,n) - return alternatives[r],trace_alternatives and formatters["value %a, taking %a"](value,r) - elseif value=="first" then - return alternatives[1],trace_alternatives and formatters["value %a, taking %a"](value,1) - elseif value=="last" then - return alternatives[n],trace_alternatives and formatters["value %a, taking %a"](value,n) - else - value=tonumber(value) - if type(value)~="number" then - return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,1) - elseif value>n then - local defaultalt=otf.defaultnodealternate - if defaultalt=="first" then - return alternatives[n],trace_alternatives and formatters["invalid value %s, taking %a"](value,1) - elseif defaultalt=="last" then - return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,n) - else - return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range") - end - elseif value==0 then - return start.char,trace_alternatives and formatters["invalid value %a, %s"](value,"no change") - elseif value<1 then - return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1) - else - return alternatives[value],trace_alternatives and formatters["value %a, taking %a"](value,value) - end - end -end -local function multiple_glyphs(head,start,multiple,ignoremarks) - local nofmultiples=#multiple - if nofmultiples>0 then - start.char=multiple[1] - if nofmultiples>1 then - local sn=start.next - for k=2,nofmultiples do - local n=copy_node(start) - n.char=multiple[k] - n.next=sn - n.prev=start - if sn then - sn.prev=n - end - start.next=n - start=n - end - end - return head,start,true - else - if trace_multiples then - logprocess("no multiple for %s",gref(start.char)) - end - return head,start,false - end -end -function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence) - local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue - local choice,comment=get_alternative_glyph(start,alternative,value,trace_alternatives) - if choice then - if trace_alternatives then - logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment) - end - start.char=choice - else - if trace_alternatives then - logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment) - end - end - return head,start,true -end -function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence) - if trace_multiples then - logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) - end - return multiple_glyphs(head,start,multiple,sequence.flags[1]) -end -function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) - local s,stop,discfound=start.next,nil,false - local startchar=start.char - if marks[startchar] then - while s do - local id=s.id - if id==glyph_code and s.font==currentfont and s.subtype<256 then - local lg=ligature[s.char] - if lg then - stop=s - ligature=lg - s=s.next - else - break - end - else - break - end - end - if stop then - local lig=ligature.ligature - if lig then - if trace_ligatures then - local stopchar=stop.char - head,start=markstoligature(kind,lookupname,head,start,stop,lig) - logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) - else - head,start=markstoligature(kind,lookupname,head,start,stop,lig) - end - return head,start,true - else - end - end - else - local skipmark=sequence.flags[1] - while s do - local id=s.id - if id==glyph_code and s.subtype<256 then - if s.font==currentfont then - local char=s.char - if skipmark and marks[char] then - s=s.next - else - local lg=ligature[char] - if lg then - stop=s - ligature=lg - s=s.next - else - break - end - end - else - break - end - elseif id==disc_code then - discfound=true - s=s.next - else - break - end - end - local lig=ligature.ligature - if lig then - if stop then - if trace_ligatures then - local stopchar=stop.char - head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) - logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) - else - head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) - end - return head,start,true - else - start.char=lig - if trace_ligatures then - logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig)) - end - return head,start,true - end - else - end - end - return head,start,false -end -function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) - local markchar=start.char - if marks[markchar] then - local base=start.prev - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - local basechar=base.char - if marks[basechar] then - while true do - base=base.prev - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - basechar=base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) - end - return head,start,false - end - end - end - local baseanchors=descriptions[basechar] - if baseanchors then - baseanchors=baseanchors.anchors - end - if baseanchors then - local baseanchors=baseanchors['basechar'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", - pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return head,start,true - end - end - end - if trace_bugs then - logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - end - end - elseif trace_bugs then - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) - end - elseif trace_bugs then - logwarning("%s: prev node is no char",pref(kind,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) - end - return head,start,false -end -function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence) - local markchar=start.char - if marks[markchar] then - local base=start.prev - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - local basechar=base.char - if marks[basechar] then - while true do - base=base.prev - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - basechar=base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) - end - return head,start,false - end - end - end - local index=start[a_ligacomp] - local baseanchors=descriptions[basechar] - if baseanchors then - baseanchors=baseanchors.anchors - if baseanchors then - local baseanchors=baseanchors['baselig'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - ba=ba[index] - if ba then - local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", - pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) - end - return head,start,true - else - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index) - end - end - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) - end - elseif trace_bugs then - logwarning("%s: prev node is no char",pref(kind,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) - end - return head,start,false -end -function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) - local markchar=start.char - if marks[markchar] then - local base=start.prev - local slc=start[a_ligacomp] - if slc then - while base do - local blc=base[a_ligacomp] - if blc and blc~=slc then - base=base.prev - else - break - end - end - end - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - local basechar=base.char - local baseanchors=descriptions[basechar] - if baseanchors then - baseanchors=baseanchors.anchors - if baseanchors then - baseanchors=baseanchors['basemark'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", - pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return head,start,true - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) - end - elseif trace_bugs then - logwarning("%s: prev node is no mark",pref(kind,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) - end - return head,start,false -end -function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) - local alreadydone=cursonce and start[a_cursbase] - if not alreadydone then - local done=false - local startchar=start.char - if marks[startchar] then - if trace_cursive then - logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) - end - else - local nxt=start.next - while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do - local nextchar=nxt.char - if marks[nextchar] then - nxt=nxt.next - else - local entryanchors=descriptions[nextchar] - if entryanchors then - entryanchors=entryanchors.anchors - if entryanchors then - entryanchors=entryanchors['centry'] - if entryanchors then - local al=anchorlookups[lookupname] - for anchor,entry in next,entryanchors do - if al[anchor] then - local exit=exitanchors[anchor] - if exit then - local dx,dy,bound=setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) - if trace_cursive then - logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) - end - done=true - break - end - end - end - end - end - elseif trace_bugs then - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) - end - break - end - end - end - return head,start,done - else - if trace_cursive and trace_details then - logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) - end - return head,start,false - end -end -function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) - local startchar=start.char - local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) - end - return head,start,false -end -function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) - local snext=start.next - if not snext then - return head,start,false - else - local prev,done=start,false - local factor=tfmdata.parameters.factor - local lookuptype=lookuptypes[lookupname] - while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do - local nextchar=snext.char - local krn=kerns[nextchar] - if not krn and marks[nextchar] then - prev=snext - snext=snext.next - else - if not krn then - elseif type(krn)=="table" then - if lookuptype=="pair" then - local a,b=krn[2],krn[3] - if a and #a>0 then - local startchar=start.char - local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - if b and #b>0 then - local startchar=start.char - local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) - if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - else - report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) - end - done=true - elseif krn~=0 then - local k=setkern(snext,factor,rlmode,krn) - if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) - end - done=true - end - break - end - end - return head,start,done - end -end -local chainmores={} -local chainprocs={} -local function logprocess(...) - if trace_steps then - registermessage(...) - end - report_subchain(...) -end -local logwarning=report_subchain -local function logprocess(...) - if trace_steps then - registermessage(...) - end - report_chain(...) -end -local logwarning=report_chain -function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) - logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return head,start,false -end -function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) - logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return head,start,false -end -function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) - local char=start.char - local replacement=replacements[char] - if replacement then - if trace_singles then - logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) - end - start.char=replacement - return head,start,true - else - return head,start,false - end -end -function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) - local current=start - local subtables=currentlookup.subtables - if #subtables>1 then - logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) - end - while current do - if current.id==glyph_code then - local currentchar=current.char - local lookupname=subtables[1] - local replacement=lookuphash[lookupname] - if not replacement then - if trace_bugs then - logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) - end - else - replacement=replacement[currentchar] - if not replacement or replacement=="" then - if trace_bugs then - logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) - end - else - if trace_singles then - logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) - end - current.char=replacement - end - end - return head,start,true - elseif current==stop then - break - else - current=current.next - end - end - return head,start,false -end -chainmores.gsub_single=chainprocs.gsub_single -function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local startchar=start.char - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local replacements=lookuphash[lookupname] - if not replacements then - if trace_bugs then - logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) - end - else - replacements=replacements[startchar] - if not replacements or replacement=="" then - if trace_bugs then - logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) - end - else - if trace_multiples then - logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) - end - return multiple_glyphs(head,start,replacements,currentlookup.flags[1]) - end - end - return head,start,false -end -chainmores.gsub_multiple=chainprocs.gsub_multiple -function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local current=start - local subtables=currentlookup.subtables - local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue - while current do - if current.id==glyph_code then - local currentchar=current.char - local lookupname=subtables[1] - local alternatives=lookuphash[lookupname] - if not alternatives then - if trace_bugs then - logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname)) - end - else - alternatives=alternatives[currentchar] - if alternatives then - local choice,comment=get_alternative_glyph(current,alternatives,value,trace_alternatives) - if choice then - if trace_alternatives then - logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment) - end - start.char=choice - else - if trace_alternatives then - logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) - end - end - elseif trace_bugs then - logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment) - end - end - return head,start,true - elseif current==stop then - break - else - current=current.next - end - end - return head,start,false -end -chainmores.gsub_alternate=chainprocs.gsub_alternate -function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) - local startchar=start.char - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local ligatures=lookuphash[lookupname] - if not ligatures then - if trace_bugs then - logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) - end - else - ligatures=ligatures[startchar] - if not ligatures then - if trace_bugs then - logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) - end - else - local s=start.next - local discfound=false - local last=stop - local nofreplacements=0 - local skipmark=currentlookup.flags[1] - while s do - local id=s.id - if id==disc_code then - s=s.next - discfound=true - else - local schar=s.char - if skipmark and marks[schar] then - s=s.next - else - local lg=ligatures[schar] - if lg then - ligatures,last,nofreplacements=lg,s,nofreplacements+1 - if s==stop then - break - else - s=s.next - end - else - break - end - end - end - end - local l2=ligatures.ligature - if l2 then - if chainindex then - stop=last - end - if trace_ligatures then - if start==stop then - logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) - else - logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) - end - end - head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) - return head,start,true,nofreplacements - elseif trace_bugs then - if start==stop then - logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) - else - logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) - end - end - end - end - return head,start,false,0 -end -chainmores.gsub_ligature=chainprocs.gsub_ligature -function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar=start.char - if marks[markchar] then - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local markanchors=lookuphash[lookupname] - if markanchors then - markanchors=markanchors[markchar] - end - if markanchors then - local base=start.prev - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - local basechar=base.char - if marks[basechar] then - while true do - base=base.prev - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - basechar=base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) - end - return head,start,false - end - end - end - local baseanchors=descriptions[basechar].anchors - if baseanchors then - local baseanchors=baseanchors['basechar'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", - cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return head,start,true - end - end - end - if trace_bugs then - logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) - end - return head,start,false -end -function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar=start.char - if marks[markchar] then - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local markanchors=lookuphash[lookupname] - if markanchors then - markanchors=markanchors[markchar] - end - if markanchors then - local base=start.prev - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - local basechar=base.char - if marks[basechar] then - while true do - base=base.prev - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - basechar=base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) - end - return head,start,false - end - end - end - local index=start[a_ligacomp] - local baseanchors=descriptions[basechar].anchors - if baseanchors then - local baseanchors=baseanchors['baselig'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - ba=ba[index] - if ba then - local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", - cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) - end - return head,start,true - end - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) - end - elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) - end - return head,start,false -end -function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar=start.char - if marks[markchar] then - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local markanchors=lookuphash[lookupname] - if markanchors then - markanchors=markanchors[markchar] - end - if markanchors then - local base=start.prev - local slc=start[a_ligacomp] - if slc then - while base do - local blc=base[a_ligacomp] - if blc and blc~=slc then - base=base.prev - else - break - end - end - end - if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then - local basechar=base.char - local baseanchors=descriptions[basechar].anchors - if baseanchors then - baseanchors=baseanchors['basemark'] - if baseanchors then - local al=anchorlookups[lookupname] - for anchor,ba in next,baseanchors do - if al[anchor] then - local ma=markanchors[anchor] - if ma then - local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", - cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return head,start,true - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) - end - return head,start,false -end -function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local alreadydone=cursonce and start[a_cursbase] - if not alreadydone then - local startchar=start.char - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local exitanchors=lookuphash[lookupname] - if exitanchors then - exitanchors=exitanchors[startchar] - end - if exitanchors then - local done=false - if marks[startchar] then - if trace_cursive then - logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) - end - else - local nxt=start.next - while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do - local nextchar=nxt.char - if marks[nextchar] then - nxt=nxt.next - else - local entryanchors=descriptions[nextchar] - if entryanchors then - entryanchors=entryanchors.anchors - if entryanchors then - entryanchors=entryanchors['centry'] - if entryanchors then - local al=anchorlookups[lookupname] - for anchor,entry in next,entryanchors do - if al[anchor] then - local exit=exitanchors[anchor] - if exit then - local dx,dy,bound=setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) - if trace_cursive then - logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) - end - done=true - break - end - end - end - end - end - elseif trace_bugs then - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) - end - break - end - end - end - return head,start,done - else - if trace_cursive and trace_details then - logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) - end - return head,start,false - end - end - return head,start,false -end -function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) - local startchar=start.char - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local kerns=lookuphash[lookupname] - if kerns then - kerns=kerns[startchar] - if kerns then - local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) - end - end - end - return head,start,false -end -chainmores.gpos_single=chainprocs.gpos_single -function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) - local snext=start.next - if snext then - local startchar=start.char - local subtables=currentlookup.subtables - local lookupname=subtables[1] - local kerns=lookuphash[lookupname] - if kerns then - kerns=kerns[startchar] - if kerns then - local lookuptype=lookuptypes[lookupname] - local prev,done=start,false - local factor=tfmdata.parameters.factor - while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do - local nextchar=snext.char - local krn=kerns[nextchar] - if not krn and marks[nextchar] then - prev=snext - snext=snext.next - else - if not krn then - elseif type(krn)=="table" then - if lookuptype=="pair" then - local a,b=krn[2],krn[3] - if a and #a>0 then - local startchar=start.char - local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - if b and #b>0 then - local startchar=start.char - local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) - if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - else - report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) - local a,b=krn[2],krn[6] - if a and a~=0 then - local k=setkern(snext,factor,rlmode,a) - if trace_kerns then - logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) - end - end - if b and b~=0 then - logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) - end - end - done=true - elseif krn~=0 then - local k=setkern(snext,factor,rlmode,krn) - if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) - end - done=true - end - break - end - end - return head,start,done - end - end - end - return head,start,false -end -chainmores.gpos_pair=chainprocs.gpos_pair -local function show_skip(kind,chainname,char,ck,class) - if ck[9] then - logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) - else - logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) - end -end -local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) - local flags=sequence.flags - local done=false - local skipmark=flags[1] - local skipligature=flags[2] - local skipbase=flags[3] - local someskip=skipmark or skipligature or skipbase - local markclass=sequence.markclass - local skipped=false - for k=1,#contexts do - local match=true - local current=start - local last=start - local ck=contexts[k] - local seq=ck[3] - local s=#seq - if s==1 then - match=current.id==glyph_code and current.font==currentfont and current.subtype<256 and seq[1][current.char] - else - local f,l=ck[4],ck[5] - if f==1 and f==l then - else - if f==l then - else - local n=f+1 - last=last.next - while n<=l do - if last then - local id=last.id - if id==glyph_code then - if last.font==currentfont and last.subtype<256 then - local char=last.char - local ccd=descriptions[char] - if ccd then - local class=ccd.class - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - skipped=true - if trace_skips then - show_skip(kind,chainname,char,ck,class) - end - last=last.next - elseif seq[n][char] then - if n1 then - local prev=start.prev - if prev then - local n=f-1 - while n>=1 do - if prev then - local id=prev.id - if id==glyph_code then - if prev.font==currentfont and prev.subtype<256 then - local char=prev.char - local ccd=descriptions[char] - if ccd then - local class=ccd.class - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - skipped=true - if trace_skips then - show_skip(kind,chainname,char,ck,class) - end - elseif seq[n][char] then - n=n -1 - else - match=false - break - end - else - match=false - break - end - else - match=false - break - end - elseif id==disc_code then - elseif seq[n][32] then - n=n -1 - else - match=false - break - end - prev=prev.prev - elseif seq[n][32] then - n=n -1 - else - match=false - break - end - end - elseif f==2 then - match=seq[1][32] - else - for n=f-1,1 do - if not seq[n][32] then - match=false - break - end - end - end - end - if match and s>l then - local current=last and last.next - if current then - local n=l+1 - while n<=s do - if current then - local id=current.id - if id==glyph_code then - if current.font==currentfont and current.subtype<256 then - local char=current.char - local ccd=descriptions[char] - if ccd then - local class=ccd.class - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - skipped=true - if trace_skips then - show_skip(kind,chainname,char,ck,class) - end - elseif seq[n][char] then - n=n+1 - else - match=false - break - end - else - match=false - break - end - else - match=false - break - end - elseif id==disc_code then - elseif seq[n][32] then - n=n+1 - else - match=false - break - end - current=current.next - elseif seq[n][32] then - n=n+1 - else - match=false - break - end - end - elseif s-l==1 then - match=seq[s][32] - else - for n=l+1,s do - if not seq[n][32] then - match=false - break - end - end - end - end - end - if match then - if trace_contexts then - local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5] - local char=start.char - if ck[9] then - logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a", - cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) - else - logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a", - cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) - end - end - local chainlookups=ck[6] - if chainlookups then - local nofchainlookups=#chainlookups - if nofchainlookups==1 then - local chainlookupname=chainlookups[1] - local chainlookup=lookuptable[chainlookupname] - if chainlookup then - local cp=chainprocs[chainlookup.type] - if cp then - local ok - head,start,ok=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) - if ok then - done=true - end - else - logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) - end - else - logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname)) - end - else - local i=1 - repeat - if skipped then - while true do - local char=start.char - local ccd=descriptions[char] - if ccd then - local class=ccd.class - if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then - start=start.next - else - break - end - else - break - end - end - end - local chainlookupname=chainlookups[i] - local chainlookup=lookuptable[chainlookupname] - if not chainlookup then - i=i+1 - else - local cp=chainmores[chainlookup.type] - if not cp then - logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) - i=i+1 - else - local ok,n - head,start,ok,n=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) - if ok then - done=true - i=i+(n or 1) - else - i=i+1 - end - end - end - if start then - start=start.next - else - end - until i>nofchainlookups - end - else - local replacements=ck[7] - if replacements then - head,start,done=chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) - else - done=true - if trace_contexts then - logprocess("%s: skipping match",cref(kind,chainname)) - end - end - end - end - end - return head,start,done -end -local verbose_handle_contextchain=function(font,...) - logwarning("no verbose handler installed, reverting to 'normal'") - otf.setcontextchain() - return normal_handle_contextchain(...) -end -otf.chainhandlers={ - normal=normal_handle_contextchain, - verbose=verbose_handle_contextchain, -} -function otf.setcontextchain(method) - if not method or method=="normal" or not otf.chainhandlers[method] then - if handlers.contextchain then - logwarning("installing normal contextchain handler") - end - handlers.contextchain=normal_handle_contextchain - else - logwarning("installing contextchain handler %a",method) - local handler=otf.chainhandlers[method] - handlers.contextchain=function(...) - return handler(currentfont,...) - end - end - handlers.gsub_context=handlers.contextchain - handlers.gsub_contextchain=handlers.contextchain - handlers.gsub_reversecontextchain=handlers.contextchain - handlers.gpos_contextchain=handlers.contextchain - handlers.gpos_context=handlers.contextchain -end -otf.setcontextchain() -local missing={} -local function logprocess(...) - if trace_steps then - registermessage(...) - end - report_process(...) -end -local logwarning=report_process -local function report_missing_cache(typ,lookup) - local f=missing[currentfont] if not f then f={} missing[currentfont]=f end - local t=f[typ] if not t then t={} f[typ]=t end - if not t[lookup] then - t[lookup]=true - logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname) - end -end -local resolved={} -local lookuphashes={} -setmetatableindex(lookuphashes,function(t,font) - local lookuphash=fontdata[font].resources.lookuphash - if not lookuphash or not next(lookuphash) then - lookuphash=false - end - t[font]=lookuphash - return lookuphash -end) -local autofeatures=fonts.analyzers.features -local function initialize(sequence,script,language,enabled) - local features=sequence.features - if features then - for kind,scripts in next,features do - local valid=enabled[kind] - if valid then - local languages=scripts[script] or scripts[wildcard] - if languages and (languages[language] or languages[wildcard]) then - return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence } - end - end - end - end - return false -end -function otf.dataset(tfmdata,font) - local shared=tfmdata.shared - local properties=tfmdata.properties - local language=properties.language or "dflt" - local script=properties.script or "dflt" - local enabled=shared.features - local res=resolved[font] - if not res then - res={} - resolved[font]=res - end - local rs=res[script] - if not rs then - rs={} - res[script]=rs - end - local rl=rs[language] - if not rl then - rl={ - } - rs[language]=rl - local sequences=tfmdata.resources.sequences -for s=1,#sequences do - local v=enabled and initialize(sequences[s],script,language,enabled) - if v then - rl[#rl+1]=v - end -end - end - return rl -end -local function featuresprocessor(head,font,attr) - local lookuphash=lookuphashes[font] - if not lookuphash then - return head,false - end - if trace_steps then - checkstep(head) - end - tfmdata=fontdata[font] - descriptions=tfmdata.descriptions - characters=tfmdata.characters - resources=tfmdata.resources - marks=resources.marks - anchorlookups=resources.lookup_to_anchor - lookuptable=resources.lookups - lookuptypes=resources.lookuptypes - currentfont=font - rlmode=0 - local sequences=resources.sequences - local done=false - local datasets=otf.dataset(tfmdata,font,attr) - local dirstack={} - for s=1,#datasets do - local dataset=datasets[s] - featurevalue=dataset[1] - local sequence=dataset[5] - local rlparmode=0 - local topstack=0 - local success=false - local attribute=dataset[2] - local chain=dataset[3] - local typ=sequence.type - local subtables=sequence.subtables - if chain<0 then - local handler=handlers[typ] - local start=find_node_tail(head) - while start do - local id=start.id - if id==glyph_code then - if start.font==font and start.subtype<256 then - local a=start[0] - if a then - a=a==attr - else - a=true - end - if a then - for i=1,#subtables do - local lookupname=subtables[i] - local lookupcache=lookuphash[lookupname] - if lookupcache then - local lookupmatch=lookupcache[start.char] - if lookupmatch then - head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if success then - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - if start then start=start.prev end - else - start=start.prev - end - else - start=start.prev - end - else - start=start.prev - end - end - else - local handler=handlers[typ] - local ns=#subtables - local start=head - rlmode=0 - if ns==1 then - local lookupname=subtables[1] - local lookupcache=lookuphash[lookupname] - if not lookupcache then - report_missing_cache(typ,lookupname) - else - local function subrun(start) - local head=start - local done=false - while start do - local id=start.id - if id==glyph_code and start.font==font and start.subtype<256 then - local a=start[0] - if a then - a=(a==attr) and (not attribute or start[a_state]==attribute) - else - a=not attribute or start[a_state]==attribute - end - if a then - local lookupmatch=lookupcache[start.char] - if lookupmatch then - local ok - head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) - if ok then - done=true - end - end - if start then start=start.next end - else - start=start.next - end - else - start=start.next - end - end - if done then - success=true - return head - end - end - local function kerndisc(disc) - local prev=disc.prev - local next=disc.next - if prev and next then - prev.next=next - local a=prev[0] - if a then - a=(a==attr) and (not attribute or prev[a_state]==attribute) - else - a=not attribute or prev[a_state]==attribute - end - if a then - local lookupmatch=lookupcache[prev.char] - if lookupmatch then - local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) - if ok then - done=true - success=true - end - end - end - prev.next=disc - end - return next - end - while start do - local id=start.id - if id==glyph_code then - if start.font==font and start.subtype<256 then - local a=start[0] - if a then - a=(a==attr) and (not attribute or start[a_state]==attribute) - else - a=not attribute or start[a_state]==attribute - end - if a then - local lookupmatch=lookupcache[start.char] - if lookupmatch then - local ok - head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) - if ok then - success=true - end - end - if start then start=start.next end - else - start=start.next - end - else - start=start.next - end - elseif id==disc_code then - if start.subtype==discretionary_code then - local pre=start.pre - if pre then - local new=subrun(pre) - if new then start.pre=new end - end - local post=start.post - if post then - local new=subrun(post) - if new then start.post=new end - end - local replace=start.replace - if replace then - local new=subrun(replace) - if new then start.replace=new end - end -elseif typ=="gpos_single" or typ=="gpos_pair" then - kerndisc(start) - end - start=start.next - elseif id==whatsit_code then - local subtype=start.subtype - if subtype==dir_code then - local dir=start.dir - if dir=="+TRT" or dir=="+TLT" then - topstack=topstack+1 - dirstack[topstack]=dir - elseif dir=="-TRT" or dir=="-TLT" then - topstack=topstack-1 - end - local newdir=dirstack[topstack] - if newdir=="+TRT" then - rlmode=-1 - elseif newdir=="+TLT" then - rlmode=1 - else - rlmode=rlparmode - end - if trace_directions then - report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) - end - elseif subtype==localpar_code then - local dir=start.dir - if dir=="TRT" then - rlparmode=-1 - elseif dir=="TLT" then - rlparmode=1 - else - rlparmode=0 - end - rlmode=rlparmode - if trace_directions then - report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) - end - end - start=start.next - elseif id==math_code then - start=end_of_math(start).next - else - start=start.next - end - end - end - else - local function subrun(start) - local head=start - local done=false - while start do - local id=start.id - if id==glyph_code and start.id==font and start.subtype<256 then - local a=start[0] - if a then - a=(a==attr) and (not attribute or start[a_state]==attribute) - else - a=not attribute or start[a_state]==attribute - end - if a then - for i=1,ns do - local lookupname=subtables[i] - local lookupcache=lookuphash[lookupname] - if lookupcache then - local lookupmatch=lookupcache[start.char] - if lookupmatch then - local ok - head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if ok then - done=true - break - elseif not start then - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - if start then start=start.next end - else - start=start.next - end - else - start=start.next - end - end - if done then - success=true - return head - end - end - local function kerndisc(disc) - local prev=disc.prev - local next=disc.next - if prev and next then - prev.next=next - local a=prev[0] - if a then - a=(a==attr) and (not attribute or prev[a_state]==attribute) - else - a=not attribute or prev[a_state]==attribute - end - if a then - for i=1,ns do - local lookupname=subtables[i] - local lookupcache=lookuphash[lookupname] - if lookupcache then - local lookupmatch=lookupcache[prev.char] - if lookupmatch then - local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if ok then - done=true - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - end - prev.next=disc - end - return next - end - while start do - local id=start.id - if id==glyph_code then - if start.font==font and start.subtype<256 then - local a=start[0] - if a then - a=(a==attr) and (not attribute or start[a_state]==attribute) - else - a=not attribute or start[a_state]==attribute - end - if a then - for i=1,ns do - local lookupname=subtables[i] - local lookupcache=lookuphash[lookupname] - if lookupcache then - local lookupmatch=lookupcache[start.char] - if lookupmatch then - local ok - head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if ok then - success=true - break - elseif not start then - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - if start then start=start.next end - else - start=start.next - end - else - start=start.next - end - elseif id==disc_code then - if start.subtype==discretionary_code then - local pre=start.pre - if pre then - local new=subrun(pre) - if new then start.pre=new end - end - local post=start.post - if post then - local new=subrun(post) - if new then start.post=new end - end - local replace=start.replace - if replace then - local new=subrun(replace) - if new then start.replace=new end - end -elseif typ=="gpos_single" or typ=="gpos_pair" then - kerndisc(start) - end - start=start.next - elseif id==whatsit_code then - local subtype=start.subtype - if subtype==dir_code then - local dir=start.dir - if dir=="+TRT" or dir=="+TLT" then - topstack=topstack+1 - dirstack[topstack]=dir - elseif dir=="-TRT" or dir=="-TLT" then - topstack=topstack-1 - end - local newdir=dirstack[topstack] - if newdir=="+TRT" then - rlmode=-1 - elseif newdir=="+TLT" then - rlmode=1 - else - rlmode=rlparmode - end - if trace_directions then - report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) - end - elseif subtype==localpar_code then - local dir=start.dir - if dir=="TRT" then - rlparmode=-1 - elseif dir=="TLT" then - rlparmode=1 - else - rlparmode=0 - end - rlmode=rlparmode - if trace_directions then - report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) - end - end - start=start.next - elseif id==math_code then - start=end_of_math(start).next - else - start=start.next - end - end - end - end - if success then - done=true - end - if trace_steps then - registerstep(head) - end - end - return head,done -end -local function generic(lookupdata,lookupname,unicode,lookuphash) - local target=lookuphash[lookupname] - if target then - target[unicode]=lookupdata - else - lookuphash[lookupname]={ [unicode]=lookupdata } - end -end -local action={ - substitution=generic, - multiple=generic, - alternate=generic, - position=generic, - ligature=function(lookupdata,lookupname,unicode,lookuphash) - local target=lookuphash[lookupname] - if not target then - target={} - lookuphash[lookupname]=target - end - for i=1,#lookupdata do - local li=lookupdata[i] - local tu=target[li] - if not tu then - tu={} - target[li]=tu - end - target=tu - end - target.ligature=unicode - end, - pair=function(lookupdata,lookupname,unicode,lookuphash) - local target=lookuphash[lookupname] - if not target then - target={} - lookuphash[lookupname]=target - end - local others=target[unicode] - local paired=lookupdata[1] - if others then - others[paired]=lookupdata - else - others={ [paired]=lookupdata } - target[unicode]=others - end - end, -} -local function prepare_lookups(tfmdata) - local rawdata=tfmdata.shared.rawdata - local resources=rawdata.resources - local lookuphash=resources.lookuphash - local anchor_to_lookup=resources.anchor_to_lookup - local lookup_to_anchor=resources.lookup_to_anchor - local lookuptypes=resources.lookuptypes - local characters=tfmdata.characters - local descriptions=tfmdata.descriptions - for unicode,character in next,characters do - local description=descriptions[unicode] - if description then - local lookups=description.slookups - if lookups then - for lookupname,lookupdata in next,lookups do - action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) - end - end - local lookups=description.mlookups - if lookups then - for lookupname,lookuplist in next,lookups do - local lookuptype=lookuptypes[lookupname] - for l=1,#lookuplist do - local lookupdata=lookuplist[l] - action[lookuptype](lookupdata,lookupname,unicode,lookuphash) - end - end - end - local list=description.kerns - if list then - for lookup,krn in next,list do - local target=lookuphash[lookup] - if target then - target[unicode]=krn - else - lookuphash[lookup]={ [unicode]=krn } - end - end - end - local list=description.anchors - if list then - for typ,anchors in next,list do - if typ=="mark" or typ=="cexit" then - for name,anchor in next,anchors do - local lookups=anchor_to_lookup[name] - if lookups then - for lookup,_ in next,lookups do - local target=lookuphash[lookup] - if target then - target[unicode]=anchors - else - lookuphash[lookup]={ [unicode]=anchors } - end - end - end - end - end - end - end - end - end -end -local function split(replacement,original) - local result={} - for i=1,#replacement do - result[original[i]]=replacement[i] - end - return result -end -local valid={ - coverage={ chainsub=true,chainpos=true,contextsub=true }, - reversecoverage={ reversesub=true }, - glyphs={ chainsub=true,chainpos=true }, -} -local function prepare_contextchains(tfmdata) - local rawdata=tfmdata.shared.rawdata - local resources=rawdata.resources - local lookuphash=resources.lookuphash - local lookups=rawdata.lookups - if lookups then - for lookupname,lookupdata in next,rawdata.lookups do - local lookuptype=lookupdata.type - if lookuptype then - local rules=lookupdata.rules - if rules then - local format=lookupdata.format - local validformat=valid[format] - if not validformat then - report_prepare("unsupported format %a",format) - elseif not validformat[lookuptype] then - report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookupname) - else - local contexts=lookuphash[lookupname] - if not contexts then - contexts={} - lookuphash[lookupname]=contexts - end - local t,nt={},0 - for nofrules=1,#rules do - local rule=rules[nofrules] - local current=rule.current - local before=rule.before - local after=rule.after - local replacements=rule.replacements - local sequence={} - local nofsequences=0 - if before then - for n=1,#before do - nofsequences=nofsequences+1 - sequence[nofsequences]=before[n] - end - end - local start=nofsequences+1 - for n=1,#current do - nofsequences=nofsequences+1 - sequence[nofsequences]=current[n] - end - local stop=nofsequences - if after then - for n=1,#after do - nofsequences=nofsequences+1 - sequence[nofsequences]=after[n] - end - end - if sequence[1] then - nt=nt+1 - t[nt]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements } - for unic,_ in next,sequence[start] do - local cu=contexts[unic] - if not cu then - contexts[unic]=t - end - end - end - end - end - else - end - else - report_prepare("missing lookuptype for lookupname %a",lookupname) - end - end - end -end -local function featuresinitializer(tfmdata,value) - if true then - local rawdata=tfmdata.shared.rawdata - local properties=rawdata.properties - if not properties.initialized then - local starttime=trace_preparing and os.clock() - local resources=rawdata.resources - resources.lookuphash=resources.lookuphash or {} - prepare_contextchains(tfmdata) - prepare_lookups(tfmdata) - properties.initialized=true - if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname) - end - end - end -end -registerotffeature { - name="features", - description="features", - default=true, - initializers={ - position=1, - node=featuresinitializer, - }, - processors={ - node=featuresprocessor, - } -} -otf.handlers=handlers - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-otp']={ - version=1.001, - comment="companion to font-otf.lua (packing)", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -local next,type=next,type -local sort,concat=table.sort,table.concat -local sortedhash=table.sortedhash -local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end) -local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) -local report_otf=logs.reporter("fonts","otf loading") -fonts=fonts or {} -local handlers=fonts.handlers or {} -fonts.handlers=handlers -local otf=handlers.otf or {} -handlers.otf=otf -local enhancers=otf.enhancers or {} -otf.enhancers=enhancers -local glists=otf.glists or { "gsub","gpos" } -otf.glists=glists -local criterium=1 -local threshold=0 -local function tabstr_normal(t) - local s={} - local n=0 - for k,v in next,t do - n=n+1 - if type(v)=="table" then - s[n]=k..">"..tabstr_normal(v) - elseif v==true then - s[n]=k.."+" - elseif v then - s[n]=k.."="..v - else - s[n]=k.."-" - end - end - if n==0 then - return "" - elseif n==1 then - return s[1] - else - sort(s) - return concat(s,",") - end -end -local function tabstr_flat(t) - local s={} - local n=0 - for k,v in next,t do - n=n+1 - s[n]=k.."="..v - end - if n==0 then - return "" - elseif n==1 then - return s[1] - else - sort(s) - return concat(s,",") - end -end -local function tabstr_mixed(t) - local s={} - local n=#t - if n==0 then - return "" - elseif n==1 then - local k=t[1] - if k==true then - return "++" - elseif k==false then - return "--" - else - return tostring(k) - end - else - for i=1,n do - local k=t[i] - if k==true then - s[i]="++" - elseif k==false then - s[i]="--" - else - s[i]=k - end - end - return concat(s,",") - end -end -local function tabstr_boolean(t) - local s={} - local n=0 - for k,v in next,t do - n=n+1 - if v then - s[n]=k.."+" - else - s[n]=k.."-" - end - end - if n==0 then - return "" - elseif n==1 then - return s[1] - else - sort(s) - return concat(s,",") - end -end -local function packdata(data) - if data then - local h,t,c={},{},{} - local hh,tt,cc={},{},{} - local nt,ntt=0,0 - local function pack_normal(v) - local tag=tabstr_normal(v) - local ht=h[tag] - if ht then - c[ht]=c[ht]+1 - return ht - else - nt=nt+1 - t[nt]=v - h[tag]=nt - c[nt]=1 - return nt - end - end - local function pack_flat(v) - local tag=tabstr_flat(v) - local ht=h[tag] - if ht then - c[ht]=c[ht]+1 - return ht - else - nt=nt+1 - t[nt]=v - h[tag]=nt - c[nt]=1 - return nt - end - end - local function pack_boolean(v) - local tag=tabstr_boolean(v) - local ht=h[tag] - if ht then - c[ht]=c[ht]+1 - return ht - else - nt=nt+1 - t[nt]=v - h[tag]=nt - c[nt]=1 - return nt - end - end - local function pack_indexed(v) - local tag=concat(v," ") - local ht=h[tag] - if ht then - c[ht]=c[ht]+1 - return ht - else - nt=nt+1 - t[nt]=v - h[tag]=nt - c[nt]=1 - return nt - end - end - local function pack_mixed(v) - local tag=tabstr_mixed(v) - local ht=h[tag] - if ht then - c[ht]=c[ht]+1 - return ht - else - nt=nt+1 - t[nt]=v - h[tag]=nt - c[nt]=1 - return nt - end - end - local function pack_final(v) - if c[v]<=criterium then - return t[v] - else - local hv=hh[v] - if hv then - return hv - else - ntt=ntt+1 - tt[ntt]=t[v] - hh[v]=ntt - cc[ntt]=c[v] - return ntt - end - end - end - local function success(stage,pass) - if nt==0 then - if trace_loading or trace_packing then - report_otf("pack quality: nothing to pack") - end - return false - elseif nt>=threshold then - local one,two,rest=0,0,0 - if pass==1 then - for k,v in next,c do - if v==1 then - one=one+1 - elseif v==2 then - two=two+1 - else - rest=rest+1 - end - end - else - for k,v in next,cc do - if v>20 then - rest=rest+1 - elseif v>10 then - two=two+1 - else - one=one+1 - end - end - data.tables=tt - end - if trace_loading or trace_packing then - report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)",stage,pass,one+two+rest,one,two,rest,criterium) - end - return true - else - if trace_loading or trace_packing then - report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)",stage,pass,nt,threshold) - end - return false - end - end - local function packers(pass) - if pass==1 then - return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed - else - return pack_final,pack_final,pack_final,pack_final,pack_final - end - end - local resources=data.resources - local lookuptypes=resources.lookuptypes - for pass=1,2 do - if trace_packing then - report_otf("start packing: stage 1, pass %s",pass) - end - local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) - for unicode,description in next,data.descriptions do - local boundingbox=description.boundingbox - if boundingbox then - description.boundingbox=pack_indexed(boundingbox) - end - local slookups=description.slookups - if slookups then - for tag,slookup in next,slookups do - local what=lookuptypes[tag] - if what=="pair" then - local t=slookup[2] if t then slookup[2]=pack_indexed(t) end - local t=slookup[3] if t then slookup[3]=pack_indexed(t) end - elseif what~="substitution" then - slookups[tag]=pack_indexed(slookup) - end - end - end - local mlookups=description.mlookups - if mlookups then - for tag,mlookup in next,mlookups do - local what=lookuptypes[tag] - if what=="pair" then - for i=1,#mlookup do - local lookup=mlookup[i] - local t=lookup[2] if t then lookup[2]=pack_indexed(t) end - local t=lookup[3] if t then lookup[3]=pack_indexed(t) end - end - elseif what~="substitution" then - for i=1,#mlookup do - mlookup[i]=pack_indexed(mlookup[i]) - end - end - end - end - local kerns=description.kerns - if kerns then - for tag,kern in next,kerns do - kerns[tag]=pack_flat(kern) - end - end - local math=description.math - if math then - local kerns=math.kerns - if kerns then - for tag,kern in next,kerns do - kerns[tag]=pack_normal(kern) - end - end - end - local anchors=description.anchors - if anchors then - for what,anchor in next,anchors do - if what=="baselig" then - for _,a in next,anchor do - for k=1,#a do - a[k]=pack_indexed(a[k]) - end - end - else - for k,v in next,anchor do - anchor[k]=pack_indexed(v) - end - end - end - end - local altuni=description.altuni - if altuni then - for i=1,#altuni do - altuni[i]=pack_flat(altuni[i]) - end - end - end - local lookups=data.lookups - if lookups then - for _,lookup in next,lookups do - local rules=lookup.rules - if rules then - for i=1,#rules do - local rule=rules[i] - local r=rule.before if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end - local r=rule.after if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end - local r=rule.current if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end - local r=rule.replacements if r then rule.replacements=pack_flat (r) end - local r=rule.lookups if r then rule.lookups=pack_indexed(r) end - end - end - end - end - local anchor_to_lookup=resources.anchor_to_lookup - if anchor_to_lookup then - for anchor,lookup in next,anchor_to_lookup do - anchor_to_lookup[anchor]=pack_normal(lookup) - end - end - local lookup_to_anchor=resources.lookup_to_anchor - if lookup_to_anchor then - for lookup,anchor in next,lookup_to_anchor do - lookup_to_anchor[lookup]=pack_normal(anchor) - end - end - local sequences=resources.sequences - if sequences then - for feature,sequence in next,sequences do - local flags=sequence.flags - if flags then - sequence.flags=pack_normal(flags) - end - local subtables=sequence.subtables - if subtables then - sequence.subtables=pack_normal(subtables) - end - local features=sequence.features - if features then - for script,feature in next,features do - features[script]=pack_normal(feature) - end - end - end - end - local lookups=resources.lookups - if lookups then - for name,lookup in next,lookups do - local flags=lookup.flags - if flags then - lookup.flags=pack_normal(flags) - end - local subtables=lookup.subtables - if subtables then - lookup.subtables=pack_normal(subtables) - end - end - end - local features=resources.features - if features then - for _,what in next,glists do - local list=features[what] - if list then - for feature,spec in next,list do - list[feature]=pack_normal(spec) - end - end - end - end - if not success(1,pass) then - return - end - end - if nt>0 then - for pass=1,2 do - if trace_packing then - report_otf("start packing: stage 2, pass %s",pass) - end - local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) - for unicode,description in next,data.descriptions do - local kerns=description.kerns - if kerns then - description.kerns=pack_normal(kerns) - end - local math=description.math - if math then - local kerns=math.kerns - if kerns then - math.kerns=pack_normal(kerns) - end - end - local anchors=description.anchors - if anchors then - description.anchors=pack_normal(anchors) - end - local mlookups=description.mlookups - if mlookups then - for tag,mlookup in next,mlookups do - mlookups[tag]=pack_normal(mlookup) - end - end - local altuni=description.altuni - if altuni then - description.altuni=pack_normal(altuni) - end - end - local lookups=data.lookups - if lookups then - for _,lookup in next,lookups do - local rules=lookup.rules - if rules then - for i=1,#rules do - local rule=rules[i] - local r=rule.before if r then rule.before=pack_normal(r) end - local r=rule.after if r then rule.after=pack_normal(r) end - local r=rule.current if r then rule.current=pack_normal(r) end - end - end - end - end - local sequences=resources.sequences - if sequences then - for feature,sequence in next,sequences do - sequence.features=pack_normal(sequence.features) - end - end - if not success(2,pass) then - end - end - for pass=1,2 do - local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) - for unicode,description in next,data.descriptions do - local slookups=description.slookups - if slookups then - description.slookups=pack_normal(slookups) - end - local mlookups=description.mlookups - if mlookups then - description.mlookups=pack_normal(mlookups) - end - end - end - end - end -end -local unpacked_mt={ - __index=function(t,k) - t[k]=false - return k - end -} -local function unpackdata(data) - if data then - local tables=data.tables - if tables then - local resources=data.resources - local lookuptypes=resources.lookuptypes - local unpacked={} - setmetatable(unpacked,unpacked_mt) - for unicode,description in next,data.descriptions do - local tv=tables[description.boundingbox] - if tv then - description.boundingbox=tv - end - local slookups=description.slookups - if slookups then - local tv=tables[slookups] - if tv then - description.slookups=tv - slookups=unpacked[tv] - end - if slookups then - for tag,lookup in next,slookups do - local what=lookuptypes[tag] - if what=="pair" then - local tv=tables[lookup[2]] - if tv then - lookup[2]=tv - end - local tv=tables[lookup[3]] - if tv then - lookup[3]=tv - end - elseif what~="substitution" then - local tv=tables[lookup] - if tv then - slookups[tag]=tv - end - end - end - end - end - local mlookups=description.mlookups - if mlookups then - local tv=tables[mlookups] - if tv then - description.mlookups=tv - mlookups=unpacked[tv] - end - if mlookups then - for tag,list in next,mlookups do - local tv=tables[list] - if tv then - mlookups[tag]=tv - list=unpacked[tv] - end - if list then - local what=lookuptypes[tag] - if what=="pair" then - for i=1,#list do - local lookup=list[i] - local tv=tables[lookup[2]] - if tv then - lookup[2]=tv - end - local tv=tables[lookup[3]] - if tv then - lookup[3]=tv - end - end - elseif what~="substitution" then - for i=1,#list do - local tv=tables[list[i]] - if tv then - list[i]=tv - end - end - end - end - end - end - end - local kerns=description.kerns - if kerns then - local tm=tables[kerns] - if tm then - description.kerns=tm - kerns=unpacked[tm] - end - if kerns then - for k,kern in next,kerns do - local tv=tables[kern] - if tv then - kerns[k]=tv - end - end - end - end - local math=description.math - if math then - local kerns=math.kerns - if kerns then - local tm=tables[kerns] - if tm then - math.kerns=tm - kerns=unpacked[tm] - end - if kerns then - for k,kern in next,kerns do - local tv=tables[kern] - if tv then - kerns[k]=tv - end - end - end - end - end - local anchors=description.anchors - if anchors then - local ta=tables[anchors] - if ta then - description.anchors=ta - anchors=unpacked[ta] - end - if anchors then - for tag,anchor in next,anchors do - if tag=="baselig" then - for _,list in next,anchor do - for i=1,#list do - local tv=tables[list[i]] - if tv then - list[i]=tv - end - end - end - else - for a,data in next,anchor do - local tv=tables[data] - if tv then - anchor[a]=tv - end - end - end - end - end - end - local altuni=description.altuni - if altuni then - local altuni=tables[altuni] - if altuni then - description.altuni=altuni - for i=1,#altuni do - local tv=tables[altuni[i]] - if tv then - altuni[i]=tv - end - end - end - end - end - local lookups=data.lookups - if lookups then - for _,lookup in next,lookups do - local rules=lookup.rules - if rules then - for i=1,#rules do - local rule=rules[i] - local before=rule.before - if before then - local tv=tables[before] - if tv then - rule.before=tv - before=unpacked[tv] - end - if before then - for i=1,#before do - local tv=tables[before[i]] - if tv then - before[i]=tv - end - end - end - end - local after=rule.after - if after then - local tv=tables[after] - if tv then - rule.after=tv - after=unpacked[tv] - end - if after then - for i=1,#after do - local tv=tables[after[i]] - if tv then - after[i]=tv - end - end - end - end - local current=rule.current - if current then - local tv=tables[current] - if tv then - rule.current=tv - current=unpacked[tv] - end - if current then - for i=1,#current do - local tv=tables[current[i]] - if tv then - current[i]=tv - end - end - end - end - local replacements=rule.replacements - if replacements then - local tv=tables[replacements] - if tv then - rule.replacements=tv - end - end - local fore=rule.fore - if fore then - local tv=tables[fore] - if tv then - rule.fore=tv - end - end - local back=rule.back - if back then - local tv=tables[back] - if tv then - rule.back=tv - end - end - local names=rule.names - if names then - local tv=tables[names] - if tv then - rule.names=tv - end - end - local lookups=rule.lookups - if lookups then - local tv=tables[lookups] - if tv then - rule.lookups=tv - end - end - end - end - end - end - local anchor_to_lookup=resources.anchor_to_lookup - if anchor_to_lookup then - for anchor,lookup in next,anchor_to_lookup do - local tv=tables[lookup] - if tv then - anchor_to_lookup[anchor]=tv - end - end - end - local lookup_to_anchor=resources.lookup_to_anchor - if lookup_to_anchor then - for lookup,anchor in next,lookup_to_anchor do - local tv=tables[anchor] - if tv then - lookup_to_anchor[lookup]=tv - end - end - end - local ls=resources.sequences - if ls then - for _,feature in next,ls do - local flags=feature.flags - if flags then - local tv=tables[flags] - if tv then - feature.flags=tv - end - end - local subtables=feature.subtables - if subtables then - local tv=tables[subtables] - if tv then - feature.subtables=tv - end - end - local features=feature.features - if features then - local tv=tables[features] - if tv then - feature.features=tv - features=unpacked[tv] - end - if features then - for script,data in next,features do - local tv=tables[data] - if tv then - features[script]=tv - end - end - end - end - end - end - local lookups=resources.lookups - if lookups then - for _,lookup in next,lookups do - local flags=lookup.flags - if flags then - local tv=tables[flags] - if tv then - lookup.flags=tv - end - end - local subtables=lookup.subtables - if subtables then - local tv=tables[subtables] - if tv then - lookup.subtables=tv - end - end - end - end - local features=resources.features - if features then - for _,what in next,glists do - local feature=features[what] - if feature then - for tag,spec in next,feature do - local tv=tables[spec] - if tv then - feature[tag]=tv - end - end - end - end - end - data.tables=nil - end - end -end -if otf.enhancers.register then - otf.enhancers.register("pack",packdata) - otf.enhancers.register("unpack",unpackdata) -end -otf.enhancers.unpack=unpackdata - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luatex-fonts-lua']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end -local fonts=fonts -fonts.formats.lua="lua" -function fonts.readers.lua(specification) - local fullname=specification.filename or "" - if fullname=="" then - local forced=specification.forced or "" - if forced~="" then - fullname=specification.name.."."..forced - else - fullname=specification.name - end - end - local fullname=resolvers.findfile(fullname) or "" - if fullname~="" then - local loader=loadfile(fullname) - loader=loader and loader() - return loader and loader(specification) - end -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['font-def']={ - 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 format,gmatch,match,find,lower,gsub=string.format,string.gmatch,string.match,string.find,string.lower,string.gsub -local tostring,next=tostring,next -local lpegmatch=lpeg.match -local suffixonly,removesuffix=file.suffix,file.removesuffix -local allocate=utilities.storage.allocate -local trace_defining=false trackers .register("fonts.defining",function(v) trace_defining=v end) -local directive_embedall=false directives.register("fonts.embedall",function(v) directive_embedall=v end) -trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading") -trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*") -local report_defining=logs.reporter("fonts","defining") -local fonts=fonts -local fontdata=fonts.hashes.identifiers -local readers=fonts.readers -local definers=fonts.definers -local specifiers=fonts.specifiers -local constructors=fonts.constructors -local fontgoodies=fonts.goodies -readers.sequence=allocate { 'otf','ttf','afm','tfm','lua' } -local variants=allocate() -specifiers.variants=variants -definers.methods=definers.methods or {} -local internalized=allocate() -local lastdefined=nil -local loadedfonts=constructors.loadedfonts -local designsizes=constructors.designsizes -local resolvefile=fontgoodies and fontgoodies.filenames and fontgoodies.filenames.resolve or function(s) return s end -local splitter,splitspecifiers=nil,"" -local P,C,S,Cc=lpeg.P,lpeg.C,lpeg.S,lpeg.Cc -local left=P("(") -local right=P(")") -local colon=P(":") -local space=P(" ") -definers.defaultlookup="file" -local prefixpattern=P(false) -local function addspecifier(symbol) - splitspecifiers=splitspecifiers..symbol - local method=S(splitspecifiers) - local lookup=C(prefixpattern)*colon - local sub=left*C(P(1-left-right-method)^1)*right - local specification=C(method)*C(P(1)^1) - local name=C((1-sub-specification)^1) - splitter=P((lookup+Cc(""))*name*(sub+Cc(""))*(specification+Cc(""))) -end -local function addlookup(str,default) - prefixpattern=prefixpattern+P(str) -end -definers.addlookup=addlookup -addlookup("file") -addlookup("name") -addlookup("spec") -local function getspecification(str) - return lpegmatch(splitter,str or "") -end -definers.getspecification=getspecification -function definers.registersplit(symbol,action,verbosename) - addspecifier(symbol) - variants[symbol]=action - if verbosename then - variants[verbosename]=action - end -end -local function makespecification(specification,lookup,name,sub,method,detail,size) - size=size or 655360 - if not lookup or lookup=="" then - lookup=definers.defaultlookup - end - if trace_defining then - report_defining("specification %a, lookup %a, name %a, sub %a, method %a, detail %a", - specification,lookup,name,sub,method,detail) - end - local t={ - lookup=lookup, - specification=specification, - size=size, - name=name, - sub=sub, - method=method, - detail=detail, - resolved="", - forced="", - features={}, - } - return t -end -definers.makespecification=makespecification -function definers.analyze(specification,size) - local lookup,name,sub,method,detail=getspecification(specification or "") - return makespecification(specification,lookup,name,sub,method,detail,size) -end -definers.resolvers=definers.resolvers or {} -local resolvers=definers.resolvers -function resolvers.file(specification) - local name=resolvefile(specification.name) - local suffix=lower(suffixonly(name)) - if fonts.formats[suffix] then - specification.forced=suffix - specification.forcedname=name - specification.name=removesuffix(name) - else - specification.name=name - end -end -function resolvers.name(specification) - local resolve=fonts.names.resolve - if resolve then - local resolved,sub=resolve(specification.name,specification.sub,specification) - if resolved then - specification.resolved=resolved - specification.sub=sub - local suffix=lower(suffixonly(resolved)) - if fonts.formats[suffix] then - specification.forced=suffix - specification.forcedname=resolved - specification.name=removesuffix(resolved) - else - specification.name=resolved - end - end - else - resolvers.file(specification) - end -end -function resolvers.spec(specification) - local resolvespec=fonts.names.resolvespec - if resolvespec then - local resolved,sub=resolvespec(specification.name,specification.sub,specification) - if resolved then - specification.resolved=resolved - specification.sub=sub - specification.forced=lower(suffixonly(resolved)) - specification.forcedname=resolved - specification.name=removesuffix(resolved) - end - else - resolvers.name(specification) - end -end -function definers.resolve(specification) - if not specification.resolved or specification.resolved=="" then - local r=resolvers[specification.lookup] - if r then - r(specification) - end - end - if specification.forced=="" then - specification.forced=nil - specification.forcedname=nil - end - specification.hash=lower(specification.name..' @ '..constructors.hashfeatures(specification)) - if specification.sub and specification.sub~="" then - specification.hash=specification.sub..' @ '..specification.hash - end - return specification -end -function definers.applypostprocessors(tfmdata) - local postprocessors=tfmdata.postprocessors - if postprocessors then - local properties=tfmdata.properties - for i=1,#postprocessors do - local extrahash=postprocessors[i](tfmdata) - if type(extrahash)=="string" and extrahash~="" then - extrahash=gsub(lower(extrahash),"[^a-z]","-") - properties.fullname=format("%s-%s",properties.fullname,extrahash) - end - end - end - return tfmdata -end -local function checkembedding(tfmdata) - local properties=tfmdata.properties - local embedding - if directive_embedall then - embedding="full" - elseif properties and properties.filename and constructors.dontembed[properties.filename] then - embedding="no" - else - embedding="subset" - end - if properties then - properties.embedding=embedding - else - tfmdata.properties={ embedding=embedding } - end - tfmdata.embedding=embedding -end -function definers.loadfont(specification) - local hash=constructors.hashinstance(specification) - local tfmdata=loadedfonts[hash] - if not tfmdata then - local forced=specification.forced or "" - if forced~="" then - local reader=readers[lower(forced)] - tfmdata=reader and reader(specification) - if not tfmdata then - report_defining("forced type %a of %a not found",forced,specification.name) - end - else - local sequence=readers.sequence - for s=1,#sequence do - local reader=sequence[s] - if readers[reader] then - if trace_defining then - report_defining("trying (reader sequence driven) type %a for %a with file %a",reader,specification.name,specification.filename) - end - tfmdata=readers[reader](specification) - if tfmdata then - break - else - specification.filename=nil - end - end - end - end - if tfmdata then - tfmdata=definers.applypostprocessors(tfmdata) - checkembedding(tfmdata) - loadedfonts[hash]=tfmdata - designsizes[specification.hash]=tfmdata.parameters.designsize - end - end - if not tfmdata then - report_defining("font with asked name %a is not found using lookup %a",specification.name,specification.lookup) - end - return tfmdata -end -function constructors.checkvirtualids() -end -function constructors.readanddefine(name,size) - local specification=definers.analyze(name,size) - local method=specification.method - if method and variants[method] then - specification=variants[method](specification) - end - specification=definers.resolve(specification) - local hash=constructors.hashinstance(specification) - local id=definers.registered(hash) - if not id then - local tfmdata=definers.loadfont(specification) - if tfmdata then - tfmdata.properties.hash=hash - constructors.checkvirtualids(tfmdata) - id=font.define(tfmdata) - definers.register(tfmdata,id) - else - id=0 - end - end - return fontdata[id],id -end -function definers.current() - return lastdefined -end -function definers.registered(hash) - local id=internalized[hash] - return id,id and fontdata[id] -end -function definers.register(tfmdata,id) - if tfmdata and id then - local hash=tfmdata.properties.hash - if not hash then - report_defining("registering font, id %a, name %a, invalid hash",id,tfmdata.properties.filename or "?") - elseif not internalized[hash] then - internalized[hash]=id - if trace_defining then - report_defining("registering font, id %s, hash %a",id,hash) - end - fontdata[id]=tfmdata - end - end -end -function definers.read(specification,size,id) - statistics.starttiming(fonts) - if type(specification)=="string" then - specification=definers.analyze(specification,size) - end - local method=specification.method - if method and variants[method] then - specification=variants[method](specification) - end - specification=definers.resolve(specification) - local hash=constructors.hashinstance(specification) - local tfmdata=definers.registered(hash) - if tfmdata then - if trace_defining then - report_defining("already hashed: %s",hash) - end - else - tfmdata=definers.loadfont(specification) - if tfmdata then - if trace_defining then - report_defining("loaded and hashed: %s",hash) - end - tfmdata.properties.hash=hash - if id then - definers.register(tfmdata,id) - end - else - if trace_defining then - report_defining("not loaded and hashed: %s",hash) - end - end - end - lastdefined=tfmdata or id - if not tfmdata then - report_defining("unknown font %a, loading aborted",specification.name) - elseif trace_defining and type(tfmdata)=="table" then - local properties=tfmdata.properties or {} - local parameters=tfmdata.parameters or {} - report_defining("using %s font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a", - properties.format,id,properties.name,parameters.size,properties.encodingbytes, - properties.encodingname,properties.fullname,file.basename(properties.filename)) - end - statistics.stoptiming(fonts) - return tfmdata -end -function font.getfont(id) - return fontdata[id] -end -callbacks.register('define_font',definers.read,"definition of fonts (tfmdata preparation)") - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luatex-font-def']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end -local fonts=fonts -fonts.constructors.namemode="specification" -function fonts.definers.getspecification(str) - return "",str,"",":",str -end -local list={} -local function issome () list.lookup='name' end -local function isfile () list.lookup='file' end -local function isname () list.lookup='name' end -local function thename(s) list.name=s end -local function issub (v) list.sub=v end -local function iscrap (s) list.crap=string.lower(s) end -local function iskey (k,v) list[k]=v end -local function istrue (s) list[s]=true end -local function isfalse(s) list[s]=false end -local P,S,R,C=lpeg.P,lpeg.S,lpeg.R,lpeg.C -local spaces=P(" ")^0 -local namespec=(1-S("/:("))^0 -local crapspec=spaces*P("/")*(((1-P(":"))^0)/iscrap)*spaces -local filename_1=P("file:")/isfile*(namespec/thename) -local filename_2=P("[")*P(true)/isname*(((1-P("]"))^0)/thename)*P("]") -local fontname_1=P("name:")/isname*(namespec/thename) -local fontname_2=P(true)/issome*(namespec/thename) -local sometext=(R("az","AZ","09")+S("+-."))^1 -local truevalue=P("+")*spaces*(sometext/istrue) -local falsevalue=P("-")*spaces*(sometext/isfalse) -local keyvalue=(C(sometext)*spaces*P("=")*spaces*C(sometext))/iskey -local somevalue=sometext/istrue -local subvalue=P("(")*(C(P(1-S("()"))^1)/issub)*P(")") -local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces -local options=P(":")*spaces*(P(";")^0*option)^0 -local pattern=(filename_1+filename_2+fontname_1+fontname_2)*subvalue^0*crapspec^0*options^0 -local function colonized(specification) - list={} - lpeg.match(pattern,specification.specification) - list.crap=nil - if list.name then - specification.name=list.name - list.name=nil - end - if list.lookup then - specification.lookup=list.lookup - list.lookup=nil - end - if list.sub then - specification.sub=list.sub - list.sub=nil - end - specification.features.normal=fonts.handlers.otf.features.normalize(list) - return specification -end -fonts.definers.registersplit(":",colonized,"cryptic") -fonts.definers.registersplit("",colonized,"more cryptic") -function fonts.definers.applypostprocessors(tfmdata) - local postprocessors=tfmdata.postprocessors - if postprocessors then - for i=1,#postprocessors do - local extrahash=postprocessors[i](tfmdata) - if type(extrahash)=="string" and extrahash~="" then - extrahash=string.gsub(lower(extrahash),"[^a-z]","-") - tfmdata.properties.fullname=format("%s-%s",tfmdata.properties.fullname,extrahash) - end - end - end - return tfmdata -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luatex-fonts-ext']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end -local fonts=fonts -local otffeatures=fonts.constructors.newfeatures("otf") -local function initializeitlc(tfmdata,value) - if value then - local parameters=tfmdata.parameters - local italicangle=parameters.italicangle - if italicangle and italicangle~=0 then - local properties=tfmdata.properties - local factor=tonumber(value) or 1 - properties.hasitalics=true - properties.autoitalicamount=factor*(parameters.uwidth or 40)/2 - end - end -end -otffeatures.register { - name="itlc", - description="italic correction", - initializers={ - base=initializeitlc, - node=initializeitlc, - } -} -local function initializeslant(tfmdata,value) - value=tonumber(value) - if not value then - value=0 - elseif value>1 then - value=1 - elseif value<-1 then - value=-1 - end - tfmdata.parameters.slantfactor=value -end -otffeatures.register { - name="slant", - description="slant glyphs", - initializers={ - base=initializeslant, - node=initializeslant, - } -} -local function initializeextend(tfmdata,value) - value=tonumber(value) - if not value then - value=0 - elseif value>10 then - value=10 - elseif value<-10 then - value=-10 - end - tfmdata.parameters.extendfactor=value -end -otffeatures.register { - name="extend", - description="scale glyphs horizontally", - initializers={ - base=initializeextend, - node=initializeextend, - } -} -fonts.protrusions=fonts.protrusions or {} -fonts.protrusions.setups=fonts.protrusions.setups or {} -local setups=fonts.protrusions.setups -local function initializeprotrusion(tfmdata,value) - if value then - local setup=setups[value] - if setup then - local factor,left,right=setup.factor or 1,setup.left or 1,setup.right or 1 - local emwidth=tfmdata.parameters.quad - tfmdata.parameters.protrusion={ - auto=true, - } - for i,chr in next,tfmdata.characters do - local v,pl,pr=setup[i],nil,nil - if v then - pl,pr=v[1],v[2] - end - if pl and pl~=0 then chr.left_protruding=left*pl*factor end - if pr and pr~=0 then chr.right_protruding=right*pr*factor end - end - end - end -end -otffeatures.register { - name="protrusion", - description="shift characters into the left and or right margin", - initializers={ - base=initializeprotrusion, - node=initializeprotrusion, - } -} -fonts.expansions=fonts.expansions or {} -fonts.expansions.setups=fonts.expansions.setups or {} -local setups=fonts.expansions.setups -local function initializeexpansion(tfmdata,value) - if value then - local setup=setups[value] - if setup then - local factor=setup.factor or 1 - tfmdata.parameters.expansion={ - stretch=10*(setup.stretch or 0), - shrink=10*(setup.shrink or 0), - step=10*(setup.step or 0), - auto=true, - } - for i,chr in next,tfmdata.characters do - local v=setup[i] - if v and v~=0 then - chr.expansion_factor=v*factor - else - chr.expansion_factor=factor - end - end - end - end -end -otffeatures.register { - name="expansion", - description="apply hz optimization", - initializers={ - base=initializeexpansion, - node=initializeexpansion, - } -} -function fonts.loggers.onetimemessage() end -local byte=string.byte -fonts.expansions.setups['default']={ - stretch=2,shrink=2,step=.5,factor=1, - [byte('A')]=0.5,[byte('B')]=0.7,[byte('C')]=0.7,[byte('D')]=0.5,[byte('E')]=0.7, - [byte('F')]=0.7,[byte('G')]=0.5,[byte('H')]=0.7,[byte('K')]=0.7,[byte('M')]=0.7, - [byte('N')]=0.7,[byte('O')]=0.5,[byte('P')]=0.7,[byte('Q')]=0.5,[byte('R')]=0.7, - [byte('S')]=0.7,[byte('U')]=0.7,[byte('W')]=0.7,[byte('Z')]=0.7, - [byte('a')]=0.7,[byte('b')]=0.7,[byte('c')]=0.7,[byte('d')]=0.7,[byte('e')]=0.7, - [byte('g')]=0.7,[byte('h')]=0.7,[byte('k')]=0.7,[byte('m')]=0.7,[byte('n')]=0.7, - [byte('o')]=0.7,[byte('p')]=0.7,[byte('q')]=0.7,[byte('s')]=0.7,[byte('u')]=0.7, - [byte('w')]=0.7,[byte('z')]=0.7, - [byte('2')]=0.7,[byte('3')]=0.7,[byte('6')]=0.7,[byte('8')]=0.7,[byte('9')]=0.7, -} -fonts.protrusions.setups['default']={ - factor=1,left=1,right=1, - [0x002C]={ 0,1 }, - [0x002E]={ 0,1 }, - [0x003A]={ 0,1 }, - [0x003B]={ 0,1 }, - [0x002D]={ 0,1 }, - [0x2013]={ 0,0.50 }, - [0x2014]={ 0,0.33 }, - [0x3001]={ 0,1 }, - [0x3002]={ 0,1 }, - [0x060C]={ 0,1 }, - [0x061B]={ 0,1 }, - [0x06D4]={ 0,1 }, -} -fonts.handlers.otf.features.normalize=function(t) - if t.rand then - t.rand="random" - end - return t -end -function fonts.helpers.nametoslot(name) - local t=type(name) - if t=="string" then - local tfmdata=fonts.hashes.identifiers[currentfont()] - local shared=tfmdata and tfmdata.shared - local fntdata=shared and shared.rawdata - return fntdata and fntdata.resources.unicodes[name] - elseif t=="number" then - return n - end -end -fonts.encodings=fonts.encodings or {} -local reencodings={} -fonts.encodings.reencodings=reencodings -local function specialreencode(tfmdata,value) - local encoding=value and reencodings[value] - if encoding then - local temp={} - local char=tfmdata.characters - for k,v in next,encoding do - temp[k]=char[v] - end - for k,v in next,temp do - char[k]=temp[k] - end - return string.format("reencoded:%s",value) - end -end -local function reencode(tfmdata,value) - tfmdata.postprocessors=tfmdata.postprocessors or {} - table.insert(tfmdata.postprocessors, - function(tfmdata) - return specialreencode(tfmdata,value) - end - ) -end -otffeatures.register { - name="reencode", - description="reencode characters", - manipulators={ - base=reencode, - node=reencode, - } -} - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules={} end modules ['luatex-fonts-cbk']={ - version=1.001, - comment="companion to luatex-*.tex", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files" -} -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end -local fonts=fonts -local nodes=nodes -local traverse_id=node.traverse_id -local glyph_code=nodes.nodecodes.glyph -function nodes.handlers.characters(head) - local fontdata=fonts.hashes.identifiers - if fontdata then - local usedfonts,done,prevfont={},false,nil - for n in traverse_id(glyph_code,head) do - local font=n.font - if font~=prevfont then - prevfont=font - local used=usedfonts[font] - if not used then - local tfmdata=fontdata[font] - if tfmdata then - local shared=tfmdata.shared - if shared then - local processors=shared.processes - if processors and #processors>0 then - usedfonts[font]=processors - done=true - end - end - end - end - end - end - if done then - for font,processors in next,usedfonts do - for i=1,#processors do - local h,d=processors[i](head,font,0) - head,done=h or head,done or d - end - end - end - return head,true - else - return head,false - end -end -function nodes.simple_font_handler(head) - head=nodes.handlers.characters(head) - nodes.injections.handler(head) - nodes.handlers.protectglyphs(head) - head=node.ligaturing(head) - head=node.kerning(head) - return head -end - -end -- closure diff --git a/luaotfload-fonts-cbk.lua b/luaotfload-fonts-cbk.lua deleted file mode 100644 index 9db94f6..0000000 --- a/luaotfload-fonts-cbk.lua +++ /dev/null @@ -1,68 +0,0 @@ -if not modules then modules = { } end modules ['luatex-fonts-cbk'] = { - version = 1.001, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end - -local fonts = fonts -local nodes = nodes - --- Fonts: (might move to node-gef.lua) - -local traverse_id = node.traverse_id -local glyph_code = nodes.nodecodes.glyph - -function nodes.handlers.characters(head) - local fontdata = fonts.hashes.identifiers - if fontdata then - local usedfonts, done, prevfont = { }, false, nil - for n in traverse_id(glyph_code,head) do - local font = n.font - if font ~= prevfont then - prevfont = font - local used = usedfonts[font] - if not used then - local tfmdata = fontdata[font] -- - if tfmdata then - local shared = tfmdata.shared -- we need to check shared, only when same features - if shared then - local processors = shared.processes - if processors and #processors > 0 then - usedfonts[font] = processors - done = true - end - end - end - end - end - end - if done then - for font, processors in next, usedfonts do - for i=1,#processors do - local h, d = processors[i](head,font,0) - head, done = h or head, done or d - end - end - end - return head, true - else - return head, false - end -end - -function nodes.simple_font_handler(head) --- lang.hyphenate(head) - head = nodes.handlers.characters(head) - nodes.injections.handler(head) - nodes.handlers.protectglyphs(head) - head = node.ligaturing(head) - head = node.kerning(head) - return head -end diff --git a/luaotfload-fonts-def.lua b/luaotfload-fonts-def.lua deleted file mode 100644 index 0c2f0db..0000000 --- a/luaotfload-fonts-def.lua +++ /dev/null @@ -1,97 +0,0 @@ -if not modules then modules = { } end modules ['luatex-font-def'] = { - version = 1.001, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end - -local fonts = fonts - --- A bit of tuning for definitions. - -fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload - --- tricky: we sort of bypass the parser and directly feed all into --- the sub parser - -function fonts.definers.getspecification(str) - return "", str, "", ":", str -end - --- the generic name parser (different from context!) - -local list = { } - -local function issome () list.lookup = 'name' end -- xetex mode prefers name (not in context!) -local function isfile () list.lookup = 'file' end -local function isname () list.lookup = 'name' end -local function thename(s) list.name = s end -local function issub (v) list.sub = v end -local function iscrap (s) list.crap = string.lower(s) end -local function iskey (k,v) list[k] = v end -local function istrue (s) list[s] = true end -local function isfalse(s) list[s] = false end - -local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C - -local spaces = P(" ")^0 -local namespec = (1-S("/:("))^0 -- was: (1-S("/: ("))^0 -local crapspec = spaces * P("/") * (((1-P(":"))^0)/iscrap) * spaces -local filename_1 = P("file:")/isfile * (namespec/thename) -local filename_2 = P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]") -local fontname_1 = P("name:")/isname * (namespec/thename) -local fontname_2 = P(true)/issome * (namespec/thename) -local sometext = (R("az","AZ","09") + S("+-."))^1 -local truevalue = P("+") * spaces * (sometext/istrue) -local falsevalue = P("-") * spaces * (sometext/isfalse) -local keyvalue = (C(sometext) * spaces * P("=") * spaces * C(sometext))/iskey -local somevalue = sometext/istrue -local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim -local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces -local options = P(":") * spaces * (P(";")^0 * option)^0 - -local pattern = (filename_1 + filename_2 + fontname_1 + fontname_2) * subvalue^0 * crapspec^0 * options^0 - -local function colonized(specification) -- xetex mode - list = { } - lpeg.match(pattern,specification.specification) - list.crap = nil -- style not supported, maybe some day - if list.name then - specification.name = list.name - list.name = nil - end - if list.lookup then - specification.lookup = list.lookup - list.lookup = nil - end - if list.sub then - specification.sub = list.sub - list.sub = nil - end - specification.features.normal = fonts.handlers.otf.features.normalize(list) - return specification -end - -fonts.definers.registersplit(":",colonized,"cryptic") -fonts.definers.registersplit("", colonized,"more cryptic") -- catches \font\text=[names] - -function fonts.definers.applypostprocessors(tfmdata) - local postprocessors = tfmdata.postprocessors - if postprocessors then - for i=1,#postprocessors do - local extrahash = postprocessors[i](tfmdata) -- after scaling etc - if type(extrahash) == "string" and extrahash ~= "" then - -- e.g. a reencoding needs this - extrahash = string.gsub(lower(extrahash),"[^a-z]","-") - tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) - end - end - end - return tfmdata -end diff --git a/luaotfload-fonts-enc.lua b/luaotfload-fonts-enc.lua deleted file mode 100644 index e20c3a0..0000000 --- a/luaotfload-fonts-enc.lua +++ /dev/null @@ -1,28 +0,0 @@ -if not modules then modules = { } end modules ['luatex-font-enc'] = { - version = 1.001, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end - -local fonts = fonts -fonts.encodings = { } -fonts.encodings.agl = { } - -setmetatable(fonts.encodings.agl, { __index = function(t,k) - if k == "unicodes" then - texio.write(" ") - local unicodes = dofile(resolvers.findfile("font-age.lua")) - fonts.encodings.agl = { unicodes = unicodes } - return unicodes - else - return nil - end -end }) - diff --git a/luaotfload-fonts-ext.lua b/luaotfload-fonts-ext.lua deleted file mode 100644 index b60d045..0000000 --- a/luaotfload-fonts-ext.lua +++ /dev/null @@ -1,272 +0,0 @@ -if not modules then modules = { } end modules ['luatex-fonts-ext'] = { - version = 1.001, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end - -local fonts = fonts -local otffeatures = fonts.constructors.newfeatures("otf") - --- A few generic extensions. - -local function initializeitlc(tfmdata,value) - if value then - -- the magic 40 and it formula come from Dohyun Kim but we might need another guess - local parameters = tfmdata.parameters - local italicangle = parameters.italicangle - if italicangle and italicangle ~= 0 then - local properties = tfmdata.properties - local factor = tonumber(value) or 1 - properties.hasitalics = true - properties.autoitalicamount = factor * (parameters.uwidth or 40)/2 - end - end -end - -otffeatures.register { - name = "itlc", - description = "italic correction", - initializers = { - base = initializeitlc, - node = initializeitlc, - } -} - --- slant and extend - -local function initializeslant(tfmdata,value) - value = tonumber(value) - if not value then - value = 0 - elseif value > 1 then - value = 1 - elseif value < -1 then - value = -1 - end - tfmdata.parameters.slantfactor = value -end - -otffeatures.register { - name = "slant", - description = "slant glyphs", - initializers = { - base = initializeslant, - node = initializeslant, - } -} - -local function initializeextend(tfmdata,value) - value = tonumber(value) - if not value then - value = 0 - elseif value > 10 then - value = 10 - elseif value < -10 then - value = -10 - end - tfmdata.parameters.extendfactor = value -end - -otffeatures.register { - name = "extend", - description = "scale glyphs horizontally", - initializers = { - base = initializeextend, - node = initializeextend, - } -} - --- expansion and protrusion - -fonts.protrusions = fonts.protrusions or { } -fonts.protrusions.setups = fonts.protrusions.setups or { } - -local setups = fonts.protrusions.setups - -local function initializeprotrusion(tfmdata,value) - if value then - local setup = setups[value] - if setup then - local factor, left, right = setup.factor or 1, setup.left or 1, setup.right or 1 - local emwidth = tfmdata.parameters.quad - tfmdata.parameters.protrusion = { - auto = true, - } - for i, chr in next, tfmdata.characters do - local v, pl, pr = setup[i], nil, nil - if v then - pl, pr = v[1], v[2] - end - if pl and pl ~= 0 then chr.left_protruding = left *pl*factor end - if pr and pr ~= 0 then chr.right_protruding = right*pr*factor end - end - end - end -end - -otffeatures.register { - name = "protrusion", - description = "shift characters into the left and or right margin", - initializers = { - base = initializeprotrusion, - node = initializeprotrusion, - } -} - -fonts.expansions = fonts.expansions or { } -fonts.expansions.setups = fonts.expansions.setups or { } - -local setups = fonts.expansions.setups - -local function initializeexpansion(tfmdata,value) - if value then - local setup = setups[value] - if setup then - local factor = setup.factor or 1 - tfmdata.parameters.expansion = { - stretch = 10 * (setup.stretch or 0), - shrink = 10 * (setup.shrink or 0), - step = 10 * (setup.step or 0), - auto = true, - } - for i, chr in next, tfmdata.characters do - local v = setup[i] - if v and v ~= 0 then - chr.expansion_factor = v*factor - else -- can be option - chr.expansion_factor = factor - end - end - end - end -end - -otffeatures.register { - name = "expansion", - description = "apply hz optimization", - initializers = { - base = initializeexpansion, - node = initializeexpansion, - } -} - --- left over - -function fonts.loggers.onetimemessage() end - --- example vectors - -local byte = string.byte - -fonts.expansions.setups['default'] = { - - stretch = 2, shrink = 2, step = .5, factor = 1, - - [byte('A')] = 0.5, [byte('B')] = 0.7, [byte('C')] = 0.7, [byte('D')] = 0.5, [byte('E')] = 0.7, - [byte('F')] = 0.7, [byte('G')] = 0.5, [byte('H')] = 0.7, [byte('K')] = 0.7, [byte('M')] = 0.7, - [byte('N')] = 0.7, [byte('O')] = 0.5, [byte('P')] = 0.7, [byte('Q')] = 0.5, [byte('R')] = 0.7, - [byte('S')] = 0.7, [byte('U')] = 0.7, [byte('W')] = 0.7, [byte('Z')] = 0.7, - [byte('a')] = 0.7, [byte('b')] = 0.7, [byte('c')] = 0.7, [byte('d')] = 0.7, [byte('e')] = 0.7, - [byte('g')] = 0.7, [byte('h')] = 0.7, [byte('k')] = 0.7, [byte('m')] = 0.7, [byte('n')] = 0.7, - [byte('o')] = 0.7, [byte('p')] = 0.7, [byte('q')] = 0.7, [byte('s')] = 0.7, [byte('u')] = 0.7, - [byte('w')] = 0.7, [byte('z')] = 0.7, - [byte('2')] = 0.7, [byte('3')] = 0.7, [byte('6')] = 0.7, [byte('8')] = 0.7, [byte('9')] = 0.7, -} - -fonts.protrusions.setups['default'] = { - - factor = 1, left = 1, right = 1, - - [0x002C] = { 0, 1 }, -- comma - [0x002E] = { 0, 1 }, -- period - [0x003A] = { 0, 1 }, -- colon - [0x003B] = { 0, 1 }, -- semicolon - [0x002D] = { 0, 1 }, -- hyphen - [0x2013] = { 0, 0.50 }, -- endash - [0x2014] = { 0, 0.33 }, -- emdash - [0x3001] = { 0, 1 }, -- ideographic comma 、 - [0x3002] = { 0, 1 }, -- ideographic full stop 。 - [0x060C] = { 0, 1 }, -- arabic comma ، - [0x061B] = { 0, 1 }, -- arabic semicolon ؛ - [0x06D4] = { 0, 1 }, -- arabic full stop ۔ - -} - --- normalizer - -fonts.handlers.otf.features.normalize = function(t) - if t.rand then - t.rand = "random" - end - return t -end - --- bonus - -function fonts.helpers.nametoslot(name) - local t = type(name) - if t == "string" then - local tfmdata = fonts.hashes.identifiers[currentfont()] - local shared = tfmdata and tfmdata.shared - local fntdata = shared and shared.rawdata - return fntdata and fntdata.resources.unicodes[name] - elseif t == "number" then - return n - end -end - --- \font\test=file:somefont:reencode=mymessup --- --- fonts.encodings.reencodings.mymessup = { --- [109] = 110, -- m --- [110] = 109, -- n --- } - -fonts.encodings = fonts.encodings or { } -local reencodings = { } -fonts.encodings.reencodings = reencodings - -local function specialreencode(tfmdata,value) - -- we forget about kerns as we assume symbols and we - -- could issue a message if ther are kerns but it's - -- a hack anyway so we odn't care too much here - local encoding = value and reencodings[value] - if encoding then - local temp = { } - local char = tfmdata.characters - for k, v in next, encoding do - temp[k] = char[v] - end - for k, v in next, temp do - char[k] = temp[k] - end - -- if we use the font otherwise luatex gets confused so - -- we return an additional hash component for fullname - return string.format("reencoded:%s",value) - end -end - -local function reencode(tfmdata,value) - tfmdata.postprocessors = tfmdata.postprocessors or { } - table.insert(tfmdata.postprocessors, - function(tfmdata) - return specialreencode(tfmdata,value) - end - ) -end - -otffeatures.register { - name = "reencode", - description = "reencode characters", - manipulators = { - base = reencode, - node = reencode, - } -} diff --git a/luaotfload-fonts-inj.lua b/luaotfload-fonts-inj.lua deleted file mode 100644 index ae48150..0000000 --- a/luaotfload-fonts-inj.lua +++ /dev/null @@ -1,526 +0,0 @@ -if not modules then modules = { } end modules ['node-inj'] = { - version = 1.001, - comment = "companion to node-ini.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files", -} - --- This is very experimental (this will change when we have luatex > .50 and --- a few pending thingies are available. Also, Idris needs to make a few more --- test fonts. Btw, future versions of luatex will have extended glyph properties --- that can be of help. Some optimizations can go away when we have faster machines. - --- todo: make a special one for context - -local next = next -local utfchar = utf.char - -local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) - -local report_injections = logs.reporter("nodes","injections") - -local attributes, nodes, node = attributes, nodes, node - -fonts = fonts -local fontdata = fonts.hashes.identifiers - -nodes.injections = nodes.injections or { } -local injections = nodes.injections - -local nodecodes = nodes.nodecodes -local glyph_code = nodecodes.glyph -local kern_code = nodecodes.kern -local nodepool = nodes.pool -local newkern = nodepool.kern - -local traverse_id = node.traverse_id -local insert_node_before = node.insert_before -local insert_node_after = node.insert_after - -local a_kernpair = attributes.private('kernpair') -local a_ligacomp = attributes.private('ligacomp') -local a_markbase = attributes.private('markbase') -local a_markmark = attributes.private('markmark') -local a_markdone = attributes.private('markdone') -local a_cursbase = attributes.private('cursbase') -local a_curscurs = attributes.private('curscurs') -local a_cursdone = attributes.private('cursdone') - --- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as --- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner --- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure --- that this code is not 100% okay but examples are needed to figure things out. - -function injections.installnewkern(nk) - newkern = nk or newkern -end - -local cursives = { } -local marks = { } -local kerns = { } - --- Currently we do gpos/kern in a bit inofficial way but when we have the extra fields in --- glyphnodes to manipulate ht/dp/wd explicitly I will provide an alternative; also, we --- can share tables. - --- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs --- checking with husayni (volt and fontforge). - -function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) - local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) - local ws, wn = tfmstart.width, tfmnext.width - local bound = #cursives + 1 - start[a_cursbase] = bound - nxt[a_curscurs] = bound - cursives[bound] = { rlmode, dx, dy, ws, wn } - return dx, dy, bound -end - -function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) - local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] - -- dy = y - h - if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then - local bound = current[a_kernpair] - if bound then - local kb = kerns[bound] - -- inefficient but singles have less, but weird anyway, needs checking - kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h - else - bound = #kerns + 1 - current[a_kernpair] = bound - kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width } - end - return x, y, w, h, bound - end - return x, y, w, h -- no bound -end - -function injections.setkern(current,factor,rlmode,x,tfmchr) - local dx = factor*x - if dx ~= 0 then - local bound = #kerns + 1 - current[a_kernpair] = bound - kerns[bound] = { rlmode, dx } - return dx, bound - else - return 0, 0 - end -end - -function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) -- ba=baseanchor, ma=markanchor - local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) -- the index argument is no longer used but when this - local bound = base[a_markbase] -- fails again we should pass it - local index = 1 - if bound then - local mb = marks[bound] - if mb then - -- if not index then index = #mb + 1 end - index = #mb + 1 - mb[index] = { dx, dy, rlmode } - start[a_markmark] = bound - start[a_markdone] = index - return dx, dy, bound - else - report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound) - end - end --- index = index or 1 - index = index or 1 - bound = #marks + 1 - base[a_markbase] = bound - start[a_markmark] = bound - start[a_markdone] = index - marks[bound] = { [index] = { dx, dy, rlmode, baseismark } } - return dx, dy, bound -end - -local function dir(n) - return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" -end - -local function trace(head) - report_injections("begin run") - for n in traverse_id(glyph_code,head) do - if n.subtype < 256 then - local kp = n[a_kernpair] - local mb = n[a_markbase] - local mm = n[a_markmark] - local md = n[a_markdone] - local cb = n[a_cursbase] - local cc = n[a_curscurs] - local char = n.char - report_injections("font %s, char %U, glyph %c",n.font,char,char) - if kp then - local k = kerns[kp] - if k[3] then - report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5]) - else - report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2]) - end - end - if mb then - report_injections(" markbase: bound %a",mb) - end - if mm then - local m = marks[mm] - if mb then - local m = m[mb] - if m then - report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2]) - else - report_injections(" markmark: bound %a, missing index",mm) - end - else - m = m[1] - report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2]) - end - end - if cb then - report_injections(" cursbase: bound %a",cb) - end - if cc then - local c = cursives[cc] - report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3]) - end - end - end - report_injections("end run") -end - --- todo: reuse tables (i.e. no collection), but will be extra fields anyway --- todo: check for attribute - --- We can have a fast test on a font being processed, so we can check faster for marks etc --- but I'll make a context variant anyway. - -local function show_result(head) - local current = head - local skipping = false - while current do - local id = current.id - if id == glyph_code then - report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset) - skipping = false - elseif id == kern_code then - report_injections("kern: %p",current.kern) - skipping = false - elseif not skipping then - report_injections() - skipping = true - end - current = current.next - end -end - -function injections.handler(head,where,keep) - local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) - if has_marks or has_cursives then - if trace_injections then - trace(head) - end - -- in the future variant we will not copy items but refs to tables - local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0 - if has_kerns then -- move outside loop - local nf, tm = nil, nil - for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts - if n.subtype < 256 then - nofvalid = nofvalid + 1 - valid[nofvalid] = n - if n.font ~= nf then - nf = n.font - tm = fontdata[nf].resources.marks - end - if tm then - mk[n] = tm[n.char] - end - local k = n[a_kernpair] - if k then - local kk = kerns[k] - if kk then - local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0 - local dy = y - h - if dy ~= 0 then - ky[n] = dy - end - if w ~= 0 or x ~= 0 then - wx[n] = kk - end - rl[n] = kk[1] -- could move in test - end - end - end - end - else - local nf, tm = nil, nil - for n in traverse_id(glyph_code,head) do - if n.subtype < 256 then - nofvalid = nofvalid + 1 - valid[nofvalid] = n - if n.font ~= nf then - nf = n.font - tm = fontdata[nf].resources.marks - end - if tm then - mk[n] = tm[n.char] - end - end - end - end - if nofvalid > 0 then - -- we can assume done == true because we have cursives and marks - local cx = { } - if has_kerns and next(ky) then - for n, k in next, ky do - n.yoffset = k - end - end - -- todo: reuse t and use maxt - if has_cursives then - local p_cursbase, p = nil, nil - -- since we need valid[n+1] we can also use a "while true do" - local t, d, maxt = { }, { }, 0 - for i=1,nofvalid do -- valid == glyphs - local n = valid[i] - if not mk[n] then - local n_cursbase = n[a_cursbase] - if p_cursbase then - local n_curscurs = n[a_curscurs] - if p_cursbase == n_curscurs then - local c = cursives[n_curscurs] - if c then - local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] - if rlmode >= 0 then - dx = dx - ws - else - dx = dx + wn - end - if dx ~= 0 then - cx[n] = dx - rl[n] = rlmode - end - -- if rlmode and rlmode < 0 then - dy = -dy - -- end - maxt = maxt + 1 - t[maxt] = p - d[maxt] = dy - else - maxt = 0 - end - end - elseif maxt > 0 then - local ny = n.yoffset - for i=maxt,1,-1 do - ny = ny + d[i] - local ti = t[i] - ti.yoffset = ti.yoffset + ny - end - maxt = 0 - end - if not n_cursbase and maxt > 0 then - local ny = n.yoffset - for i=maxt,1,-1 do - ny = ny + d[i] - local ti = t[i] - ti.yoffset = ny - end - maxt = 0 - end - p_cursbase, p = n_cursbase, n - end - end - if maxt > 0 then - local ny = n.yoffset - for i=maxt,1,-1 do - ny = ny + d[i] - local ti = t[i] - ti.yoffset = ny - end - maxt = 0 - end - if not keep then - cursives = { } - end - end - if has_marks then - for i=1,nofvalid do - local p = valid[i] - local p_markbase = p[a_markbase] - if p_markbase then - local mrks = marks[p_markbase] - local nofmarks = #mrks - for n in traverse_id(glyph_code,p.next) do - local n_markmark = n[a_markmark] - if p_markbase == n_markmark then - local index = n[a_markdone] or 1 - local d = mrks[index] - if d then - local rlmode = d[3] - -- - local k = wx[p] - if k then - local x = k[2] - local w = k[4] - if w then - if rlmode and rlmode >= 0 then - -- kern(x) glyph(p) kern(w-x) mark(n) - n.xoffset = p.xoffset - p.width + d[1] - (w-x) - else - -- kern(w-x) glyph(p) kern(x) mark(n) - n.xoffset = p.xoffset - d[1] - x - end - else - if rlmode and rlmode >= 0 then - -- okay for husayni - n.xoffset = p.xoffset - p.width + d[1] - else - -- needs checking: is x ok here? - n.xoffset = p.xoffset - d[1] - x - end - end - else - if rlmode and rlmode >= 0 then - n.xoffset = p.xoffset - p.width + d[1] - else - n.xoffset = p.xoffset - d[1] - end - local w = n.width - if w ~= 0 then - insert_node_before(head,n,newkern(-w/2)) - insert_node_after(head,n,newkern(-w/2)) - end - end - -- -- - if mk[p] then - n.yoffset = p.yoffset + d[2] - else - n.yoffset = n.yoffset + p.yoffset + d[2] - end - -- - if nofmarks == 1 then - break - else - nofmarks = nofmarks - 1 - end - end - else - -- KE: there can be sequences in ligatures - end - end - end - end - if not keep then - marks = { } - end - end - -- todo : combine - if next(wx) then - for n, k in next, wx do - -- only w can be nil (kernclasses), can be sped up when w == nil - local x = k[2] - local w = k[4] - if w then - local rl = k[1] -- r2l = k[6] - local wx = w - x - if rl < 0 then -- KE: don't use r2l here - if wx ~= 0 then - insert_node_before(head,n,newkern(wx)) -- type 0/2 - end - if x ~= 0 then - insert_node_after (head,n,newkern(x)) -- type 0/2 - end - else - if x ~= 0 then - insert_node_before(head,n,newkern(x)) -- type 0/2 - end - if wx ~= 0 then - insert_node_after (head,n,newkern(wx)) -- type 0/2 - end - end - elseif x ~= 0 then - -- this needs checking for rl < 0 but it is unlikely that a r2l script - -- uses kernclasses between glyphs so we're probably safe (KE has a - -- problematic font where marks interfere with rl < 0 in the previous - -- case) - insert_node_before(head,n,newkern(x)) -- a real font kern, type 0 - end - end - end - if next(cx) then - for n, k in next, cx do - if k ~= 0 then - local rln = rl[n] - if rln and rln < 0 then - insert_node_before(head,n,newkern(-k)) -- type 0/2 - else - insert_node_before(head,n,newkern(k)) -- type 0/2 - end - end - end - end - if not keep then - kerns = { } - end - -- if trace_injections then - -- show_result(head) - -- end - return head, true - elseif not keep then - kerns, cursives, marks = { }, { }, { } - end - elseif has_kerns then - if trace_injections then - trace(head) - end - for n in traverse_id(glyph_code,head) do - if n.subtype < 256 then - local k = n[a_kernpair] - if k then - local kk = kerns[k] - if kk then - local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4] - if y and y ~= 0 then - n.yoffset = y -- todo: h ? - end - if w then - -- copied from above - -- local r2l = kk[6] - local wx = w - x - if rl < 0 then -- KE: don't use r2l here - if wx ~= 0 then - insert_node_before(head,n,newkern(wx)) - end - if x ~= 0 then - insert_node_after (head,n,newkern(x)) - end - else - if x ~= 0 then - insert_node_before(head,n,newkern(x)) - end - if wx ~= 0 then - insert_node_after(head,n,newkern(wx)) - end - end - else - -- simple (e.g. kernclass kerns) - if x ~= 0 then - insert_node_before(head,n,newkern(x)) - end - end - end - end - end - end - if not keep then - kerns = { } - end - -- if trace_injections then - -- show_result(head) - -- end - return head, true - else - -- no tracing needed - end - return head, false -end diff --git a/luaotfload-fonts-lua.lua b/luaotfload-fonts-lua.lua deleted file mode 100644 index ec3fe38..0000000 --- a/luaotfload-fonts-lua.lua +++ /dev/null @@ -1,33 +0,0 @@ -if not modules then modules = { } end modules ['luatex-fonts-lua'] = { - version = 1.001, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end - -local fonts = fonts -fonts.formats.lua = "lua" - -function fonts.readers.lua(specification) - local fullname = specification.filename or "" - if fullname == "" then - local forced = specification.forced or "" - if forced ~= "" then - fullname = specification.name .. "." .. forced - else - fullname = specification.name - end - end - local fullname = resolvers.findfile(fullname) or "" - if fullname ~= "" then - local loader = loadfile(fullname) - loader = loader and loader() - return loader and loader(specification) - end -end diff --git a/luaotfload-fonts-otn.lua b/luaotfload-fonts-otn.lua deleted file mode 100644 index c57be5f..0000000 --- a/luaotfload-fonts-otn.lua +++ /dev/null @@ -1,2848 +0,0 @@ -if not modules then modules = { } end modules ['font-otn'] = { - 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", -} - --- preprocessors = { "nodes" } - --- this is still somewhat preliminary and it will get better in due time; --- much functionality could only be implemented thanks to the husayni font --- of Idris Samawi Hamid to who we dedicate this module. - --- in retrospect it always looks easy but believe it or not, it took a lot --- of work to get proper open type support done: buggy fonts, fuzzy specs, --- special made testfonts, many skype sessions between taco, idris and me, --- torture tests etc etc ... unfortunately the code does not show how much --- time it took ... - --- todo: --- --- kerning is probably not yet ok for latin around dics nodes (interesting challenge) --- extension infrastructure (for usage out of context) --- sorting features according to vendors/renderers --- alternative loop quitters --- check cursive and r2l --- find out where ignore-mark-classes went --- default features (per language, script) --- handle positions (we need example fonts) --- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) --- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests) --- remove some optimizations (when I have a faster machine) --- --- maybe redo the lot some way (more context specific) - ---[[ldx-- -

This module is a bit more split up that I'd like but since we also want to test -with plain it has to be so. This module is part of -and discussion about improvements and functionality mostly happens on the - mailing list.

- -

The specification of OpenType is kind of vague. Apart from a lack of a proper -free specifications there's also the problem that Microsoft and Adobe -may have their own interpretation of how and in what order to apply features. -In general the Microsoft website has more detailed specifications and is a -better reference. There is also some information in the FontForge help files.

- -

Because there is so much possible, fonts might contain bugs and/or be made to -work with certain rederers. These may evolve over time which may have the side -effect that suddenly fonts behave differently.

- -

After a lot of experiments (mostly by Taco, me and Idris) we're now at yet another -implementation. Of course all errors are mine and of course the code can be -improved. There are quite some optimizations going on here and processing speed -is currently acceptable. Not all functions are implemented yet, often because I -lack the fonts for testing. Many scripts are not yet supported either, but I will -look into them as soon as users ask for it.

- -

Because there are different interpretations possible, I will extend the code -with more (configureable) variants. I can also add hooks for users so that they can -write their own extensions.

- -

Glyphs are indexed not by unicode but in their own way. This is because there is no -relationship with unicode at all, apart from the fact that a font might cover certain -ranges of characters. One character can have multiple shapes. However, at the - end we use unicode so and all extra glyphs are mapped into a private -space. This is needed because we need to access them and has to include -then in the output eventually.

- -

The raw table as it coms from gets reorganized in to fit out needs. -In that table is packed (similar tables are shared) and cached on disk -so that successive runs can use the optimized table (after loading the table is -unpacked). The flattening code used later is a prelude to an even more compact table -format (and as such it keeps evolving).

- -

This module is sparsely documented because it is a moving target. The table format -of the reader changes and we experiment a lot with different methods for supporting -features.

- -

As with the code, we may decide to store more information in the - table.

- -

Incrementing the version number will force a re-cache. We jump the number by one -when there's a fix in the library or code that -results in different tables.

---ldx]]-- - --- action handler chainproc chainmore comment --- --- gsub_single ok ok ok --- gsub_multiple ok ok not implemented yet --- gsub_alternate ok ok not implemented yet --- gsub_ligature ok ok ok --- gsub_context ok -- --- gsub_contextchain ok -- --- gsub_reversecontextchain ok -- --- chainsub -- ok --- reversesub -- ok --- gpos_mark2base ok ok --- gpos_mark2ligature ok ok --- gpos_mark2mark ok ok --- gpos_cursive ok untested --- gpos_single ok ok --- gpos_pair ok ok --- gpos_context ok -- --- gpos_contextchain ok -- --- --- todo: contextpos and contextsub and class stuff --- --- actions: --- --- handler : actions triggered by lookup --- chainproc : actions triggered by contextual lookup --- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij) --- --- remark: the 'not implemented yet' variants will be done when we have fonts that use them --- remark: we need to check what to do with discretionaries - --- We used to have independent hashes for lookups but as the tags are unique --- we now use only one hash. If needed we can have multiple again but in that --- case I will probably prefix (i.e. rename) the lookups in the cached font file. - --- Todo: make plugin feature that operates on char/glyphnode arrays - -local concat, insert, remove = table.concat, table.insert, table.remove -local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip -local type, next, tonumber, tostring = type, next, tonumber, tostring -local lpegmatch = lpeg.match -local random = math.random -local formatters = string.formatters - -local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes - -local registertracker = trackers.register - -local fonts = fonts -local otf = fonts.handlers.otf - -local trace_lookups = false registertracker("otf.lookups", function(v) trace_lookups = v end) -local trace_singles = false registertracker("otf.singles", function(v) trace_singles = v end) -local trace_multiples = false registertracker("otf.multiples", function(v) trace_multiples = v end) -local trace_alternatives = false registertracker("otf.alternatives", function(v) trace_alternatives = v end) -local trace_ligatures = false registertracker("otf.ligatures", function(v) trace_ligatures = v end) -local trace_contexts = false registertracker("otf.contexts", function(v) trace_contexts = v end) -local trace_marks = false registertracker("otf.marks", function(v) trace_marks = v end) -local trace_kerns = false registertracker("otf.kerns", function(v) trace_kerns = v end) -local trace_cursive = false registertracker("otf.cursive", function(v) trace_cursive = v end) -local trace_preparing = false registertracker("otf.preparing", function(v) trace_preparing = v end) -local trace_bugs = false registertracker("otf.bugs", function(v) trace_bugs = v end) -local trace_details = false registertracker("otf.details", function(v) trace_details = v end) -local trace_applied = false registertracker("otf.applied", function(v) trace_applied = v end) -local trace_steps = false registertracker("otf.steps", function(v) trace_steps = v end) -local trace_skips = false registertracker("otf.skips", function(v) trace_skips = v end) -local trace_directions = false registertracker("otf.directions", function(v) trace_directions = v end) - -local report_direct = logs.reporter("fonts","otf direct") -local report_subchain = logs.reporter("fonts","otf subchain") -local report_chain = logs.reporter("fonts","otf chain") -local report_process = logs.reporter("fonts","otf process") -local report_prepare = logs.reporter("fonts","otf prepare") -local report_warning = logs.reporter("fonts","otf warning") - -registertracker("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) -registertracker("otf.normal_chain", function(v) otf.setcontextchain(v and "normal") end) - -registertracker("otf.replacements", "otf.singles,otf.multiples,otf.alternatives,otf.ligatures") -registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") -registertracker("otf.actions","otf.replacements,otf.positions") -registertracker("otf.injections","nodes.injections") - -registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") - -local insert_node_after = node.insert_after -local delete_node = nodes.delete -local copy_node = node.copy -local find_node_tail = node.tail or node.slide -local flush_node_list = node.flush_list -local end_of_math = node.end_of_math - -local setmetatableindex = table.setmetatableindex - -local zwnj = 0x200C -local zwj = 0x200D -local wildcard = "*" -local default = "dflt" - -local nodecodes = nodes.nodecodes -local whatcodes = nodes.whatcodes -local glyphcodes = nodes.glyphcodes -local disccodes = nodes.disccodes - -local glyph_code = nodecodes.glyph -local glue_code = nodecodes.glue -local disc_code = nodecodes.disc -local whatsit_code = nodecodes.whatsit -local math_code = nodecodes.math - -local dir_code = whatcodes.dir -local localpar_code = whatcodes.localpar - -local discretionary_code = disccodes.discretionary - -local ligature_code = glyphcodes.ligature - -local privateattribute = attributes.private - --- Something is messed up: we have two mark / ligature indices, one at the injection --- end and one here ... this is bases in KE's patches but there is something fishy --- there as I'm pretty sure that for husayni we need some connection (as it's much --- more complex than an average font) but I need proper examples of all cases, not --- of only some. - -local a_state = privateattribute('state') -local a_markbase = privateattribute('markbase') -local a_markmark = privateattribute('markmark') -local a_markdone = privateattribute('markdone') -- assigned at the injection end -local a_cursbase = privateattribute('cursbase') -local a_curscurs = privateattribute('curscurs') -local a_cursdone = privateattribute('cursdone') -local a_kernpair = privateattribute('kernpair') -local a_ligacomp = privateattribute('ligacomp') -- assigned here (ideally it should be combined) - -local injections = nodes.injections -local setmark = injections.setmark -local setcursive = injections.setcursive -local setkern = injections.setkern -local setpair = injections.setpair - -local markonce = true -local cursonce = true -local kernonce = true - -local fonthashes = fonts.hashes -local fontdata = fonthashes.identifiers - -local otffeatures = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register - -local onetimemessage = fonts.loggers.onetimemessage or function() end - -otf.defaultnodealternate = "none" -- first last - --- we share some vars here, after all, we have no nested lookups and less code - -local tfmdata = false -local characters = false -local descriptions = false -local resources = false -local marks = false -local currentfont = false -local lookuptable = false -local anchorlookups = false -local lookuptypes = false -local handlers = { } -local rlmode = 0 -local featurevalue = false - --- head is always a whatsit so we can safely assume that head is not changed - --- we use this for special testing and documentation - -local checkstep = (nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end -local registerstep = (nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end -local registermessage = (nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end - -local function logprocess(...) - if trace_steps then - registermessage(...) - end - report_direct(...) -end - -local function logwarning(...) - report_direct(...) -end - -local f_unicode = formatters["%U"] -local f_uniname = formatters["%U (%s)"] -local f_unilist = formatters["% t (% t)"] - -local function gref(n) -- currently the same as in font-otb - if type(n) == "number" then - local description = descriptions[n] - local name = description and description.name - if name then - return f_uniname(n,name) - else - return f_unicode(n) - end - elseif n then - local num, nam = { }, { } - for i=1,#n do - local ni = n[i] - if tonumber(ni) then -- later we will start at 2 - local di = descriptions[ni] - num[i] = f_unicode(ni) - nam[i] = di and di.name or "-" - end - end - return f_unilist(num,nam) - else - return "" - end -end - -local function cref(kind,chainname,chainlookupname,lookupname,index) -- not in the mood to alias f_ - if index then - return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookupname,index) - elseif lookupname then - return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookupname) - elseif chainlookupname then - return formatters["feature %a, chain %a, sub %a"](kind,chainname,chainlookupname) - elseif chainname then - return formatters["feature %a, chain %a"](kind,chainname) - else - return formatters["feature %a"](kind) - end -end - -local function pref(kind,lookupname) - return formatters["feature %a, lookup %a"](kind,lookupname) -end - --- We can assume that languages that use marks are not hyphenated. We can also assume --- that at most one discretionary is present. - --- We do need components in funny kerning mode but maybe I can better reconstruct then --- as we do have the font components info available; removing components makes the --- previous code much simpler. Also, later on copying and freeing becomes easier. --- However, for arabic we need to keep them around for the sake of mark placement --- and indices. - -local function copy_glyph(g) -- next and prev are untouched ! - local components = g.components - if components then - g.components = nil - local n = copy_node(g) - g.components = components - return n - else - return copy_node(g) - end -end - --- start is a mark and we need to keep that one - -local function markstoligature(kind,lookupname,head,start,stop,char) - if start == stop and start.char == char then - return head, start - else - local prev = start.prev - local next = stop.next - start.prev = nil - stop.next = nil - local base = copy_glyph(start) - if head == start then - head = base - end - base.char = char - base.subtype = ligature_code - base.components = start - if prev then - prev.next = base - end - if next then - next.prev = base - end - base.next = next - base.prev = prev - return head, base - end -end - --- The next code is somewhat complicated by the fact that some fonts can have ligatures made --- from ligatures that themselves have marks. This was identified by Kai in for instance --- arabtype: KAF LAM SHADDA ALEF FATHA (0x0643 0x0644 0x0651 0x0627 0x064E). This becomes --- KAF LAM-ALEF with a SHADDA on the first and a FATHA op de second component. In a next --- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the --- third component. - -local function getcomponentindex(start) - if start.id ~= glyph_code then - return 0 - elseif start.subtype == ligature_code then - local i = 0 - local components = start.components - while components do - i = i + getcomponentindex(components) - components = components.next - end - return i - elseif not marks[start.char] then - return 1 - else - return 0 - end -end - --- eventually we will do positioning in an other way (needs addional w/h/d fields) - -local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head - if start == stop and start.char == char then - start.char = char - return head, start - end - local prev = start.prev - local next = stop.next - start.prev = nil - stop.next = nil - local base = copy_glyph(start) - if start == head then - head = base - end - base.char = char - base.subtype = ligature_code - base.components = start -- start can have components - if prev then - prev.next = base - end - if next then - next.prev = base - end - base.next = next - base.prev = prev - if not discfound then - local deletemarks = markflag ~= "mark" - local components = start - local baseindex = 0 - local componentindex = 0 - local head = base - local current = base - -- first we loop over the glyphs in start .. stop - while start do - local char = start.char - if not marks[char] then - baseindex = baseindex + componentindex - componentindex = getcomponentindex(start) - elseif not deletemarks then -- quite fishy - start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) - end - head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components - elseif trace_marks then - logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char)) - end - start = start.next - end - -- we can have one accent as part of a lookup and another following - -- local start = components -- was wrong (component scanning was introduced when more complex ligs in devanagari was added) - local start = current.next - while start and start.id == glyph_code do - local char = start.char - if marks[char] then - start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) - if trace_marks then - logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) - end - else - break - end - start = start.next - end - end - return head, base -end - -function handlers.gsub_single(head,start,kind,lookupname,replacement) - if trace_singles then - logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) - end - start.char = replacement - return head, start, true -end - -local function get_alternative_glyph(start,alternatives,value,trace_alternatives) - local n = #alternatives - if value == "random" then - local r = random(1,n) - return alternatives[r], trace_alternatives and formatters["value %a, taking %a"](value,r) - elseif value == "first" then - return alternatives[1], trace_alternatives and formatters["value %a, taking %a"](value,1) - elseif value == "last" then - return alternatives[n], trace_alternatives and formatters["value %a, taking %a"](value,n) - else - value = tonumber(value) - if type(value) ~= "number" then - return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) - elseif value > n then - local defaultalt = otf.defaultnodealternate - if defaultalt == "first" then - return alternatives[n], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) - elseif defaultalt == "last" then - return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,n) - else - return false, trace_alternatives and formatters["invalid value %a, %s"](value,"out of range") - end - elseif value == 0 then - return start.char, trace_alternatives and formatters["invalid value %a, %s"](value,"no change") - elseif value < 1 then - return alternatives[1], trace_alternatives and formatters["invalid value %a, taking %a"](value,1) - else - return alternatives[value], trace_alternatives and formatters["value %a, taking %a"](value,value) - end - end -end - -local function multiple_glyphs(head,start,multiple,ignoremarks) - local nofmultiples = #multiple - if nofmultiples > 0 then - start.char = multiple[1] - if nofmultiples > 1 then - local sn = start.next - for k=2,nofmultiples do -- todo: use insert_node --- untested: --- --- while ignoremarks and marks[sn.char] then --- local sn = sn.next --- end - local n = copy_node(start) -- ignore components - n.char = multiple[k] - n.next = sn - n.prev = start - if sn then - sn.prev = n - end - start.next = n - start = n - end - end - return head, start, true - else - if trace_multiples then - logprocess("no multiple for %s",gref(start.char)) - end - return head, start, false - end -end - -function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence) - local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue - local choice, comment = get_alternative_glyph(start,alternative,value,trace_alternatives) - if choice then - if trace_alternatives then - logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment) - end - start.char = choice - else - if trace_alternatives then - logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment) - end - end - return head, start, true -end - -function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence) - if trace_multiples then - logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) - end - return multiple_glyphs(head,start,multiple,sequence.flags[1]) -end - -function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) - local s, stop, discfound = start.next, nil, false - local startchar = start.char - if marks[startchar] then - while s do - local id = s.id - if id == glyph_code and s.font == currentfont and s.subtype<256 then - local lg = ligature[s.char] - if lg then - stop = s - ligature = lg - s = s.next - else - break - end - else - break - end - end - if stop then - local lig = ligature.ligature - if lig then - if trace_ligatures then - local stopchar = stop.char - head, start = markstoligature(kind,lookupname,head,start,stop,lig) - logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) - else - head, start = markstoligature(kind,lookupname,head,start,stop,lig) - end - return head, start, true - else - -- ok, goto next lookup - end - end - else - local skipmark = sequence.flags[1] - while s do - local id = s.id - if id == glyph_code and s.subtype<256 then - if s.font == currentfont then - local char = s.char - if skipmark and marks[char] then - s = s.next - else - local lg = ligature[char] - if lg then - stop = s - ligature = lg - s = s.next - else - break - end - end - else - break - end - elseif id == disc_code then - discfound = true - s = s.next - else - break - end - end - local lig = ligature.ligature - if lig then - if stop then - if trace_ligatures then - local stopchar = stop.char - head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) - logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) - else - head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) - end - return head, start, true - else - -- weird but happens (in some arabic font) - start.char = lig - if trace_ligatures then - logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig)) - end - return head, start, true - end - else - -- weird but happens - end - end - return head, start, false -end - ---[[ldx-- -

We get hits on a mark, but we're not sure if the it has to be applied so -we need to explicitly test for basechar, baselig and basemark entries.

---ldx]]-- - -function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) - local markchar = start.char - if marks[markchar] then - local base = start.prev -- [glyph] [start=mark] - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then - local basechar = base.char - if marks[basechar] then - while true do - base = base.prev - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then - basechar = base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) - end - return head, start, false - end - end - end - local baseanchors = descriptions[basechar] - if baseanchors then - baseanchors = baseanchors.anchors - end - if baseanchors then - local baseanchors = baseanchors['basechar'] - if baseanchors then - local al = anchorlookups[lookupname] - for anchor,ba in next, baseanchors do - if al[anchor] then - local ma = markanchors[anchor] - if ma then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", - pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return head, start, true - end - end - end - if trace_bugs then - logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - end - end - elseif trace_bugs then - -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) - end - elseif trace_bugs then - logwarning("%s: prev node is no char",pref(kind,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) - end - return head, start, false -end - -function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence) - -- check chainpos variant - local markchar = start.char - if marks[markchar] then - local base = start.prev -- [glyph] [optional marks] [start=mark] - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then - local basechar = base.char - if marks[basechar] then - while true do - base = base.prev - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then - basechar = base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) - end - return head, start, false - end - end - end - local index = start[a_ligacomp] - local baseanchors = descriptions[basechar] - if baseanchors then - baseanchors = baseanchors.anchors - if baseanchors then - local baseanchors = baseanchors['baselig'] - if baseanchors then - local al = anchorlookups[lookupname] - for anchor, ba in next, baseanchors do - if al[anchor] then - local ma = markanchors[anchor] - if ma then - ba = ba[index] - if ba then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index - if trace_marks then - logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", - pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) - end - return head, start, true - else - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index) - end - end - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) - end - elseif trace_bugs then - logwarning("%s: prev node is no char",pref(kind,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) - end - return head, start, false -end - -function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) - local markchar = start.char - if marks[markchar] then - local base = start.prev -- [glyph] [basemark] [start=mark] - local slc = start[a_ligacomp] - if slc then -- a rather messy loop ... needs checking with husayni - while base do - local blc = base[a_ligacomp] - if blc and blc ~= slc then - base = base.prev - else - break - end - end - end - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go - local basechar = base.char - local baseanchors = descriptions[basechar] - if baseanchors then - baseanchors = baseanchors.anchors - if baseanchors then - baseanchors = baseanchors['basemark'] - if baseanchors then - local al = anchorlookups[lookupname] - for anchor,ba in next, baseanchors do - if al[anchor] then - local ma = markanchors[anchor] - if ma then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", - pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return head, start, true - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) - end - elseif trace_bugs then - logwarning("%s: prev node is no mark",pref(kind,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) - end - return head, start, false -end - -function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) -- to be checked - local alreadydone = cursonce and start[a_cursbase] - if not alreadydone then - local done = false - local startchar = start.char - if marks[startchar] then - if trace_cursive then - logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) - end - else - local nxt = start.next - while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do - local nextchar = nxt.char - if marks[nextchar] then - -- should not happen (maybe warning) - nxt = nxt.next - else - local entryanchors = descriptions[nextchar] - if entryanchors then - entryanchors = entryanchors.anchors - if entryanchors then - entryanchors = entryanchors['centry'] - if entryanchors then - local al = anchorlookups[lookupname] - for anchor, entry in next, entryanchors do - if al[anchor] then - local exit = exitanchors[anchor] - if exit then - local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) - if trace_cursive then - logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) - end - done = true - break - end - end - end - end - end - elseif trace_bugs then - -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) - end - break - end - end - end - return head, start, done - else - if trace_cursive and trace_details then - logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) - end - return head, start, false - end -end - -function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) - local startchar = start.char - local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) - end - return head, start, false -end - -function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) - -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too - -- todo: kerns in components of ligatures - local snext = start.next - if not snext then - return head, start, false - else - local prev, done = start, false - local factor = tfmdata.parameters.factor - local lookuptype = lookuptypes[lookupname] - while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do - local nextchar = snext.char - local krn = kerns[nextchar] - if not krn and marks[nextchar] then - prev = snext - snext = snext.next - else - if not krn then - -- skip - elseif type(krn) == "table" then - if lookuptype == "pair" then -- probably not needed - local a, b = krn[2], krn[3] - if a and #a > 0 then - local startchar = start.char - local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - if b and #b > 0 then - local startchar = start.char - local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) - if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - else -- wrong ... position has different entries - report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) - -- local a, b = krn[2], krn[6] - -- if a and a ~= 0 then - -- local k = setkern(snext,factor,rlmode,a) - -- if trace_kerns then - -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) - -- end - -- end - -- if b and b ~= 0 then - -- logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) - -- end - end - done = true - elseif krn ~= 0 then - local k = setkern(snext,factor,rlmode,krn) - if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) - end - done = true - end - break - end - end - return head, start, done - end -end - ---[[ldx-- -

I will implement multiple chain replacements once I run into a font that uses -it. It's not that complex to handle.

---ldx]]-- - -local chainmores = { } -local chainprocs = { } - -local function logprocess(...) - if trace_steps then - registermessage(...) - end - report_subchain(...) -end - -local logwarning = report_subchain - -local function logprocess(...) - if trace_steps then - registermessage(...) - end - report_chain(...) -end - -local logwarning = report_chain - --- We could share functions but that would lead to extra function calls with many --- arguments, redundant tests and confusing messages. - -function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) - logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return head, start, false -end - -function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) - logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return head, start, false -end - --- The reversesub is a special case, which is why we need to store the replacements --- in a bit weird way. There is no lookup and the replacement comes from the lookup --- itself. It is meant mostly for dealing with Urdu. - -function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) - local char = start.char - local replacement = replacements[char] - if replacement then - if trace_singles then - logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) - end - start.char = replacement - return head, start, true - else - return head, start, false - end -end - ---[[ldx-- -

This chain stuff is somewhat tricky since we can have a sequence of actions to be -applied: single, alternate, multiple or ligature where ligature can be an invalid -one in the sense that it will replace multiple by one but not neccessary one that -looks like the combination (i.e. it is the counterpart of multiple then). For -example, the following is valid:

- - -xxxabcdexxx [single a->A][multiple b->BCD][ligature cde->E] xxxABCDExxx - - -

Therefore we we don't really do the replacement here already unless we have the -single lookup case. The efficiency of the replacements can be improved by deleting -as less as needed but that would also make the code even more messy.

---ldx]]-- - --- local function delete_till_stop(head,start,stop,ignoremarks) -- keeps start --- local n = 1 --- if start == stop then --- -- done --- elseif ignoremarks then --- repeat -- start x x m x x stop => start m --- local next = start.next --- if not marks[next.char] then --- local components = next.components --- if components then -- probably not needed --- flush_node_list(components) --- end --- head = delete_node(head,next) --- end --- n = n + 1 --- until next == stop --- else -- start x x x stop => start --- repeat --- local next = start.next --- local components = next.components --- if components then -- probably not needed --- flush_node_list(components) --- end --- head = delete_node(head,next) --- n = n + 1 --- until next == stop --- end --- return head, n --- end - ---[[ldx-- -

Here we replace start by a single variant, First we delete the rest of the -match.

---ldx]]-- - -function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) - -- todo: marks ? - local current = start - local subtables = currentlookup.subtables - if #subtables > 1 then - logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) - end - while current do - if current.id == glyph_code then - local currentchar = current.char - local lookupname = subtables[1] -- only 1 - local replacement = lookuphash[lookupname] - if not replacement then - if trace_bugs then - logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) - end - else - replacement = replacement[currentchar] - if not replacement or replacement == "" then - if trace_bugs then - logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) - end - else - if trace_singles then - logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) - end - current.char = replacement - end - end - return head, start, true - elseif current == stop then - break - else - current = current.next - end - end - return head, start, false -end - -chainmores.gsub_single = chainprocs.gsub_single - ---[[ldx-- -

Here we replace start by a sequence of new glyphs. First we delete the rest of -the match.

---ldx]]-- - -function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - -- local head, n = delete_till_stop(head,start,stop) - local startchar = start.char - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local replacements = lookuphash[lookupname] - if not replacements then - if trace_bugs then - logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) - end - else - replacements = replacements[startchar] - if not replacements or replacement == "" then - if trace_bugs then - logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) - end - else - if trace_multiples then - logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) - end - return multiple_glyphs(head,start,replacements,currentlookup.flags[1]) - end - end - return head, start, false -end - -chainmores.gsub_multiple = chainprocs.gsub_multiple - ---[[ldx-- -

Here we replace start by new glyph. First we delete the rest of the match.

---ldx]]-- - --- char_1 mark_1 -> char_x mark_1 (ignore marks) --- char_1 mark_1 -> char_x - --- to be checked: do we always have just one glyph? --- we can also have alternates for marks --- marks come last anyway --- are there cases where we need to delete the mark - -function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local current = start - local subtables = currentlookup.subtables - local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue - while current do - if current.id == glyph_code then -- is this check needed? - local currentchar = current.char - local lookupname = subtables[1] - local alternatives = lookuphash[lookupname] - if not alternatives then - if trace_bugs then - logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname)) - end - else - alternatives = alternatives[currentchar] - if alternatives then - local choice, comment = get_alternative_glyph(current,alternatives,value,trace_alternatives) - if choice then - if trace_alternatives then - logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment) - end - start.char = choice - else - if trace_alternatives then - logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) - end - end - elseif trace_bugs then - logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment) - end - end - return head, start, true - elseif current == stop then - break - else - current = current.next - end - end - return head, start, false -end - -chainmores.gsub_alternate = chainprocs.gsub_alternate - ---[[ldx-- -

When we replace ligatures we use a helper that handles the marks. I might change -this function (move code inline and handle the marks by a separate function). We -assume rather stupid ligatures (no complex disc nodes).

---ldx]]-- - -function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) - local startchar = start.char - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local ligatures = lookuphash[lookupname] - if not ligatures then - if trace_bugs then - logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) - end - else - ligatures = ligatures[startchar] - if not ligatures then - if trace_bugs then - logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) - end - else - local s = start.next - local discfound = false - local last = stop - local nofreplacements = 0 - local skipmark = currentlookup.flags[1] - while s do - local id = s.id - if id == disc_code then - s = s.next - discfound = true - else - local schar = s.char - if skipmark and marks[schar] then -- marks - s = s.next - else - local lg = ligatures[schar] - if lg then - ligatures, last, nofreplacements = lg, s, nofreplacements + 1 - if s == stop then - break - else - s = s.next - end - else - break - end - end - end - end - local l2 = ligatures.ligature - if l2 then - if chainindex then - stop = last - end - if trace_ligatures then - if start == stop then - logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) - else - logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) - end - end - head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) - return head, start, true, nofreplacements - elseif trace_bugs then - if start == stop then - logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) - else - logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) - end - end - end - end - return head, start, false, 0 -end - -chainmores.gsub_ligature = chainprocs.gsub_ligature - -function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar = start.char - if marks[markchar] then - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local markanchors = lookuphash[lookupname] - if markanchors then - markanchors = markanchors[markchar] - end - if markanchors then - local base = start.prev -- [glyph] [start=mark] - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then - local basechar = base.char - if marks[basechar] then - while true do - base = base.prev - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then - basechar = base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) - end - return head, start, false - end - end - end - local baseanchors = descriptions[basechar].anchors - if baseanchors then - local baseanchors = baseanchors['basechar'] - if baseanchors then - local al = anchorlookups[lookupname] - for anchor,ba in next, baseanchors do - if al[anchor] then - local ma = markanchors[anchor] - if ma then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", - cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return head, start, true - end - end - end - if trace_bugs then - logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) - end - return head, start, false -end - -function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar = start.char - if marks[markchar] then - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local markanchors = lookuphash[lookupname] - if markanchors then - markanchors = markanchors[markchar] - end - if markanchors then - local base = start.prev -- [glyph] [optional marks] [start=mark] - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then - local basechar = base.char - if marks[basechar] then - while true do - base = base.prev - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then - basechar = base.char - if not marks[basechar] then - break - end - else - if trace_bugs then - logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) - end - return head, start, false - end - end - end - -- todo: like marks a ligatures hash - local index = start[a_ligacomp] - local baseanchors = descriptions[basechar].anchors - if baseanchors then - local baseanchors = baseanchors['baselig'] - if baseanchors then - local al = anchorlookups[lookupname] - for anchor,ba in next, baseanchors do - if al[anchor] then - local ma = markanchors[anchor] - if ma then - ba = ba[index] - if ba then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", - cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) - end - return head, start, true - end - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) - end - elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) - end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) - end - return head, start, false -end - -function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar = start.char - if marks[markchar] then - -- local alreadydone = markonce and start[a_markmark] - -- if not alreadydone then - -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local markanchors = lookuphash[lookupname] - if markanchors then - markanchors = markanchors[markchar] - end - if markanchors then - local base = start.prev -- [glyph] [basemark] [start=mark] - local slc = start[a_ligacomp] - if slc then -- a rather messy loop ... needs checking with husayni - while base do - local blc = base[a_ligacomp] - if blc and blc ~= slc then - base = base.prev - else - break - end - end - end - if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go - local basechar = base.char - local baseanchors = descriptions[basechar].anchors - if baseanchors then - baseanchors = baseanchors['basemark'] - if baseanchors then - local al = anchorlookups[lookupname] - for anchor,ba in next, baseanchors do - if al[anchor] then - local ma = markanchors[anchor] - if ma then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", - cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return head, start, true - end - end - end - if trace_bugs then - logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) - end - end - end - elseif trace_bugs then - logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) - end - elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) - end - -- elseif trace_marks and trace_details then - -- logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) - -- end - elseif trace_bugs then - logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) - end - return head, start, false -end - -function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local alreadydone = cursonce and start[a_cursbase] - if not alreadydone then - local startchar = start.char - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local exitanchors = lookuphash[lookupname] - if exitanchors then - exitanchors = exitanchors[startchar] - end - if exitanchors then - local done = false - if marks[startchar] then - if trace_cursive then - logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) - end - else - local nxt = start.next - while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do - local nextchar = nxt.char - if marks[nextchar] then - -- should not happen (maybe warning) - nxt = nxt.next - else - local entryanchors = descriptions[nextchar] - if entryanchors then - entryanchors = entryanchors.anchors - if entryanchors then - entryanchors = entryanchors['centry'] - if entryanchors then - local al = anchorlookups[lookupname] - for anchor, entry in next, entryanchors do - if al[anchor] then - local exit = exitanchors[anchor] - if exit then - local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) - if trace_cursive then - logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) - end - done = true - break - end - end - end - end - end - elseif trace_bugs then - -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) - end - break - end - end - end - return head, start, done - else - if trace_cursive and trace_details then - logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) - end - return head, start, false - end - end - return head, start, false -end - -function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) - -- untested .. needs checking for the new model - local startchar = start.char - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local kerns = lookuphash[lookupname] - if kerns then - kerns = kerns[startchar] -- needed ? - if kerns then - local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) - end - end - end - return head, start, false -end - -chainmores.gpos_single = chainprocs.gpos_single -- okay? - --- when machines become faster i will make a shared function - -function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) - local snext = start.next - if snext then - local startchar = start.char - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local kerns = lookuphash[lookupname] - if kerns then - kerns = kerns[startchar] - if kerns then - local lookuptype = lookuptypes[lookupname] - local prev, done = start, false - local factor = tfmdata.parameters.factor - while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do - local nextchar = snext.char - local krn = kerns[nextchar] - if not krn and marks[nextchar] then - prev = snext - snext = snext.next - else - if not krn then - -- skip - elseif type(krn) == "table" then - if lookuptype == "pair" then - local a, b = krn[2], krn[3] - if a and #a > 0 then - local startchar = start.char - local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - if b and #b > 0 then - local startchar = start.char - local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) - if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - else - report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) - local a, b = krn[2], krn[6] - if a and a ~= 0 then - local k = setkern(snext,factor,rlmode,a) - if trace_kerns then - logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) - end - end - if b and b ~= 0 then - logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) - end - end - done = true - elseif krn ~= 0 then - local k = setkern(snext,factor,rlmode,krn) - if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) - end - done = true - end - break - end - end - return head, start, done - end - end - end - return head, start, false -end - -chainmores.gpos_pair = chainprocs.gpos_pair -- okay? - --- what pointer to return, spec says stop --- to be discussed ... is bidi changer a space? --- elseif char == zwnj and sequence[n][32] then -- brrr - --- somehow l or f is global --- we don't need to pass the currentcontext, saves a bit --- make a slow variant then can be activated but with more tracing - -local function show_skip(kind,chainname,char,ck,class) - if ck[9] then - logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) - else - logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) - end -end - -local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) - -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] - local flags = sequence.flags - local done = false - local skipmark = flags[1] - local skipligature = flags[2] - local skipbase = flags[3] - local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) - local markclass = sequence.markclass -- todo, first we need a proper test - local skipped = false - for k=1,#contexts do - local match = true - local current = start - local last = start - local ck = contexts[k] - local seq = ck[3] - local s = #seq - -- f..l = mid string - if s == 1 then - -- never happens - match = current.id == glyph_code and current.font == currentfont and current.subtype<256 and seq[1][current.char] - else - -- maybe we need a better space check (maybe check for glue or category or combination) - -- we cannot optimize for n=2 because there can be disc nodes - local f, l = ck[4], ck[5] - -- current match - if f == 1 and f == l then -- current only - -- already a hit - -- match = true - else -- before/current/after | before/current | current/after - -- no need to test first hit (to be optimized) - if f == l then -- new, else last out of sync (f is > 1) - -- match = true - else - local n = f + 1 - last = last.next - while n <= l do - if last then - local id = last.id - if id == glyph_code then - if last.font == currentfont and last.subtype<256 then - local char = last.char - local ccd = descriptions[char] - if ccd then - local class = ccd.class - if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then - skipped = true - if trace_skips then - show_skip(kind,chainname,char,ck,class) - end - last = last.next - elseif seq[n][char] then - if n < l then - last = last.next - end - n = n + 1 - else - match = false - break - end - else - match = false - break - end - else - match = false - break - end - elseif id == disc_code then - last = last.next - else - match = false - break - end - else - match = false - break - end - end - end - end - -- before - if match and f > 1 then - local prev = start.prev - if prev then - local n = f-1 - while n >= 1 do - if prev then - local id = prev.id - if id == glyph_code then - if prev.font == currentfont and prev.subtype<256 then -- normal char - local char = prev.char - local ccd = descriptions[char] - if ccd then - local class = ccd.class - if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then - skipped = true - if trace_skips then - show_skip(kind,chainname,char,ck,class) - end - elseif seq[n][char] then - n = n -1 - else - match = false - break - end - else - match = false - break - end - else - match = false - break - end - elseif id == disc_code then - -- skip 'm - elseif seq[n][32] then - n = n -1 - else - match = false - break - end - prev = prev.prev - elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces - n = n -1 - else - match = false - break - end - end - elseif f == 2 then - match = seq[1][32] - else - for n=f-1,1 do - if not seq[n][32] then - match = false - break - end - end - end - end - -- after - if match and s > l then - local current = last and last.next - if current then - -- removed optimization for s-l == 1, we have to deal with marks anyway - local n = l + 1 - while n <= s do - if current then - local id = current.id - if id == glyph_code then - if current.font == currentfont and current.subtype<256 then -- normal char - local char = current.char - local ccd = descriptions[char] - if ccd then - local class = ccd.class - if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then - skipped = true - if trace_skips then - show_skip(kind,chainname,char,ck,class) - end - elseif seq[n][char] then - n = n + 1 - else - match = false - break - end - else - match = false - break - end - else - match = false - break - end - elseif id == disc_code then - -- skip 'm - elseif seq[n][32] then -- brrr - n = n + 1 - else - match = false - break - end - current = current.next - elseif seq[n][32] then - n = n + 1 - else - match = false - break - end - end - elseif s-l == 1 then - match = seq[s][32] - else - for n=l+1,s do - if not seq[n][32] then - match = false - break - end - end - end - end - end - if match then - -- ck == currentcontext - if trace_contexts then - local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5] - local char = start.char - if ck[9] then - logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a", - cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) - else - logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a", - cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) - end - end - local chainlookups = ck[6] - if chainlookups then - local nofchainlookups = #chainlookups - -- we can speed this up if needed - if nofchainlookups == 1 then - local chainlookupname = chainlookups[1] - local chainlookup = lookuptable[chainlookupname] - if chainlookup then - local cp = chainprocs[chainlookup.type] - if cp then - local ok - head, start, ok = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) - if ok then - done = true - end - else - logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) - end - else -- shouldn't happen - logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname)) - end - else - local i = 1 - repeat - if skipped then - while true do - local char = start.char - local ccd = descriptions[char] - if ccd then - local class = ccd.class - if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then - start = start.next - else - break - end - else - break - end - end - end - local chainlookupname = chainlookups[i] - local chainlookup = lookuptable[chainlookupname] - if not chainlookup then - -- okay, n matches, < n replacements - i = i + 1 - else - local cp = chainmores[chainlookup.type] - if not cp then - -- actually an error - logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) - i = i + 1 - else - local ok, n - head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) - -- messy since last can be changed ! - if ok then - done = true - -- skip next one(s) if ligature - i = i + (n or 1) - else - i = i + 1 - end - end - end - if start then - start = start.next - else - -- weird - end - until i > nofchainlookups - end - else - local replacements = ck[7] - if replacements then - head, start, done = chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) -- sequence - else - done = true -- can be meant to be skipped - if trace_contexts then - logprocess("%s: skipping match",cref(kind,chainname)) - end - end - end - end - end - return head, start, done -end - --- Because we want to keep this elsewhere (an because speed is less an issue) we --- pass the font id so that the verbose variant can access the relevant helper tables. - -local verbose_handle_contextchain = function(font,...) - logwarning("no verbose handler installed, reverting to 'normal'") - otf.setcontextchain() - return normal_handle_contextchain(...) -end - -otf.chainhandlers = { - normal = normal_handle_contextchain, - verbose = verbose_handle_contextchain, -} - -function otf.setcontextchain(method) - if not method or method == "normal" or not otf.chainhandlers[method] then - if handlers.contextchain then -- no need for a message while making the format - logwarning("installing normal contextchain handler") - end - handlers.contextchain = normal_handle_contextchain - else - logwarning("installing contextchain handler %a",method) - local handler = otf.chainhandlers[method] - handlers.contextchain = function(...) - return handler(currentfont,...) -- hm, get rid of ... - end - end - handlers.gsub_context = handlers.contextchain - handlers.gsub_contextchain = handlers.contextchain - handlers.gsub_reversecontextchain = handlers.contextchain - handlers.gpos_contextchain = handlers.contextchain - handlers.gpos_context = handlers.contextchain -end - -otf.setcontextchain() - -local missing = { } -- we only report once - -local function logprocess(...) - if trace_steps then - registermessage(...) - end - report_process(...) -end - -local logwarning = report_process - -local function report_missing_cache(typ,lookup) - local f = missing[currentfont] if not f then f = { } missing[currentfont] = f end - local t = f[typ] if not t then t = { } f[typ] = t end - if not t[lookup] then - t[lookup] = true - logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname) - end -end - -local resolved = { } -- we only resolve a font,script,language pair once - --- todo: pass all these 'locals' in a table - -local lookuphashes = { } - -setmetatableindex(lookuphashes, function(t,font) - local lookuphash = fontdata[font].resources.lookuphash - if not lookuphash or not next(lookuphash) then - lookuphash = false - end - t[font] = lookuphash - return lookuphash -end) - --- fonts.hashes.lookups = lookuphashes - -local autofeatures = fonts.analyzers.features -- was: constants - -local function initialize(sequence,script,language,enabled) - local features = sequence.features - if features then - for kind, scripts in next, features do - local valid = enabled[kind] - if valid then - local languages = scripts[script] or scripts[wildcard] - if languages and (languages[language] or languages[wildcard]) then - return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence } - end - end - end - end - return false -end - -function otf.dataset(tfmdata,font) -- generic variant, overloaded in context - local shared = tfmdata.shared - local properties = tfmdata.properties - local language = properties.language or "dflt" - local script = properties.script or "dflt" - local enabled = shared.features - local res = resolved[font] - if not res then - res = { } - resolved[font] = res - end - local rs = res[script] - if not rs then - rs = { } - res[script] = rs - end - local rl = rs[language] - if not rl then - rl = { - -- indexed but we can also add specific data by key - } - rs[language] = rl - local sequences = tfmdata.resources.sequences --- setmetatableindex(rl, function(t,k) --- if type(k) == "number" then --- local v = enabled and initialize(sequences[k],script,language,enabled) --- t[k] = v --- return v --- end --- end) -for s=1,#sequences do - local v = enabled and initialize(sequences[s],script,language,enabled) - if v then - rl[#rl+1] = v - end -end - end - return rl -end - --- elseif id == glue_code then --- if p[5] then -- chain --- local pc = pp[32] --- if pc then --- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) --- if ok then --- done = true --- end --- if start then start = start.next end --- else --- start = start.next --- end --- else --- start = start.next --- end - --- there will be a new direction parser (pre-parsed etc) - --- less bytecode: 290 -> 254 --- --- attr = attr or false --- --- local a = getattr(start,0) --- if (a == attr and (not attribute or getattr(start,a_state) == attribute)) or (not attribute or getattr(start,a_state) == attribute) then --- -- the action --- end - -local function featuresprocessor(head,font,attr) - - local lookuphash = lookuphashes[font] -- we can also check sequences here - - if not lookuphash then - return head, false - end - - if trace_steps then - checkstep(head) - end - - tfmdata = fontdata[font] - descriptions = tfmdata.descriptions - characters = tfmdata.characters - resources = tfmdata.resources - - marks = resources.marks - anchorlookups = resources.lookup_to_anchor - lookuptable = resources.lookups - lookuptypes = resources.lookuptypes - - currentfont = font - rlmode = 0 - - local sequences = resources.sequences - local done = false - local datasets = otf.dataset(tfmdata,font,attr) - - local dirstack = { } -- could move outside function - - -- We could work on sub start-stop ranges instead but I wonder if there is that - -- much speed gain (experiments showed that it made not much sense) and we need - -- to keep track of directions anyway. Also at some point I want to play with - -- font interactions and then we do need the full sweeps. - - -- Keeping track of the headnode is needed for devanagari (I generalized it a bit - -- so that multiple cases are also covered.) - - for s=1,#datasets do - local dataset = datasets[s] - featurevalue = dataset[1] -- todo: pass to function instead of using a global - - local sequence = dataset[5] -- sequences[s] -- also dataset[5] - local rlparmode = 0 - local topstack = 0 - local success = false - local attribute = dataset[2] - local chain = dataset[3] -- sequence.chain or 0 - local typ = sequence.type - local subtables = sequence.subtables - if chain < 0 then - -- this is a limited case, no special treatments like 'init' etc - local handler = handlers[typ] - -- we need to get rid of this slide! probably no longer needed in latest luatex - local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo - while start do - local id = start.id - if id == glyph_code then - if start.font == font and start.subtype<256 then - local a = start[0] - if a then - a = a == attr - else - a = true - end - if a then - for i=1,#subtables do - local lookupname = subtables[i] - local lookupcache = lookuphash[lookupname] - if lookupcache then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if success then - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - if start then start = start.prev end - else - start = start.prev - end - else - start = start.prev - end - else - start = start.prev - end - end - else - local handler = handlers[typ] - local ns = #subtables - local start = head -- local ? - rlmode = 0 -- to be checked ? - if ns == 1 then -- happens often - local lookupname = subtables[1] - local lookupcache = lookuphash[lookupname] - if not lookupcache then -- also check for empty cache - report_missing_cache(typ,lookupname) - else - - local function subrun(start) - -- mostly for gsub, gpos would demand a more clever approach - local head = start - local done = false - while start do - local id = start.id - if id == glyph_code and start.font == font and start.subtype <256 then - local a = start[0] - if a then - a = (a == attr) and (not attribute or start[a_state] == attribute) - else - a = not attribute or start[a_state] == attribute - end - if a then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - -- sequence kan weg - local ok - head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) - if ok then - done = true - end - end - if start then start = start.next end - else - start = start.next - end - else - start = start.next - end - end - if done then - success = true - return head - end - end - - local function kerndisc(disc) -- we can assume that prev and next are glyphs - local prev = disc.prev - local next = disc.next - if prev and next then - prev.next = next - -- next.prev = prev - local a = prev[0] - if a then - a = (a == attr) and (not attribute or prev[a_state] == attribute) - else - a = not attribute or prev[a_state] == attribute - end - if a then - local lookupmatch = lookupcache[prev.char] - if lookupmatch then - -- sequence kan weg - local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) - if ok then - done = true - success = true - end - end - end - prev.next = disc - -- next.prev = disc - end - return next - end - - while start do - local id = start.id - if id == glyph_code then - if start.font == font and start.subtype<256 then - local a = start[0] - if a then - a = (a == attr) and (not attribute or start[a_state] == attribute) - else - a = not attribute or start[a_state] == attribute - end - if a then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - -- sequence kan weg - local ok - head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) - if ok then - success = true - end - end - if start then start = start.next end - else - start = start.next - end - else - start = start.next - end - elseif id == disc_code then - -- mostly for gsub - if start.subtype == discretionary_code then - local pre = start.pre - if pre then - local new = subrun(pre) - if new then start.pre = new end - end - local post = start.post - if post then - local new = subrun(post) - if new then start.post = new end - end - local replace = start.replace - if replace then - local new = subrun(replace) - if new then start.replace = new end - end -elseif typ == "gpos_single" or typ == "gpos_pair" then - kerndisc(start) - end - start = start.next - elseif id == whatsit_code then -- will be function - local subtype = start.subtype - if subtype == dir_code then - local dir = start.dir - if dir == "+TRT" or dir == "+TLT" then - topstack = topstack + 1 - dirstack[topstack] = dir - elseif dir == "-TRT" or dir == "-TLT" then - topstack = topstack - 1 - end - local newdir = dirstack[topstack] - if newdir == "+TRT" then - rlmode = -1 - elseif newdir == "+TLT" then - rlmode = 1 - else - rlmode = rlparmode - end - if trace_directions then - report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) - end - elseif subtype == localpar_code then - local dir = start.dir - if dir == "TRT" then - rlparmode = -1 - elseif dir == "TLT" then - rlparmode = 1 - else - rlparmode = 0 - end - -- one might wonder if the par dir should be looked at, so we might as well drop the next line - rlmode = rlparmode - if trace_directions then - report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) - end - end - start = start.next - elseif id == math_code then - start = end_of_math(start).next - else - start = start.next - end - end - end - else - - local function subrun(start) - -- mostly for gsub, gpos would demand a more clever approach - local head = start - local done = false - while start do - local id = start.id - if id == glyph_code and start.id == font and start.subtype <256 then - local a = start[0] - if a then - a = (a == attr) and (not attribute or start[a_state] == attribute) - else - a = not attribute or start[a_state] == attribute - end - if a then - for i=1,ns do - local lookupname = subtables[i] - local lookupcache = lookuphash[lookupname] - if lookupcache then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - -- we could move all code inline but that makes things even more unreadable - local ok - head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if ok then - done = true - break - elseif not start then - -- don't ask why ... shouldn't happen - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - if start then start = start.next end - else - start = start.next - end - else - start = start.next - end - end - if done then - success = true - return head - end - end - - local function kerndisc(disc) -- we can assume that prev and next are glyphs - local prev = disc.prev - local next = disc.next - if prev and next then - prev.next = next - -- next.prev = prev - local a = prev[0] - if a then - a = (a == attr) and (not attribute or prev[a_state] == attribute) - else - a = not attribute or prev[a_state] == attribute - end - if a then - for i=1,ns do - local lookupname = subtables[i] - local lookupcache = lookuphash[lookupname] - if lookupcache then - local lookupmatch = lookupcache[prev.char] - if lookupmatch then - -- we could move all code inline but that makes things even more unreadable - local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if ok then - done = true - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - end - prev.next = disc - -- next.prev = disc - end - return next - end - - while start do - local id = start.id - if id == glyph_code then - if start.font == font and start.subtype<256 then - local a = start[0] - if a then - a = (a == attr) and (not attribute or start[a_state] == attribute) - else - a = not attribute or start[a_state] == attribute - end - if a then - for i=1,ns do - local lookupname = subtables[i] - local lookupcache = lookuphash[lookupname] - if lookupcache then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - -- we could move all code inline but that makes things even more unreadable - local ok - head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if ok then - success = true - break - elseif not start then - -- don't ask why ... shouldn't happen - break - end - end - else - report_missing_cache(typ,lookupname) - end - end - if start then start = start.next end - else - start = start.next - end - else - start = start.next - end - elseif id == disc_code then - -- mostly for gsub - if start.subtype == discretionary_code then - local pre = start.pre - if pre then - local new = subrun(pre) - if new then start.pre = new end - end - local post = start.post - if post then - local new = subrun(post) - if new then start.post = new end - end - local replace = start.replace - if replace then - local new = subrun(replace) - if new then start.replace = new end - end -elseif typ == "gpos_single" or typ == "gpos_pair" then - kerndisc(start) - end - start = start.next - elseif id == whatsit_code then - local subtype = start.subtype - if subtype == dir_code then - local dir = start.dir - if dir == "+TRT" or dir == "+TLT" then - topstack = topstack + 1 - dirstack[topstack] = dir - elseif dir == "-TRT" or dir == "-TLT" then - topstack = topstack - 1 - end - local newdir = dirstack[topstack] - if newdir == "+TRT" then - rlmode = -1 - elseif newdir == "+TLT" then - rlmode = 1 - else - rlmode = rlparmode - end - if trace_directions then - report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) - end - elseif subtype == localpar_code then - local dir = start.dir - if dir == "TRT" then - rlparmode = -1 - elseif dir == "TLT" then - rlparmode = 1 - else - rlparmode = 0 - end - rlmode = rlparmode - if trace_directions then - report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) - end - end - start = start.next - elseif id == math_code then - start = end_of_math(start).next - else - start = start.next - end - end - end - end - if success then - done = true - end - if trace_steps then -- ? - registerstep(head) - end - end - return head, done -end - -local function generic(lookupdata,lookupname,unicode,lookuphash) - local target = lookuphash[lookupname] - if target then - target[unicode] = lookupdata - else - lookuphash[lookupname] = { [unicode] = lookupdata } - end -end - -local action = { - - substitution = generic, - multiple = generic, - alternate = generic, - position = generic, - - ligature = function(lookupdata,lookupname,unicode,lookuphash) - local target = lookuphash[lookupname] - if not target then - target = { } - lookuphash[lookupname] = target - end - for i=1,#lookupdata do - local li = lookupdata[i] - local tu = target[li] - if not tu then - tu = { } - target[li] = tu - end - target = tu - end - target.ligature = unicode - end, - - pair = function(lookupdata,lookupname,unicode,lookuphash) - local target = lookuphash[lookupname] - if not target then - target = { } - lookuphash[lookupname] = target - end - local others = target[unicode] - local paired = lookupdata[1] - if others then - others[paired] = lookupdata - else - others = { [paired] = lookupdata } - target[unicode] = others - end - end, - -} - -local function prepare_lookups(tfmdata) - - local rawdata = tfmdata.shared.rawdata - local resources = rawdata.resources - local lookuphash = resources.lookuphash - local anchor_to_lookup = resources.anchor_to_lookup - local lookup_to_anchor = resources.lookup_to_anchor - local lookuptypes = resources.lookuptypes - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - - -- we cannot free the entries in the descriptions as sometimes we access - -- then directly (for instance anchors) ... selectively freeing does save - -- much memory as it's only a reference to a table and the slot in the - -- description hash is not freed anyway - - for unicode, character in next, characters do -- we cannot loop over descriptions ! - - local description = descriptions[unicode] - - if description then - - local lookups = description.slookups - if lookups then - for lookupname, lookupdata in next, lookups do - action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) - end - end - - local lookups = description.mlookups - if lookups then - for lookupname, lookuplist in next, lookups do - local lookuptype = lookuptypes[lookupname] - for l=1,#lookuplist do - local lookupdata = lookuplist[l] - action[lookuptype](lookupdata,lookupname,unicode,lookuphash) - end - end - end - - local list = description.kerns - if list then - for lookup, krn in next, list do -- ref to glyph, saves lookup - local target = lookuphash[lookup] - if target then - target[unicode] = krn - else - lookuphash[lookup] = { [unicode] = krn } - end - end - end - - local list = description.anchors - if list then - for typ, anchors in next, list do -- types - if typ == "mark" or typ == "cexit" then -- or entry? - for name, anchor in next, anchors do - local lookups = anchor_to_lookup[name] - if lookups then - for lookup, _ in next, lookups do - local target = lookuphash[lookup] - if target then - target[unicode] = anchors - else - lookuphash[lookup] = { [unicode] = anchors } - end - end - end - end - end - end - end - - end - - end - -end - -local function split(replacement,original) - local result = { } - for i=1,#replacement do - result[original[i]] = replacement[i] - end - return result -end - -local valid = { - coverage = { chainsub = true, chainpos = true, contextsub = true }, - reversecoverage = { reversesub = true }, - glyphs = { chainsub = true, chainpos = true }, -} - -local function prepare_contextchains(tfmdata) - local rawdata = tfmdata.shared.rawdata - local resources = rawdata.resources - local lookuphash = resources.lookuphash - local lookups = rawdata.lookups - if lookups then - for lookupname, lookupdata in next, rawdata.lookups do - local lookuptype = lookupdata.type - if lookuptype then - local rules = lookupdata.rules - if rules then - local format = lookupdata.format - local validformat = valid[format] - if not validformat then - report_prepare("unsupported format %a",format) - elseif not validformat[lookuptype] then - -- todo: dejavu-serif has one (but i need to see what use it has) - report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookupname) - else - local contexts = lookuphash[lookupname] - if not contexts then - contexts = { } - lookuphash[lookupname] = contexts - end - local t, nt = { }, 0 - for nofrules=1,#rules do - local rule = rules[nofrules] - local current = rule.current - local before = rule.before - local after = rule.after - local replacements = rule.replacements - local sequence = { } - local nofsequences = 0 - -- Eventually we can store start, stop and sequence in the cached file - -- but then less sharing takes place so best not do that without a lot - -- of profiling so let's forget about it. - if before then - for n=1,#before do - nofsequences = nofsequences + 1 - sequence[nofsequences] = before[n] - end - end - local start = nofsequences + 1 - for n=1,#current do - nofsequences = nofsequences + 1 - sequence[nofsequences] = current[n] - end - local stop = nofsequences - if after then - for n=1,#after do - nofsequences = nofsequences + 1 - sequence[nofsequences] = after[n] - end - end - if sequence[1] then - -- Replacements only happen with reverse lookups as they are single only. We - -- could pack them into current (replacement value instead of true) and then - -- use sequence[start] instead but it's somewhat ugly. - nt = nt + 1 - t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } - for unic, _ in next, sequence[start] do - local cu = contexts[unic] - if not cu then - contexts[unic] = t - end - end - end - end - end - else - -- no rules - end - else - report_prepare("missing lookuptype for lookupname %a",lookupname) - end - end - end -end - --- we can consider lookuphash == false (initialized but empty) vs lookuphash == table - -local function featuresinitializer(tfmdata,value) - if true then -- value then - -- beware we need to use the topmost properties table - local rawdata = tfmdata.shared.rawdata - local properties = rawdata.properties - if not properties.initialized then - local starttime = trace_preparing and os.clock() - local resources = rawdata.resources - resources.lookuphash = resources.lookuphash or { } - prepare_contextchains(tfmdata) - prepare_lookups(tfmdata) - properties.initialized = true - if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname) - end - end - end -end - -registerotffeature { - name = "features", - description = "features", - default = true, - initializers = { - position = 1, - node = featuresinitializer, - }, - processors = { - node = featuresprocessor, - } -} - --- This can be used for extra handlers, but should be used with care! - -otf.handlers = handlers diff --git a/luaotfload-fonts-tfm.lua b/luaotfload-fonts-tfm.lua deleted file mode 100644 index b9bb1bd..0000000 --- a/luaotfload-fonts-tfm.lua +++ /dev/null @@ -1,38 +0,0 @@ -if not modules then modules = { } end modules ['luatex-fonts-tfm'] = { - version = 1.001, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end - -local fonts = fonts -local tfm = { } -fonts.handlers.tfm = tfm -fonts.formats.tfm = "type1" -- we need to have at least a value here - -function fonts.readers.tfm(specification) - local fullname = specification.filename or "" - if fullname == "" then - local forced = specification.forced or "" - if forced ~= "" then - fullname = specification.name .. "." .. forced - else - fullname = specification.name - end - end - local foundname = resolvers.findbinfile(fullname, 'tfm') or "" - if foundname == "" then - foundname = resolvers.findbinfile(fullname, 'ofm') or "" - end - if foundname ~= "" then - specification.filename = foundname - specification.format = "ofm" - return font.read_tfm(specification.filename,specification.size) - end -end diff --git a/luaotfload-letterspace.lua b/luaotfload-letterspace.lua deleted file mode 100644 index 20f29f5..0000000 --- a/luaotfload-letterspace.lua +++ /dev/null @@ -1,544 +0,0 @@ -if not modules then modules = { } end modules ['letterspace'] = { - version = "2.5", - comment = "companion to luaotfload-main.lua", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL; adapted by Philipp Gesang", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local log = luaotfload.log -local report = log.report - -local getmetatable = getmetatable -local require = require -local setmetatable = setmetatable -local tonumber = tonumber - -local next = next -local nodes, node, fonts = nodes, node, fonts - -local find_node_tail = node.tail or node.slide -local free_node = node.free -local copy_node = node.copy -local new_node = node.new -local insert_node_before = node.insert_before - -local nodepool = nodes.pool - -local new_kern = nodepool.kern -local new_glue = nodepool.glue - -local nodecodes = nodes.nodecodes - -local glyph_code = nodecodes.glyph -local kern_code = nodecodes.kern -local disc_code = nodecodes.disc -local math_code = nodecodes.math - -local fonthashes = fonts.hashes -local chardata = fonthashes.characters -local quaddata = fonthashes.quads -local otffeatures = fonts.constructors.newfeatures "otf" - ---[[doc-- - - Since the letterspacing method was derived initially from Context’s - typo-krn.lua we keep the sub-namespace “letterspace” inside the - “luaotfload” table. - ---doc]]-- - -luaotfload.letterspace = luaotfload.letterspace or { } -local letterspace = luaotfload.letterspace - -letterspace.keepligature = false -letterspace.keeptogether = false - ----=================================================================--- ---- preliminary definitions ----=================================================================--- --- We set up a layer emulating some Context internals that are needed --- for the letterspacing callback. ------------------------------------------------------------------------ ---- node-ini ------------------------------------------------------------------------ - -local bothways = function (t) return table.swapped (t, t) end -local kerncodes = bothways { [0] = "fontkern" - , [1] = "userkern" - , [2] = "accentkern" - } - -kerncodes.kerning = kerncodes.fontkern --- idiosyncrasy -local kerning_code = kerncodes.kerning -local userkern_code = kerncodes.userkern - - ------------------------------------------------------------------------ ---- node-res ------------------------------------------------------------------------ - -nodes.pool = nodes.pool or { } -local pool = nodes.pool - -local kern = new_node ("kern", kerncodes.userkern) -local glue_spec = new_node "glue_spec" - -pool.kern = function (k) - local n = copy_node (kern) - n.kern = k - return n -end - -pool.glue = function (width, stretch, shrink, - stretch_order, shrink_order) - local n = new_node"glue" - if not width then - -- no spec - elseif width == false or tonumber(width) then - local s = copy_node(glue_spec) - if width then s.width = width end - if stretch then s.stretch = stretch end - if shrink then s.shrink = shrink end - if stretch_order then s.stretch_order = stretch_order end - if shrink_order then s.shrink_order = shrink_order end - n.spec = s - else - -- shared - n.spec = copy_node(width) - end - return n -end - ------------------------------------------------------------------------ ---- font-hsh ------------------------------------------------------------------------ ---- some initialization resembling font-hsh -local fonthashes = fonts.hashes -local identifiers = fonthashes.identifiers --- was: fontdata -local chardata = fonthashes.characters -local quaddata = fonthashes.quads -local parameters = fonthashes.parameters - ---- ('a, 'a) hash -> (('a, 'a) hash -> 'a -> 'a) -> ('a, 'a) hash -local setmetatableindex = function (t, f) - local mt = getmetatable(t) - if mt then - mt.__index = f - else - setmetatable(t, { __index = f }) - end - return t -end - -if not parameters then - parameters = { } - setmetatableindex(parameters, function(t, k) - if k == true then - return parameters[currentfont()] - else - local parameters = identifiers[k].parameters - t[k] = parameters - return parameters - end - end) - --fonthashes.parameters = parameters -end - -if not chardata then - chardata = { } - setmetatableindex(chardata, function(t, k) - if k == true then - return chardata[currentfont()] - else - local tfmdata = identifiers[k] - if not tfmdata then --- unsafe - tfmdata = font.fonts[k] - end - if tfmdata then - local characters = tfmdata.characters - t[k] = characters - return characters - end - end - end) - fonthashes.characters = chardata -end - -if not quaddata then - quaddata = { } - setmetatableindex(quaddata, function(t, k) - if k == true then - return quads[currentfont()] - else - local parameters = parameters[k] - local quad = parameters and parameters.quad or 0 - t[k] = quad - return quad - end - end) - --fonthashes.quads = quaddata -end - ----=================================================================--- ---- character kerning functionality ----=================================================================--- - -local kern_injector = function (fillup, kern) - if fillup then - local g = new_glue(kern) - local s = g.spec - s.stretch = kern - s.stretch_order = 1 - return g - else - return new_kern(kern) - end -end - ---[[doc-- - - Caveat lector. - This is an adaptation of the Context character kerning mechanism - that emulates XeTeX-style fontwise letterspacing. Note that in its - present state it is far inferior to the original, which is - attribute-based and ignores font-boundaries. Nevertheless, due to - popular demand the following callback has been added. - ---doc]]-- - -local kernfactors = { } --- fontid -> factor - -local kerncharacters -kerncharacters = function (head) - local start, done = head, false - local lastfont = nil - local keepligature = letterspace.keepligature --- function - local keeptogether = letterspace.keeptogether --- function - local fillup = false - - local identifiers = fonthashes.identifiers - local kernfactors = kernfactors - - local firstkern = true - - while start do - local id = start.id - if id == glyph_code then - - --- 1) look up kern factor (slow, but cached rudimentarily) - local krn - local fontid = start.font - do - krn = kernfactors[fontid] - if not krn then - local tfmdata = identifiers[fontid] - if not tfmdata then -- unsafe - tfmdata = font.fonts[fontid] - end - if tfmdata then - fontproperties = tfmdata.properties - if fontproperties then - krn = fontproperties.kerncharacters - end - end - kernfactors[fontid] = krn - end - if not krn or krn == 0 then - firstkern = true - goto nextnode - elseif firstkern then - firstkern = false - if (id ~= disc_code) and (not start.components) then - --- not a ligature, skip node - goto nextnode - end - end - end - - if krn == "max" then - krn = .25 - fillup = true - else - fillup = false - end - - lastfont = fontid - - --- 2) resolve ligatures - local c = start.components - - if c then - if keepligature and keepligature(start) then - -- keep 'm - else - --- c = kerncharacters (c) --> taken care of after replacing - local s = start - local p, n = s.prev, s.next - local tail = find_node_tail(c) - if p then - p.next = c - c.prev = p - else - head = c - end - if n then - n.prev = tail - end - tail.next = n - start = c - s.components = nil - -- we now leak nodes ! - -- free_node(s) - done = true - end - end -- kern ligature - - --- 3) apply the extra kerning - local prev = start.prev - if prev then - local pid = prev.id - - if not pid then - -- nothing - - elseif pid == kern_code then - if prev.subtype == kerning_code --- context does this by means of an - or prev.subtype == userkern_code --- attribute; we may need a test - then - if keeptogether and prev.prev.id == glyph_code and keeptogether(prev.prev,start) then - -- keep - else - prev.subtype = userkern_code - prev.kern = prev.kern + quaddata[lastfont]*krn -- here - done = true - end - end - - elseif pid == glyph_code then - if prev.font == lastfont then - local prevchar, lastchar = prev.char, start.char - if keeptogether and keeptogether(prev,start) then - -- keep 'm - elseif identifiers[lastfont] then - local kerns = chardata[lastfont][prevchar].kerns - local kern = kerns and kerns[lastchar] or 0 - krn = kern + quaddata[lastfont]*krn -- here - insert_node_before(head,start,kern_injector(fillup,krn)) - done = true - end - else - krn = quaddata[lastfont]*krn -- here - insert_node_before(head,start,kern_injector(fillup,krn)) - done = true - end - - elseif pid == disc_code then - -- a bit too complicated, we can best not copy and just calculate - -- but we could have multiple glyphs involved so ... - local disc = prev -- disc - local pre, post, replace = disc.pre, disc.post, disc.replace - local prv, nxt = disc.prev, disc.next - - if pre and prv then -- must pair with start.prev - -- this one happens in most cases - local before = copy_node(prv) - pre.prev = before - before.next = pre - before.prev = nil - pre = kerncharacters (before) - pre = pre.next - pre.prev = nil - disc.pre = pre - free_node(before) - end - - if post and nxt then -- must pair with start - local after = copy_node(nxt) - local tail = find_node_tail(post) - tail.next = after - after.prev = tail - after.next = nil - post = kerncharacters (post) - tail.next = nil - disc.post = post - free_node(after) - end - - if replace and prv and nxt then -- must pair with start and start.prev - local before = copy_node(prv) - local after = copy_node(nxt) - local tail = find_node_tail(replace) - replace.prev = before - before.next = replace - before.prev = nil - tail.next = after - after.prev = tail - after.next = nil - replace = kerncharacters (before) - replace = replace.next - replace.prev = nil - after.prev.next = nil - disc.replace = replace - free_node(after) - free_node(before) - elseif identifiers[lastfont] then - if prv and prv.id == glyph_code and prv.font == lastfont then - local prevchar, lastchar = prv.char, start.char - local kerns = chardata[lastfont][prevchar].kerns - local kern = kerns and kerns[lastchar] or 0 - krn = kern + quaddata[lastfont]*krn -- here - else - krn = quaddata[lastfont]*krn -- here - end - disc.replace = kern_injector(false,krn) -- only kerns permitted, no glue - end - - end - end - end - - ::nextnode:: - if start then - start = start.next - end - end - return head, done -end - ----=================================================================--- ---- integration ----=================================================================--- - ---- · callback: kerncharacters ---- · enabler: enablefontkerning ---- · disabler: disablefontkerning - ---- callback wrappers - ---- (node_t -> node_t) -> string -> string list -> bool -local registered_as = { } --- procname -> callbacks -local add_processor = function (processor, name, ...) - local callbacks = { ... } - for i=1, #callbacks do - luatexbase.add_to_callback(callbacks[i], processor, name) - end - registered_as[name] = callbacks --- for removal - return true -end - ---- string -> bool -local remove_processor = function (name) - local callbacks = registered_as[name] - if callbacks then - for i=1, #callbacks do - luatexbase.remove_from_callback(callbacks[i], name) - end - return true - end - return false --> unregistered -end - ---- now for the simplistic variant ---- unit -> bool -local enablefontkerning = function ( ) - return add_processor( kerncharacters - , "luaotfload.letterspace" - , "pre_linebreak_filter" - , "hpack_filter") -end - ---- unit -> bool -local disablefontkerning = function ( ) - return remove_processor "luaotfload.letterspace" -end - ---[[doc-- - - Fontwise kerning is enabled via the “kernfactor” option at font - definition time. Unlike the Context implementation which relies on - Luatex attributes, it uses a font property for passing along the - letterspacing factor of a node. - - The callback is activated the first time a letterspaced font is - requested and stays active until the end of the run. Since the font - is a property of individual glyphs, every glyph in the entire - document must be checked for the kern property. This is quite - inefficient compared to Context’s attribute based approach, but Xetex - compatibility reduces our options significantly. - ---doc]]-- - - -local fontkerning_enabled = false --- callback state - ---- fontobj -> float -> unit -local initializefontkerning = function (tfmdata, factor) - if factor ~= "max" then - factor = tonumber (factor) or 0 - end - if factor == "max" or factor ~= 0 then - local fontproperties = tfmdata.properties - if fontproperties then - --- hopefully this field stays unused otherwise - fontproperties.kerncharacters = factor - end - if not fontkerning_enabled then - fontkerning_enabled = enablefontkerning () - end - end -end - ---- like the font colorization, fontwise kerning is hooked into the ---- feature mechanism - -otffeatures.register { - name = "kernfactor", - description = "kernfactor", - initializers = { - base = initializefontkerning, - node = initializefontkerning, - } -} - ---[[doc-- - - The “letterspace” feature is essentially identical with the above - “kernfactor” method, but scales the factor to percentages to match - Xetex’s behavior. (See the Xetex reference, page 5, section 1.2.2.) - - Since Xetex doesn’t appear to have a (documented) “max” keyword, we - assume all input values are numeric. - ---doc]]-- - -local initializecompatfontkerning = function (tfmdata, percentage) - local factor = tonumber (percentage) - if not factor then - report ("both", 0, "letterspace", - "Invalid argument to letterspace: %s (type %q), " .. - "was expecting percentage as Lua number instead.", - percentage, type (percentage)) - return - end - return initializefontkerning (tfmdata, factor * 0.01) -end - -otffeatures.register { - name = "letterspace", - description = "letterspace", - initializers = { - base = initializecompatfontkerning, - node = initializecompatfontkerning, - } -} - ---[[example-- - -See https://bitbucket.org/phg/lua-la-tex-tests/src/tip/pln-letterspace-8-compare.tex -for an example. - ---example]]-- - ---- vim:sw=2:ts=2:expandtab:tw=71 - diff --git a/luaotfload-loaders.lua b/luaotfload-loaders.lua deleted file mode 100644 index 2aa8c7c..0000000 --- a/luaotfload-loaders.lua +++ /dev/null @@ -1,30 +0,0 @@ -if not modules then modules = { } end modules ["loaders"] = { - version = "2.5", - comment = "companion to luaotfload-main.lua", - author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local fonts = fonts -local readers = fonts.readers -local handlers = fonts.handlers -local formats = fonts.formats - -local pfb_reader = function (specification) - return readers.opentype (specification, "pfb", "type1") -end - -local pfa_reader = function (specification) - return readers.opentype (specification, "pfa", "type1") -end - -formats.pfa = "type1" -readers.pfa = pfa_reader -handlers.pfa = { } - -formats.pfb = "type1" -readers.pfb = pfb_reader -handlers.pfb = { } - --- vim:tw=71:sw=2:ts=2:expandtab diff --git a/luaotfload-log.lua b/luaotfload-log.lua deleted file mode 100644 index 5698c84..0000000 --- a/luaotfload-log.lua +++ /dev/null @@ -1,404 +0,0 @@ -if not modules then modules = { } end modules ["luaotfload-log"] = { - version = "2.5", - comment = "companion to Luaotfload", - author = "Khaled Hosny, Elie Roux, Philipp Gesang", - copyright = "Luaotfload Development Team", - license = "GNU GPL v2.0" -} - ---[[doc-- -The logging system is slow in general, as we always have the function -call overhead even if we aren’t going to output anything. On the other -hand, the more efficient approach followed by Context isn’t an option -because we lack a user interface to toggle per-subsystem tracing. ---doc]]-- - -local module_name = "luaotfload" --- prefix for messages - -luaotfload = luaotfload or { } -luaotfload.log = luaotfload.log or { } -local log = luaotfload.log - -local ioopen = io.open -local iowrite = io.write -local lfsisdir = lfs.isdir -local lfsisfile = lfs.isfile -local osdate = os.date -local ostime = os.time -local osuuid = os.uuid -local select = select -local stringformat = string.format -local stringsub = string.sub -local tableconcat = table.concat -local texiowrite_nl = texio.write_nl -local texiowrite = texio.write -local type = type - -local dummyfunction = function () end - -local texjob = false -if tex and (tex.jobname or tex.formatname) then - --- TeX - texjob = true -end - -local loglevel = 0 --- default -local logout = "log" - ---- int -> bool -local set_loglevel = function (n) - if type(n) == "number" then - loglevel = n - end - return true -end -log.set_loglevel = set_loglevel - ---- unit -> int -local get_loglevel = function ( ) - return loglevel -end -log.get_loglevel = get_loglevel - -local writeln --- pointer to terminal/log writer -local statusln --- terminal writer that reuses the current line -local first_status = true --- indicate the begin of a status region - -local log_msg = [[ -logging output redirected to %s -to monitor the progress run "tail -f %s" in another terminal -]] - -local tmppath = os.getenv "TMPDIR" or "/tmp" - -local choose_logfile = function ( ) - if lfsisdir (tmppath) then - local fname - repeat --- ensure that file of that name doesn’t exist - fname = tmppath .. "/luaotfload-log-" .. osuuid() - until not lfsisfile (fname) - iowrite (stringformat (log_msg, fname, fname)) - return ioopen (fname, "w") - end - --- missing /tmp - return false -end - -local set_logout = function (s, finalizers) - if s == "stdout" then - logout = "redirect" - elseif s == "file" then --- inject custom logger - logout = "redirect" - local chan = choose_logfile () - chan:write (stringformat ("logging initiated at %s", - osdate ("%F %T", ostime ()))) - local writefile = function (...) - if select ("#", ...) == 2 then - chan:write (select (2, ...)) - else - chan:write (select (1, ...)) - end - end - local writefile_nl= function (...) - chan:write "\n" - if select ("#", ...) == 2 then - chan:write (select (2, ...)) - else - chan:write (select (1, ...)) - end - end - - local writeln_orig = writeln - - texiowrite = writefile - texiowrite_nl = writefile_nl - writeln = writefile_nl - statusln = dummyfunction - - finalizers[#finalizers+1] = function () - chan:write (stringformat ("\nlogging finished at %s\n", - osdate ("%F %T", ostime ()))) - chan:close () - texiowrite = texio.write - texiowrite_nl = texio.write_nl - writeln = writeln_orig - end - --else --- remains “log” - end - return finalizers -end - -log.set_logout = set_logout - -local basic_logger = function (category, fmt, ...) - local res = { module_name, "|", category, ":" } - if fmt then - res [#res + 1] = stringformat (fmt, ...) - end - texiowrite_nl (logout, tableconcat(res, " ")) -end - ---- with faux db update with maximum verbosity: ---- ---- --------- -------- ---- buffering time (s) ---- --------- -------- ---- full 4.12 ---- line 4.20 ---- none 4.39 ---- --------- -------- ---- - -io.stdout:setvbuf "no" -io.stderr:setvbuf "no" - -local kill_line = "\r\x1b[K" - -if texjob == true then - --- We imitate the texio.* functions so the output is consistent. - writeln = function (str) - iowrite "\n" - iowrite(str) - end - statusln = function (str) - if first_status == false then - iowrite (kill_line) - else - iowrite "\n" - end - iowrite (str) - end -else - writeln = function (str) - iowrite(str) - iowrite "\n" - end - statusln = function (str) - if first_status == false then - iowrite (kill_line) - end - iowrite (str) - end -end - -stdout = function (writer, category, ...) - local res = { module_name, "|", category, ":" } - local nargs = select("#", ...) - if nargs == 0 then - --writeln tableconcat(res, " ") - --return - elseif nargs == 1 then - res[#res+1] = select(1, ...) -- around 30% faster than unpack() - else - res[#res+1] = stringformat(...) - end - writer (tableconcat(res, " ")) -end - ---- at default (zero), we aim to be quiet -local level_ids = { common = 1, loading = 2, search = 3 } - ---[[doc-- - - The report() logger is used more or less all over luaotfload. - Its requirements are twofold: - - 1) Provide two logging channels, the terminal and the log file; - 2) Allow for control over verbosity levels. - - The first part is addressed by specifying the log *mode* as the - first argument that can be either “log”, meaning the log file, or - “both”: log file and stdout. Anything else is taken as referring to - stdout only. - - Verbosity levels, though not as fine-grained as e.g. Context’s - system of tracers, allow keeping the logging spam caused by - different subsystems manageable. By default, luaotfload will not - emit anything if things are running smoothly on level zero. Only - warning messages are relayed, while the other messages are skipped - over. (This is a little sub-optimal performance-wise since the - function calls to the logger are executed regardless.) The log - level during a Luatex run can be adjusted by setting the “loglevel” - field in config.luaotfload, or by calling log.set_loglevel() as - defined above. - ---doc]]-- - -local report = function (mode, lvl, ...) - if type(lvl) == "string" then - lvl = level_ids[lvl] - end - if not lvl then lvl = 0 end - - if loglevel >= lvl then - if mode == "log" then - basic_logger (...) - elseif mode == "both" and logout ~= "redirect" then - basic_logger (...) - stdout (writeln, ...) - else - stdout (writeln, ...) - end - end -end - -log.report = report - ---[[doc-- - - status_logger -- Overwrites the most recently printed line of the - terminal. Its purpose is to provide feedback without spamming - stdout with irrelevant messages, i.e. when building the database. - - Status logging must be initialized by calling status_start() and - properly reset via status_stop(). - - The arguments low and high indicate the loglevel threshold at which - linewise and full logging is triggered, respectively. E.g. - - names_status (1, 4, "term", "Hello, world!") - - will print nothing if the loglevel is less than one, reuse the - current line if the loglevel ranges from one to three inclusively, - and output the message on a separate line otherwise. - ---doc]]-- - -local status_logger = function (mode, ...) - if mode == "log" then - basic_logger (...) - else - if mode == "both" and logout ~= "redirect" then - basic_logger (...) - stdout (statusln, ...) - else - stdout (statusln, ...) - end - first_status = false - end -end - ---[[doc-- - - status_start -- Initialize status logging. This installs the status - logger if the loglevel is in the specified range, and the normal - logger otherwise. It also resets the first line state which - causing the next line printed using the status logger to not kill - the current line. - ---doc]]-- - -local status_writer -local status_low = 99 -local status_high = 99 - -local status_start = function (low, high) - first_status = true - status_low = low - status_high = high - - if os.type == "windows" --- Assume broken terminal. - or os.getenv "TERM" == "dumb" - then - status_writer = function (mode, ...) - report (mode, high, ...) - end - return - end - - if low <= loglevel and loglevel < high then - status_writer = status_logger - else - status_writer = function (mode, ...) - report (mode, high, ...) - end - end -end - ---[[doc-- - - status_stop -- Finalize a status region by outputting a newline and - printing a message. - ---doc]]-- - -local status_stop = function (...) - if first_status == false then - status_writer(...) - if texjob == false then - writeln "" - end - end -end - -log.names_status = function (...) status_writer (...) end -log.names_status_start = status_start -log.names_status_stop = status_stop - ---[[doc-- - - The fontloader comes with the Context logging mechanisms - inaccessible. Instead, it provides dumb fallbacks based - on the functions in texio.write*() that can be overridden - by providing a function texio.reporter(). - - The fontloader output can be quite verbose, so we disable - it entirely by default. - ---doc]]-- - -local texioreporter = function (message) - report ("log", 2, message) -end - -texio.reporter = texioreporter - ---[[doc-- - - Adobe Glyph List. - ------------------------------------------------------------------- - - Context provides a somewhat different font-age.lua from an unclear - origin. Unfortunately, the file name it reads from is hard-coded - in font-enc.lua, so we have to replace the entire table. - - This shouldn’t cause any complications. Due to its implementation - the glyph list will be loaded upon loading a OTF or TTF for the - first time during a TeX run. (If one sticks to TFM/OFM then it is - never read at all.) For this reason we can install a metatable that - looks up the file of our choosing and only falls back to the - Context one in case it cannot be found. - ---doc]]-- - -if fonts then --- need to be running TeX - if next(fonts.encodings.agl) then - --- unnecessary because the file shouldn’t be loaded at this time - --- but we’re just making sure - fonts.encodings.agl = nil - collectgarbage"collect" - end - - - fonts.encodings.agl = { } - - setmetatable(fonts.encodings.agl, { __index = function (t, k) - if k == "unicodes" then - local glyphlist = resolvers.findfile"luaotfload-glyphlist.lua" - if glyphlist then - report ("log", 1, "load", "loading the Adobe glyph list") - else - glyphlist = resolvers.findfile"font-age.lua" - report ("both", 0, "load", - "loading the extended glyph list from ConTeXt") - end - local unicodes = dofile(glyphlist) - fonts.encodings.agl = { unicodes = unicodes } - return unicodes - else - return nil - end - end }) -end - --- vim:tw=71:sw=4:ts=4:expandtab diff --git a/luaotfload-main.lua b/luaotfload-main.lua deleted file mode 100644 index f5f012d..0000000 --- a/luaotfload-main.lua +++ /dev/null @@ -1,711 +0,0 @@ ------------------------------------------------------------------------ --- FILE: luaotfload-main.lua --- DESCRIPTION: Luatex fontloader initialization --- REQUIREMENTS: luatex v.0.78 or later, the lualibs package --- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang --- VERSION: same as Luaotfload --- MODIFIED: 2014-02-09 14:42:22+0100 ------------------------------------------------------------------------ --- ---- Note: ---- This file was part of the original luaotfload.dtx and has been ---- converted to a pure Lua file during the transition from Luaotfload ---- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex) ---- markup. - -if not modules then modules = { } end modules ["luaotfload-main"] = { - version = "2.5", - comment = "fontloader initialization", - author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "GNU General Public License v. 2.0" -} - - ---[[doc-- - - This file initializes the system and loads the font loader. To - minimize potential conflicts between other packages and the code - imported from \CONTEXT, several precautions are in order. Some of - the functionality that the font loader expects to be present, like - raw access to callbacks, are assumed to have been disabled by - \identifier{luatexbase} when this file is processed. In some cases - it is possible to trick it by putting dummies into place and - restoring the behavior from \identifier{luatexbase} after - initilization. Other cases such as attribute allocation require - that we hook the functionality from \identifier{luatexbase} into - locations where they normally wouldn’t be. - - Anyways we can import the code base without modifications, which is - due mostly to the extra effort by Hans Hagen to make \LUATEX-Fonts - self-contained and encapsulate it, and especially due to his - willingness to incorporate our suggestions. - ---doc]]-- - -luaotfload = luaotfload or { } -local luaotfload = luaotfload -luaotfload.log = luaotfload.log or { } - -config = config or { } -config.luaotfload = config.luaotfload or { } -------.luaotfload.resolver = config.luaotfload.resolver or "normal" -config.luaotfload.resolver = config.luaotfload.resolver or "cached" -config.luaotfload.definer = config.luaotfload.definer or "patch" -config.luaotfload.loglevel = config.luaotfload.loglevel or 2 -config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter" -config.luaotfload.prioritize = config.luaotfload.prioritize or "sys" -config.luaotfload.names_dir = config.luaotfload.names_dir or "names" -config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts" -config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua" -config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" - -if not config.luaotfload.strip then - config.luaotfload.strip = true -end - -luaotfload.module = { - name = "luaotfload", - version = 2.50000, - date = "2014/**/**", - description = "OpenType layout system.", - author = "Elie Roux & Hans Hagen", - copyright = "Elie Roux", - license = "GPL v2.0" -} - -local luatexbase = luatexbase - -local setmetatable = setmetatable -local type, next = type, next - -local kpsefind_file = kpse.find_file -local lfsisfile = lfs.isfile - -local add_to_callback, create_callback = - luatexbase.add_to_callback, luatexbase.create_callback -local reset_callback, call_callback = - luatexbase.reset_callback, luatexbase.call_callback - -local dummy_function = function () end - -local error, warning, info, log = - luatexbase.provides_module(luaotfload.module) - -luaotfload.log.tex = { - error = error, - warning = warning, - info = info, - log = log, -} - ---[[doc-- - - We set the minimum version requirement for \LUATEX to v0.76, - because the font loader requires recent features like direct - attribute indexing and \luafunction{node.end_of_math()} that aren’t - available in earlier versions.\footnote{% - See Taco’s announcement of v0.76: - \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042} - and this commit by Hans that introduced those features. - \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}. - } - ---doc]]-- - -local luatex_version = 76 - -if tex.luatexversion < luatex_version then - warning("LuaTeX v%.2f is old, v%.2f is recommended.", - tex.luatexversion/100, - luatex_version /100) - --- we install a fallback for older versions as a safety - if not node.end_of_math then - local math_t = node.id"math" - local traverse_nodes = node.traverse_id - node.end_of_math = function (n) - for n in traverse_nodes(math_t, n.next) do - return n - end - end - end -end - ---[[doc-- - - \subsection{Module loading} - We load the files imported from \CONTEXT with this function. It - automatically prepends the prefix \fileent{luaotfload-} to its - argument, so we can refer to the files with their actual \CONTEXT - name. - ---doc]]-- - -local fl_prefix = "luaotfload" -- “luatex” for luatex-plain -local loadmodule = function (name) - require(fl_prefix .."-"..name) -end - -loadmodule "log.lua" --- messages; used to be part of -override -local log = luaotfload.log -local report = log.report - -log.set_loglevel(config.luaotfload.loglevel) - ---[[doc-- - - Before \TeX Live 2013 version, \LUATEX had a bug that made ofm fonts - fail when called with their extension. There was a side-effect making - ofm totally unloadable when luaotfload was present. The following - lines are a patch for this bug. The utility of these lines is - questionable as they are not necessary since \TeX Live 2013. They - should be removed in the next version. - ---doc]]-- - -local Cs, P, lpegmatch = lpeg.Cs, lpeg.P, lpeg.match - -local p_dot, p_slash = P".", P"/" -local p_suffix = (p_dot * (1 - p_dot - p_slash)^1 * P(-1)) / "" -local p_removesuffix = Cs((p_suffix + 1)^1) - -local find_vf_file = function (name) - local fullname = kpsefind_file(name, "ovf") - if not fullname then - --fullname = kpsefind_file(file.removesuffix(name), "ovf") - fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf") - end - if fullname then - report ("log", 0, "main", - "loading virtual font file %s.", fullname) - end - return fullname -end - ---[[doc-- - - \subsection{Preparing the Font Loader} - We treat the fontloader as a black box so behavior is consistent - between formats. - We load the fontloader code directly in the same fashion as the - Plain format \identifier{luatex-fonts} that is part of Context. - How this is executed depends on the presence on the - \emphasis{merged font loader code}. - In \identifier{luaotfload} this is contained in the file - \fileent{luaotfload-merged.lua}. - If this file cannot be found, the original libraries from \CONTEXT - of which the merged code was composed are loaded instead. - Since these files are not shipped with Luaotfload, an installation - of Context is required. - (Since we pull the fontloader directly from the Context minimals, - the necessary Context version is likely to be more recent than that - of other TeX distributions like Texlive.) - The imported font loader will call \luafunction{callback.register} - once while reading \fileent{font-def.lua}. - This is unavoidable unless we modify the imported files, but - harmless if we make it call a dummy instead. - However, this problem might vanish if we decide to do the merging - ourselves, like the \identifier{lualibs} package does. - With this step we would obtain the freedom to load our own - overrides in the process right where they are needed, at the cost - of losing encapsulation. - The decision on how to progress is currently on indefinite hold. - ---doc]]-- - -local starttime = os.gettimeofday() - -local trapped_register = callback.register -callback.register = dummy_function - ---[[doc-- - - By default, the fontloader requires a number of \emphasis{private - attributes} for internal use. - These must be kept consistent with the attribute handling methods - as provided by \identifier{luatexbase}. - Our strategy is to override the function that allocates new - attributes before we initialize the font loader, making it a - wrapper around \luafunction{luatexbase.new_attribute}.\footnote{% - Many thanks, again, to Hans Hagen for making this part - configurable! - } - The attribute identifiers are prefixed “\fileent{luaotfload@}” to - avoid name clashes. - ---doc]]-- - -do - local new_attribute = luatexbase.new_attribute - local the_attributes = luatexbase.attributes - - attributes = attributes or { } - - attributes.private = function (name) - local attr = "luaotfload@" .. name --- used to be: “otfl@” - local number = the_attributes[attr] - if not number then - number = new_attribute(attr) - end - return number - end -end - ---[[doc-- - - These next lines replicate the behavior of - \fileent{luatex-fonts.lua}. - ---doc]]-- - -local context_environment = { } - -local push_namespaces = function () - report ("log", 1, "main", "push namespace for font loader") - local normalglobal = { } - for k, v in next, _G do - normalglobal[k] = v - end - return normalglobal -end - -local pop_namespaces = function (normalglobal, isolate) - if normalglobal then - local _G = _G - local mode = "non-destructive" - if isolate then mode = "destructive" end - report ("log", 1, "main", "pop namespace from font loader -- " .. mode) - for k, v in next, _G do - if not normalglobal[k] then - context_environment[k] = v - if isolate then - _G[k] = nil - end - end - end - for k, v in next, normalglobal do - _G[k] = v - end - -- just to be sure: - setmetatable(context_environment,_G) - else - report ("both", 0, "main", - "irrecoverable error during pop_namespace: no globals to restore") - os.exit() - end -end - -luaotfload.context_environment = context_environment -luaotfload.push_namespaces = push_namespaces -luaotfload.pop_namespaces = pop_namespaces - -local our_environment = push_namespaces() - ---[[doc-- - - The font loader requires that the attribute with index zero be - zero. We happily oblige. - (Cf. \fileent{luatex-fonts-nod.lua}.) - ---doc]]-- - -tex.attribute[0] = 0 - ---[[doc-- - - Now that things are sorted out we can finally load the fontloader. - ---doc]]-- - -loadmodule "fontloader.lua" ----loadmodule"font-odv.lua" --- <= Devanagari support from Context - -if fonts then - - if not fonts._merge_loaded_message_done_ then - report ("log", 0, "main", [["I am using the merged fontloader here.]]) - report ("log", 0, "main", [[ If you run into problems or experience unexpected]]) - report ("log", 0, "main", [[ behaviour, and if you have ConTeXt installed you can try]]) - report ("log", 0, "main", [[ to delete the file 'luaotfload-fontloader.lua' as I might]]) - report ("log", 0, "main", [[ then use the possibly updated libraries. The merged]]) - report ("log", 0, "main", [[ version is not supported as it is a frozen instance.]]) - report ("log", 0, "main", [[ Problems can be reported to the ConTeXt mailing list."]]) - end - fonts._merge_loaded_message_done_ = true - -else--- the loading sequence is known to change, so this might have to - --- be updated with future updates! - --- do not modify it though unless there is a change to the merged - --- package! - loadmodule("l-lua.lua") - loadmodule("l-lpeg.lua") - loadmodule("l-function.lua") - loadmodule("l-string.lua") - loadmodule("l-table.lua") - loadmodule("l-io.lua") - loadmodule("l-file.lua") - loadmodule("l-boolean.lua") - loadmodule("l-math.lua") - loadmodule("util-str.lua") - loadmodule('luatex-basics-gen.lua') - loadmodule('data-con.lua') - loadmodule('luatex-basics-nod.lua') - loadmodule('font-ini.lua') - loadmodule('font-con.lua') - loadmodule('luatex-fonts-enc.lua') - loadmodule('font-cid.lua') - loadmodule('font-map.lua') - loadmodule('luatex-fonts-syn.lua') - loadmodule('luatex-fonts-tfm.lua') - loadmodule('font-oti.lua') - loadmodule('font-otf.lua') - loadmodule('font-otb.lua') - loadmodule('luatex-fonts-inj.lua') --> since 2014-01-07, replaces node-inj.lua - loadmodule('font-ota.lua') - loadmodule('luatex-fonts-otn.lua') --> since 2014-01-07, replaces font-otn.lua - loadmodule('font-otp.lua') --> since 2013-04-23 - loadmodule('luatex-fonts-lua.lua') - loadmodule('font-def.lua') - loadmodule('luatex-fonts-def.lua') - loadmodule('luatex-fonts-ext.lua') - loadmodule('luatex-fonts-cbk.lua') -end --- non-merge fallback scope - ---[[doc-- - - Here we adjust the globals created during font loader - initialization. If the second argument to - \luafunction{pop_namespaces()} is \verb|true| this will restore the - state of \luafunction{_G}, eliminating every global generated since - the last call to \luafunction{push_namespaces()}. At the moment we - see no reason to do this, and since the font loader is considered - an essential part of \identifier{luatex} as well as a very well - organized piece of code, we happily concede it the right to add to - \luafunction{_G} if needed. - ---doc]]-- - -pop_namespaces(our_environment, false)-- true) - -report ("both", 0, "main", - "fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) - ---[[doc-- - - \subsection{Callbacks} - After the fontloader is ready we can restore the callback trap from - \identifier{luatexbase}. - ---doc]]-- - -callback.register = trapped_register - ---[[doc-- - - We do our own callback handling with the means provided by - luatexbase. - Note: \luafunction{pre_linebreak_filter} and - \luafunction{hpack_filter} are coupled in \CONTEXT in the concept - of \emphasis{node processor}. - ---doc]]-- - -add_to_callback("pre_linebreak_filter", - nodes.simple_font_handler, - "luaotfload.node_processor", - 1) -add_to_callback("hpack_filter", - nodes.simple_font_handler, - "luaotfload.node_processor", - 1) -add_to_callback("find_vf_file", - find_vf_file, "luaotfload.find_vf_file") - -loadmodule "override.lua" --- load glyphlist on demand - ---[[doc-- - - Now we load the modules written for \identifier{luaotfload}. - ---doc]]-- -loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax -loadmodule "loaders.lua" --- “font-pfb” new in 2.0, added 2011 -loadmodule "database.lua" --- “font-nms” -loadmodule "colors.lua" --- “font-clr” - ---[[doc-- - - Relying on the \verb|name:| resolver for everything has been the - source of permanent trouble with the database. - With the introduction of the new syntax parser we now have enough - granularity to distinguish between the \XETEX emulation layer and - the genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts. - Another benefit is that we can now easily plug in or replace new - lookup behaviors if necessary. - The name resolver remains untouched, but it calls - \luafunction{fonts.names.resolve()} internally anyways (see - \fileent{luaotfload-database.lua}). - ---doc]]-- - -local filesuffix = file.suffix -local fileremovesuffix = file.removesuffix -local request_resolvers = fonts.definers.resolvers -local formats = fonts.formats -local names = fonts.names -formats.ofm = "type1" - -fonts.encodings.known = fonts.encodings.known or { } - ---[[doc-- - - \identifier{luaotfload} promises easy access to system fonts. - Without additional precautions, this cannot be achieved by - \identifier{kpathsea} alone, because it searches only the - \fileent{texmf} directories by default. - Although it is possible for \identifier{kpathsea} to include extra - paths by adding them to the \verb|OSFONTDIR| environment variable, - this is still short of the goal »\emphasis{it just works!}«. - When building the font database \identifier{luaotfload} scans - system font directories anyways, so we already have all the - information for looking sytem fonts. - With the release version 2.2 the file names are indexed in the - database as well and we are ready to resolve \verb|file:| lookups - this way. - Thus we no longer need to call the \identifier{kpathsea} library in - most cases when looking up font files, only when generating the - database, and when verifying the existence of a file in the - \fileent{texmf} tree. - ---doc]]-- - -local resolve_file = names.crude_file_lookup ---local resolve_file = names.crude_file_lookup_verbose -local resolve_name = names.resolve_name - -local file_resolver = function (specification) - local name = resolve_file (specification.name) - local suffix = filesuffix(name) - if formats[suffix] then - specification.forced = suffix - specification.forcedname = file.removesuffix(name) - else - specification.name = name - end -end - -request_resolvers.file = file_resolver - ---[[doc-- - - We classify as \verb|anon:| those requests that have neither a - prefix nor brackets. According to Khaled\footnote{% - \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}. - } - they are the \XETEX equivalent of a \verb|name:| request, so we - will be treating them as such. - ---doc]]-- - ---request_resolvers.anon = request_resolvers.name - ---[[doc-- - - There is one drawback, though. - This syntax is also used for requesting fonts in \identifier{Type1} - (\abbrev{tfm}, \abbrev{ofm}) format. - These are essentially \verb|file:| lookups and must be caught - before the \verb|name:| resolver kicks in, lest they cause the - database to update. - Even if we were to require the \verb|file:| prefix for all - \identifier{Type1} requests, tests have shown that certain fonts - still include further fonts (e.~g. \fileent{omlgcb.ofm} will ask - for \fileent{omsecob.tfm}) \emphasis{using the old syntax}. - For this reason, we introduce an extra check with an early return. - ---doc]]-- - -local type1_formats = { "tfm", "ofm", } - -request_resolvers.anon = function (specification) - local name = specification.name - for i=1, #type1_formats do - local format = type1_formats[i] - if resolvers.findfile(name, format) then - specification.forcedname = file.addsuffix(name, format) - specification.forced = format - return - end - end - --- under some weird circumstances absolute paths get - --- passed to the definer; we have to catch them - --- before the name: resolver misinterprets them. - name = specification.specification - local exists, _ = lfsisfile(name) - if exists then --- garbage; we do this because we are nice, - --- not because it is correct - report ("log", 1, "load", "file %q exists", name) - report ("log", 1, "load", - "... overriding borked anon: lookup with path: lookup") - specification.name = name - request_resolvers.path(specification) - return - end - request_resolvers.name(specification) -end - ---[[doc-- - - Prior to version 2.2, \identifier{luaotfload} did not distinguish - \verb|file:| and \verb|path:| lookups, causing complications with - the resolver. - Now we test if the requested name is an absolute path in the file - system, otherwise we fall back to the \verb|file:| lookup. - ---doc]]-- - -request_resolvers.path = function (specification) - local name = specification.name - local exists, _ = lfsisfile(name) - if not exists then -- resort to file: lookup - report ("log", 1, "load", - "path lookup of %q unsuccessful, falling back to file:", - name) - file_resolver (specification) - else - local suffix = filesuffix (name) - if formats[suffix] then - specification.forced = suffix - specification.name = file.removesuffix(name) - specification.forcedname = name - else - specification.name = name - end - end -end - ---[[doc-- - - {\bfseries EXPERIMENTAL}: - \identifier{kpse}-only resolver, for those who can do without - system fonts. - ---doc]]-- - -request_resolvers.kpse = function (specification) - local name = specification.name - local suffix = filesuffix(name) - if suffix and formats[suffix] then - name = file.removesuffix(name) - if resolvers.findfile(name, suffix) then - specification.forced = suffix - specification.forcedname = name - return - end - end - for t, format in next, formats do --- brute force - if kpse.find_file (name, format) then - specification.forced = t - specification.name = name - return - end - end -end - ---[[doc-- - - The \verb|name:| resolver wraps the database function - \luafunction{resolve_name}. - ---doc]]-- - ---- fonts.names.resolvers.name -- Customized version of the ---- generic name resolver. - -request_resolvers.name = function (specification) - local resolved, subfont = resolve_name (specification) - if resolved then - specification.resolved = resolved - specification.sub = subfont - specification.forced = filesuffix (resolved) - specification.forcedname = resolved - specification.name = fileremovesuffix (resolved) - else - file_resolver (specification) - end -end - ---[[doc-- - - Also {\bfseries EXPERIMENTAL}: custom file resolvers via callback. - ---doc]]-- -create_callback("luaotfload.resolve_font", "simple", dummy_function) - -request_resolvers.my = function (specification) - call_callback("luaotfload.resolve_font", specification) -end - ---[[doc-- - - We create a callback for patching fonts on the fly, to be used by - other packages. - It initially contains the empty function that we are going to - override below. - ---doc]]-- - -create_callback("luaotfload.patch_font", "simple", dummy_function) - ---[[doc-- - - \subsection{\CONTEXT override} - \label{define-font} - We provide a simplified version of the original font definition - callback. - ---doc]]-- - -local read_font_file = fonts.definers.read - ---- spec -> size -> id -> tmfdata -local patch_defined_font = function (specification, size, id) - local tfmdata = read_font_file(specification, size, id) - if type(tfmdata) == "table" and tfmdata.shared then - --- We need to test for the “shared” field here - --- or else the fontspec capheight callback will - --- operate on tfm fonts. - call_callback("luaotfload.patch_font", tfmdata, specification) - end - return tfmdata -end - -reset_callback "define_font" - ---[[doc-- - - Finally we register the callbacks. - ---doc]]-- - -local font_definer = config.luaotfload.definer - -if font_definer == "generic" then - add_to_callback("define_font", - fonts.definers.read, - "luaotfload.define_font", - 1) -elseif font_definer == "patch" then - add_to_callback("define_font", - patch_defined_font, - "luaotfload.define_font", - 1) -end - -loadmodule "features.lua" --- contains what was “font-ltx” and “font-otc” -loadmodule "letterspace.lua" --- extra character kerning -loadmodule "auxiliary.lua" --- additionaly high-level functionality (new) - -luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec - --- vim:tw=79:sw=4:ts=4:et diff --git a/luaotfload-override.lua b/luaotfload-override.lua deleted file mode 100644 index b75530b..0000000 --- a/luaotfload-override.lua +++ /dev/null @@ -1,52 +0,0 @@ -if not modules then modules = { } end modules ["luaotfload-override"] = { - version = "2.5", - comment = "companion to Luaotfload", - author = "Khaled Hosny, Elie Roux, Philipp Gesang", - copyright = "Luaotfload Development Team", - license = "GNU GPL v2.0" -} - -local findfile = resolvers.findfile -local encodings = fonts.encodings - -local log = luaotfload.log -local report = log.report - ---[[doc-- - - Adobe Glyph List. - ------------------------------------------------------------------- - - Context provides a somewhat different font-age.lua from an unclear - origin. Unfortunately, the file name it reads from is hard-coded - in font-enc.lua, so we have to replace the entire table. - - This shouldn’t cause any complications. Due to its implementation - the glyph list will be loaded upon loading a OTF or TTF for the - first time during a TeX run. (If one sticks to TFM/OFM then it is - never read at all.) For this reason we can install a metatable that - looks up the file of our choosing and only falls back to the - Context one in case it cannot be found. - ---doc]]-- - -encodings.agl = { } - -setmetatable(fonts.encodings.agl, { __index = function (t, k) - if k ~= "unicodes" then - return nil - end - local glyphlist = findfile "luaotfload-glyphlist.lua" - if glyphlist then - report ("log", 1, "load", "loading the Adobe glyph list") - else - glyphlist = findfile "font-age.lua" - report ("both", 0, "load", - "loading the extended glyph list from ConTeXt") - end - local unicodes = dofile(glyphlist) - encodings.agl = { unicodes = unicodes } - return unicodes -end }) - --- vim:tw=71:sw=4:ts=4:expandtab diff --git a/luaotfload-parsers.lua b/luaotfload-parsers.lua deleted file mode 100644 index 1048e1d..0000000 --- a/luaotfload-parsers.lua +++ /dev/null @@ -1,578 +0,0 @@ -#!/usr/bin/env texlua -------------------------------------------------------------------------------- --- FILE: luaotfload-parsers.lua --- DESCRIPTION: various lpeg-based parsers used in Luaotfload --- REQUIREMENTS: Luaotfload > 2.4 --- AUTHOR: Philipp Gesang (Phg), --- VERSION: same as Luaotfload --- CREATED: 2014-01-14 10:15:20+0100 -------------------------------------------------------------------------------- --- - -if not modules then modules = { } end modules ['luaotfload-parsers'] = { - version = "2.5", - comment = "companion to luaotfload-main.lua", - author = "Philipp Gesang", - copyright = "Luaotfload Development Team", - license = "GNU GPL v2.0" -} - -luaotfload = luaotfload or { } -luaotfload.parsers = luaotfload.parsers or { } -local parsers = luaotfload.parsers - -local lpeg = require "lpeg" -local P, R, S = lpeg.P, lpeg.R, lpeg.S -local lpegmatch = lpeg.match -local C, Cc, Cf = lpeg.C, lpeg.Cc, lpeg.Cf -local Cg, Cmt, Cs, Ct = lpeg.Cg, lpeg.Cmt, lpeg.Cs, lpeg.Ct - -local kpse = kpse -local kpseexpand_path = kpse.expand_path -local kpsereadable_file = kpse.readable_file - -local file = file -local filejoin = file.join -local filedirname = file.dirname - -local io = io -local ioopen = io.open - -local log = luaotfload.log -local report = log.report - -local string = string -local stringsub = string.sub -local stringfind = string.find -local stringlower = string.lower - -local mathceil = math.ceil - -local lfs = lfs -local lfsisfile = lfs.isfile -local lfsisdir = lfs.isdir - -------------------------------------------------------------------------------- ---- COMMON PATTERNS -------------------------------------------------------------------------------- - -local dot = P"." -local colon = P":" -local semicolon = P";" -local comma = P"," -local noncomma = 1 - comma -local slash = P"/" -local equals = P"=" -local lbrk, rbrk = P"[", P"]" - -local spacing = S" \t\v" -local linebreak = S"\n\r" -local whitespace = spacing + linebreak -local ws = spacing^0 -local xmlws = whitespace^1 - -local digit = R"09" -local alpha = R("az", "AZ") -local anum = alpha + digit -local decimal = digit^1 * (dot * digit^0)^-1 - -------------------------------------------------------------------------------- ---- FONTCONFIG -------------------------------------------------------------------------------- - ---[[doc-- - - For fonts installed on the operating system, there are several - options to make Luaotfload index them: - - - 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 function scan_texmf_fonts(). - - - Otherwise - - under Windows and Mac OSX, we take a look at some hardcoded - directories, - - under Unix, it reads /etc/fonts/fonts.conf and processes the - directories specified there. - - This means that if you have fonts in fancy directories, you need to - set them in OSFONTDIR. - - Beware: OSFONTDIR is a kpathsea variable, so fonts found in these - paths, though technically system fonts, are registered in the - category “texmf”, not “system”. This may have consequences for the - lookup order when a font file (or a font with the same name - information) is located in both the system and the texmf tree. - ---doc]]-- - -local tag_name = C(alpha^1) -local comment = P"" - ----> header specifica -local xml_declaration = P"")^0 * P"?>" -local xml_doctype = P"")^0 * P">" -local header = xml_declaration^-1 - * (xml_doctype + comment + xmlws)^0 - ----> enforce root node -local root_start = P"<" * xmlws^-1 * P"fontconfig" * xmlws^-1 * P">" -local root_stop = P"" - -local dquote, squote = P[["]], P"'" -local xml_namestartchar = S":_" + alpha --- ascii only, funk the rest -local xml_namechar = S":._" + alpha + digit -local xml_name = xmlws^-1 - * C(xml_namestartchar * xml_namechar^0) -local xml_attvalue = dquote * C((1 - S[[%&"]])^1) * dquote * xmlws^-1 - + squote * C((1 - S[[%&']])^1) * squote * xmlws^-1 -local xml_attr = Cg(xml_name * P"=" * xml_attvalue) -local xml_attr_list = Cf(Ct"" * xml_attr^1, rawset) - ---[[doc-- - scan_node creates a parser for a given xml tag. ---doc]]-- ---- string -> bool -> lpeg_t -local scan_node = function (tag) - --- Node attributes go into a table with the index “attributes” - --- (relevant for “prefix="xdg"” and the likes). - local p_tag = P(tag) - local with_attributes = P"<" * p_tag - * Cg(xml_attr_list, "attributes")^-1 - * xmlws^-1 - * P">" - local plain = P"<" * p_tag * xmlws^-1 * P">" - local node_start = plain + with_attributes - local node_stop = P"" - --- there is no nesting, the earth is flat ... - local node = node_start - * Cc(tag) * C(comment + (1 - node_stop)^1) - * node_stop - return Ct(node) -- returns {string, string [, attributes = { key = val }] } -end - ---[[doc-- - At the moment, the interesting tags are “dir” for - directory declarations, and “include” for including - further configuration files. - - spec: http://freedesktop.org/software/fontconfig/fontconfig-user.html ---doc]]-- -local include_node = scan_node"include" -local dir_node = scan_node"dir" - -local element = dir_node - + include_node - + comment --> ignore - + P(1-root_stop) --> skip byte - -local root = root_start * Ct(element^0) * root_stop -local p_cheapxml = header * root - ---lpeg.print(p_cheapxml) ---> 757 rules with v0.10 - ---[[doc-- - fonts_conf_scanner() handles configuration files. - It is called on an abolute path to a config file (e.g. - /home/luser/.config/fontconfig/fonts.conf) and returns a list - of the nodes it managed to extract from the file. ---doc]]-- ---- string -> path list -local fonts_conf_scanner = function (path) - local fh = ioopen(path, "r") - if not fh then - report("both", 3, "db", "Cannot open fontconfig file %s.", path) - return - end - local raw = fh:read"*all" - fh:close() - - local confdata = lpegmatch(p_cheapxml, raw) - if not confdata then - report("both", 3, "db", "Cannot scan fontconfig file %s.", path) - return - end - return confdata -end - -local p_conf = P".conf" * P(-1) -local p_filter = (1 - p_conf)^1 * p_conf - -local conf_filter = function (path) - if lpegmatch (p_filter, path) then - return true - end - return false -end - ---[[doc-- - read_fonts_conf_indeed() is called with six arguments; the - latter three are tables that represent the state and are - always returned. - The first three are - · the path to the file - · the expanded $HOME - · the expanded $XDG_CONFIG_DIR ---doc]]-- ---- string -> string -> string -> tab -> tab -> (tab * tab * tab) -local read_fonts_conf_indeed -read_fonts_conf_indeed = function (start, home, xdg_home, - acc, done, dirs_done, - find_files) - - local paths = fonts_conf_scanner(start) - if not paths then --- nothing to do - return acc, done, dirs_done - end - - for i=1, #paths do - local pathobj = paths[i] - local kind, path = pathobj[1], pathobj[2] - local attributes = pathobj.attributes - if attributes and attributes.prefix == "xdg" then - --- this prepends the xdg root (usually ~/.config) - path = filejoin(xdg_home, path) - end - - if kind == "dir" then - if stringsub(path, 1, 1) == "~" then - path = filejoin(home, stringsub(path, 2)) - end - --- We exclude paths with texmf in them, as they should be - --- found anyway; also duplicates are ignored by checking - --- if they are elements of dirs_done. - --- - --- FIXME does this mean we cannot access paths from - --- distributions (e.g. Context minimals) installed - --- separately? - if not (stringfind(path, "texmf") or dirs_done[path]) then - acc[#acc+1] = path - dirs_done[path] = true - end - - elseif kind == "include" then - --- here the path can be four things: a directory or a file, - --- in absolute or relative path. - if stringsub(path, 1, 1) == "~" then - path = filejoin(home, stringsub(path, 2)) - elseif --- if the path is relative, we make it absolute - not ( lfsisfile(path) or lfsisdir(path) ) - then - path = filejoin(filedirname(start), path) - end - if lfsisfile(path) - and kpsereadable_file(path) - and not done[path] - then - --- we exclude path with texmf in them, as they should - --- be found otherwise - acc = read_fonts_conf_indeed( - path, home, xdg_home, - acc, done, dirs_done) - elseif lfsisdir(path) then --- arrow code ahead - local config_files = find_files (path, conf_filter) - for _, filename in next, config_files do - if not done[filename] then - acc = read_fonts_conf_indeed( - filename, home, xdg_home, - acc, done, dirs_done) - end - end - end --- match “kind” - end --- iterate paths - end - - --inspect(acc) - --inspect(done) - return acc, done, dirs_done - end --- read_fonts_conf_indeed() - ---[[doc-- - read_fonts_conf() sets up an accumulator and two sets - for tracking what’s been done. - - Also, the environment variables HOME and XDG_CONFIG_HOME -- - which are constants anyways -- are expanded so don’t have to - repeat that over and over again as with the old parser. - Now they’re just passed on to every call of - read_fonts_conf_indeed(). - - read_fonts_conf() is also the only reference visible outside - the closure. ---doc]]-- - ---- list -> (string -> function option -> string list) -> list - -local read_fonts_conf = function (path_list, find_files) - local home = kpseexpand_path"~" --- could be os.getenv"HOME" - local xdg_home = kpseexpand_path"$XDG_CONFIG_HOME" - if xdg_home == "" then xdg_home = filejoin(home, ".config") end - local acc = { } ---> list: paths collected - local done = { } ---> set: files inspected - local dirs_done = { } ---> set: dirs in list - for i=1, #path_list do --- we keep the state between files - acc, done, dirs_done = read_fonts_conf_indeed( - path_list[i], home, xdg_home, - acc, done, dirs_done, - find_files) - end - return acc -end - -luaotfload.parsers.read_fonts_conf = read_fonts_conf - - - -------------------------------------------------------------------------------- ---- MISC PARSERS -------------------------------------------------------------------------------- - - -local trailingslashes = slash^1 * P(-1) -local stripslashes = C((1 - trailingslashes)^0) -parsers.stripslashes = stripslashes - -local splitcomma = Ct((C(noncomma^1) + comma)^1) -parsers.splitcomma = splitcomma - - - -------------------------------------------------------------------------------- ---- FONT REQUEST -------------------------------------------------------------------------------- - - ---[[doc------------------------------------------------------------------------ - - The luaotfload font request syntax (see manual) - has a canonical form: - - \font=:: - - where - is the control sequence that activates the font - is either “file” or “name”, determining the lookup - is either a file name (no path) or a font - name, depending on the lookup - is a list of switches or options, separated by - semicolons or commas; a switch is of the form “+” foo - or “-” foo, options are of the form lhs “=” rhs - - however, to ensure backward compatibility we also have - support for Xetex-style requests. - - for the Xetex emulation see: - · The XeTeX Reference Guide by Will Robertson, 2011 - · The XeTeX Companion by Michel Goosens, 2010 - · About XeTeX by Jonathan Kew, 2005 - - - caueat emptor. - - the request is parsed into one of **four** different lookup - categories: the regular ones, file and name, as well as the - Xetex compatibility ones, path and anon. (maybe a better choice - of identifier would be “ambig”.) - - according to my reconstruction, the correct chaining of the - lookups for each category is as follows: - - | File -> ( db/filename lookup ) - - | Name -> ( db/name lookup, - db/filename lookup ) - - | Path -> ( db/filename lookup, - fullpath lookup ) - - | Anon -> ( kpse.find_file(), // <- for tfm, ofm - db/name lookup, - db/filename lookup, - fullpath lookup ) - - caching of successful lookups is essential. we now as of v2.2 - have a lookup cache that is stored in a separate file. it - pertains only to name: lookups, and is described in more detail - in luaotfload-database.lua. - -------------------------------------------------------------------------------- - - One further incompatibility between Xetex and Luatex-Fonts consists - in their option list syntax: apparently, Xetex requires key-value - options to be prefixed by a "+" (ascii “plus”) character. We - silently accept this as well, dropping the first byte if it is a - plus or minus character. - - Reference: https://github.com/lualatex/luaotfload/issues/79#issuecomment-18104483 - ---doc]]------------------------------------------------------------------------ - - -local handle_normal_option = function (key, val) - val = stringlower(val) - --- the former “toboolean()” handler - if val == "true" then - val = true - elseif val == "false" then - val = false - end - return key, val -end - ---[[doc-- - - Xetex style indexing begins at zero which we just increment before - passing it along to the font loader. Ymmv. - ---doc]]-- - -local handle_xetex_option = function (key, val) - val = stringlower(val) - local numeric = tonumber(val) --- decimal only; keeps colors intact - if numeric then --- ugh - if mathceil(numeric) == numeric then -- integer, possible index - val = tostring(numeric + 1) - end - elseif val == "true" then - val = true - elseif val == "false" then - val = false - end - return key, val -end - ---[[doc-- - - Instead of silently ignoring invalid options we emit a warning to - the log. - - Note that we have to return a pair to please rawset(). This creates - an entry on the resulting features hash which will later be removed - during set_default_features(). - ---doc]]-- - -local handle_invalid_option = function (opt) - report("log", 0, "load", "font option %q unknown.", opt) - return "", false -end - ---[[doc-- - - Dirty test if a file: request is actually a path: lookup; don’t - ask! Note this fails on Windows-style absolute paths. These will - *really* have to use the correct request. - ---doc]]-- - -local check_garbage = function (_,i, garbage) - if stringfind(garbage, "/") then - report("log", 0, "load", --- ffs use path! - "warning: path in file: lookups is deprecated; ") - report("log", 0, "load", "use bracket syntax instead!") - report("log", 0, "load", - "position: %d; full match: %q", - i, garbage) - return true - end - return false -end - -local featuresep = comma + semicolon - ---- modifiers --------------------------------------------------------- ---[[doc-- - The slash notation: called “modifiers” (Kew) or “font options” - (Robertson, Goosens) - we only support the shorthands for italic / bold / bold italic - shapes, as well as setting optical size, the rest is ignored. ---doc]]-- -local style_modifier = (P"BI" + P"IB" + P"bi" + P"ib" + S"biBI") - / stringlower -local size_modifier = S"Ss" * P"=" --- optical size - * Cc"optsize" * C(decimal) -local other_modifier = P"AAT" + P"aat" --- apple stuff; unsupported - + P"ICU" + P"icu" --- not applicable - + P"GR" + P"gr" --- sil stuff; unsupported -local garbage_modifier = ((1 - colon - slash)^0 * Cc(false)) -local modifier = slash * (other_modifier --> ignore - + Cs(style_modifier) --> collect - + Ct(size_modifier) --> collect - + garbage_modifier) --> warn -local modifier_list = Cg(Ct(modifier^0), "modifiers") - ---- lookups ----------------------------------------------------------- -local fontname = C((1-S":(/")^1) --- like luatex-fonts -local unsupported = Cmt((1-S":(")^1, check_garbage) -local prefixed = P"name:" * ws * Cg(fontname, "name") ---- initially we intended file: to emulate the behavior of ---- luatex-fonts, i.e. no paths allowed. after all, we do have XeTeX ---- emulation with the path lookup and it interferes with db lookups. ---- turns out fontspec and other widely used packages rely on file: ---- with paths already, so we’ll add a less strict rule here. anyways, ---- we’ll emit a warning. - + P"file:" * ws * Cg(unsupported, "path") - + P"file:" * ws * Cg(fontname, "file") ---- EXPERIMENTAL: kpse lookup - + P"kpse:" * ws * Cg(fontname, "kpse") ---- EXPERIMENTAL: custom lookup - + P"my:" * ws * Cg(fontname, "my") -local unprefixed = Cg(fontname, "anon") -local path_lookup = lbrk * Cg(C((1-rbrk)^1), "path") * rbrk - ---- features ---------------------------------------------------------- -local field_char = anum + S"+-." --- sic! -local field = field_char^1 ---- assignments are “lhs=rhs” ---- or “+lhs=rhs” (Xetex-style) ---- switches are “+key” | “-key” -local normal_option = C(field) * ws * equals * ws * C(field) * ws -local xetex_option = P"+" * ws * normal_option -local ignore_option = (1 - equals - featuresep)^1 - * equals - * (1 - featuresep)^1 -local assignment = xetex_option / handle_xetex_option - + normal_option / handle_normal_option - + ignore_option / handle_invalid_option -local switch = P"+" * ws * C(field) * Cc(true) - + P"-" * ws * C(field) * Cc(false) - + C(field) * Cc(true) --- default -local feature_expr = ws * Cg(assignment + switch) * ws -local option = feature_expr -local feature_list = Cf(Ct"" - * option - * (featuresep * option^-1)^0 - , rawset) - * featuresep^-1 - ---- other ------------------------------------------------------------- ---- This rule is present in the original parser. It sets the “sub” ---- field of the specification which allows addressing a specific ---- font inside a TTC container. Neither in Luatex-Fonts nor in ---- Luaotfload is this documented, so we might as well silently drop ---- it. However, as backward compatibility is one of our prime goals we ---- just insert it here and leave it undocumented until someone cares ---- to ask. (Note: afair subfonts are numbered, but this rule matches a ---- string; I won’t mess with it though until someone reports a ---- problem.) ---- local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim ---- Note to self: subfonts apparently start at index 0. Tested with ---- Cambria.ttc that includes “Cambria Math” at 0 and “Cambria” at 1. ---- Other values cause luatex to segfault. -local subfont = P"(" * Cg((1 - S"()")^1, "sub") * P")" ---- top-level rules --------------------------------------------------- ---- \font\foo=: -local features = Cg(feature_list, "features") -local specification = (prefixed + unprefixed) - * subfont^-1 - * modifier_list^-1 -local font_request = Ct(path_lookup * (colon^-1 * features)^-1 - + specification * (colon * features)^-1) - --- lpeg.print(font_request) ---- v2.5 parser: 1065 rules ---- v1.2 parser: 230 rules - -luaotfload.parsers.font_request = font_request - diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua deleted file mode 100755 index 35765b5..0000000 --- a/luaotfload-tool.lua +++ /dev/null @@ -1,1263 +0,0 @@ -#!/usr/bin/env texlua ------------------------------------------------------------------------ --- FILE: luaotfload-tool.lua --- DESCRIPTION: database functionality --- REQUIREMENTS: luaotfload 2.5 --- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang --- VERSION: 2.5 --- LICENSE: GPL v2.0 --- MODIFIED: 2014-01-14 13:17:04+0100 ------------------------------------------------------------------------ - -luaotfload = luaotfload or { } -local version = "2.5" --- .- -luaotfload.version = version -luaotfload.self = "luaotfload-tool" - ---[[doc-- - -luaotfload-tool(1) - -This file was originally written (as \fileent{mkluatexfontdb.lua}) by -Elie Roux and Khaled Hosny and, as a derived work of ConTeXt, is -provided under the terms of the GPL v2.0 license as printed in full -text in the manual (luaotfload.pdf). - - \url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. - -This file is a wrapper for the luaotfload font names module -(luaotfload-database.lua). It is part of the luaotfload bundle, please -see the luaotfload documentation for more info. Report bugs to - - \url{https://github.com/lualatex/luaotfload/issues}. - ---doc]]-- - -kpse.set_program_name "luatex" - ---[[doc-- - - We test for Lua 5.1 by means of capability detection to see if - we’re running an outdated Luatex. If so, we bail. - - \url{http://lua-users.org/wiki/LuaVersionCompatibility} - ---doc]]-- - - -local ioopen = io.open -local iowrite = io.write -local kpsefind_file = kpse.find_file -local next = next -local osdate = os.date -local ostype = os.type -local stringexplode = string.explode -local stringformat = string.format -local stringlower = string.lower -local stringrep = string.rep -local tableconcat = table.concat -local texiowrite_nl = texio.write_nl -local texiowrite = texio.write -local tonumber = tonumber -local type = type - -local runtime -if _G.getfenv ~= nil then -- 5.1 or LJ - if _G.jit ~= nil then - runtime = { "jit", jit.version } - else - runtime = { "stock", _VERSION } - print "FATAL ERROR" - print "Luaotfload requires a Luatex version >=0.76." - print "Please update your TeX distribution!" - os.exit (-1) - end -else -- 5.2 - runtime = { "stock", _VERSION } -end - - -local C, Ct, P, S = lpeg.C, lpeg.Ct, lpeg.P, lpeg.S -local lpegmatch = lpeg.match - -local loader_file = "luatexbase.loader.lua" -local loader_path = assert(kpsefind_file(loader_file, "lua"), - "File '"..loader_file.."' not found") - - -string.quoted = string.quoted or function (str) - return string.format("%q",str) -end - -require(loader_path) - -config = config or { } -local config = config -local luaotfloadconfig = config.luaotfload or { } -config.luaotfload = luaotfloadconfig -luaotfloadconfig.version = luaotfloadconfig.version or version -luaotfloadconfig.names_dir = luaotfloadconfig.names_dir or "names" -luaotfloadconfig.cache_dir = luaotfloadconfig.cache_dir or "fonts" -luaotfloadconfig.index_file = luaotfloadconfig.index_file - or "luaotfload-names.lua" -luaotfloadconfig.formats = luaotfloadconfig.formats - or "otf,ttf,ttc,dfont" -luaotfloadconfig.reload = false -if not luaotfloadconfig.strip then - luaotfloadconfig.strip = true -end - -config.lualibs = config.lualibs or { } -config.lualibs.verbose = false -config.lualibs.prefer_merged = true -config.lualibs.load_extended = true - -require "lualibs" -local tabletohash = table.tohash -local stringsplit = string.split - ---[[doc-- -\fileent{luatex-basics-gen.lua} calls functions from the -\luafunction{texio.*} library; too much for our taste. -We intercept them with dummies. - -Also, it sets up dummies in place of the tables created by the Context -libraries. Since we have loaded the lualibs already this would cause -collateral damage for some libraries whose namespace would be -overridden. We employ our usual backup-restore strategy to work around -this. (Postponing the loading of the lualibs code is not an option -because the functionality is needed by basics-gen itself.) ---doc]]-- - -local dummy_function = function ( ) end -local backup = { - write = texio.write, - write_nl = texio.write_nl, - utilities = utilities, -} - -texio.write, texio.write_nl = dummy_function, dummy_function -require"luaotfload-basics-gen.lua" - -texio.write, texio.write_nl = backup.write, backup.write_nl -utilities = backup.utilities - -require"luaotfload-log.lua" --- this populates the luaotfload.log.* namespace -require"luaotfload-parsers" --- fonts.conf and request syntax -require"luaotfload-database" -require"alt_getopt" - -local names = fonts.names -local status_file = "luaotfload-status" -local luaotfloadstatus = require (status_file) -luaotfloadconfig.status = luaotfloadstatus -local sanitize_fontname = names.sanitize_fontname - -local log = luaotfload.log -local report = log.report - -local pathdata = names.path -local names_plain = pathdata.index.lua -local names_gzip = names_plain .. ".gz" -local names_bin = pathdata.index.luc - -local help_messages = { - ["luaotfload-tool"] = [[ - -Usage: %s [OPTIONS...] - - Luaotfload font management and diagnostic utility. - This program is part of the Luaotfload package. - - Valid options are: - -------------------------------------------------------------------------------- - VERBOSITY AND DIAGNOSTICS - - -q --quiet don't output anything - -v --verbose=LEVEL be more verbose (print the searched directories) - -vv print the loaded fonts - -vvv print all steps of directory searching - --log=stdout redirect log output to stdout - - -V --version print version and exit - -h --help print this message - --diagnose=CHECK run a self test procedure; one of "files", - "environment", "index", "permissions", or - "repository" - -------------------------------------------------------------------------------- - DATABASE - - -u --update update the database - -n --no-reload suppress db update - --no-strip keep redundant information in db - -f --force force re-indexing all fonts - -c --no-compress do not gzip index file (text version only) - -l --flush-lookups empty lookup cache of font requests - -D --dry-run skip loading of fonts, just scan - --formats=[+|-]EXTENSIONS set, add, or subtract formats to index - -p --prefer-texmf prefer fonts in the TEXMF over system fonts - --max-fonts=N process at most N font files - - --find="font name" query the database for a font name - -F --fuzzy look for approximate matches if --find fails - --limit=n limit display of fuzzy matches to - (default: n = 1) - - -i --info display basic font metadata - -I --inspect display detailed font metadata - -w --warnings display warnings generated by the - fontloader library - - --list= output list of entries by field - --list=: restrict to entries with = - --fields=,,…, which fields to print with --list - -b --show-blacklist show blacklisted files - -The font database will be saved to - %s - %s - -------------------------------------------------------------------------------- - FONT CACHE - - --cache= operate on font cache, where is - "show", "purge", or "erase" - -The font cache will be written to - %s - -]], - mkluatexfontdb = [[ -FATAL ERROR -As of Luaotfload v2.5, legacy behavior is not supported anymore. Please -update your scripts and/or habits! Kthxbye. -]], - short = [[ -Usage: luaotfload-tool [--help] [--version] [--verbose=] - [--update] [--force] [--prefer-texmf] - [--dry-run] [--formats=] - [--find=] [--fuzzy] [--info] [--inspect] - [--list=] [--fields=] - [--cache=] [--flush-lookups] - [--show-blacklist] [--diagnose=] - -Enter 'luaotfload-tool --help' for a larger list of options. -]] -} - -local help_msg = function (version) - local template = help_messages[version] - iowrite(stringformat(template, - luaotfload.self, --- names_plain, - names_gzip, - names_bin, - caches.getwritablepath ( - luaotfloadconfig.cache_dir))) -end - -local about = [[ -%s: - Luaotfload font management and diagnostic utility. - License: GNU GPL v2.0. - Report problems to -]] - -local version_msg = function ( ) - local out = function (...) texiowrite_nl (stringformat (...)) end - out (about, luaotfload.self) - out ("%s version %q", luaotfload.self, version) - out ("revision %q", luaotfloadstatus.notes.revision) - out ("database version %q", names.version) - out ("Lua interpreter: %s; version %q", runtime[1], runtime[2]) - out ("Luatex SVN revision %d", status.luatex_svn) - out ("Luatex version %.2f.%d", - status.luatex_version / 100, - status.luatex_revision) - out "" -end - - ---- makeshift formatting - -local head_adornchars = { - [1] = "*", [2] = "=", [3] = "~", [4] = "-", [5] = "·", -} - -local textwidth = 80 -local wd_leftcolumn = math.floor(textwidth * .25) -local key_fmt = stringformat([[%%%ds]], wd_leftcolumn) -local val_fmt = [[%s]] -local fieldseparator = ":" -local info_fmt = key_fmt .. fieldseparator .. " " .. val_fmt - -local currentdepth = 0 -local counterstack = { } -- counters per level -local counterformat = "%d" - -local format_counter = function (stack) - local acc = { } - for lvl=1, #stack do - acc[#acc+1] = stringformat(counterformat, stack[lvl]) - end - return tableconcat(acc, ".") -end - -local print_heading = function (title, level) - local structuredata - if currentdepth == level then -- top is current - counterstack[#counterstack] = counterstack[#counterstack] + 1 - elseif currentdepth < level then -- push new - counterstack[#counterstack+1] = 1 - else -- pop - local diff = currentdepth - level - while diff > 0 do - counterstack[#counterstack] = nil - diff = diff - 1 - end - counterstack[#counterstack] = counterstack[#counterstack] + 1 - end - currentdepth = level - - texiowrite_nl "" - if not level or level > #head_adornchars then - level = #head_adornchars - end - local adornchar = head_adornchars[level] - - local counter = format_counter(counterstack) - - local s = adornchar .. adornchar .. " " - .. counter .. " " - .. title .. " " - texiowrite_nl (s .. stringrep(adornchar, textwidth-utf.len(s))) -end - -local baseindent = " " - ---[[doc-- - - show_info_items -- Together with show_info_table prints the table returned by - fontloader.info(), recursing into nested tables if appropriate (as necessitated - by Luatex versions 0.78+ which include the pfminfo table in the result. - ---doc]]-- - -local show_info_table show_info_table = function (t, depth) - depth = depth or 0 - local indent = stringrep (baseindent, depth) - local keys = table.sortedkeys (t) - for n = 1, #keys do - local key = keys [n] - local val = t [key] - if type (val) == "table" then - texiowrite_nl (indent .. stringformat (info_fmt, key, "")) - show_info_table (val, depth + 1) - else - texiowrite_nl (indent .. stringformat (info_fmt, key, val)) - end - end -end - -local show_info_items = function (fontinfo) - print_heading (fontinfo.fullname, 1) - texiowrite_nl "" - show_info_table (fontinfo) - texiowrite_nl "" -end - -local p_eol = S"\n\r"^1 -local p_space = S" \t\v"^0 -local p_line = p_space * C((1 - p_eol)^1)^-1 -local p_lines = Ct(p_line * (p_eol^1 * p_line^-1)^0) - -local show_fontloader_warnings = function (ws) - local nws = #ws - print_heading(stringformat( - [[the fontloader emitted %d warnings]], - nws), 2) - texiowrite_nl "" - for i=1, nws do - local w = ws[i] - texiowrite_nl (stringformat("%d:", i)) - local lines = lpegmatch(p_lines, w) - for i=1, #lines do - local line = lines[i] - texiowrite_nl(" · " .. line) - end - texiowrite_nl "" - end -end - -local p_spacechar = S" \n\r\t\v" -local p_wordchar = (1 - p_spacechar) -local p_whitespace = p_spacechar^1 -local p_word = C(p_wordchar^1) -local p_words = Ct(p_word * (p_whitespace * p_word)^0) - ---- string -> int -> string list -local reflow = function (text, width) - local words - if type(text) == "string" then - words = lpegmatch(p_words, text) - if #words < 2 then - return { text } - end - else - words = text - if #words < 2 then - return words - end - end - - local space = " " - local utflen = utf.len - local reflowed = { } - - local first = words[1] - local linelen = #first - local line = { first } - - for i=2, #words do - local word = words[i] - local lword = utflen(word) - linelen = linelen + lword + 1 - if linelen > width then - reflowed[#reflowed+1] = tableconcat(line) - linelen = #word - line = { word } - else - line[#line+1] = space - line[#line+1] = word - end - end - reflowed[#reflowed+1] = tableconcat(line) - return reflowed -end - ---- string -> 'a -> string list -local print_field = function (key, val) - val = tostring(val) - local lhs = stringformat(key_fmt, key) .. fieldseparator .. " " - local wd_lhs = #lhs - local lines = reflow(val, textwidth - wd_lhs) - - texiowrite_nl(lhs) - texiowrite(lines[1]) - if #lines > 1 then - local indent = stringrep(" ", wd_lhs) - for i=2, #lines do - texiowrite_nl(indent) - texiowrite (lines[i]) - end - end -end - -local display_names = function (names) - print_heading("Font Metadata", 2) - for i=1, #names do - local lang, namedata = names[i].lang, names[i].names - print_heading(stringformat("Language: %s ", i, lang), 3) - texiowrite_nl "" - if namedata then - for field, value in next, namedata do - print_field(field, value) - end - end - end -end - ---- see luafflib.c -local general_fields = { - --- second: l -> literal | n -> length | d -> date - { "fullname", "l", "font name" }, - { "version", "l", "font version" }, - { "creationtime", "d", "creation time" }, - { "modificationtime", "d", "modification time" }, - { "subfonts", "n", "number of subfonts" }, - { "glyphcnt", "l", "number of glyphs" }, - { "weight", "l", "weight indicator" }, - { "design_size", "l", "design size" }, - { "design_range_bottom", "l", "design size min" }, - { "design_range_top", "l", "design size max" }, - { "fontstyle_id", "l", "font style id" }, - { "fontstyle_name", "S", "font style name" }, - { "strokewidth", "l", "stroke width" }, - { "units_per_em", "l", "units per em" }, - { "ascent", "l", "ascender height" }, - { "descent", "l", "descender height" }, - { "comments", "l", "comments" }, - { "os2_version", "l", "os2 version" }, - { "sfd_version", "l", "sfd version" }, -} - -local display_general = function (fullinfo) - texiowrite_nl "" - print_heading("General Information", 2) - texiowrite_nl "" - for i=1, #general_fields do - local field = general_fields[i] - local key, mode, desc = unpack(field) - local val - if mode == "l" then - val = fullinfo[key] - elseif mode == "S" then --- style names table - local data = fullinfo[key] - if type (data) == "table" then - if #data > 0 then - for n = 1, #data do - local nth = data[n] - if nth.lang == 1033 then - val = nth.name - goto found - end - end - val = next (data).name - else - val = "" - end - ::found:: - else - val = data - end - elseif mode == "n" then - local v = fullinfo[key] - if v then - val = #fullinfo[key] - end - elseif mode == "d" then - if ostype == "unix" then - val = osdate("%F %T", fullinfo[key]) - else - --- the MS compiler doesn’t support C99, so - --- strftime is missing some functionality; - --- see loslib.c for details. - val = osdate("%Y-%m-d %H:%M:%S", fullinfo[key]) - end - end - if not val then - val = "" - end - print_field(desc, val) - end -end - -local print_features = function (features) - for tag, data in next, features do - print_heading(tag, 4) - for script, languages in next, data do - local field = stringformat(key_fmt, script).. fieldseparator .. " " - local wd_field = #field - local lines = reflow(languages.list, textwidth - wd_field) - local indent = stringrep(" ", wd_field) - texiowrite_nl(field) - texiowrite(lines[1]) - if #lines > 1 then - for i=1, #lines do - texiowrite_nl(indent .. lines[i]) - end - end - end - end -end - -local extract_feature_info = function (set) - local collected = { } - for i=1, #set do - local features = set[i].features - if features then - for j=1, #features do - local feature = features[j] - local scripts = feature.scripts - local tagname = stringlower(feature.tag) - local entry = collected[tagname] or { } - - for k=1, #scripts do - local script = scripts[k] - local scriptname = stringlower(script.script) - local c_script = entry[scriptname] or { - list = { }, - set = { }, - } - local list, set = c_script.list, c_script.set - - for l=1, #script.langs do - local langname = stringlower(script.langs[l]) - if not set[langname] then - list[#list+1] = langname - set[langname] = true - end - end - entry[scriptname] = c_script - end - collected[tagname] = entry - end - end - end - return collected -end - -local display_feature_set = function (set) - local collected = extract_feature_info(set) - print_features(collected) -end - -local display_features = function (gsub, gpos) - texiowrite_nl "" - - if gsub or gpos then - print_heading("Features", 2) - - if gsub then - print_heading("GSUB Features", 3) - display_feature_set(gsub) - end - - if gpos then - print_heading("GPOS Features", 3) - display_feature_set(gpos) - end - end -end - -local show_full_info = function (path, subfont, warnings) - local rawinfo, warn = fontloader.open(path, subfont) - if warnings then - show_fontloader_warnings(warn) - end - if not rawinfo then - texiowrite_nl(stringformat([[cannot open font %s]], path)) - return - end - local fontdata = { } - local fullinfo = fontloader.to_table(rawinfo) - local fields = fontloader.fields(rawinfo) - fontloader.close(rawinfo) - display_names(fullinfo.names) - display_general(fullinfo) - display_features(fullinfo.gsub, fullinfo.gpos) -end - ---- Subfonts returned by fontloader.info() do not correspond ---- to the actual indices required by fontloader.open(), so ---- we try and locate the correct one by matching the request ---- against the full name. - -local subfont_by_name -subfont_by_name = function (lst, askedname, n) - if not n then - return subfont_by_name (lst, askedname, 1) - end - - local font = lst[n] - if font then - if sanitize_fontname (font.fullname) == askedname then - return font - end - return subfont_by_name (lst, askedname, n+1) - end - return false -end - ---[[doc-- -The font info knows two levels of detail: - - a) basic information returned by fontloader.info(); and - b) detailed information that is a subset of the font table - returned by fontloader.open(). ---doc]]-- - -local show_font_info = function (basename, askedname, detail, warnings) - local filenames = names.data().files - local index = filenames.base[basename] - local fullname = filenames.full[index] - askedname = sanitize_fontname (askedname) - if not fullname then -- texmf - fullname = resolvers.findfile(basename) - end - if fullname then - local shortinfo = fontloader.info(fullname) - local nfonts = #shortinfo - if nfonts > 0 then -- true type collection - local subfont - if askedname then - report (true, 1, "resolve", - [[%s is part of the font collection %s]], - askedname, basename) - subfont = subfont_by_name(shortinfo, askedname) - end - if subfont then - show_info_items(subfont) - if detail == true then - show_full_info(fullname, subfont, warnings) - end - else -- list all subfonts - report (true, 1, "resolve", - [[%s is a font collection]], basename) - for subfont = 1, nfonts do - report (true, 1, "resolve", - [[Showing info for font no. %d]], n) - show_info_items(shortinfo[subfont]) - if detail == true then - show_full_info(fullname, subfont, warnings) - end - end - end - else - show_info_items(shortinfo) - if detail == true then - show_full_info(fullname, subfont, warnings) - end - end - else - report (true, 1, "resolve", "Font %s not found", filename) - end -end - ---[[-- -Running the scripts triggers one or more actions that have to be -executed in the correct order. To avoid duplication we track them in a -set. ---]]-- - -local action_sequence = { - "loglevel", "help", "version", "diagnose", - "blacklist", "cache", "flush", "generate", - "list", "query", -} - -local action_pending = tabletohash(action_sequence, false) - -action_pending.loglevel = true --- always set the loglevel -action_pending.generate = false --- this is the default action - -local actions = { } --- (jobspec -> (bool * bool)) list - -actions.loglevel = function (job) - log.set_loglevel(job.log_level) - report ("info", 3, "util", "Setting log level", "%d", job.log_level) - report ("log", 2, "util", "Lua=%q", _VERSION) - return true, true -end - -actions.version = function (job) - version_msg() - return true, false -end - -actions.help = function (job) - help_msg (job.help_version or "luaotfload-tool") - return true, false -end - -actions.blacklist = function (job) - names.read_blacklist() - local n = 0 - for n, entry in next, table.sortedkeys(names.blacklist) do - iowrite (stringformat("(%d %s)\n", n, entry)) - end - return true, false -end - -actions.generate = function (job) - local fontnames, savedname - fontnames = names.update(fontnames, job.force_reload, job.dry_run) - report ("info", 2, "db", "Fonts in the database: %i", #fontnames.mappings) - if names.data() then - return true, true - end - return false, false -end - -actions.flush = function (job) - local success, lookups = names.flush_lookup_cache() - if success then - local success = names.save_lookups() - if success then - report ("info", 2, "cache", "Lookup cache emptied") - return true, true - end - end - return false, false -end - -local cache_directives = { - ["purge"] = names.purge_cache, - ["erase"] = names.erase_cache, - ["show"] = names.show_cache, -} - -actions.cache = function (job) - local directive = cache_directives[job.cache] - if not directive or type(directive) ~= "function" then - report ("info", 2, "cache", - "Invalid font cache directive %s.", job.cache) - return false, false - end - if directive() then - return true, true - end - return false, false -end - -actions.query = function (job) - - require "luaotfload-features" - - local query = job.query - - local tmpspec = { - name = query, - lookup = "name", - specification = query, - optsize = 0, - features = { }, - } - - tmpspec = names.handle_request (tmpspec) - - if not tmpspec.size then - tmpspec.size = 655360 --- assume 10pt - end - - local foundname, subfont, success - - if tmpspec.lookup == "name" - or tmpspec.lookup == "anon" --- not *exactly* as resolvers.anon - then - foundname, subfont = names.resolve_name (tmpspec) - if foundname then - foundname, _, success = names.crude_file_lookup (foundname) - end - elseif tmpspec.lookup == "file" then - foundname, _, success = - names.crude_file_lookup (tmpspec.name) - end - - if success then - report (false, 0, "resolve", "Font %q found!", query) - if subfont then - report (false, 0, "resolve", - "Resolved file name %q, subfont nr. %q", - foundname, subfont) - else - report (false, 0, "resolve", - "Resolved file name %q", foundname) - end - if job.show_info then - show_font_info (foundname, query, job.full_info, job.warnings) - iowrite "\n" - end - else - report (false, 0, "resolve", "Cannot find %q in index.", query) - report (false, 0, "resolve", - "Hint: use the --fuzzy option to display suggestions.", - query) - if job.fuzzy == true then - report (false, 0, "resolve", - "Looking for close matches, this may take a while ...") - local _success = names.find_closest(query, job.fuzzy_limit) - end - end - return true, true -end - ---- --list= ---- --list=: ---- ---- --list= --fields=,,,... - -local get_fields get_fields = function (entry, fields, acc, n) - if not acc then - return get_fields (entry, fields, { }, 1) - end - - local field = fields [n] - if field then - local chain = stringsplit (field, "->") - local tmp = entry - for i = 1, #chain - 1 do - tmp = tmp [chain [i]] - if not tmp then - --- invalid field - break - end - end - if tmp then - local value = tmp [chain [#chain]] - acc[#acc+1] = value or false - else - acc[#acc+1] = false - end - return get_fields (entry, fields, acc, n+1) - end - return acc -end - -local separator = "\t" --- could be “,” for csv - -local format_fields format_fields = function (fields, acc, n) - if not acc then - return format_fields(fields, { }, 1) - end - - local field = fields[n] - if field ~= nil then - if field == false then - acc[#acc+1] = "" - else - acc[#acc+1] = tostring(field) - end - return format_fields(fields, acc, n+1) - end - return tableconcat(acc, separator) -end - -local set_primary_field -set_primary_field = function (fields, addme, acc, n) - if not acc then - return set_primary_field(fields, addme, { addme }, 1) - end - - local field = fields[n] - if field then - if field ~= addme then - acc[#acc+1] = field - end - return set_primary_field(fields, addme, acc, n+1) - end - return acc -end - -local splitcomma = luaotfload.parsers.splitcomma - -actions.list = function (job) - local criterion = job.criterion - local asked_fields = job.asked_fields - local name_index = names.data () - - if asked_fields then - asked_fields = lpegmatch(splitcomma, asked_fields) - end - - if not asked_fields then - --- some defaults - asked_fields = { "plainname", "version", } - end - - if not name_index then - name_index = names.load() - end - - local mappings = name_index.mappings - local nmappings = #mappings - - if criterion == "*" then - report (false, 1, "list", "All %d entries", nmappings) - for i=1, nmappings do - local entry = mappings[i] - local fields = get_fields(entry, asked_fields) - --- we could collect these instead ... - local formatted = format_fields(fields) - texiowrite_nl(formatted) - end - - else - criterion = stringexplode(criterion, ":") --> { field, value } - local asked_value = criterion[2] - criterion = criterion[1] - asked_fields = set_primary_field(asked_fields, criterion) - - report (false, 1, "list", "By %s", criterion) - - --- firstly, build a list of fonts to operate on - local targets = { } - if asked_value then --- only those whose value matches - report (false, 2, "list", "Restricting to value %s", asked_value) - for i=1, nmappings do - local entry = mappings[i] - if entry[criterion] - and tostring(entry[criterion]) == asked_value - then - targets[#targets+1] = entry - end - end - - else --- whichever have the field, sorted - local categories, by_category = { }, { } - for i=1, nmappings do - local entry = mappings[i] - local tmp = entry - local chain = stringsplit (criterion, "->") - for i = 1, #chain - 1 do - tmp = tmp [chain [i]] - if not tmp then - break - end - end - local value = tmp and tmp [chain [#chain]] or "" - if value then - --value = tostring(value) - local entries = by_category[value] - if not entries then - entries = { entry } - categories[#categories+1] = value - else - entries[#entries+1] = entry - end - by_category[value] = entries - end - end - table.sort(categories) - - for i=1, #categories do - local entries = by_category[categories[i]] - for j=1, #entries do - targets[#targets+1] = entries[j] - end - end - end - local ntargets = #targets - report (false, 2, "list", "%d entries", ntargets) - - --- now, output the collection - for i=1, ntargets do - local entry = targets[i] - local fields = get_fields(entry, asked_fields) - local formatted = format_fields(fields) - texiowrite_nl(formatted) - end - end - - texiowrite_nl "" - - return true, true -end - -actions.diagnose = function (job) - --- diagnostics are loaded on demand - local diagnose = require "luaotfload-diagnostics.lua" - return diagnose (job) -end - ---- stuff to be carried out prior to exit - -local finalizers = { } - ---- returns false if at least one of the actions failed, mainly ---- for closing io channels -local finalize = function () - local success = true - for _, fun in next, finalizers do - if type (fun) == "function" then - if fun () == false then success = false end - end - end - return success -end - ---[[-- -Command-line processing. -luaotfload-tool relies on the script alt_getopt to process argv and -analyzes its output. - -TODO with extended lualibs we have the functionality from the -environment.* namespace that could eliminate the dependency on -alt_getopt. ---]]-- - -local process_cmdline = function ( ) -- unit -> jobspec - local result = { -- jobspec - force_reload = nil, - full_info = false, - warnings = false, - criterion = "", - query = "", - log_level = 0, --- 2 is approx. the old behavior - } - - local long_options = { - cache = 1, - ["no-compress"] = "c", - diagnose = 1, - ["dry-run"] = "D", - ["flush-lookups"] = "l", - fields = 1, - find = 1, - force = "f", - formats = 1, - fuzzy = "F", - help = "h", - info = "i", - inspect = "I", - limit = 1, - list = 1, - log = 1, - ["max-fonts"] = 1, - ["no-reload"] = "n", - ["no-strip"] = 0, - ["skip-read"] = "R", - ["prefer-texmf"] = "p", - quiet = "q", - ["show-blacklist"] = "b", - stats = "S", - update = "u", - verbose = 1, - version = "V", - warnings = "w", - } - - local short_options = "bcDfFiIlnpqRSuvVhw" - - local options, _, optarg = - alt_getopt.get_ordered_opts (arg, short_options, long_options) - - local nopts = #options - for n=1, nopts do - local v = options[n] - if v == "q" then - result.log_level = 0 - elseif v == "u" then - action_pending["generate"] = true - elseif v == "v" then - if result.log_level > 0 then - result.log_level = result.log_level + 1 - else - result.log_level = 1 - end - elseif v == "V" then - action_pending["version"] = true - elseif v == "h" then - action_pending["help"] = true - elseif v == "f" then - result.update = true - result.force_reload = 1 - elseif v == "verbose" then - local lvl = optarg[n] - if lvl then - lvl = tonumber(lvl) - result.log_level = lvl - if lvl > 2 then - result.warnings = true - end - end - elseif v == "w" then - result.warnings = true - elseif v == "log" then - local str = optarg[n] - if str then - finalizers = log.set_logout(str, finalizers) - end - elseif v == "find" then - action_pending["query"] = true - result.query = optarg[n] - elseif v == "F" then - result.fuzzy = true - elseif v == "limit" then - local lim = optarg[n] - if lim then - result.fuzzy_limit = tonumber(lim) - end - elseif v == "i" then - result.show_info = true - elseif v == "I" then - result.show_info = true - result.full_info = true - elseif v == "l" then - action_pending["flush"] = true - elseif v == "list" then - action_pending["list"] = true - result.criterion = optarg[n] - elseif v == "fields" then - result.asked_fields = optarg[n] - elseif v == "cache" then - action_pending["cache"] = true - result.cache = optarg[n] - elseif v == "D" then - result.dry_run = true - elseif v == "p" then - names.set_location_precedence { - "local", "texmf", "system" - } - elseif v == "b" then - action_pending["blacklist"] = true - elseif v == "diagnose" then - action_pending["diagnose"] = true - result.asked_diagnostics = optarg[n] - elseif v == "formats" then - names.set_font_filter (optarg[n]) - elseif v == "n" then - luaotfloadconfig.update_live = false - elseif v == "S" then - luaotfloadconfig.statistics = true - elseif v == "R" then - --- dev only, undocumented - luaotfloadconfig.skip_read = true - elseif v == "c" then - luaotfloadconfig.compress = false - elseif v == "no-strip" then - luaotfloadconfig.strip = false - elseif v == "max-fonts" then - local n = optarg[n] - if n then - n = tonumber(n) - if n and n > 0 then - luaotfloadconfig.max_fonts = n - end - end - end - end - - if nopts == 0 then - action_pending["help"] = true - result.help_version = "short" - end - return result -end - -local main = function ( ) -- unit -> int - local retval = 0 - local job = process_cmdline() - --- inspect(action_pending) --- inspect(job) - - for i=1, #action_sequence do - local actionname = action_sequence[i] - local exit = false - if action_pending[actionname] then - report ("log", 3, "util", "Preparing for task", "%s", actionname) - - local action = actions[actionname] - local success, continue = action(job) - - if not success then - report (false, 0, "util", - "Could not finish task", "%s", actionname) - retval = -1 - exit = true - elseif not continue then - report (false, 3, "util", - "Task completed, exiting", "%s", actionname) - exit = true - else - report (false, 3, "util", - "Task completed successfully", "%s", actionname) - end - end - if exit then break end - end - - if finalize () == false then - retval = -1 - end - - --texiowrite_nl"" - return retval -end - -return main() - --- vim:tw=71:sw=4:ts=4:expandtab diff --git a/luaotfload.sty b/luaotfload.sty deleted file mode 100644 index a235d6b..0000000 --- a/luaotfload.sty +++ /dev/null @@ -1,45 +0,0 @@ -%% Copyright (C) 2009-2014 -%% -%% by Elie Roux -%% and Khaled Hosny -%% and Philipp Gesang -%% -%% This file is part of Luaotfload. -%% -%% Home: https://github.com/lualatex/luaotfload -%% Support: . -%% -%% Luaotfload is under the GPL v2.0 (exactly) license. -%% -%% ---------------------------------------------------------------------------- -%% -%% Luaotfload is free software; you can redistribute it and/or -%% modify it under the terms of the GNU General Public License -%% as published by the Free Software Foundation; version 2 -%% of the License. -%% -%% Luaotfload is distributed in the hope that it will be useful, -%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -%% GNU General Public License for more details. -%% -%% You should have received a copy of the GNU General Public License -%% along with Luaotfload; if not, see . -%% -%% ---------------------------------------------------------------------------- -%% -%% Classical Plain+\LATEX package initialization. -%% -\csname ifluaotfloadloaded\endcsname -\let\ifluaotfloadloaded\endinput -\bgroup\expandafter\expandafter\expandafter\egroup -\expandafter\ifx\csname ProvidesPackage\endcsname\relax - \input luatexbase.sty -\else - \NeedsTeXFormat{LaTeX2e} - \ProvidesPackage{luaotfload}% - [2014/42/42 v2.5 OpenType layout system] - \RequirePackage{luatexbase} -\fi -\RequireLuaModule{luaotfload-main} - diff --git a/src/luaotfload-auxiliary.lua b/src/luaotfload-auxiliary.lua new file mode 100644 index 0000000..716af98 --- /dev/null +++ b/src/luaotfload-auxiliary.lua @@ -0,0 +1,783 @@ +#!/usr/bin/env texlua +----------------------------------------------------------------------- +-- FILE: luaotfload-auxiliary.lua +-- DESCRIPTION: part of luaotfload +-- REQUIREMENTS: luaotfload 2.5 +-- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang +-- VERSION: 2.5 +-- MODIFIED: 2014-01-02 21:24:25+0100 +----------------------------------------------------------------------- +-- + +--- this file addresses issue #24 +--- https://github.com/lualatex/luaotfload/issues/24# + +luaotfload = luaotfload or {} +luaotfload.aux = luaotfload.aux or { } + +local aux = luaotfload.aux +local log = luaotfload.log +local report = log.report +local fonthashes = fonts.hashes +local identifiers = fonthashes.identifiers + +local fontid = font.id +local texsprint = tex.sprint + +local dofile = dofile +local getmetatable = getmetatable +local setmetatable = setmetatable +local utf8 = unicode.utf8 +local stringlower = string.lower +local stringformat = string.format +local stringgsub = string.gsub +local stringbyte = string.byte +local stringfind = string.find +local tablecopy = table.copy + +----------------------------------------------------------------------- +--- font patches +----------------------------------------------------------------------- + +--- https://github.com/khaledhosny/luaotfload/issues/54 + +local rewrite_fontname = function (tfmdata, specification) + tfmdata.name = [["]] .. specification .. [["]] +end + +local rewriting = false + +local start_rewrite_fontname = function () + if rewriting == false then + luatexbase.add_to_callback ( + "luaotfload.patch_font", + rewrite_fontname, + "luaotfload.rewrite_fontname") + rewriting = true + report ("log", 0, "aux", + "start rewriting tfmdata.name field") + end +end + +aux.start_rewrite_fontname = start_rewrite_fontname + +local stop_rewrite_fontname = function () + if rewriting == true then + luatexbase.remove_fromt_callback + ("luaotfload.patch_font", "luaotfload.rewrite_fontname") + rewriting = false + report ("log", 0, "aux", + "stop rewriting tfmdata.name field") + end +end + +aux.stop_rewrite_fontname = stop_rewrite_fontname + + +--[[doc-- +This sets two dimensions apparently relied upon by the unicode-math +package. +--doc]]-- + +local set_sscale_dimens = function (fontdata) + local mathconstants = fontdata.MathConstants + local parameters = fontdata.parameters + if mathconstants then + parameters[10] = mathconstants.ScriptPercentScaleDown or 70 + parameters[11] = mathconstants.ScriptScriptPercentScaleDown or 50 + end + return fontdata +end + +luatexbase.add_to_callback( + "luaotfload.patch_font", + set_sscale_dimens, + "luaotfload.aux.set_sscale_dimens") + +--- fontobj -> int +local lookup_units = function (fontdata) + local metadata = fontdata.shared and fontdata.shared.rawdata.metadata + if metadata and metadata.units_per_em then + return metadata.units_per_em + elseif fontdata.parameters and fontdata.parameters.units then + return fontdata.parameters.units + elseif fontdata.units then --- v1.x + return fontdata.units + end + return 1000 +end + +--[[doc-- +This callback corrects some values of the Cambria font. +--doc]]-- +--- fontobj -> unit +local patch_cambria_domh = function (fontdata) + local mathconstants = fontdata.MathConstants + if mathconstants and fontdata.psname == "CambriaMath" then + --- my test Cambria has 2048 + local units_per_em = fontdata.units_per_em or lookup_units(fontdata) + local sz = fontdata.parameters.size or fontdata.size + local mh = 2800 / units_per_em * sz + if mathconstants.DisplayOperatorMinHeight < mh then + mathconstants.DisplayOperatorMinHeight = mh + end + end +end + +luatexbase.add_to_callback( + "luaotfload.patch_font", + patch_cambria_domh, + "luaotfload.aux.patch_cambria_domh") + +--[[doc-- + +Comment from fontspec: + + “Here we patch fonts tfm table to emulate \XeTeX's \cs{fontdimen8}, + which stores the caps-height of the font. (Cf.\ \cs{fontdimen5} which + stores the x-height.) + + Falls back to measuring the glyph if the font doesn't contain the + necessary information. + This needs to be extended for fonts that don't contain an `X'.” + +--doc]]-- + +local set_capheight = function (fontdata) + local shared = fontdata.shared + local parameters = fontdata.parameters + local capheight + if shared and shared.rawdata.metadata.pfminfo then + local units_per_em = parameters.units + local size = parameters.size + local os2_capheight = shared.rawdata.metadata.pfminfo.os2_capheight + + if os2_capheight > 0 then + capheight = os2_capheight / units_per_em * size + else + local X8 = stringbyte"X" + if fontdata.characters[X8] then + capheight = fontdata.characters[X8].height + else + capheight = parameters.ascender / units_per_em * size + end + end + else + local X8 = stringbyte"X" + if fontdata.characters[X8] then + capheight = fontdata.characters[X8].height + end + end + if capheight then + --- is this legit? afaics there’s nothing else on the + --- array part of that table + fontdata.parameters[8] = capheight + end +end + +luatexbase.add_to_callback( + "luaotfload.patch_font", + set_capheight, + "luaotfload.aux.set_capheight") + +----------------------------------------------------------------------- +--- glyphs and characters +----------------------------------------------------------------------- + +local agl = fonts.encodings.agl + +--- int -> int -> bool +local font_has_glyph = function (font_id, codepoint) + local fontdata = fonts.hashes.identifiers[font_id] + if fontdata then + if fontdata.characters[codepoint] ~= nil then return true end + end + return false +end + +aux.font_has_glyph = font_has_glyph + +--- undocumented + +local raw_slot_of_name = function (font_id, glyphname) + local fontdata = font.fonts[font_id] + if fontdata.type == "virtual" then --- get base font for glyph idx + local codepoint = agl.unicodes[glyphname] + local glyph = fontdata.characters[codepoint] + if fontdata.characters[codepoint] then + return codepoint + end + end + return false +end + +--[[doc-- + + This one is approximately “name_to_slot” from the microtype package; + note that it is all about Adobe Glyph names and glyph slots in the + font. The names and values may diverge from actual Unicode. + + http://www.adobe.com/devnet/opentype/archives/glyph.html + + The “unsafe” switch triggers a fallback lookup in the raw fonts + table. As some of the information is stored as references, this may + have unpredictable side-effects. + +--doc]]-- + +--- int -> string -> bool -> (int | false) +local slot_of_name = function (font_id, glyphname, unsafe) + local fontdata = identifiers[font_id] + if fontdata then + local unicode = fontdata.resources.unicodes[glyphname] + if unicode then + if type(unicode) == "number" then + return unicode + else + return unicode[1] --- for multiple components + end +-- else +-- --- missing + end + elseif unsafe == true then -- for Robert + return raw_slot_of_name(font_id, glyphname) + end + return false +end + +aux.slot_of_name = slot_of_name + +--[[doc-- + + Inverse of above; not authoritative as to my knowledge the official + inverse of the AGL is the AGLFN. Maybe this whole issue should be + dealt with in a separate package that loads char-def.lua and thereby + solves the problem for the next couple decades. + + http://partners.adobe.com/public/developer/en/opentype/aglfn13.txt + +--doc]]-- + +local indices + +--- int -> (string | false) +local name_of_slot = function (codepoint) + if not indices then --- this will load the glyph list + local unicodes = agl.unicodes + indices = table.swapped(unicodes) + end + local glyphname = indices[codepoint] + if glyphname then + return glyphname + end + return false +end + +aux.name_of_slot = name_of_slot + +--[[doc-- + + In Context, characters.data is where the data from char-def.lua + resides. The file is huge (>3.7 MB as of 2013) and not part of the + isolated font loader. Nevertheless, we include a partial version + generated by the mkcharacters script that contains only the + a subset of the fields of each character defined. + + Currently, these are (compare the mkcharacters script!) + + · "direction" + · "mirror" + · "category" + · "textclass" + + The directional information is required for packages like Simurgh [0] + to work correctly. In an early stage [1] it was necessary to load + further files from Context directly, including the full blown version + of char-def. Since we have no use for most of the so imported + functionality, the required parts have been isolated and are now + instated along with luaotfload-characters.lua. We can extend the set + of imported features easily should it not be enough. + + [0] https://github.com/persian-tex/simurgh + [1] http://tex.stackexchange.com/a/132301/14066 + +--doc]]-- + +characters = characters or { } --- should be created in basics-gen +characters.data = nil +local chardef = "luaotfload-characters" + +do + local setmetatableindex = function (t, f) + local mt = getmetatable (t) + if mt then + mt.__index = f + else + setmetatable (t, { __index = f }) + end + end + + --- there are some special tables for each field that provide access + --- to fields of the character table by means of a metatable + + local mkcharspecial = function (characters, tablename, field) + + local chardata = characters.data + + if chardata then + local newspecial = { } + characters [tablename] = newspecial --> e.g. “characters.data.mirrors” + + local idx = function (t, char) + local c = chardata [char] + if c then + local m = c [field] --> e.g. “mirror” + if m then + t [char] = m + return m + end + end + newspecial [char] = false + return char + end + + setmetatableindex (newspecial, idx) + end + + end + + local mkcategories = function (characters) -- different from the others + + local chardata = characters.data + + setmetatable (characters, { __index = function (t, char) + if char then + local c = chardata [char] + c = c.category or char + t [char] = c + return c + end + end}) + + end + + local load_failed = false + local chardata --> characters.data; loaded on demand + + local load_chardef = function () + + report ("both", 1, "aux", "Loading character metadata from %s.", chardef) + chardata = dofile (kpse.find_file (chardef, "lua")) + + if chardata == nil then + warning ("Could not load %s; continuing \z + with empty character table.", + chardef) + chardata = { } + load_failed = true + end + + characters = { } --- nuke metatable + characters.data = chardata + + --- institute some of the functionality from char-ini.lua + + mkcharspecial (characters, "mirrors", "mirror") + mkcharspecial (characters, "directions", "direction") + mkcharspecial (characters, "textclasses", "textclass") + mkcategories (characters) + + end + + local charindex = function (t, k) + if chardata == nil and load_failed ~= true then + load_chardef () + end + + return characters [k] + end + + setmetatableindex (characters, charindex) + +end + +----------------------------------------------------------------------- +--- features / scripts / languages +----------------------------------------------------------------------- +--- lots of arrowcode ahead + +--[[doc-- +This function, modeled after “check_script()” from fontspec, returns +true if in the given font, the script “asked_script” is accounted for in at +least one feature. +--doc]]-- + +--- int -> string -> bool +local provides_script = function (font_id, asked_script) + asked_script = stringlower(asked_script) + if font_id and font_id > 0 then + local fontdata = identifiers[font_id].shared.rawdata + if fontdata then + local fontname = fontdata.metadata.fontname + local features = fontdata.resources.features + for method, featuredata in next, features do + --- where method: "gpos" | "gsub" + for feature, data in next, featuredata do + if data[asked_script] then + report ("log", 1, "aux", + "font no %d (%s) defines feature %s for script %s", + font_id, fontname, feature, asked_script) + return true + end + end + end + report ("log", 0, "aux", + "font no %d (%s) defines no feature for script %s", + font_id, fontname, asked_script) + end + end + report ("log", 0, "aux", "no font with id %d", font_id) + return false +end + +aux.provides_script = provides_script + +--[[doc-- +This function, modeled after “check_language()” from fontspec, returns +true if in the given font, the language with tage “asked_language” is +accounted for in the script with tag “asked_script” in at least one +feature. +--doc]]-- + +--- int -> string -> string -> bool +local provides_language = function (font_id, asked_script, asked_language) + asked_script = stringlower(asked_script) + asked_language = stringlower(asked_language) + if font_id and font_id > 0 then + local fontdata = identifiers[font_id].shared.rawdata + if fontdata then + local fontname = fontdata.metadata.fontname + local features = fontdata.resources.features + for method, featuredata in next, features do + --- where method: "gpos" | "gsub" + for feature, data in next, featuredata do + local scriptdata = data[asked_script] + if scriptdata and scriptdata[asked_language] then + report ("log", 1, "aux", + "font no %d (%s) defines feature %s " + .. "for script %s with language %s", + font_id, fontname, feature, + asked_script, asked_language) + return true + end + end + end + report ("log", 0, "aux", + "font no %d (%s) defines no feature " + .. "for script %s with language %s", + font_id, fontname, asked_script, asked_language) + end + end + report ("log", 0, "aux", "no font with id %d", font_id) + return false +end + +aux.provides_language = provides_language + +--[[doc-- +We strip the syntax elements from feature definitions (shouldn’t +actually be there in the first place, but who cares ...) +--doc]]-- + +local lpeg = require"lpeg" +local C, P, S = lpeg.C, lpeg.P, lpeg.S +local lpegmatch = lpeg.match + +local sign = S"+-" +local rhs = P"=" * P(1)^0 * P(-1) +local strip_garbage = sign^-1 * C((1 - rhs)^1) + +--s = "+foo" --> foo +--ss = "-bar" --> bar +--sss = "baz" --> baz +--t = "foo=bar" --> foo +--tt = "+bar=baz" --> bar +--ttt = "-baz=true" --> baz +-- +--print(lpeg.match(strip_garbage, s)) +--print(lpeg.match(strip_garbage, ss)) +--print(lpeg.match(strip_garbage, sss)) +--print(lpeg.match(strip_garbage, t)) +--print(lpeg.match(strip_garbage, tt)) +--print(lpeg.match(strip_garbage, ttt)) + +--[[doc-- +This function, modeled after “check_feature()” from fontspec, returns +true if in the given font, the language with tag “asked_language” is +accounted for in the script with tag “asked_script” in feature +“asked_feature”. +--doc]]-- + +--- int -> string -> string -> string -> bool +local provides_feature = function (font_id, asked_script, + asked_language, asked_feature) + asked_script = stringlower(asked_script) + asked_language = stringlower(asked_language) + asked_feature = lpegmatch(strip_garbage, asked_feature) + + if font_id and font_id > 0 then + local fontdata = identifiers[font_id].shared.rawdata + if fontdata then + local features = fontdata.resources.features + local fontname = fontdata.metadata.fontname + for method, featuredata in next, features do + --- where method: "gpos" | "gsub" + local feature = featuredata[asked_feature] + if feature then + local scriptdata = feature[asked_script] + if scriptdata and scriptdata[asked_language] then + report ("log", 1, "aux", + "font no %d (%s) defines feature %s " + .. "for script %s with language %s", + font_id, fontname, asked_feature, + asked_script, asked_language) + return true + end + end + end + report ("log", 0, "aux", + "font no %d (%s) does not define feature %s for script %s with language %s", + font_id, fontname, asked_feature, asked_script, asked_language) + end + end + report ("log", 0, "aux", "no font with id %d", font_id) + return false +end + +aux.provides_feature = provides_feature + +----------------------------------------------------------------------- +--- font dimensions +----------------------------------------------------------------------- + +--- int -> string -> int +local get_math_dimension = function (font_id, dimenname) + if type(font_id) == "string" then + font_id = fontid(font_id) --- safeguard + end + local fontdata = identifiers[font_id] + local mathdata = fontdata.mathparameters + if mathdata then + return mathdata[dimenname] or 0 + end + return 0 +end + +aux.get_math_dimension = get_math_dimension + +--- int -> string -> unit +local sprint_math_dimension = function (font_id, dimenname) + if type(font_id) == "string" then + font_id = fontid(font_id) + end + local dim = get_math_dimension(font_id, dimenname) + texsprint(luatexbase.catcodetables["latex-package"], dim, "sp") +end + +aux.sprint_math_dimension = sprint_math_dimension + +----------------------------------------------------------------------- +--- extra database functions +----------------------------------------------------------------------- + +local namesresolve = fonts.names.resolve +local namesscan_dir = fonts.names.scan_dir + +--[====[-- TODO -> port this to new db model + +--- local directories ------------------------------------------------- + +--- migrated from luaotfload-database.lua +--- https://github.com/lualatex/luaotfload/pull/61#issuecomment-17776975 + +--- string -> (int * int) +local scan_external_dir = function (dir) + local old_names, new_names = names.data() + if not old_names then + old_names = load_names() + end + new_names = tablecopy(old_names) + local n_scanned, n_new = scan_dir(dir, old_names, new_names) + --- FIXME + --- This doesn’t seem right. If a db update is triggered after this + --- point, then the added fonts will be saved along with it -- + --- which is not as “temporarily” as it should be. (This should be + --- addressed during a refactoring of names_resolve().) + names.data = new_names + return n_scanned, n_new +end + +aux.scan_external_dir = scan_external_dir + +--]====]-- + +aux.scan_external_dir = function () + print "ERROR: scan_external_dir() is not implemented" +end + +--- db queries -------------------------------------------------------- + +--- https://github.com/lualatex/luaotfload/issues/74 +--- string -> (string * int) +local resolve_fontname = function (name) + local foundname, subfont, success = namesresolve(nil, nil, { + name = name, + lookup = "name", + optsize = 0, + specification = "name:" .. name, + }) + if success then + return foundname, subfont + end + return false, false +end + +aux.resolve_fontname = resolve_fontname + +--- string list -> (string * int) +local resolve_fontlist +resolve_fontlist = function (names, n) + if not n then + return resolve_fontlist(names, 1) + end + local this = names[n] + if this then + local foundname, subfont = resolve_fontname(this) + if foundname then + return foundname, subfont + end + return resolve_fontlist(names, n+1) + end + return false, false +end + +aux.resolve_fontlist = resolve_fontlist + +--- loaded fonts ------------------------------------------------------ + +--- just a proof of concept + +--- fontobj -> string list -> (string list) list +local get_font_data get_font_data = function (tfmdata, keys, acc, n) + if not acc then + return get_font_data(tfmdata, keys, {}, 1) + end + local key = keys[n] + if key then + local val = tfmdata[key] + if val then + acc[#acc+1] = val + else + acc[#acc+1] = false + end + return get_font_data(tfmdata, keys, acc, n+1) + end + return acc +end + +--[[doc-- + + The next one operates on the fonts.hashes.identifiers table. + It returns a list containing tuples of font ids and the + contents of the fields specified in the first argument. + Font table entries that were created indirectly -- e.g. by + \letterspacefont or during font expansion -- will not be + listed. + +--doc]]-- + +local default_keys = { "fullname" } + +--- string list -> (int * string list) list +local get_loaded_fonts get_loaded_fonts = function (keys, acc, lastid) + if not acc then + if not keys then + keys = default_keys + end + return get_loaded_fonts(keys, {}, lastid) + end + local id, tfmdata = next(identifiers, lastid) + if id then + local data = get_font_data(tfmdata, keys) + acc[#acc+1] = { id, data } + return get_loaded_fonts (keys, acc, id) + end + return acc +end + +aux.get_loaded_fonts = get_loaded_fonts + +--- Raw access to the font.* namespace is unsafe so no documentation on +--- this one. +local get_raw_fonts = function ( ) + local res = { } + for i, v in font.each() do + if v.filename then + res[#res+1] = { i, v } + end + end + return res +end + +aux.get_raw_fonts = get_raw_fonts + +----------------------------------------------------------------------- +--- font parameters +----------------------------------------------------------------------- +--- analogy of font-hsh + +fonthashes.parameters = fonthashes.parameters or { } +fonthashes.quads = fonthashes.quads or { } + +local parameters = fonthashes.parameters or { } +local quads = fonthashes.quads or { } + +setmetatable(parameters, { __index = function (t, font_id) + local tfmdata = identifiers[font_id] + if not tfmdata then --- unsafe; avoid + tfmdata = font.fonts[font_id] + end + if tfmdata and type(tfmdata) == "table" then + local fontparameters = tfmdata.parameters + t[font_id] = fontparameters + return fontparameters + end + return nil +end}) + +--[[doc-- + + Note that the reason as to why we prefer functions over table indices + is that functions are much safer against unintended manipulation. + This justifies the overhead they cost. + +--doc]]-- + +--- int -> (number | false) +local get_quad = function (font_id) + local quad = quads[font_id] + if quad then + return quad + end + local fontparameters = parameters[font_id] + if fontparameters then + local quad = fontparameters.quad or 0 + quads[font_id] = quad + return quad + end + return false +end + +aux.get_quad = get_quad + +-- vim:tw=71:sw=2:ts=2:expandtab diff --git a/src/luaotfload-basics-gen.lua b/src/luaotfload-basics-gen.lua new file mode 100644 index 0000000..9cf5b93 --- /dev/null +++ b/src/luaotfload-basics-gen.lua @@ -0,0 +1,343 @@ +if not modules then modules = { } end modules ['luat-basics-gen'] = { + version = 1.100, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local dummyfunction = function() +end + +local dummyreporter = function(c) + return function(...) + (texio.reporter or texio.write_nl)(c .. " : " .. string.formatters(...)) + end +end + +statistics = { + register = dummyfunction, + starttiming = dummyfunction, + stoptiming = dummyfunction, + elapsedtime = nil, +} + +directives = { + register = dummyfunction, + enable = dummyfunction, + disable = dummyfunction, +} + +trackers = { + register = dummyfunction, + enable = dummyfunction, + disable = dummyfunction, +} + +experiments = { + register = dummyfunction, + enable = dummyfunction, + disable = dummyfunction, +} + +storage = { -- probably no longer needed + register = dummyfunction, + shared = { }, +} + +logs = { + new = dummyreporter, + reporter = dummyreporter, + messenger = dummyreporter, + report = dummyfunction, +} + +callbacks = { + register = function(n,f) return callback.register(n,f) end, + +} + +utilities = { + storage = { + allocate = function(t) return t or { } end, + mark = function(t) return t or { } end, + }, +} + +characters = characters or { + data = { } +} + +-- we need to cheat a bit here + +texconfig.kpse_init = true + +resolvers = resolvers or { } -- no fancy file helpers used + +local remapper = { + otf = "opentype fonts", + ttf = "truetype fonts", + ttc = "truetype fonts", + dfont = "truetype fonts", -- "truetype dictionary", + cid = "cid maps", + cidmap = "cid maps", + fea = "font feature files", + pfa = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! + pfb = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this! + afm = "afm", +} + +function resolvers.findfile(name,fileformat) + name = string.gsub(name,"\\","/") + if not fileformat or fileformat == "" then + fileformat = file.suffix(name) + if fileformat == "" then + fileformat = "tex" + end + end + fileformat = string.lower(fileformat) + fileformat = remapper[fileformat] or fileformat + local found = kpse.find_file(name,fileformat) + if not found or found == "" then + found = kpse.find_file(name,"other text files") + end + return found +end + +-- function resolvers.findbinfile(name,fileformat) +-- if not fileformat or fileformat == "" then +-- fileformat = file.suffix(name) +-- end +-- return resolvers.findfile(name,(fileformat and remapper[fileformat]) or fileformat) +-- end + +resolvers.findbinfile = resolvers.findfile + +function resolvers.loadbinfile(filename,filetype) + local data = io.loaddata(filename) + return true, data, #data +end + +function resolvers.resolve(s) + return s +end + +function resolvers.unresolve(s) + return s +end + +-- Caches ... I will make a real stupid version some day when I'm in the +-- mood. After all, the generic code does not need the more advanced +-- ConTeXt features. Cached data is not shared between ConTeXt and other +-- usage as I don't want any dependency at all. Also, ConTeXt might have +-- different needs and tricks added. + +--~ containers.usecache = true + +caches = { } + +local writable = nil +local readables = { } +local usingjit = jit + +if not caches.namespace or caches.namespace == "" or caches.namespace == "context" then + caches.namespace = 'generic' +end + +do + + -- standard context tree setup + + local cachepaths = kpse.expand_var('$TEXMFCACHE') or "" + + -- quite like tex live or so (the weird $TEXMFCACHE test seems to be needed on miktex) + + if cachepaths == "" or cachepaths == "$TEXMFCACHE" then + cachepaths = kpse.expand_var('$TEXMFVAR') or "" + end + + -- this also happened to be used (the weird $TEXMFVAR test seems to be needed on miktex) + + if cachepaths == "" or cachepaths == "$TEXMFVAR" then + cachepaths = kpse.expand_var('$VARTEXMF') or "" + end + + -- and this is a last resort (hm, we could use TEMP or TEMPDIR) + + if cachepaths == "" then + local fallbacks = { "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" } + for i=1,#fallbacks do + cachepaths = os.getenv(fallbacks[i]) or "" + if cachepath ~= "" and lfs.isdir(cachepath) then + break + end + end + end + + if cachepaths == "" then + cachepaths = "." + end + + cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") + + for i=1,#cachepaths do + local cachepath = cachepaths[i] + if not lfs.isdir(cachepath) then + lfs.mkdirs(cachepath) -- needed for texlive and latex + if lfs.isdir(cachepath) then + texio.write(string.format("(created cache path: %s)",cachepath)) + end + end + if file.is_writable(cachepath) then + writable = file.join(cachepath,"luatex-cache") + lfs.mkdir(writable) + writable = file.join(writable,caches.namespace) + lfs.mkdir(writable) + break + end + end + + for i=1,#cachepaths do + if file.is_readable(cachepaths[i]) then + readables[#readables+1] = file.join(cachepaths[i],"luatex-cache",caches.namespace) + end + end + + if not writable then + texio.write_nl("quiting: fix your writable cache path") + os.exit() + elseif #readables == 0 then + texio.write_nl("quiting: fix your readable cache path") + os.exit() + elseif #readables == 1 and readables[1] == writable then + texio.write(string.format("(using cache: %s)",writable)) + else + texio.write(string.format("(using write cache: %s)",writable)) + texio.write(string.format("(using read cache: %s)",table.concat(readables, " "))) + end + +end + +function caches.getwritablepath(category,subcategory) + local path = file.join(writable,category) + lfs.mkdir(path) + path = file.join(path,subcategory) + lfs.mkdir(path) + return path +end + +function caches.getreadablepaths(category,subcategory) + local t = { } + for i=1,#readables do + t[i] = file.join(readables[i],category,subcategory) + end + return t +end + +local function makefullname(path,name) + if path and path ~= "" then + return file.addsuffix(file.join(path,name),"lua"), file.addsuffix(file.join(path,name),usingjit and "lub" or "luc") + end +end + +function caches.is_writable(path,name) + local fullname = makefullname(path,name) + return fullname and file.is_writable(fullname) +end + +function caches.loaddata(paths,name) + for i=1,#paths do + local data = false + local luaname, lucname = makefullname(paths[i],name) + if lucname and lfs.isfile(lucname) then -- maybe also check for size + texio.write(string.format("(load luc: %s)",lucname)) + data = loadfile(lucname) + if data then + data = data() + end + if data then + return data + else + texio.write(string.format("(loading failed: %s)",lucname)) + end + end + if luaname and lfs.isfile(luaname) then + texio.write(string.format("(load lua: %s)",luaname)) + data = loadfile(luaname) + if data then + data = data() + end + if data then + return data + end + end + end +end + +function caches.savedata(path,name,data) + local luaname, lucname = makefullname(path,name) + if luaname then + texio.write(string.format("(save: %s)",luaname)) + table.tofile(luaname,data,true) + if lucname and type(caches.compile) == "function" then + os.remove(lucname) -- better be safe + texio.write(string.format("(save: %s)",lucname)) + caches.compile(data,luaname,lucname) + end + end +end + +-- According to KH os.execute is not permitted in plain/latex so there is +-- no reason to use the normal context way. So the method here is slightly +-- different from the one we have in context. We also use different suffixes +-- as we don't want any clashes (sharing cache files is not that handy as +-- context moves on faster.) +-- +-- Beware: serialization might fail on large files (so maybe we should pcall +-- this) in which case one should limit the method to luac and enable support +-- for execution. + +-- function caches.compile(data,luaname,lucname) +-- local d = io.loaddata(luaname) +-- if not d or d == "" then +-- d = table.serialize(data,true) -- slow +-- end +-- if d and d ~= "" then +-- local f = io.open(lucname,'w') +-- if f then +-- local s = loadstring(d) +-- if s then +-- f:write(string.dump(s,true)) +-- end +-- f:close() +-- end +-- end +-- end + +function caches.compile(data,luaname,lucname) + local d = io.loaddata(luaname) + if not d or d == "" then + d = table.serialize(data,true) -- slow + end + if d and d ~= "" then + local f = io.open(lucname,'wb') + if f then + local s = loadstring(d) + if s then + f:write(string.dump(s,true)) + end + f:close() + end + end +end + +-- + +function table.setmetatableindex(t,f) + setmetatable(t,{ __index = f }) +end diff --git a/src/luaotfload-basics-nod.lua b/src/luaotfload-basics-nod.lua new file mode 100644 index 0000000..50a1e86 --- /dev/null +++ b/src/luaotfload-basics-nod.lua @@ -0,0 +1,167 @@ +if not modules then modules = { } end modules ['luatex-fonts-nod'] = { + version = 1.001, + comment = "companion to luatex-fonts.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +-- Don't depend on code here as it is only needed to complement the +-- font handler code. + +-- Attributes: + +if tex.attribute[0] ~= 0 then + + texio.write_nl("log","!") + texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") + texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") + texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.") + texio.write_nl("log","!") + + tex.attribute[0] = 0 -- else no features + +end + +attributes = attributes or { } +attributes.unsetvalue = -0x7FFFFFFF + +local numbers, last = { }, 127 + +attributes.private = attributes.private or function(name) + local number = numbers[name] + if not number then + if last < 255 then + last = last + 1 + end + number = last + numbers[name] = number + end + return number +end + +-- Nodes: + +nodes = { } +nodes.pool = { } +nodes.handlers = { } + +local nodecodes = { } for k,v in next, node.types () do nodecodes[string.gsub(v,"_","")] = k end +local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gsub(v,"_","")] = k end +local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" } + +nodes.nodecodes = nodecodes +nodes.whatcodes = whatcodes +nodes.whatsitcodes = whatcodes +nodes.glyphcodes = glyphcodes + +local free_node = node.free +local remove_node = node.remove +local new_node = node.new +local traverse_id = node.traverse_id + +local math_code = nodecodes.math + +nodes.handlers.protectglyphs = node.protect_glyphs +nodes.handlers.unprotectglyphs = node.unprotect_glyphs + +function nodes.remove(head, current, free_too) + local t = current + head, current = remove_node(head,current) + if t then + if free_too then + free_node(t) + t = nil + else + t.next, t.prev = nil, nil + end + end + return head, current, t +end + +function nodes.delete(head,current) + return nodes.remove(head,current,true) +end + +function nodes.pool.kern(k) + local n = new_node("kern",1) + n.kern = k + return n +end + +-- experimental + +local getfield = node.getfield or function(n,tag) return n[tag] end +local setfield = node.setfield or function(n,tag,value) n[tag] = value end + +nodes.getfield = getfield +nodes.setfield = setfield + +nodes.getattr = getfield +nodes.setattr = setfield + +if node.getid then nodes.getid = node.getid else function nodes.getid (n) return getfield(n,"id") end end +if node.getsubtype then nodes.getsubtype = node.getsubtype else function nodes.getsubtype(n) return getfield(n,"subtype") end end +if node.getnext then nodes.getnext = node.getnext else function nodes.getnext (n) return getfield(n,"next") end end +if node.getprev then nodes.getprev = node.getprev else function nodes.getprev (n) return getfield(n,"prev") end end +if node.getchar then nodes.getchar = node.getchar else function nodes.getchar (n) return getfield(n,"char") end end +if node.getfont then nodes.getfont = node.getfont else function nodes.getfont (n) return getfield(n,"font") end end +if node.getlist then nodes.getlist = node.getlist else function nodes.getlist (n) return getfield(n,"list") end end + +function nodes.tonut (n) return n end +function nodes.tonode(n) return n end + +-- being lazy ... just copy a bunch ... not all needed in generic but we assume +-- nodes to be kind of private anyway + +nodes.tostring = node.tostring or tostring +nodes.copy = node.copy +nodes.copy_list = node.copy_list +nodes.delete = node.delete +nodes.dimensions = node.dimensions +nodes.end_of_math = node.end_of_math +nodes.flush_list = node.flush_list +nodes.flush_node = node.flush_node +nodes.free = node.free +nodes.insert_after = node.insert_after +nodes.insert_before = node.insert_before +nodes.hpack = node.hpack +nodes.new = node.new +nodes.tail = node.tail +nodes.traverse = node.traverse +nodes.traverse_id = node.traverse_id +nodes.slide = node.slide +nodes.vpack = node.vpack + +nodes.first_glyph = node.first_glyph +nodes.first_character = node.first_character +nodes.has_glyph = node.has_glyph or node.first_glyph + +nodes.current_attr = node.current_attr +nodes.do_ligature_n = node.do_ligature_n +nodes.has_field = node.has_field +nodes.last_node = node.last_node +nodes.usedlist = node.usedlist +nodes.protrusion_skippable = node.protrusion_skippable +nodes.write = node.write + +nodes.has_attribute = node.has_attribute +nodes.set_attribute = node.set_attribute +nodes.unset_attribute = node.unset_attribute + +nodes.protect_glyphs = node.protect_glyphs +nodes.unprotect_glyphs = node.unprotect_glyphs +nodes.kerning = node.kerning +nodes.ligaturing = node.ligaturing +nodes.mlist_to_hlist = node.mlist_to_hlist + +-- in generic code, at least for some time, we stay nodes, while in context +-- we can go nuts (e.g. experimental); this split permits us us keep code +-- used elsewhere stable but at the same time play around in context + +nodes.nuts = nodes diff --git a/src/luaotfload-colors.lua b/src/luaotfload-colors.lua new file mode 100644 index 0000000..d999df6 --- /dev/null +++ b/src/luaotfload-colors.lua @@ -0,0 +1,312 @@ +if not modules then modules = { } end modules ['luaotfload-colors'] = { + version = "2.5", + comment = "companion to luaotfload-main.lua (font color)", + author = "Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2.0" +} + +--[[doc-- + +buggy coloring with the pre_output_filter when expansion is enabled + · tfmdata for different expansion values is split over different objects + · in ``initializeexpansion()``, chr.expansion_factor is set, and only + those characters that have it are affected + · in constructors.scale: chr.expansion_factor = ve*1000 if commented out + makes the bug vanish + +explanation: http://tug.org/pipermail/luatex/2013-May/004305.html + +--doc]]-- + + +local color_callback = config.luaotfload.color_callback +if not color_callback then + --- maybe this would be better as a method: "early" | "late" + color_callback = "pre_linebreak_filter" +-- color_callback = "pre_output_filter" --- old behavior, breaks expansion +end + + +local newnode = node.new +local nodetype = node.id +local traverse_nodes = node.traverse +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after + +local stringformat = string.format +local stringgsub = string.gsub +local stringfind = string.find +local stringsub = string.sub + +local otffeatures = fonts.constructors.newfeatures("otf") +local identifiers = fonts.hashes.identifiers +local registerotffeature = otffeatures.register + +local add_color_callback --[[ this used to be a global‽ ]] + +--[[doc-- +This converts a single octet into a decimal with three digits of +precision. The optional second argument limits precision to a single +digit. +--doc]]-- + +--- string -> bool? -> string +local hex_to_dec = function (hex,one) --- one isn’t actually used anywhere ... + if one then + return stringformat("%.1g", tonumber(hex, 16)/255) + else + return stringformat("%.3g", tonumber(hex, 16)/255) + end +end + +--[[doc-- +Color string validator / parser. +--doc]]-- + +local lpeg = require"lpeg" +local lpegmatch = lpeg.match +local C, Cg, Ct, P, R, S = lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.R, lpeg.S + +local digit16 = R("09", "af", "AF") +local octet = C(digit16 * digit16) + +local p_rgb = octet * octet * octet +local p_rgba = p_rgb * octet +local valid_digits = C(p_rgba + p_rgb) -- matches eight or six hex digits + +local p_Crgb = Cg(octet/hex_to_dec, "red") --- for captures + * Cg(octet/hex_to_dec, "green") + * Cg(octet/hex_to_dec, "blue") +local p_Crgba = p_Crgb * Cg(octet/hex_to_dec, "alpha") +local extract_color = Ct(p_Crgba + p_Crgb) + +--- string -> (string | nil) +local sanitize_color_expression = function (digits) + digits = tostring(digits) + local sanitized = lpegmatch(valid_digits, digits) + if not sanitized then + luaotfload.warning( + "%q is not a valid rgb[a] color expression", digits) + return nil + end + return sanitized +end + +--[[doc-- +``setcolor`` modifies tfmdata.properties.color in place +--doc]]-- + +--- fontobj -> string -> unit +--- +--- (where “string” is a rgb value as three octet +--- hexadecimal, with an optional fourth transparency +--- value) +--- +local setcolor = function (tfmdata, value) + local sanitized = sanitize_color_expression(value) + local properties = tfmdata.properties + + if sanitized then + properties.color = sanitized + add_color_callback() + end +end + +registerotffeature { + name = "color", + description = "color", + initializers = { + base = setcolor, + node = setcolor, + } +} + + +--- something is carried around in ``res`` +--- for later use by color_handler() --- but what? + +local res = nil + +--- float -> unit +local function pageresources(alpha) + res = res or {} + res[alpha] = true +end + +--- we store results of below color handler as tuples of +--- push/pop strings +local color_cache = { } --- (string, (string * string)) hash_t + +--- string -> (string * string) +local hex_to_rgba = function (digits) + if not digits then + return + end + + --- this is called like a thousand times, so some + --- memoizing is in order. + local cached = color_cache[digits] + if not cached then + local push, pop + local rgb = lpegmatch(extract_color, digits) + if rgb.alpha then + pageresources(rgb.alpha) + push = stringformat( + "/TransGs%g gs %s %s %s rg", + rgb.alpha, + rgb.red, + rgb.green, + rgb.blue) + pop = "0 g /TransGs1 gs" + else + push = stringformat( + "%s %s %s rg", + rgb.red, + rgb.green, + rgb.blue) + pop = "0 g" + end + color_cache[digits] = { push, pop } + return push, pop + end + + return cached[1], cached[2] +end + +--- Luatex internal types + +local glyph_t = nodetype("glyph") +local hlist_t = nodetype("hlist") +local vlist_t = nodetype("vlist") +local whatsit_t = nodetype("whatsit") +local page_insert_t = nodetype("page_insert") +local sub_box_t = nodetype("sub_box") + +--- node -> nil | -1 | color‽ +local lookup_next_color +lookup_next_color = function (head) --- paragraph material + for n in traverse_nodes(head) do + local n_id = n.id + + if n_id == glyph_t then + local n_font + if identifiers[n_font] + and identifiers[n_font].properties + and identifiers[n_font].properties.color + then + return identifiers[n.font].properties.color + else + return -1 + end + + elseif n_id == vlist_t or n_id == hlist_t or n_id == sub_box_t then + local r = lookup_next_color(n.list) + if r then + return r + end + + elseif n_id == whatsit_t or n_id == page_insert_t then + return -1 + end + end + return nil +end + +--[[doc-- +While the second argument and second returned value are apparently +always nil when the function is called, they temporarily take string +values during the node list traversal. +--doc]]-- + +local cnt = 0 +--- node -> string -> int -> (node * string) +local node_colorize +node_colorize = function (head, current_color, next_color) + for n in traverse_nodes(head) do + local n_id = n.id + local nextnode = n.next + + if n_id == hlist_t or n_id == vlist_t or n_id == sub_box_t then + local next_color_in = lookup_next_color(nextnode) or next_color + n.list, current_color = node_colorize(n.list, current_color, next_color_in) + + elseif n_id == glyph_t then + cnt = cnt + 1 + local tfmdata = identifiers[n.font] + + --- colorization is restricted to those fonts + --- that received the “color” property upon + --- loading (see ``setcolor()`` above) + if tfmdata and tfmdata.properties and tfmdata.properties.color then + local font_color = tfmdata.properties.color +-- luaotfload.info( +-- "n: %d; %s; %d %s, %s", +-- cnt, utf.char(n.char), n.font, "", font_color) + if font_color ~= current_color then + local pushcolor = hex_to_rgba(font_color) + local push = newnode(whatsit_t, 8) + push.mode = 1 + push.data = pushcolor + head = insert_node_before(head, n, push) + current_color = font_color + end + local next_color_in = lookup_next_color (nextnode) or next_color + if next_color_in ~= font_color then + local _, popcolor = hex_to_rgba(font_color) + local pop = newnode(whatsit_t, 8) + pop.mode = 1 + pop.data = popcolor + head = insert_node_after(head, n, pop) + current_color = nil + end + +-- else +-- luaotfload.info( +-- "n: %d; %s; %d %s", +-- cnt, utf.char(n.char), n.font, "") + end + end + end + return head, current_color +end + +--- node -> node +local color_handler = function (head) + local new_head = node_colorize(head, nil, nil) + -- now append our page resources + if res then + res["1"] = true + local tpr, t = tex.pdfpageresources, "" + for k in pairs(res) do + local str = stringformat("/TransGs%s<>", k, k, k) + if not stringfind(tpr,str) then + t = t .. str + end + end + if t ~= "" then + if not stringfind(tpr,"/ExtGState<<.*>>") then + tpr = tpr.."/ExtGState<<>>" + end + tpr = stringgsub(tpr,"/ExtGState<<","%1"..t) + tex.pdfpageresources = tpr + end + res = nil -- reset res + end + return new_head +end + +local color_callback_activated = 0 + +--- unit -> unit +add_color_callback = function ( ) + if color_callback_activated == 0 then + luatexbase.add_to_callback(color_callback, + color_handler, + "luaotfload.color_handler") + color_callback_activated = 1 + end +end + +-- vim:tw=71:sw=4:ts=4:expandtab + diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua new file mode 100644 index 0000000..4b2d201 --- /dev/null +++ b/src/luaotfload-database.lua @@ -0,0 +1,3445 @@ +if not modules then modules = { } end modules ['luaotfload-database'] = { + version = "2.5", + comment = "companion to luaotfload-main.lua", + author = "Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2.0" +} + +--[[doc-- + + Some statistics: + + a) TL 2012, mkluatexfontdb --force + b) v2.4, luaotfload-tool --update --force + c) v2.4, luaotfload-tool --update --force --formats=+afm,pfa,pfb + d) Context, mtxrun --script fonts --reload --force + + (Keep in mind that Context does index fewer fonts since it + considers only the contents of the minimals tree, not the + tex live one!) + + time (m:s) peak VmSize (kB) + a 1:19 386 018 + b 0:37 715 797 + c 2:27 1 017 674 + d 0:44 1 082 313 + + Most of the increase in memory consumption from version 1.x to 2.2+ + can be attributed to the move from single-pass to a multi-pass + approach to building the index: Information is first gathered from + all reachable fonts and only afterwards processed, classified and + discarded. Also, there is a good deal of additional stuff kept in + the database now: two extra tables for file names and font families + have been added, making font lookups more efficient while improving + maintainability of the code. + +--doc]]-- + +local lpeg = require "lpeg" +local P, Cc, lpegmatch = lpeg.P, lpeg.Cc, lpeg.match + +local parsers = luaotfload.parsers +local read_fonts_conf = parsers.read_fonts_conf +local stripslashes = parsers.stripslashes +local splitcomma = parsers.splitcomma + +local log = luaotfload.log +local report = log.report +local report_status = log.names_status +local report_status_start = log.names_status_start +local report_status_stop = log.names_status_stop + + +--- Luatex builtins +local load = load +local next = next +local require = require +local tonumber = tonumber +local unpack = table.unpack + +local fontloaderinfo = fontloader.info +local fontloaderclose = fontloader.close +local fontloaderopen = fontloader.open +----- fontloaderto_table = fontloader.to_table +local gzipopen = gzip.open +local iolines = io.lines +local ioopen = io.open +local iopopen = io.popen +local kpseexpand_path = kpse.expand_path +local kpsefind_file = kpse.find_file +local kpselookup = kpse.lookup +local kpsereadable_file = kpse.readable_file +local lfsattributes = lfs.attributes +local lfschdir = lfs.chdir +local lfscurrentdir = lfs.currentdir +local lfsdir = lfs.dir +local mathabs = math.abs +local mathmin = math.min +local osgetenv = os.getenv +local osgettimeofday = os.gettimeofday +local osremove = os.remove +local stringfind = string.find +local stringformat = string.format +local stringgmatch = string.gmatch +local stringgsub = string.gsub +local stringlower = string.lower +local stringsub = string.sub +local stringupper = string.upper +local tableconcat = table.concat +local tablesort = table.sort +local utf8gsub = unicode.utf8.gsub +local utf8lower = unicode.utf8.lower +local utf8len = unicode.utf8.len +local zlibcompress = zlib.compress + +--- these come from Lualibs/Context +local filebasename = file.basename +local filecollapsepath = file.collapsepath or file.collapse_path +local filedirname = file.dirname +local fileextname = file.extname +local fileiswritable = file.iswritable +local filejoin = file.join +local filenameonly = file.nameonly +local filereplacesuffix = file.replacesuffix +local filesplitpath = file.splitpath or file.split_path +local filesuffix = file.suffix +local getwritablepath = caches.getwritablepath +local lfsisdir = lfs.isdir +local lfsisfile = lfs.isfile +local lfsmkdirs = lfs.mkdirs +local lpegsplitat = lpeg.splitat +local stringis_empty = string.is_empty +local stringsplit = string.split +local stringstrip = string.strip +local tableappend = table.append +local tablecontains = table.contains +local tablecopy = table.copy +local tablefastcopy = table.fastcopy +local tabletofile = table.tofile +local tabletohash = table.tohash +local tableserialize = table.serialize +local runasscript = caches == nil +--- the font loader namespace is “fonts”, same as in Context +--- we need to put some fallbacks into place for when running +--- as a script +fonts = fonts or { } +fonts.names = fonts.names or { } +fonts.definers = fonts.definers or { } + +local luaotfloadconfig = config.luaotfload --- always present +luaotfloadconfig.resolver = luaotfloadconfig.resolver or "normal" +luaotfloadconfig.formats = luaotfloadconfig.formats or "otf,ttf,ttc,dfont" +luaotfloadconfig.strip = luaotfloadconfig.strip == true + +--- this option allows for disabling updates +--- during a TeX run +luaotfloadconfig.update_live = luaotfloadconfig.update_live ~= false +luaotfloadconfig.compress = luaotfloadconfig.compress ~= false + +local names = fonts.names +local name_index = nil --> upvalue for names.data +local lookup_cache = nil --> for names.lookups +names.version = 2.5 +names.data = nil --- contains the loaded database +names.lookups = nil --- contains the lookup cache + +names.path = { index = { }, lookups = { } } +names.path.globals = { + prefix = "", --- writable_path/names_dir + names_dir = luaotfloadconfig.names_dir or "names", + index_file = luaotfloadconfig.index_file + or "luaotfload-names.lua", + lookups_file = "luaotfload-lookup-cache.lua", +} + +--- string -> (string * string) +local make_luanames = function (path) + return filereplacesuffix(path, "lua"), + filereplacesuffix(path, "luc") +end + +--- The “termwidth” value is only considered when printing +--- short status messages, e.g. when building the database +--- online. +if not luaotfloadconfig.termwidth then + local tw = 79 + if not ( os.type == "windows" --- Assume broken terminal. + or osgetenv "TERM" == "dumb") + then + local p = iopopen "tput cols" + if p then + result = tonumber (p:read "*all") + p:close () + if result then + tw = result + else + report ("log", 2, "db", "tput returned non-number.") + end + else + report ("log", 2, "db", "Shell escape disabled or tput executable missing.") + report ("log", 2, "db", "Assuming 79 cols terminal width.") + end + end + luaotfloadconfig.termwidth = tw +end + +local format_precedence = { + "otf", "ttc", "ttf", + "dfont", "afm", "pfb", + "pfa", +} + +local location_precedence = { + "local", "system", "texmf", +} + +local set_location_precedence = function (precedence) + location_precedence = precedence +end + +--[[doc-- + We use the functions in the cache.* namespace that come with the + fontloader (see luat-basics-gen). it’s safe to use for the most part + since most checks and directory creations are already done. It + uses TEXMFCACHE or TEXMFVAR as starting points. + + There is one quirk, though: ``getwritablepath()`` will always + assume that files in subdirectories of the cache tree are writable. + It gives no feedback at all if it fails to open a file in write + mode. This may cause trouble when the index or lookup cache were + created by different user. +--doc]]-- + +if not runasscript then + local globals = names.path.globals + local names_dir = globals.names_dir + + prefix = getwritablepath (names_dir, "") + if not prefix then + luaotfload.error + ("Impossible to find a suitable writeable cache...") + else + prefix = lpegmatch (stripslashes, prefix) + report ("log", 0, "db", + "Root cache directory is %s.", prefix) + end + + globals.prefix = prefix + local lookup_path = names.path.lookups + local index = names.path.index + local lookups_file = filejoin (prefix, globals.lookups_file) + local index_file = filejoin (prefix, globals.index_file) + lookup_path.lua, lookup_path.luc = make_luanames (lookups_file) + index.lua, index.luc = make_luanames (index_file) +else --- running as script, inject some dummies + caches = { } + local dummy_function = function () end + log = { report = dummy_function, + report_status = dummy_function, + report_status_start = dummy_function, + report_status_stop = dummy_function, } +end + + +--[[doc-- +Auxiliary functions +--doc]]-- + +--- fontnames contain all kinds of garbage; as a precaution we +--- lowercase and strip them of non alphanumerical characters + +--- string -> string + +local invalidchars = "[^%a%d]" + +local sanitize_fontname = function (str) + if str ~= nil then + str = utf8gsub (utf8lower (str), invalidchars, "") + return str + end + return nil +end + +local sanitize_fontnames = function (rawnames) + local result = { } + for category, namedata in next, rawnames do + + if type (namedata) == "string" then + result [category] = utf8gsub (utf8lower (namedata), + invalidchars, + "") + else + local target = { } + for field, name in next, namedata do + target [field] = utf8gsub (utf8lower (name), + invalidchars, + "") + end + result [category] = target + end + end + return result +end + +local find_files_indeed +find_files_indeed = function (acc, dirs, filter) + if not next (dirs) then --- done + return acc + end + + local pwd = lfscurrentdir () + local dir = dirs[#dirs] + dirs[#dirs] = nil + + if lfschdir (dir) then + lfschdir (pwd) + + local newfiles = { } + for ent in lfsdir (dir) do + if ent ~= "." and ent ~= ".." then + local fullpath = dir .. "/" .. ent + if filter (fullpath) == true then + if lfsisdir (fullpath) then + dirs[#dirs+1] = fullpath + elseif lfsisfile (fullpath) then + newfiles[#newfiles+1] = fullpath + end + end + end + end + return find_files_indeed (tableappend (acc, newfiles), + dirs, filter) + end + --- could not cd into, so we skip it + return find_files_indeed (acc, dirs, filter) +end + +local dummyfilter = function () return true end + +--- the optional filter function receives the full path of a file +--- system entity. a filter applies if the first argument it returns is +--- true. + +--- string -> function? -> string list +local find_files = function (root, filter) + if lfsisdir (root) then + return find_files_indeed ({}, { root }, filter or dummyfilter) + end +end + + +--[[doc-- +This is a sketch of the luaotfload db: + + type dbobj = { + families : familytable; + files : filemap; + status : filestatus; + mappings : fontentry list; + meta : metadata; + names : namedata; // TODO: check for relevance after db is finalized + } + and familytable = { + local : (format, familyentry) hash; // specified with include dir + texmf : (format, familyentry) hash; + system : (format, familyentry) hash; + } + and familyentry = { + regular : sizes; + italic : sizes; + bold : sizes; + bolditalic : sizes; + } + and sizes = { + default : int; // points into mappings or names + optical : (int, int) list; // design size -> index entry + } + and metadata = { + formats : string list; // { "otf", "ttf", "ttc", "dfont" } + statistics : TODO; + version : float; + } + and filemap = { + base : { + local : (string, int) hash; // basename -> idx + system : (string, int) hash; + texmf : (string, int) hash; + }; + bare : { + local : (string, (string, int) hash) hash; // location -> (barename -> idx) + system : (string, (string, int) hash) hash; + texmf : (string, (string, int) hash) hash; + }; + full : (int, string) hash; // idx -> full path + } + and fontentry = { + barename : string; + familyname : string; + filename : string; + fontname : string; // <- metadata + fullname : string; // <- metadata + sanitized : { + family : string; + fontstyle_name : string; // <- new in 2.4 + fontname : string; // <- metadata + fullname : string; // <- namedata.names + metafamily : string; + pfullname : string; + prefmodifiers : string; + psname : string; + subfamily : string; + }; + size : int list; + slant : int; + subfont : int; + location : local | system | texmf; + weight : int; + width : int; + units_per_em : int; // mainly 1000, but also 2048 or 256 + } + and filestatus = (string, // fullname + { index : int list; // pointer into mappings + timestamp : int; }) dict + +beware that this is a reconstruction and may be incomplete. + +mtx-fonts has in names.tma: + + type names = { + cache_uuid : uuid; + cache_version : float; + datastate : uuid list; + fallbacks : (filetype, (basename, int) hash) hash; + families : (basename, int list) hash; + files : (filename, fullname) hash; + indices : (fullname, int) hash; + mappings : (filetype, (basename, int) hash) hash; + names : ? (empty hash) ?; + rejected : (basename, int) hash; + specifications: fontentry list; + } + and fontentry = { + designsize : int; + familyname : string; + filename : string; + fontname : string; + format : string; + fullname : string; + maxsize : int; + minsize : int; + modification : int; + rawname : string; + style : string; + subfamily : string; + variant : string; + weight : string; + width : string; + } + +--doc]]-- + +local initialize_namedata = function (formats) --- returns dbobj + return { + --families = { }, + status = { }, -- was: status; map abspath -> mapping + mappings = { }, -- TODO: check if still necessary after rewrite + names = { }, +-- files = { }, -- created later + meta = { + formats = formats, + statistics = { }, + version = names.version, + }, + } +end + +--[[doc-- + + Since Luaotfload does not depend on the lualibs anymore we + have to put our own small wrappers for the gzip library in + place. + + load_gzipped -- Read and decompress and entire gzipped file. + Returns the uncompressed content as a string. + +--doc]]-- + +local load_gzipped = function (filename) + local gh = gzipopen (filename,"rb") + if gh then + local data = gh:read "*all" + gh:close () + return data + end +end + +--[[doc-- + + save_gzipped -- Compress and write a string to file. The return + value is the number of bytes written. Zlib parameters are: best + compression and default strategy. + +--doc]]-- + +local save_gzipped = function (filename, data) + local gh = gzipopen (filename, "wb9") + if gh then + gh:write (data) + local bytes = gh:seek () + gh:close () + return bytes + end +end + +--- When loading a lua file we try its binary complement first, which +--- is assumed to be located at an identical path, carrying the suffix +--- .luc. + +--- string -> (string * table) +local load_lua_file = function (path) + local foundname = filereplacesuffix (path, "luc") + local code = nil + + local fh = ioopen (foundname, "rb") -- try bin first + if fh then + local chunk = fh:read"*all" + fh:close() + code = load (chunk, "b") + end + + if not code then --- fall back to text file + foundname = filereplacesuffix (path, "lua") + fh = ioopen(foundname, "rb") + if fh then + local chunk = fh:read"*all" + fh:close() + code = load (chunk, "t") + end + end + + if not code then --- probe gzipped file + foundname = filereplacesuffix (path, "lua.gz") + local chunk = load_gzipped (foundname) + if chunk then + code = load (chunk, "t") + end + end + + if not code then return nil, nil end + return foundname, code () +end + +--- define locals in scope +local crude_file_lookup +local crude_file_lookup_verbose +local find_closest +local flush_lookup_cache +local ot_fullinfo +local t1_fullinfo +local load_names +local load_lookups +local read_blacklist +local reload_db +local resolve_name +local resolve_cached +local resolve_fullpath +local save_names +local save_lookups +local update_names +local get_font_filter +local set_font_filter + +--- state of the database +local fonts_reloaded = false +local fonts_read = 0 + +--- limit output when approximate font matching (luaotfload-tool -F) +local fuzzy_limit = 1 --- display closest only + +--- bool? -> dbobj +load_names = function (dry_run) + local starttime = osgettimeofday () + local foundname, data = load_lua_file (names.path.index.lua) + + if data then + report ("both", 2, "db", + "Font names database loaded", "%s", foundname) + report ("info", 3, "db", "Loading took %0.f ms.", + 1000 * (osgettimeofday () - starttime)) + + local db_version, nms_version + if data.meta then + db_version = data.meta.version + else + --- Compatibility branch; the version info used to be + --- stored in the table root which is why updating from + --- an earlier index version broke. + db_version = data.version or -42 --- invalid + end + nms_version = names.version + if db_version ~= nms_version then + report ("both", 0, "db", + [[Version mismatch; expected %4.3f, got %4.3f.]], + nms_version, db_version) + if not fonts_reloaded then + report ("both", 0, "db", [[Force rebuild.]]) + data = update_names ({ }, true, false) + if not data then + report ("both", 0, "db", + "Database creation unsuccessful.") + end + end + end + else + report ("both", 0, "db", + [[Font names database not found, generating new one.]]) + report ("both", 0, "db", + [[This can take several minutes; please be patient.]]) + data = update_names (initialize_namedata (get_font_filter ()), + nil, dry_run) + if not data then + report ("both", 0, "db", "Database creation unsuccessful.") + end + end + return data +end + +--- unit -> unit +load_lookups = function ( ) + local foundname, data = load_lua_file(names.path.lookups.lua) + if data then + report("both", 3, "cache", + "Lookup cache loaded from %s.", foundname) + else + report("both", 1, "cache", + "No lookup cache, creating empty.") + data = { } + end + lookup_cache = data +end + +local regular_synonym = { + book = "r", + normal = "r", + plain = "r", + regular = "r", + roman = "r", +} + +local italic_synonym = { + oblique = true, + slanted = true, + italic = true, +} + +local style_category = { + regular = "r", + bold = "b", + bolditalic = "bi", + italic = "i", + r = "regular", + b = "bold", + bi = "bolditalic", + i = "italic", +} + +local type1_formats = { "tfm", "ofm", } + +local dummy_findfile = resolvers.findfile -- from basics-gen + +--- filemap -> string -> string -> (string | bool) +local verbose_lookup = function (data, kind, filename) + local found = data[kind][filename] + if found ~= nil then + found = data.full[found] + if found == nil then --> texmf + report("info", 0, "db", + "Crude file lookup: req=%s; hit=%s => kpse.", + filename, kind) + found = dummy_findfile(filename) + else + report("info", 0, "db", + "Crude file lookup: req=%s; hit=%s; ret=%s.", + filename, kind, found) + end + return found + end + return false +end + +--- string -> (string * string * bool) +crude_file_lookup_verbose = function (filename) + if not name_index then name_index = load_names() end + local mappings = name_index.mappings + local files = name_index.files + local found + + --- look up in db first ... + found = verbose_lookup(files, "bare", filename) + if found then + return found, nil, true + end + found = verbose_lookup(files, "base", filename) + if found then + return found, nil, true + end + + --- ofm and tfm, returns pair + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(filename, format) then + return file.addsuffix(filename, format), format, true + end + end + return filename, nil, false +end + +local lookup_filename = function (filename) + if not name_index then name_index = load_names () end + local files = name_index.files + local basedata = files.base + local baredata = files.bare + for i = 1, #location_precedence do + local location = location_precedence [i] + local basenames = basedata [location] + local barenames = baredata [location] + local idx + if basenames ~= nil then + idx = basenames [filename] + if idx then + goto done + end + end + if barenames ~= nil then + for j = 1, #format_precedence do + local format = format_precedence [j] + local filemap = barenames [format] + if filemap then + idx = barenames [format] [filename] + if idx then + break + end + end + end + end +::done:: + if idx then + return files.full [idx] + end + end +end + +--- string -> (string * string * bool) +crude_file_lookup = function (filename) + local found = lookup_filename (filename) + + if not found then + found = dummy_findfile(filename) + end + + if found then + return found, nil, true + end + + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(filename, format) then + return file.addsuffix(filename, format), format, true + end + end + + return filename, nil, false +end + +--[[doc-- +Existence of the resolved file name is verified differently depending +on whether the index entry has a texmf flag set. +--doc]]-- + +local get_font_file = function (index) + local entry = name_index.mappings [index] + if not entry then + return false + end + local basename = entry.basename + if entry.location == "texmf" then + if kpselookup(basename) then + return true, basename, entry.subfont + end + else --- system, local + local fullname = name_index.files.full [index] + if lfsisfile (fullname) then + return true, basename, entry.subfont + end + end + return false +end + +--[[doc-- +We need to verify if the result of a cached lookup actually exists in +the texmf or filesystem. Again, due to the schizoprenic nature of the +font managment we have to check both the system path and the texmf. +--doc]]-- + +local verify_font_file = function (basename) + local path = resolve_fullpath (basename) + if path and lfsisfile(path) then + return true + end + if kpsefind_file(basename) then + return true + end + return false +end + +--[[doc-- +Lookups can be quite costly, more so the less specific they are. +Even if we find a matching font eventually, the next time the +user compiles Eir document E will have to stand through the delay +again. +Thus, some caching of results -- even between runs -- is in order. +We’ll just store successful name: lookups in a separate cache file. + +type lookup_cache = (string, (string * num)) dict + +The spec is expected to be modified in place (ugh), so we’ll have to +catalogue what fields actually influence its behavior. + +Idk what the “spec” resolver is for. + + lookup inspects modifies + ---------- ----------------- --------------------------- + file: name forced, name + name:[*] name, style, sub, resolved, sub, name, forced + optsize, size + spec: name, sub resolved, sub, name, forced + +[*] name: contains both the name resolver from luatex-fonts and + resolve_name() below + +From my reading of font-def.lua, what a resolver does is +basically rewrite the “name” field of the specification record +with the resolution. +Also, the fields “resolved”, “sub”, “force” etc. influence the outcome. + +--doc]]-- + +local concat_char = "#" +local hash_fields = { + --- order is important + "specification", "style", "sub", "optsize", "size", +} +local n_hash_fields = #hash_fields + +--- spec -> string +local hash_request = function (specification) + local key = { } --- segments of the hash + for i=1, n_hash_fields do + local field = specification[hash_fields[i]] + if field then + key[#key+1] = field + end + end + return tableconcat(key, concat_char) +end + +--- 'a -> 'a -> table -> (string * int|boolean * boolean) +resolve_cached = function (specification) + if not lookup_cache then load_lookups () end + local request = hash_request(specification) + report("both", 4, "cache", "Looking for %q in cache ...", + request) + + local found = lookup_cache [request] + + --- case 1) cache positive ---------------------------------------- + if found then --- replay fields from cache hit + report("info", 4, "cache", "Found!") + local basename = found[1] + --- check the presence of the file in case it’s been removed + local success = verify_font_file (basename) + if success == true then + return basename, found[2], true + end + report("both", 4, "cache", "Cached file not found; resolving again.") + else + report("both", 4, "cache", "Not cached; resolving.") + end + + --- case 2) cache negative ---------------------------------------- + --- first we resolve normally ... + local filename, subfont = resolve_name (specification) + if not filename then + return nil, nil + end + --- ... then we add the fields to the cache ... ... + local entry = { filename, subfont } + report("both", 4, "cache", "New entry: %s.", request) + lookup_cache [request] = entry + + --- obviously, the updated cache needs to be stored. + --- TODO this should trigger a save only once the + --- document is compiled (finish_pdffile callback?) + report("both", 5, "cache", "Saving updated cache.") + local success = save_lookups () + if not success then --- sad, but not critical + report("both", 0, "cache", "Error writing cache.") + end + return filename, subfont +end + +--- this used to be inlined; with the lookup cache we don’t +--- have to be parsimonious wrt function calls anymore +--- “found” is the match accumulator +local add_to_match = function (found, size, face) + + local continue = true + + local optsize = face.size + + if optsize and next (optsize) then + local dsnsize, maxsize, minsize + dsnsize = optsize[1] + maxsize = optsize[2] + minsize = optsize[3] + + if size ~= nil + and (dsnsize == size or (size > minsize and size <= maxsize)) + then + found[1] = face + continue = false ---> break + else + found[#found+1] = face + end + else + found[1] = face + continue = false ---> break + end + + return found, continue +end + +local choose_closest = function (distances) + local closest = 2^51 + local match + for i = 1, #distances do + local d, index = unpack (distances [i]) + if d < closest then + closest = d + match = index + end + end + return match +end + +--[[doc-- + + choose_size -- Pick a font face of appropriate size from the list + of family members with matching style. There are three categories: + + 1. exact matches: if there is a face whose design size equals + the asked size, it is returned immediately and no further + candidates are inspected. + + 2. range matches: of all faces in whose design range the + requested size falls the one whose center the requested + size is closest to is returned. + + 3. out-of-range matches: of all other faces (i. e. whose range + is above or below the asked size) the one is chosen whose + boundary (upper or lower) is closest to the requested size. + + 4. default matches: if no design size or a design size of zero + is requested, the face with the default size is returned. + +--doc]]-- + +--- int * int * int * int list -> int -> int +local choose_size = function (sizes, askedsize) + local mappings = name_index.mappings + local match = sizes.default + local exact + local inrange = { } --- distance * index list + local norange = { } --- distance * index list + local fontname, subfont + if askedsize ~= 0 then + --- firstly, look for an exactly matching design size or + --- matching range + for i = 1, #sizes do + local dsnsize, high, low, index = unpack (sizes [i]) + if dsnsize == askedsize then + --- exact match, this is what we were looking for + exact = index + goto skip + elseif askedsize < low then + --- below range, add to the norange table + local d = low - askedsize + norange [#norange + 1] = { d, index } + elseif askedsize > high then + --- beyond range, add to the norange table + local d = askedsize - high + norange [#norange + 1] = { d, index } + else + --- range match + local d = ((low + high) / 2) - askedsize + if d < 0 then + d = -d + end + inrange [#inrange + 1] = { d, index } + end + end + end +::skip:: + if exact then + match = exact + elseif #inrange > 0 then + match = choose_closest (inrange) + elseif #norange > 0 then + match = choose_closest (norange) + end + return match +end + +--[[doc-- + + resolve_familyname -- Query the families table for an entry + matching the specification. + The parameters “name” and “style” are pre-sanitized. + +--doc]]-- +--- spec -> string -> string -> int -> string * int +local resolve_familyname = function (specification, name, style, askedsize) + local families = name_index.families + local mappings = name_index.mappings + local candidates = nil + --- arrow code alert + for i = 1, #location_precedence do + local location = location_precedence [i] + local locgroup = families [location] + for j = 1, #format_precedence do + local format = format_precedence [j] + local fmtgroup = locgroup [format] + if fmtgroup then + local familygroup = fmtgroup [name] + if familygroup then + local stylegroup = familygroup [style] + if stylegroup then --- suitable match + candidates = stylegroup + goto done + end + end + end + end + end + if true then + return nil, nil + end +::done:: + index = choose_size (candidates, askedsize) + local success, resolved, subfont = get_font_file (index) + if not success then + return nil, nil + end + report ("info", 2, "db", "Match found: %s(%d).", + resolved, subfont or 0) + return resolved, subfont +end + +local resolve_fontname = function (specification, name, style) + local mappings = name_index.mappings + local fallback = nil + local lastresort = nil + style = style_category [style] + for i = 1, #mappings do + local face = mappings [i] + local prefmodifiers = face.prefmodifiers + local subfamily = face.subfamily + if face.fontname == name + or face.splainname == name + or face.fullname == name + or face.psname == name + then + return face.basename, face.subfont + elseif face.familyname == name then + if prefmodifiers == style + or subfamily == style + then + fallback = face + elseif regular_synonym [prefmodifiers] + or regular_synonym [subfamily] + then + lastresort = face + end + elseif face.metafamily == name + and (regular_synonym [prefmodifiers] + or regular_synonym [subfamily]) + then + lastresort = face + end + end + if fallback then + return fallback.basename, fallback.subfont + end + if lastresort then + return lastresort.basename, lastresort.subfont + end + return nil, nil +end + +--[[doc-- + + resolve_name -- Perform a name: lookup. This first queries the + font families table and, if there is no match for the spec, the + font names table. + The return value is a pair consisting of the file name and the + subfont index if appropriate.. + + the request specification has the fields: + + · features: table + · normal: set of { ccmp clig itlc kern liga locl mark mkmk rlig } + · ??? + · forced: string + · lookup: "name" + · method: string + · name: string + · resolved: string + · size: int + · specification: string (== ":" ) + · sub: string + + The “size” field deserves special attention: if its value is + negative, then it actually specifies a scalefactor of the + design size of the requested font. This happens e.g. if a font is + requested without an explicit “at size”. If the font is part of a + larger collection with different design sizes, this complicates + matters a bit: Normally, the resolver prefers fonts that have a + design size as close as possible to the requested size. If no + size specified, then the design size is implied. But which design + size should that be? Xetex appears to pick the “normal” (unmarked) + size: with Adobe fonts this would be the one that is neither + “caption” nor “subhead” nor “display” &c ... For fonts by Adobe this + seems to be the one that does not receive a “prefmodifiers” field. + (IOW Adobe uses the “prefmodifiers” field to encode the design size + in more or less human readable format.) However, this is not true + of LM and EB Garamond. As this matters only where there are + multiple design sizes to a given font/style combination, we put a + workaround in place that chooses that unmarked version. + + The first return value of “resolve_name” is the file name of the + requested font (string). It can be passed to the fullname resolver + get_font_file(). + The second value is either “false” or an integer indicating the + subfont index in a TTC. + +--doc]]-- + +--- table -> string * (int | bool) +resolve_name = function (specification) + local resolved, subfont + if not name_index then name_index = load_names () end + local name = sanitize_fontname (specification.name) + local style = sanitize_fontname (specification.style) or "r" + local askedsize = specification.optsize + + if askedsize then + askedsize = tonumber (askedsize) + else + askedsize = specification.size + if askedsize and askedsize >= 0 then + askedsize = askedsize / 65536 + else + askedsize = 0 + end + end + + resolved, subfont = resolve_familyname (specification, + name, + style, + askedsize) + if not resolved then + resolved, subfont = resolve_fontname (specification, + name, + style) + end + if not resolved then + resolved = specification.name, false + end + + if not resolved then + if not fonts_reloaded then + return reload_db ("Font not found.", + resolve_name, + specification) + end + end + return resolved, subfont +end + +resolve_fullpath = function (fontname, ext) --- getfilename() + if not name_index then name_index = load_names () end + local files = name_index.files + local basedata = files.base + local baredata = files.bare + for i = 1, #location_precedence do + local location = location_precedence [i] + local basenames = basedata [location] + local idx + if basenames ~= nil then + idx = basenames [fontname] + end + if ext then + local barenames = baredata [location] [ext] + if not idx and barenames ~= nil then + idx = barenames [fontname] + end + end + if idx then + return files.full [idx] + end + end + return "" +end + +--- when reload is triggered we update the database +--- and then re-run the caller with the arg list + +--- string -> ('a -> 'a) -> 'a list -> 'a +reload_db = function (why, caller, ...) + local namedata = name_index + local formats = tableconcat (namedata.meta.formats, ",") + + report ("both", 1, "db", + "Reload initiated (formats: %s); reason: %q.", + formats, why) + + set_font_filter (formats) + namedata = update_names (namedata, false, false) + + if namedata then + fonts_reloaded = true + name_index = namedata + return caller (...) + end + + report ("both", 0, "db", "Database update unsuccessful.") +end + +--- string -> string -> int +local iterative_levenshtein = function (s1, s2) + + local costs = { } + local len1, len2 = #s1, #s2 + + for i = 0, len1 do + local last = i + for j = 0, len2 do + if i == 0 then + costs[j] = j + else + if j > 0 then + local current = costs[j-1] + if stringsub(s1, i, i) ~= stringsub(s2, j, j) then + current = mathmin(current, last, costs[j]) + 1 + end + costs[j-1] = last + last = current + end + end + end + if i > 0 then costs[len2] = last end + end + + return costs[len2]--- lower right has the distance +end + +--- string -> int -> bool +find_closest = function (name, limit) + local name = sanitize_fontname (name) + limit = limit or fuzzy_limit + + if not name_index then name_index = load_names () end + if not name_index or type (name_index) ~= "table" then + if not fonts_reloaded then + return reload_db("no database", find_closest, name) + end + return false + end + + local by_distance = { } --- (int, string list) dict + local distances = { } --- int list + local cached = { } --- (string, int) dict + local mappings = name_index.mappings + local n_fonts = #mappings + + for n = 1, n_fonts do + local current = mappings[n] + --[[ + This is simplistic but surpisingly fast. + Matching is performed against the “fullname” field + of a db record in preprocessed form. We then store the + raw “fullname” at its edit distance. + We should probably do some weighting over all the + font name categories as well as whatever agrep + does. + --]] + local fullname = current.plainname + local sfullname = current.fullname + local dist = cached[sfullname]--- maybe already calculated + + if not dist then + dist = iterative_levenshtein(name, sfullname) + cached[sfullname] = dist + end + local namelst = by_distance[dist] + if not namelst then --- first entry + namelst = { fullname } + distances[#distances+1] = dist + else --- append + namelst[#namelst+1] = fullname + end + by_distance[dist] = namelst + end + + --- print the matches according to their distance + local n_distances = #distances + if n_distances > 0 then --- got some data + tablesort(distances) + limit = mathmin(n_distances, limit) + report(false, 1, "query", + "Displaying %d distance levels.", limit) + + for i = 1, limit do + local dist = distances[i] + local namelst = by_distance[dist] + report(false, 0, "query", + "Distance from \"%s\": %s\n " + .. tableconcat (namelst, "\n "), + name, dist) + end + + return true + end + return false +end --- find_closest() + +--[[doc-- + + load_font_file -- Safely open a font file. See + + regarding the omission of ``fontloader.close()``. + + TODO -- check if fontloader.info() is ready for prime in 0.78+ + -- fields /tables needed: + -- names + -- postscriptname + -- validation_state + -- .. + +--doc]]-- + +local load_font_file = function (filename, subfont) + local rawfont, _msg = fontloaderopen (filename, subfont) + if not rawfont then + report ("log", 1, "db", "ERROR: failed to open %s.", filename) + return + end + return rawfont +end + +--- rawdata -> (int * int * int | bool) + +local get_size_info = function (metadata) + local design_size = metadata.design_size + local design_range_top = metadata.design_range_top + local design_range_bottom = metadata.design_range_bottom + + local fallback_size = design_size ~= 0 and design_size + or design_range_bottom ~= 0 and design_range_bottom + or design_range_top ~= 0 and design_range_top + + if fallback_size then + design_size = (design_size or fallback_size) / 10 + design_range_top = (design_range_top or fallback_size) / 10 + design_range_bottom = (design_range_bottom or fallback_size) / 10 + return { + design_size, design_range_top, design_range_bottom, + } + end + + return false +end + +local get_english_names = function (metadata) + local names = metadata.names + local english_names + + if names then + --inspect(names) + for _, raw_namedata in next, names do + if raw_namedata.lang == "English (US)" then + return raw_namedata.names + end + end + end + + -- no (English) names table, probably a broken font + report("both", 3, "db", + "%s: missing or broken English names table.", basename) + return { fontname = metadata.fontname, + fullname = metadata.fullname, } +end + +--[[-- + In case of broken PS names we set some dummies. However, we cannot + directly modify the font data as returned by fontloader.open() because + it is a userdata object. + + For this reason we copy what is necessary whilst keeping the table + structure the same as in the tfmdata. +--]]-- +local get_raw_info = function (metadata, basename) + local fullname + local fontname + local psname + + local validation_state = metadata.validation_state + if validation_state + and tablecontains (validation_state, "bad_ps_fontname") + then + --- Broken names table, e.g. avkv.ttf with UTF-16 strings; + --- we put some dummies in place like the fontloader + --- (font-otf.lua) does. + report("both", 3, "db", + "%s has invalid postscript font names, using dummies.", + basename) + fontname = "bad-fontname-" .. basename + fullname = "bad-fullname-" .. basename + else + fontname = metadata.fontname + fullname = metadata.fullname + end + + return { + familyname = metadata.familyname, + fontname = fontname, + fontstyle_name = metadata.fontstyle_name, + fullname = fullname, + italicangle = metadata.italicangle, + names = metadata.names, + pfminfo = metadata.pfminfo, + units_per_em = metadata.units_per_em, + version = metadata.version, + design_size = metadata.design_size, + design_range_top = metadata.design_range_top, + design_range_bottom = metadata.design_range_bottom, + } +end + +local organize_namedata = function (rawinfo, + english_names, + basename, + info) + local default_name = english_names.compatfull + or english_names.fullname + or english_names.postscriptname + or rawinfo.fullname + or rawinfo.fontname + or info.fullname + or info.fontname + local default_family = english_names.preffamily + or english_names.family + or rawinfo.familyname + or info.familyname +-- local default_modifier = english_names.prefmodifiers +-- or english_names.subfamily + local fontnames = { + --- see + --- https://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html + --- http://www.microsoft.com/typography/OTSPEC/name.htm#NameIDs + english = { + --- where a “compatfull” field is given, the value of “fullname” is + --- either identical or differs by separating the style + --- with a hyphen and omitting spaces. (According to the + --- spec, “compatfull” is “Macintosh only”.) + --- Of the three “fullname” fields, this one appears to be the one + --- with the entire name given in a legible, + --- non-abbreviated fashion, for most fonts at any rate. + --- However, in some fonts (e.g. CMU) all three fields are + --- identical. + fullname = --[[ 18 ]] english_names.compatfull + or --[[ 4 ]] english_names.fullname + or default_name, + --- we keep both the “preferred family” and the “family” + --- values around since both are valid but can turn out + --- quite differently, e.g. with Latin Modern: + --- preffamily: “Latin Modern Sans”, + --- family: “LM Sans 10” + preffamily = --[[ 16 ]] english_names.preffamilyname, + family = --[[ 1 ]] english_names.family or default_family, + prefmodifiers = --[[ 17 ]] english_names.prefmodifiers, + subfamily = --[[ 2 ]] english_names.subfamily, + psname = --[[ 6 ]] english_names.postscriptname, + }, + + metadata = { + fullname = rawinfo.fullname, + fontname = rawinfo.fontname, + familyname = rawinfo.familyname, + }, + + info = { + fullname = info.fullname, + familyname = info.familyname, + fontname = info.fontname, + }, + } + + -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size + if rawinfo.fontstyle_name then + --- not present in all fonts, often differs from the preferred + --- subfamily as well as subfamily fields, e.g. with + --- LMSans10-BoldOblique: + --- subfamily: “Bold Italic” + --- prefmodifiers: “10 Bold Oblique” + --- fontstyle_name: “Bold Oblique” + for _, name in next, rawinfo.fontstyle_name do + if name.lang == 1033 then --- I hate magic numbers + fontnames.fontstyle_name = name.name + end + end + end + + return { + sanitized = sanitize_fontnames (fontnames), + fontname = rawinfo.fontname, + fullname = rawinfo.fullname, + familyname = rawinfo.familyname, + } +end + + +local dashsplitter = lpegsplitat "-" + +local split_fontname = function (fontname) + --- sometimes the style hides in the latter part of the + --- fontname, separated by a dash, e.g. “Iwona-Regular”, + --- “GFSSolomos-Regular” + local splitted = { lpegmatch (dashsplitter, fontname) } + if next (splitted) then + return sanitize_fontname (splitted [#splitted]) + end +end + +local organize_styledata = function (fontname, + metadata, + english_names, + info) + local pfminfo = metadata.pfminfo + local names = metadata.names + + return { + --- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size + size = get_size_info (metadata), + weight = pfminfo.weight or 400, + split = split_fontname (fontname), + width = pfminfo.width, + italicangle = metadata.italicangle, + --- this is for querying, see www.ntg.nl/maps/40/07.pdf for details + units_per_em = metadata.units_per_em, + version = metadata.version, + } +end + +--[[doc-- +The data inside an Opentype font file can be quite heterogeneous. +Thus in order to get the relevant information, parts of the original +table as returned by the font file reader need to be relocated. +--doc]]-- + +--- string -> int -> bool -> string -> fontentry + +ot_fullinfo = function (filename, + subfont, + location, + basename, + format, + info) + + local metadata = load_font_file (filename, subfont) + if not metadata then + return nil + end + + local rawinfo = get_raw_info (metadata, basename) + --- Closing the file manually is a tad faster and more memory + --- efficient than having it closed by the gc + fontloaderclose (metadata) + + local english_names = get_english_names (rawinfo) + local namedata = organize_namedata (rawinfo, + english_names, + basename, + info) + local style = organize_styledata (namedata.fontname, + rawinfo, + english_names, + info) + + local res = { + file = { base = basename, + full = filename, + subfont = subfont, + location = location or "system" }, + format = format, + names = namedata, + style = style, + version = rawinfo.version, + } + return res +end + +--[[doc-- + + Type1 font inspector. In comparison with OTF, PFB’s contain a good + deal less name fields which makes it tricky in some parts to find a + meaningful representation for the database. + + Good read: http://www.adobe.com/devnet/font/pdfs/5004.AFM_Spec.pdf + +--doc]]-- + +--- string -> int -> bool -> string -> fontentry + +t1_fullinfo = function (filename, _subfont, location, basename, format) + local sanitized + local metadata = load_font_file (filename) + local fontname = metadata.fontname + local fullname = metadata.fullname + local familyname = metadata.familyname + local italicangle = metadata.italicangle + local splitstyle = split_fontname (fontname) + local style = "" + local weight + + sanitized = sanitize_fontnames ({ + fontname = fontname, + psname = fullname, + pfullname = fullname, + metafamily = family, + familyname = familyname, + weight = metadata.weight, --- string identifier + prefmodifiers = style, + }) + + weight = sanitized.weight + + if weight == "bold" then + style = weight + end + + if italicangle ~= 0 then + style = style .. "italic" + end + + return { + basename = basename, + fullpath = filename, + subfont = false, + location = location or "system", + format = format, + fullname = sanitized.fullname, + fontname = sanitized.fontname, + familyname = sanitized.familyname, + plainname = fullname, + splainname = sanitized.fullname, + psname = sanitized.fontname, + version = metadata.version, + size = false, + splitstyle = splitstyle, + fontstyle_name = style ~= "" and style or weight, + weight = metadata.pfminfo.weight or 400, + italicangle = italicangle, + } +end + +local loaders = { + dfont = ot_fullinfo, + otf = ot_fullinfo, + ttc = ot_fullinfo, + ttf = ot_fullinfo, + + pfb = t1_fullinfo, + pfa = t1_fullinfo, +} + +--- not side-effect free! + +local compare_timestamps = function (fullname, + currentstatus, + currententrystatus, + currentmappings, + targetstatus, + targetentrystatus, + targetmappings) + + local currenttimestamp = currententrystatus + and currententrystatus.timestamp + local targettimestamp = lfsattributes (fullname, "modification") + + if targetentrystatus ~= nil + and targetentrystatus.timestamp == targettimestamp then + report ("log", 3, "db", "Font %q already read.", fullname) + return false + end + + targetentrystatus.timestamp = targettimestamp + targetentrystatus.index = targetentrystatus.index or { } + + if currenttimestamp == targettimestamp + and not targetentrystatus.index [1] + then + --- copy old namedata into new + + for _, currentindex in next, currententrystatus.index do + + local targetindex = #targetentrystatus.index + local fullinfo = currentmappings [currentindex] + local location = #targetmappings + 1 + + targetmappings [location] = fullinfo + targetentrystatus.index [targetindex + 1] = location + end + + report ("log", 3, "db", "Font %q already indexed.", fullname) + + return false + end + + return true +end + +local insert_fullinfo = function (fullname, + basename, + n_font, + loader, + format, + location, + targetmappings, + targetentrystatus, + info) + + local subfont + if n_font ~= false then + subfont = n_font - 1 + else + subfont = false + n_font = 1 + end + + local fullinfo = loader (fullname, subfont, + location, basename, + format, info) + + if not fullinfo then + return false + end + + local index = targetentrystatus.index [n_font] + + if not index then + index = #targetmappings + 1 + end + + targetmappings [index] = fullinfo + targetentrystatus.index [n_font] = index + + return true +end + + + +--- we return true if the font is new or re-indexed +--- string -> dbobj -> dbobj -> bool + +local read_font_names = function (fullname, + currentnames, + targetnames, + location) + + local targetmappings = targetnames.mappings + local targetstatus = targetnames.status --- by full path + local targetentrystatus = targetstatus [fullname] + + if targetentrystatus == nil then + targetentrystatus = { } + targetstatus [fullname] = targetentrystatus + end + + local currentmappings = currentnames.mappings + local currentstatus = currentnames.status + local currententrystatus = currentstatus [fullname] + + local basename = filebasename (fullname) + local barename = filenameonly (fullname) + local entryname = fullname + + if location == "texmf" then + entryname = basename + end + + --- 1) skip if blacklisted + + if names.blacklist[fullname] or names.blacklist[basename] then + report("log", 2, "db", + "Ignoring blacklisted font %q.", fullname) + return false + end + + --- 2) skip if known with same timestamp + + if not compare_timestamps (fullname, + currentstatus, + currententrystatus, + currentmappings, + targetstatus, + targetentrystatus, + targetmappings) + then + return false + end + + --- 3) new font; choose a loader, abort if unknown + + local format = stringlower (filesuffix (basename)) + local loader = loaders [format] --- ot_fullinfo, t1_fullinfo + + if not loader then + report ("both", 0, "db", + "Unknown format: %q, skipping.", format) + return false + end + + --- 4) get basic info, abort if fontloader can’t read it + + local info = fontloaderinfo (fullname) + + if not info then + report ("log", 1, "db", + "Failed to read basic information from %q", basename) + return false + end + + + --- 5) check for subfonts and process each of them + + if type (info) == "table" and #info > 1 then --- ttc + + local success = false --- true if at least one subfont got read + + for n_font = 1, #info do + if insert_fullinfo (fullname, basename, n_font, + loader, format, location, + targetmappings, targetentrystatus, + info) + then + success = true + end + end + + return success + end + + return insert_fullinfo (fullname, basename, false, + loader, format, location, + targetmappings, targetentrystatus, + info) +end + +local path_normalize +do + --- os.type and os.name are constants so we + --- choose a normalization function in advance + --- instead of testing with every call + local os_type, os_name = os.type, os.name + local filecollapsepath = filecollapsepath + local lfsreadlink = lfs.readlink + + --- windows and dos + if os_type == "windows" or os_type == "msdos" then + --- ms platfom specific stuff + path_normalize = function (path) + path = stringgsub(path, '\\', '/') + path = stringlower(path) + path = filecollapsepath(path) + return path + end +--[[doc-- + The special treatment for cygwin was removed with a patch submitted + by Ken Brown. + Reference: http://cygwin.com/ml/cygwin/2013-05/msg00006.html +--doc]]-- + + else -- posix + path_normalize = function (path) + local dest = lfsreadlink(path) + if dest then + if kpsereadable_file(dest) then + path = dest + elseif kpsereadable_file(filejoin(filedirname(path), dest)) then + path = filejoin(file.dirname(path), dest) + else + -- broken symlink? + end + end + path = filecollapsepath(path) + return path + end + end +end + +fonts.path_normalize = path_normalize + +names.blacklist = { } + +local blacklist = names.blacklist +local p_blacklist --- prefixes of dirs + +--- string list -> string list +local collapse_prefixes = function (lst) + --- avoid redundancies in blacklist + if #lst < 2 then + return lst + end + + tablesort(lst) + local cur = lst[1] + local result = { cur } + for i=2, #lst do + local elm = lst[i] + if stringsub(elm, 1, #cur) ~= cur then + --- different prefix + cur = elm + result[#result+1] = cur + end + end + return result +end + +--- string list -> string list -> (string, bool) hash_t +local create_blacklist = function (blacklist, whitelist) + local result = { } + local dirs = { } + + report("info", 2, "db", "Blacklisting %d files and directories.", + #blacklist) + for i=1, #blacklist do + local entry = blacklist[i] + if lfsisdir(entry) then + dirs[#dirs+1] = entry + else + result[blacklist[i]] = true + end + end + + report("info", 2, "db", "Whitelisting %d files.", #whitelist) + for i=1, #whitelist do + result[whitelist[i]] = nil + end + + dirs = collapse_prefixes(dirs) + + --- build the disjunction of the blacklisted directories + for i=1, #dirs do + local p_dir = P(dirs[i]) + if p_blacklist then + p_blacklist = p_blacklist + p_dir + else + p_blacklist = p_dir + end + end + + if p_blacklist == nil then + --- always return false + p_blacklist = Cc(false) + end + + return result +end + +--- unit -> unit +read_blacklist = function () + local files = { + kpselookup ("luaotfload-blacklist.cnf", + {all=true, format="tex"}) + } + local blacklist = { } + local whitelist = { } + + if files and type(files) == "table" then + for _, path in next, files do + for line in iolines (path) do + line = stringstrip(line) -- to get rid of lines like " % foo" + local first_chr = stringsub(line, 1, 1) + if first_chr == "%" or stringis_empty(line) then + -- comment or empty line + elseif first_chr == "-" then + report ("both", 3, "db", + "Whitelisted file %q via %q.", + line, path) + whitelist[#whitelist+1] = stringsub(line, 2, -1) + else + local cmt = stringfind(line, "%%") + if cmt then + line = stringsub(line, 1, cmt - 1) + end + line = stringstrip(line) + report ("both", 3, "db", + "Blacklisted file %q via %q.", + line, path) + blacklist[#blacklist+1] = line + end + end + end + end + names.blacklist = create_blacklist(blacklist, whitelist) +end + +local p_font_filter + +do + local current_formats = { } + + local extension_pattern = function (list) + local pat + for i=#list, 1, -1 do + local e = list[i] + if not pat then + pat = P(e) + else + pat = pat + P(e) + end + end + pat = pat * P(-1) + return (1 - pat)^1 * pat + end + + --- small helper to adjust the font filter pattern (--formats + --- option) + + set_font_filter = function (formats) + + if not formats or type (formats) ~= "string" then + return + end + + if stringsub (formats, 1, 1) == "+" then -- add + formats = lpegmatch (splitcomma, stringsub (formats, 2)) + if formats then + current_formats = tableappend (current_formats, formats) + end + elseif stringsub (formats, 1, 1) == "-" then -- add + formats = lpegmatch (splitcomma, stringsub (formats, 2)) + if formats then + local newformats = { } + for i = 1, #current_formats do + local fmt = current_formats[i] + local include = true + for j = 1, #formats do + if current_formats[i] == formats[j] then + include = false + goto skip + end + end + newformats[#newformats+1] = fmt + ::skip:: + end + current_formats = newformats + end + else -- set + formats = lpegmatch (splitcomma, formats) + if formats then + current_formats = formats + end + end + + p_font_filter = extension_pattern (current_formats) + end + + get_font_filter = function (formats) + return tablefastcopy (current_formats) + end + + --- initialize + set_font_filter (luaotfloadconfig.formats) +end + +local process_dir_tree +process_dir_tree = function (acc, dirs) + if not next (dirs) then --- done + return acc + end + + local pwd = lfscurrentdir () + local dir = dirs[#dirs] + dirs[#dirs] = nil + + if lfschdir (dir) then + lfschdir (pwd) + + local newfiles = { } + local blacklist = names.blacklist + for ent in lfsdir (dir) do + --- filter right away + if ent ~= "." and ent ~= ".." and not blacklist[ent] then + local fullpath = dir .. "/" .. ent + if lfsisdir (fullpath) + and not lpegmatch (p_blacklist, fullpath) + then + dirs[#dirs+1] = fullpath + elseif lfsisfile (fullpath) then + ent = stringlower (ent) + + if lpegmatch (p_font_filter, ent) then + if filesuffix (ent) == "afm" then + --- fontloader.open() will load the afm + --- iff both files are in the same directory + local pfbpath = filereplacesuffix + (fullpath, "pfb") + if lfsisfile (pfbpath) then + newfiles[#newfiles+1] = pfbpath + end + else + newfiles[#newfiles+1] = fullpath + end + end + + end + end + end + return process_dir_tree (tableappend (acc, newfiles), dirs) + end + --- cannot cd; skip + return process_dir_tree (acc, dirs) +end + +local process_dir = function (dir) + local pwd = lfscurrentdir () + if lfschdir (dir) then + lfschdir (pwd) + + local files = { } + local blacklist = names.blacklist + for ent in lfsdir (dir) do + if ent ~= "." and ent ~= ".." and not blacklist[ent] then + local fullpath = dir .. "/" .. ent + if lfsisfile (fullpath) then + ent = stringlower (ent) + if lpegmatch (p_font_filter, ent) + then + if filesuffix (ent) == "afm" then + --- fontloader.open() will load the afm + --- iff both files are in the same + --- directory + local pfbpath = filereplacesuffix + (fullpath, "pfb") + if lfsisfile (pfbpath) then + files[#files+1] = pfbpath + end + else + files[#files+1] = fullpath + end + end + end + end + end + return files + end + return { } +end + +--- string -> bool -> string list +local find_font_files = function (root, recurse) + if lfsisdir (root) then + if recurse == true then + return process_dir_tree ({}, { root }) + else --- kpathsea already delivered the necessary subdirs + return process_dir (root) + end + end +end + +--- truncate_string -- Cut the first part of a string to fit it +--- into a given terminal width. The parameter “restrict” (int) +--- indicates the number of characters already consumed on the +--- line. +local truncate_string = function (str, restrict) + local tw = luaotfloadconfig.termwidth + local wd = tw - restrict + local len = utf8len (str) + if wd - len < 0 then + --- combined length exceeds terminal, + str = ".." .. stringsub(str, len - wd + 2) + end + return str +end + +--[[doc-- + + scan_dir() scans a directory and populates the list of fonts + with all the fonts it finds. + + · dirname : name of the directory to scan + · currentnames : current font db object + · targetnames : font db object to fill + · dry_run : don’t touch anything + +--doc]]-- + +--- string -> dbobj -> dbobj -> bool -> bool -> (int * int) + +local scan_dir = function (dirname, currentnames, targetnames, + dry_run, location) + if lpegmatch (p_blacklist, dirname) then + report ("both", 4, "db", + "Skipping blacklisted directory %s.", dirname) + --- ignore + return 0, 0 + end + local found = find_font_files (dirname, location ~= "texmf") + if not found then + report ("both", 4, "db", + "No such directory: %q; skipping.", dirname) + return 0, 0 + end + report ("both", 4, "db", "Scanning directory %s.", dirname) + + local n_new = 0 --- total of fonts collected + local n_found = #found + local max_fonts = luaotfloadconfig.max_fonts + + report ("both", 4, "db", "%d font files detected.", n_found) + for j=1, n_found do + if max_fonts and fonts_read >= max_fonts then + break + end + + local fullname = found[j] + fullname = path_normalize(fullname) + local new + + if dry_run == true then + local truncated = truncate_string (fullname, 43) + report ("log", 2, "db", + "Would have been loading %s.", fullname) + report_status ("term", "db", + "Would have been loading %s", truncated) + else + local truncated = truncate_string (fullname, 32) + report ("log", 2, "db", "Loading font %s.", fullname) + report_status ("term", "db", "Loading font %s", truncated) + local new = read_font_names (fullname, currentnames, + targetnames, texmf) + if new == true then + fonts_read = fonts_read + 1 + n_new = n_new + 1 + end + end + end + report ("both", 4, "db", "Done. %d fonts indexed in %q.", + n_found, dirname) + return n_found, n_new +end + +--- string list -> string list +local filter_out_pwd = function (dirs) + local result = { } + local pwd = path_normalize (lpegmatch (stripslashes, + lfscurrentdir ())) + for i = 1, #dirs do + --- better safe than sorry + local dir = path_normalize (lpegmatch (stripslashes, dirs[i])) + if not (dir == "." or dir == pwd) then + result[#result+1] = dir + end + end + return result +end + +local path_separator = ostype == "windows" and ";" or ":" + +--[[doc-- + + scan_texmf_fonts() scans all fonts in the texmf tree through the + kpathsea variables OPENTYPEFONTS and TTFONTS of texmf.cnf. + The current working directory comes as “.” (texlive) or absolute + path (miktex) and will always be filtered out. + +--doc]]-- + +--- dbobj -> dbobj -> bool? -> (int * int) + +local scan_texmf_fonts = function (currentnames, targetnames, dry_run) + + local n_scanned, n_new, fontdirs = 0, 0 + local osfontdir = kpseexpand_path "$OSFONTDIR" + + if stringis_empty (osfontdir) then + report ("info", 1, "db", "Scanning TEXMF fonts...") + else + report ("info", 1, "db", "Scanning TEXMF and OS fonts...") + if log.get_loglevel () > 3 then + local osdirs = filesplitpath (osfontdir) + report ("info", 0, "db", + "$OSFONTDIR has %d entries:", #osdirs) + for i = 1, #osdirs do + report ("info", 0, "db", "[%d] %s", i, osdirs[i]) + end + end + end + + fontdirs = kpseexpand_path "$OPENTYPEFONTS" + fontdirs = fontdirs .. path_separator .. kpseexpand_path "$TTFONTS" + fontdirs = fontdirs .. path_separator .. kpseexpand_path "$T1FONTS" + + if not stringis_empty (fontdirs) then + local tasks = filter_out_pwd (filesplitpath (fontdirs)) + report ("info", 3, "db", + "Initiating scan of %d directories.", #tasks) + report_status_start (2, 4) + for _, d in next, tasks do + local found, new = scan_dir (d, currentnames, targetnames, + dry_run, "texmf") + n_scanned = n_scanned + found + n_new = n_new + new + end + report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new) + end + + return n_scanned, n_new +end + +--- TODO stuff those paths into some writable table +--- unit -> string list +local function get_os_dirs () + if os.name == 'macosx' then + return { + filejoin(kpseexpand_path('~'), "Library/Fonts"), + "/Library/Fonts", + "/System/Library/Fonts", + "/Network/Library/Fonts", + } + elseif os.type == "windows" or os.type == "msdos" then + local windir = osgetenv("WINDIR") + return { filejoin(windir, 'Fonts') } + else + local fonts_conves = { --- plural, much? + "/usr/local/etc/fonts/fonts.conf", + "/etc/fonts/fonts.conf", + } + local os_dirs = read_fonts_conf(fonts_conves, find_files) + return os_dirs + end + return {} +end + +--[[doc-- + + scan_os_fonts() scans the OS fonts through + - fontconfig for Unix (reads the fonts.conf file[s] and scans the + directories) + - a static set of directories for Windows and MacOSX + + **NB**: If $OSFONTDIR is nonempty, as it appears to be by default + on Windows setups, the system fonts will have already been + processed while scanning the TEXMF. Thus, this function is + never called. + +--doc]]-- + +--- dbobj -> dbobj -> bool? -> (int * int) +local scan_os_fonts = function (currentnames, + targetnames, + dry_run) + + local n_scanned, n_new = 0, 0 + report ("info", 1, "db", "Scanning OS fonts...") + report ("info", 2, "db", + "Searching in static system directories...") + + report_status_start (2, 4) + for _, d in next, get_os_dirs () do + local found, new = scan_dir (d, currentnames, + targetnames, dry_run) + n_scanned = n_scanned + found + n_new = n_new + new + end + report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new) + + return n_scanned, n_new +end + +--- unit -> (bool, lookup_cache) +flush_lookup_cache = function () + lookup_cache = { } + collectgarbage "collect" + return true, lookup_cache +end + + +--- fontentry list -> filemap + +local generate_filedata = function (mappings) + + report ("both", 2, "db", "Creating filename map.") + + local nmappings = #mappings + + local files = { + bare = { + ["local"] = { }, + system = { }, --- mapped to mapping format -> index in full + texmf = { }, --- mapped to mapping format -> “true” + }, + base = { + ["local"] = { }, + system = { }, --- mapped to index in “full” + texmf = { }, --- set; all values are “true” + }, + full = { }, --- non-texmf + } + + local base = files.base + local bare = files.bare + local full = files.full + + local conflicts = { + basenames = 0, + barenames = 0, + } + + for index = 1, nmappings do + local entry = mappings [index] + + local filedata = entry.file + local format + local location + local fullpath + local basename + local barename + local subfont + + if filedata then --- new entry + format = entry.format --- otf, afm, ... + location = filedata.location --- texmf, system, ... + fullpath = filedata.full + basename = filedata.base + barename = filenameonly (fullpath) + subfont = filedata.subfont + else + format = entry.format --- otf, afm, ... + location = entry.location --- texmf, system, ... + fullpath = entry.fullpath + basename = entry.basename + barename = filenameonly (fullpath) + subfont = entry.subfont + end + + entry.index = index + + --- 1) add to basename table + + local inbase = base [location] --- no format since the suffix is known + + if inbase then + local present = inbase [basename] + if present then + report ("both", 4, "db", + "Conflicting basename: %q already indexed \z + in category %s, ignoring.", + barename, location) + conflicts.basenames = conflicts.basenames + 1 + + --- track conflicts per font + local conflictdata = entry.conflicts + + if not conflictdata then + entry.conflicts = { basename = present } + else -- some conflicts already detected + conflictdata.basename = present + end + + else + inbase [basename] = index + end + else + inbase = { basename = index } + base [location] = inbase + end + + --- 2) add to barename table + + local inbare = bare [location] [format] + + if inbare then + local present = inbare [barename] + if present then + report ("both", 4, "db", + "Conflicting barename: %q already indexed \z + in category %s/%s, ignoring.", + barename, location, format) + conflicts.barenames = conflicts.barenames + 1 + + --- track conflicts per font + local conflictdata = entry.conflicts + + if not conflictdata then + entry.conflicts = { barename = present } + else -- some conflicts already detected + conflictdata.barename = present + end + + else + inbare [barename] = index + end + else + inbare = { [barename] = index } + bare [location] [format] = inbare + end + + --- 3) add to fullpath map + + full [index] = fullpath + end + + return files +end + +local pick_style +local check_regular + +do + local splitfontname = lpeg.splitat "-" + + local choose_exact = function (field) + --- only clean matches, without guessing + if italic_synonym [field] then + return "i" + end + + if field == "bold" then + return "b" + end + + if field == "bolditalic" or field == "boldoblique" then + return "bi" + end + + return false + end + + pick_style = function (fontstyle_name, + prefmodifiers, + subfamily, + splitstyle) + local style + if fontstyle_name then + style = choose_exact (fontstyle_name) + end + if not style then + if prefmodifiers then + style = choose_exact (prefmodifiers) + elseif subfamily then + style = choose_exact (subfamily) + end + end + return style + end + + pick_fallback_style = function (italicangle, weight) + --- more aggressive, but only to determine bold faces + if weight > 500 then --- bold spectrum matches + if italicangle == 0 then + return tostring (weight) + else + return tostring (weight) .. "i" + end + end + return false + end + + --- we use only exact matches here since there are constructs + --- like “regularitalic” (Cabin, Bodoni Old Fashion) + + check_regular = function (fontstyle_name, + prefmodifiers, + subfamily, + splitstyle, + italicangle, + weight) + + if fontstyle_name then + return regular_synonym [fontstyle_name] + elseif prefmodifiers then + return regular_synonym [prefmodifiers] + elseif subfamily then + return regular_synonym [subfamily] + elseif splitstyle then + return regular_synonym [splitstyle] + elseif italicangle == 0 and weight == 400 then + return true + end + + return nil + end +end + +local pull_values = function (entry) + local file = entry.file + local names = entry.names + local style = entry.style + local sanitized = names.sanitized + local english = sanitized.english + local info = sanitized.info + local metadata = sanitized.metadata + + --- pull file info ... + entry.basename = file.base + entry.fullpath = file.full + entry.location = file.location + entry.subfont = file.subfont + + --- pull name info ... + entry.psname = english.psname + entry.fontname = info.fontname or metadata.fontname + entry.fullname = english.fullname or info.fullname + entry.splainname = metadata.fullname + entry.prefmodifiers = english.prefmodifiers + local metafamily = metadata.familyname + local familyname = english.preffamily or english.family + entry.familyname = familyname + if familyname ~= metafamily then + entry.metafamily = metadata.familyname + end + entry.fontstyle_name = sanitized.fontstyle_name + entry.plainname = names.fullname + entry.subfamily = english.subfamily + + --- pull style info ... + entry.italicangle = style.italicangle + entry.size = style.size + entry.splitstyle = style.split + entry.weight = style.weight + + if luaotfloadconfig.strip == true then + entry.file = nil + entry.names = nil + entry.style = nil + end +end + +local add_family = function (name, subtable, modifier, entry) + if not name then --- probably borked font + return + end + local familytable = subtable [name] + if not familytable then + familytable = { } + subtable [name] = familytable + end + + local size = entry.size + + familytable [#familytable + 1] = { + index = entry.index, + modifier = modifier, + } +end + +local get_subtable = function (families, entry) + local location = entry.location + local format = entry.format + local subtable = families [location] [format] + if not subtable then + subtable = { } + families [location] [format] = subtable + end + return subtable +end + +local collect_families = function (mappings) + + report ("info", 2, "db", "Analyzing families.") + + local families = { + ["local"] = { }, + system = { }, + texmf = { }, + } + + for i = 1, #mappings do + + local entry = mappings [i] + + if entry.file then + pull_values (entry) + end + + local subtable = get_subtable (families, entry) + + local familyname = entry.familyname + local metafamily = entry.metafamily + local fontstyle_name = entry.fontstyle_name + local prefmodifiers = entry.prefmodifiers + local subfamily = entry.subfamily + + local weight = entry.weight + local italicangle = entry.italicangle + local splitstyle = entry.splitstyle + + local modifier = pick_style (fontstyle_name, + prefmodifiers, + subfamily, + splitstyle) + + if not modifier then --- regular, exact only + modifier = check_regular (fontstyle_name, + prefmodifiers, + subfamily, + splitstyle, + italicangle, + weight) + end + + if modifier then + add_family (familyname, subtable, modifier, entry) + --- registering the metafamilies is unreliable within the + --- same table as identifiers might interfere with an + --- unmarked style that lacks a metafamily, e.g. + --- + --- iwona condensed regular -> + --- family: iwonacond + --- metafamily: iwona + --- iwona regular -> + --- family: iwona + --- metafamily: ø + --- + --- Both would be registered as under the same family, + --- i.e. “iwona”, and depending on the loading order + --- the query “name:iwona” can resolve to the condensed + --- version instead of the actual unmarked one. The only + --- way around this would be to introduce a separate + --- table for metafamilies and do fallback queries on it. + --- At the moment this is not pressing enough to justify + --- further increasing the index size, maybe if need + --- arises from the user side. +-- if metafamily and metafamily ~= familyname then +-- add_family (metafamily, subtable, modifier, entry) +-- end + elseif weight > 500 then -- in bold spectrum + modifier = pick_fallback_style (italicangle, weight) + if modifier then + add_family (familyname, subtable, modifier, entry) + end + end + end + + collectgarbage "collect" + return families +end + +--[[doc-- + + add_bold_spectrum -- For not-quite-bold faces, determine whether + they can fill in for a missing bold face slot in a matching family. + + Some families like Lucida do not contain real bold / bold italic + members. Instead, they have semibold variants at weight 600 which + we must add in a separate pass. + +--doc]]-- + +local bold_spectrum_low = 501 --- 500 is medium, 900 heavy/black +local bold_weight = 700 +local style_categories = { "r", "b", "i", "bi" } +local bold_categories = { "b", "bi" } + +local group_modifiers = function (mappings, families) + report ("info", 2, "db", "Analyzing shapes, weights, and styles.") + for location, location_data in next, families do + for format, format_data in next, location_data do + for familyname, collected in next, format_data do + local styledata = { } --- will replace the “collected” table + --- First, fill in the ordinary style data that + --- fits neatly into the four relevant modifier + --- categories. + for _, modifier in next, style_categories do + local entries + for key, info in next, collected do + if info.modifier == modifier then + if not entries then + entries = { } + end + local index = info.index + local entry = mappings [index] + local size = entry.size + if size then + entries [#entries + 1] = { + size [1], + size [2], + size [3], + index, + } + else + entries.default = index + end + collected [key] = nil + end + styledata [modifier] = entries + end + end + + --- At this point the family set may still lack + --- entries for bold or bold italic. We will fill + --- those in using the modifier with the numeric + --- weight that is closest to bold (700). + if next (collected) then --- there are uncategorized entries + for _, modifier in next, bold_categories do + if not styledata [modifier] then + local closest + local minimum = 2^51 + for key, info in next, collected do + local info_modifier = tonumber (info.modifier) and "b" or "bi" + if modifier == info_modifier then + local index = info.index + local entry = mappings [index] + local weight = entry.weight + local diff = weight < 700 and 700 - weight or weight - 700 + if diff < minimum then + minimum = diff + closest = weight + end + end + end + if closest then + --- We know there is a substitute face for the modifier. + --- Now we scan the list again to extract the size data + --- in case the shape is available at multiple sizes. + local entries = { } + for key, info in next, collected do + local info_modifier = tonumber (info.modifier) and "b" or "bi" + if modifier == info_modifier then + local index = info.index + local entry = mappings [index] + local size = entry.size + if entry.weight == closest then + if size then + entries [#entries + 1] = { + size [1], + size [2], + size [3], + index, + } + else + entries.default = index + end + end + end + end + styledata [modifier] = entries + end + end + end + end + format_data [familyname] = styledata + end + end + end + return families +end + +local cmp_sizes = function (a, b) + return a [1] < b [1] +end + +local order_design_sizes = function (families) + + report ("info", 2, "db", "Ordering design sizes.") + + for location, data in next, families do + for format, data in next, data do + for familyname, data in next, data do + for style, data in next, data do + tablesort (data, cmp_sizes) + end + end + end + end + + return families +end + +local retrieve_namedata = function (currentnames, + targetnames, + dry_run, + n_rawnames, + n_newnames) + + local rawnames, new = scan_texmf_fonts (currentnames, + targetnames, + dry_run) + + n_rawnames = n_rawnames + rawnames + n_newnames = n_newnames + new + + rawnames, new = scan_os_fonts (currentnames, targetnames, dry_run) + + n_rawnames = n_rawnames + rawnames + n_newnames = n_newnames + new + + return n_rawnames, n_newnames +end + + +--- dbobj -> stats + +local collect_statistics = function (mappings) + local sum_dsnsize, n_dsnsize = 0, 0 + + local fullname, family, families = { }, { }, { } + local subfamily, prefmodifiers, fontstyle_name = { }, { }, { } + + local addtohash = function (hash, item) + if item then + local times = hash [item] + if times then + hash [item] = times + 1 + else + hash [item] = 1 + end + end + end + + local appendtohash = function (hash, key, value) + if key and value then + local entry = hash [key] + if entry then + entry [#entry + 1] = value + else + hash [key] = { value } + end + end + end + + local addtoset = function (hash, key, value) + if key and value then + local set = hash [key] + if set then + set [value] = true + else + hash [key] = { [value] = true } + end + end + end + + local setsize = function (set) + local n = 0 + for _, _ in next, set do + n = n + 1 + end + return n + end + + local hashsum = function (hash) + local n = 0 + for _, m in next, hash do + n = n + m + end + return n + end + + for _, entry in next, mappings do + local style = entry.style + local names = entry.names.sanitized + local englishnames = names.english + + addtohash (fullname, englishnames.fullname) + addtohash (family, englishnames.family) + addtohash (subfamily, englishnames.subfamily) + addtohash (prefmodifiers, englishnames.prefmodifiers) + addtohash (fontstyle_name, names.fontstyle_name) + + addtoset (families, englishnames.family, englishnames.fullname) + + local sizeinfo = entry.style.size + if sizeinfo then + sum_dsnsize = sum_dsnsize + sizeinfo [1] + n_dsnsize = n_dsnsize + 1 + end + end + + --inspect (families) + + local n_fullname = setsize (fullname) + local n_family = setsize (family) + + if log.get_loglevel () > 1 then + local pprint_top = function (hash, n, set) + + local freqs = { } + local items = { } + + for item, value in next, hash do + if set then + freq = setsize (value) + else + freq = value + end + local ifreq = items [freq] + if ifreq then + ifreq [#ifreq + 1] = item + else + items [freq] = { item } + freqs [#freqs + 1] = freq + end + end + + tablesort (freqs) + + local from = #freqs + local to = from - (n - 1) + if to < 1 then + to = 1 + end + + for i = from, to, -1 do + local freq = freqs [i] + local itemlist = items [freq] + + if type (itemlist) == "table" then + itemlist = tableconcat (itemlist, ", ") + end + + report ("both", 0, "db", + " · %4d × %s.", + freq, itemlist) + end + end + + report ("both", 0, "", "~~~~ font index statistics ~~~~") + report ("both", 0, "db", + " · Collected %d fonts (%d names) in %d families.", + #mappings, n_fullname, n_family) + pprint_top (families, 4, true) + + report ("both", 0, "db", + " · %d different “subfamily” kinds.", + setsize (subfamily)) + pprint_top (subfamily, 4) + + report ("both", 0, "db", + " · %d different “prefmodifiers” kinds.", + setsize (prefmodifiers)) + pprint_top (prefmodifiers, 4) + + report ("both", 0, "db", + " · %d different “fontstyle_name” kinds.", + setsize (fontstyle_name)) + pprint_top (fontstyle_name, 4) + end + + local mean_dsnsize = 0 + if n_dsnsize > 0 then + mean_dsnsize = sum_dsnsize / n_dsnsize + end + + return { + mean_dsnsize = mean_dsnsize, + names = { + fullname = n_fullname, + families = n_family, + }, +-- style = { +-- subfamily = subfamily, +-- prefmodifiers = prefmodifiers, +-- fontstyle_name = fontstyle_name, +-- }, + } +end + +--- force: dictate rebuild from scratch +--- dry_dun: don’t write to the db, just scan dirs + +--- dbobj? -> bool? -> bool? -> dbobj +update_names = function (currentnames, force, dry_run) + + local targetnames + + if luaotfloadconfig.update_live == false then + report ("info", 2, "db", + "Skipping database update.") + --- skip all db updates + return currentnames or name_index + end + + local starttime = osgettimeofday () + local n_rawnames, n_newnames = 0, 0 + + --[[ + The main function, scans everything + - “targetnames” is the final table to return + - force is whether we rebuild it from scratch or not + ]] + report("both", 1, "db", "Updating the font names database" + .. (force and " forcefully." or ".")) + + --- pass 1 get raw data: read font files (normal case) or reuse + --- information present in index + + if luaotfloadconfig.skip_read == true then + --- the difference to a “dry run” is that we don’t search + --- for font files entirely. we also ignore the “force” + --- parameter since it concerns only the font files. + report ("info", 2, "db", + "Ignoring font files, reusing old data.") + currentnames = load_names (false) + targetnames = currentnames + else + if force then + currentnames = initialize_namedata (get_font_filter ()) + else + if not currentnames then + currentnames = load_names (dry_run) + end + if currentnames.meta.version ~= names.version then + report ("both", 1, "db", "No font names database or old " + .. "one found; generating new one.") + currentnames = initialize_namedata (get_font_filter ()) + end + end + + targetnames = initialize_namedata (get_font_filter ()) + + read_blacklist () + + local n_raw, n_new= retrieve_namedata (currentnames, + targetnames, + dry_run, + n_rawnames, + n_newnames) + report ("info", 3, "db", + "Scanned %d font files; %d new entries.", + n_rawnames, n_newnames) + end + + --- pass 2 (optional): collect some stats about the raw font info + if luaotfloadconfig.statistics == true then + targetnames.meta.statistics = collect_statistics + (targetnames.mappings) + end + + --- we always generate the file lookup tables because + --- non-texmf entries are redirected there and the mapping + --- needs to be 100% consistent + + --- pass 3: build filename table + targetnames.files = generate_filedata (targetnames.mappings) + + --- pass 4: build family lookup table + targetnames.families = collect_families (targetnames.mappings) + + --- pass 5: arrange style and size info + targetnames.families = group_modifiers (targetnames.mappings, + targetnames.families) + + --- pass 6: order design size tables + targetnames.families = order_design_sizes (targetnames.families) + + + report ("info", 3, "db", + "Rebuilt in %0.f ms.", + 1000 * (osgettimeofday () - starttime)) + name_index = targetnames + + if dry_run ~= true then + + save_names () + + local success, _lookups = flush_lookup_cache () + if success then + local success = save_lookups () + if success then + report ("info", 2, "cache", + "Lookup cache emptied.") + return targetnames + end + end + end + return targetnames +end + +--- unit -> bool +save_lookups = function ( ) + local path = names.path.lookups + local luaname, lucname = path.lua, path.luc + if fileiswritable (luaname) and fileiswritable (lucname) then + tabletofile (luaname, lookup_cache, true) + osremove (lucname) + caches.compile (lookup_cache, luaname, lucname) + --- double check ... + if lfsisfile (luaname) and lfsisfile (lucname) then + report ("both", 3, "cache", "Lookup cache saved.") + return true + end + report ("info", 0, "cache", "Could not compile lookup cache.") + return false + end + report ("info", 0, "cache", "Lookup cache file not writable.") + if not fileiswritable (luaname) then + report ("info", 0, "cache", "Failed to write %s.", luaname) + end + if not fileiswritable (lucname) then + report ("info", 0, "cache", "Failed to write %s.", lucname) + end + return false +end + +--- save_names() is usually called without the argument +--- dbobj? -> bool +save_names = function (currentnames) + if not currentnames then + currentnames = name_index + end + local path = names.path.index + local luaname, lucname = path.lua, path.luc + if fileiswritable (luaname) and fileiswritable (lucname) then + osremove (lucname) + local gzname = luaname .. ".gz" + if luaotfloadconfig.compress then + local serialized = tableserialize (currentnames, true) + save_gzipped (gzname, serialized) + caches.compile (currentnames, "", lucname) + else + tabletofile (luaname, currentnames, true) + caches.compile (currentnames, luaname, lucname) + end + report ("info", 2, "db", "Font index saved at ...") + local success = false + if lfsisfile (luaname) then + report ("info", 2, "db", "Text: " .. luaname) + success = true + end + if lfsisfile (gzname) then + report ("info", 2, "db", "Gzip: " .. gzname) + success = true + end + if lfsisfile (lucname) then + report ("info", 2, "db", "Byte: " .. lucname) + success = true + end + if success then + return true + else + report ("info", 0, "db", "Could not compile font index.") + return false + end + end + report ("info", 0, "db", "Index file not writable") + if not fileiswritable (luaname) then + report ("info", 0, "db", "Failed to write %s.", luaname) + end + if not fileiswritable (lucname) then + report ("info", 0, "db", "Failed to write %s.", lucname) + end + return false +end + +--[[doc-- + + Below set of functions is modeled after mtx-cache. + +--doc]]-- + +--- string -> string -> string list -> string list -> string list -> unit +local print_cache = function (category, path, luanames, lucnames, rest) + local report_indeed = function (...) + report("info", 0, "cache", ...) + end + report_indeed("Luaotfload cache: %s", category) + report_indeed("location: %s", path) + report_indeed("[raw] %4i", #luanames) + report_indeed("[compiled] %4i", #lucnames) + report_indeed("[other] %4i", #rest) + report_indeed("[total] %4i", #luanames + #lucnames + #rest) +end + +--- string -> string -> string list -> bool -> bool +local purge_from_cache = function (category, path, list, all) + report("info", 1, "cache", "Luaotfload cache: %s %s", + (all and "erase" or "purge"), category) + report("info", 1, "cache", "location: %s",path) + local n = 0 + for i=1,#list do + local filename = list[i] + if stringfind(filename,"luatex%-cache") then -- safeguard + if all then + report("info", 5, "cache", "Removing %s.", filename) + osremove(filename) + n = n + 1 + else + local suffix = filesuffix(filename) + if suffix == "lua" then + local checkname = file.replacesuffix( + filename, "lua", "luc") + if lfsisfile(checkname) then + report("info", 5, "cache", "Removing %s.", filename) + osremove(filename) + n = n + 1 + end + end + end + end + end + report("info", 1, "cache", "Removed lua files : %i", n) + return true +end + +--- string -> string list -> int -> string list -> string list -> string list -> +--- (string list * string list * string list * string list) +local collect_cache collect_cache = function (path, all, n, luanames, + lucnames, rest) + if not all then + local all = find_files (path) + + local luanames, lucnames, rest = { }, { }, { } + return collect_cache(nil, all, 1, luanames, lucnames, rest) + end + + local filename = all[n] + if filename then + local suffix = filesuffix(filename) + if suffix == "lua" then + luanames[#luanames+1] = filename + elseif suffix == "luc" then + lucnames[#lucnames+1] = filename + else + rest[#rest+1] = filename + end + return collect_cache(nil, all, n+1, luanames, lucnames, rest) + end + return luanames, lucnames, rest, all +end + +local getwritablecachepath = function ( ) + --- fonts.handlers.otf doesn’t exist outside a Luatex run, + --- so we have to improvise + local writable = getwritablepath (luaotfloadconfig.cache_dir) + if writable then + return writable + end +end + +local getreadablecachepaths = function ( ) + local readables = caches.getreadablepaths + (luaotfloadconfig.cache_dir) + local result = { } + if readables then + for i=1, #readables do + local readable = readables[i] + if lfsisdir (readable) then + result[#result+1] = readable + end + end + end + return result +end + +--- unit -> unit +local purge_cache = function ( ) + local writable_path = getwritablecachepath () + local luanames, lucnames, rest = collect_cache(writable_path) + if log.get_loglevel() > 1 then + print_cache("writable path", writable_path, luanames, lucnames, rest) + end + local success = purge_from_cache("writable path", writable_path, luanames, false) + return success +end + +--- unit -> unit +local erase_cache = function ( ) + local writable_path = getwritablecachepath () + local luanames, lucnames, rest, all = collect_cache(writable_path) + if log.get_loglevel() > 1 then + print_cache("writable path", writable_path, luanames, lucnames, rest) + end + local success = purge_from_cache("writable path", writable_path, all, true) + return success +end + +local separator = function ( ) + report("info", 0, string.rep("-", 67)) +end + +--- unit -> unit +local show_cache = function ( ) + local readable_paths = getreadablecachepaths () + local writable_path = getwritablecachepath () + local luanames, lucnames, rest = collect_cache(writable_path) + + separator () + print_cache ("writable path", writable_path, + luanames, lucnames, rest) + texio.write_nl"" + for i=1,#readable_paths do + local readable_path = readable_paths[i] + if readable_path ~= writable_path then + local luanames, lucnames = collect_cache (readable_path) + print_cache ("readable path", + readable_path, luanames, lucnames, rest) + end + end + separator() + return true +end + +----------------------------------------------------------------------- +--- export functionality to the namespace “fonts.names” +----------------------------------------------------------------------- + +names.scan_dir = scan_dir +names.set_font_filter = set_font_filter +names.flush_lookup_cache = flush_lookup_cache +names.save_lookups = save_lookups +names.load = load_names +names.data = function () return name_index end +names.save = save_names +names.update = update_names +names.crude_file_lookup = crude_file_lookup +names.crude_file_lookup_verbose = crude_file_lookup_verbose +names.read_blacklist = read_blacklist +names.sanitize_fontname = sanitize_fontname +names.getfilename = resolve_fullpath +names.set_location_precedence = set_location_precedence + +--- font cache +names.purge_cache = purge_cache +names.erase_cache = erase_cache +names.show_cache = show_cache + +--- replace the resolver from luatex-fonts +if luaotfloadconfig.resolver == "cached" then + report("both", 2, "cache", "Caching of name: lookups active.") + names.resolvespec = resolve_cached + names.resolve_name = resolve_cached +else + names.resolvespec = resolve_name + names.resolve_name = resolve_name +end + +names.find_closest = find_closest + +-- for testing purpose +names.read_fonts_conf = read_fonts_conf + +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/src/luaotfload-diagnostics.lua b/src/luaotfload-diagnostics.lua new file mode 100644 index 0000000..67119de --- /dev/null +++ b/src/luaotfload-diagnostics.lua @@ -0,0 +1,682 @@ +#!/usr/bin/env texlua +----------------------------------------------------------------------- +-- FILE: luaotfload-diagnostics.lua +-- DESCRIPTION: functionality accessible by the --diagnose option +-- REQUIREMENTS: luaotfload-tool.lua +-- AUTHOR: Philipp Gesang (Phg), +-- VERSION: 2.5 +-- MODIFIED: 2014-01-02 21:23:06+0100 +----------------------------------------------------------------------- +-- +local names = fonts.names +local luatexstatus = status +local status = config.luaotfload.status + +local kpse = require "kpse" +local kpseexpand_path = kpse.expand_path +local kpseexpand_var = kpse.expand_var +local kpsefind_file = kpse.find_file + +local lfs = require "lfs" +local lfsattributes = lfs.attributes +local lfsisfile = lfs.isfile +local lfsreadlink = lfs.readlink + +local md5 = require "md5" +local md5sumhexa = md5.sumhexa + +local ioopen = io.open + +local osgetenv = os.getenv +local osname = os.name +local osremove = os.remove +local ostype = os.type +local stringformat = string.format +local stringlower = string.lower +local stringsub = string.sub + +local fileisreadable = file.isreadable +local fileiswritable = file.iswritable +local filesplitpath = file.splitpath +local filesuffix = file.suffix +local ioloaddata = io.loaddata +local lua_of_json = utilities.json.tolua +local tableconcat = table.concat +local tablesortedkeys = table.sortedkeys +local tabletohash = table.tohash + +local lpeg = require "lpeg" +local C, Cg, Ct = lpeg.C, lpeg.Cg, lpeg.Ct +local lpegmatch = lpeg.match + +local report = luaotfload.log.report +local out = function (...) + report (false, 0, "diagnose", ...) +end + +local parsers = luaotfload.parsers +local stripslashes = parsers.stripslashes +local splitcomma = parsers.splitcomma + +local check_index = function (errcnt) + + out "================= font names ==================" + local namedata = names.data() + + if not namedata then + namedata = names.load () + end + + local mappings = namedata.mappings + + if not namedata and namedata.formats and namedata.version then + out "Database corrupt." + return errcnt + 1 + end + + out ("Database version: %.3f.", namedata.meta.version) + out ("Font formats indexed: %s.", + tableconcat (namedata.meta.formats, ", ")) + out ("%d font files indexed.", #mappings) + + local by_format = { } + for i = 1, #mappings do + local record = mappings[i] + local format = stringlower (filesuffix (record.filename)) + local count = by_format[format] + if count then + by_format[format] = count + 1 + else + by_format[format] = 1 + end + end + + local formats = tablesortedkeys (by_format) + for i = 1, #formats do + local format = formats[i] + out ("%20s: %5d", format, by_format[format]) + end + return errcnt +end + +local verify_files = function (errcnt, status) + out "================ verify files =================" + local hashes = status.hashes + local notes = status.notes + if not hashes or #hashes == 0 then + out ("FAILED: cannot read checksums from %s.", status_file) + return 1/0 + elseif not notes then + out ("FAILED: cannot read commit metadata from %s.", + status_file) + return 1/0 + end + + out ("Luaotfload revision %s.", notes.revision) + out ("Committed by %s.", notes.committer) + out ("Timestamp %s.", notes.timestamp) + + local nhashes = #hashes + out ("Testing %d files for integrity.", nhashes) + for i = 1, nhashes do + local fname, canonicalsum = unpack (hashes[i]) + local location = kpsefind_file (fname) + or kpsefind_file (fname, "texmfscripts") + if not location then + errcnt = errcnt + 1 + out ("FAILED: file %s missing.", fname) + else + out ("File: %s.", location) + local raw = ioloaddata (location) + if not raw then + errcnt = errcnt + 1 + out ("FAILED: file %d not readable.", fname) + else + local sum = md5sumhexa (raw) + if sum ~= canonicalsum then + errcnt = errcnt + 1 + out ("FAILED: checksum mismatch for file %s.", + fname) + out ("Expected %s.", canonicalsum) + out ("Got %s.", sum) + else + out ("Ok, %s passed.", fname) + end + end + end + end + return errcnt +end + +local get_tentative_attributes = function (file) + if not lfsisfile (file) then + local chan = ioopen (file, "w") + if chan then + chan:close () + local attributes = lfsattributes (file) + os.remove (file) + return attributes + end + end +end + +local p_permissions = Ct(Cg(Ct(C(1) * C(1) * C(1)), "u") + * Cg(Ct(C(1) * C(1) * C(1)), "g") + * Cg(Ct(C(1) * C(1) * C(1)), "o")) + +local analyze_permissions = function (raw) + return lpegmatch (p_permissions, raw) +end + +local get_permissions = function (t, location) + if stringsub (location, #location) == "/" then + --- strip trailing slashes (lfs idiosyncrasy on Win) + location = lpegmatch (stripslashes, location) + end + local attributes = lfsattributes (location) + + if not attributes and t == "f" then + attributes = get_tentative_attributes (location) + if not attributes then + return false + end + end + + local permissions + + if fileisreadable (location) then + --- link handling appears to be unnecessary because + --- lfs.attributes() will return the information on + --- the link target. + if mode == "link" then --follow and repeat + location = lfsreadlink (location) + attributes = lfsattributes (location) + end + end + + permissions = analyze_permissions (attributes.permissions) + + return { + location = location, + mode = attributes.mode, + owner = attributes.uid, --- useless on windows + permissions = permissions, + attributes = attributes, + } +end + +local check_conformance = function (spec, permissions, errcnt) + local uid = permissions.attributes.uid + local gid = permissions.attributes.gid + local raw = permissions.attributes.permissions + + out ("Owner: %d, group %d, permissions %s.", uid, gid, raw) + if ostype == "unix" then + if uid == 0 or gid == 0 then + out "Owned by the superuser, permission conflict likely." + errcnt = errcnt + 1 + end + end + + local user = permissions.permissions.u + if spec.r == true then + if user[1] == "r" then + out "Readable: ok." + else + out "Not readable: permissions need fixing." + errcnt = errcnt + 1 + end + end + + if spec.w == true then + if user[2] == "w" + or fileiswritable (permissions.location) then + out "Writable: ok." + else + out "Not writable: permissions need fixing." + errcnt = errcnt + 1 + end + end + + return errcnt +end + +local path = names.path + +local desired_permissions = { + { "d", {"r","w"}, function () return caches.getwritablepath () end }, + { "d", {"r","w"}, path.globals.prefix }, + { "f", {"r","w"}, path.index.lua .. ".gz" }, + { "f", {"r","w"}, path.index.luc }, + { "f", {"r","w"}, path.lookups.lua }, + { "f", {"r","w"}, path.lookups.luc }, +} + +local check_permissions = function (errcnt) + out [[=============== file permissions ==============]] + for i = 1, #desired_permissions do + local t, spec, path = unpack (desired_permissions[i]) + if type (path) == "function" then + path = path () + end + + spec = tabletohash (spec) + + out ("Checking permissions of %s.", path) + + local permissions = get_permissions (t, path) + if permissions then + --inspect (permissions) + errcnt = check_conformance (spec, permissions, errcnt) + else + errcnt = errcnt + 1 + end + end + return errcnt +end + +local check_upstream + +if kpsefind_file ("https.lua", "lua") == nil then + check_upstream = function (errcnt) + out [[============= upstream repository ============= + WARNING: Cannot retrieve repository data. + Github API access requires the luasec library. + Grab it from + and retry.]] + return errcnt + end +else +--- github api stuff begin + local https = require "ssl.https" + + local gh_api_root = [[https://api.github.com]] + local release_url = [[https://github.com/lualatex/luaotfload/releases]] + local luaotfload_repo = [[lualatex/luaotfload]] + local user_agent = [[lualatex/luaotfload integrity check]] + local shortbytes = 8 + + local gh_shortrevision = function (rev) + return stringsub (rev, 1, shortbytes) + end + + local gh_encode_parameters = function (parameters) + local acc = {} + for field, value in next, parameters do + --- unsafe, non-urlencoded coz it’s all ascii chars + acc[#acc+1] = field .. "=" .. value + end + return "?" .. tableconcat (acc, "&") + end + + local gh_make_url = function (components, parameters) + local url = tableconcat ({ gh_api_root, + unpack (components) }, + "/") + if parameters then + url = url .. gh_encode_parameters (parameters) + end + return url + end + + local alright = [[HTTP/1.1 200 OK]] + + local gh_api_request = function (...) + local args = {...} + local nargs = #args + local final = args[nargs] + local request = { + url = "", + headers = { ["user-agent"] = user_agent }, + } + if type (final) == "table" then + args[nargs] = nil + request = gh_make_url (args, final) + else + request = gh_make_url (args) + end + + out ("Requesting <%s>.", request) + local response, code, headers, status + = https.request (request) + if status ~= alright then + out "Request failed!" + return false + end + return response + end + + local gh_api_checklimit = function (headers) + local rawlimit = gh_api_request "rate_limit" + local limitdata = lua_of_json (rawlimit) + if not limitdata and limitdata.rate then + out "Cannot parse API rate limit." + return false + end + limitdata = limitdata.rate + + local limit = tonumber (limitdata.limit) + local left = tonumber (limitdata.remaining) + local reset = tonumber (limitdata.reset) + + out ("%d of %d Github API requests left.", left, limit) + if left == 0 then + out ("Cannot make any more API requests.") + if ostype == "unix" then + out ("Try again later at %s.", osdate ("%F %T", reset)) + else --- windows doesn’t C99 + out ("Try again later at %s.", + osdate ("%Y-%m-d %H:%M:%S", reset)) + end + end + return true + end + + local gh_tags = function () + out "Fetching tags from repository, please stand by." + local rawtags = gh_api_request ("repos", + luaotfload_repo, + "tags") + local taglist = lua_of_json (rawtags) + if not taglist or #taglist == 0 then + out "Cannot parse response." + return false + end + + local ntags = #taglist + out ("Repository contains %d tags.", ntags) + local _idx, latest = next (taglist) + out ("The most recent release is %s (revision %s).", + latest.name, + gh_shortrevision (latest.commit.sha)) + return latest + end + + local gh_compare = function (head, base) + if base == nil then + base = "HEAD" + end + out ("Fetching comparison between %s and %s, \z + please stand by.", + gh_shortrevision (head), + gh_shortrevision (base)) + local comparison = base .. "..." .. head + local rawstatus = gh_api_request ("repos", + luaotfload_repo, + "compare", + comparison) + local status = lua_of_json (rawstatus) + if not status then + out "Cannot parse response for status request." + return false + end + return status + end + + local gh_news = function (since) + local compared = gh_compare (since) + if not compared then + return false + end + local behind_by = compared.behind_by + local ahead_by = compared.ahead_by + local status = compared.status + out ("Comparison state: %s.", status) + if behind_by > 0 then + out ("Your Luaotfload is %d \z + revisions behind upstream.", + behind_by) + return behind_by + elseif status == "ahead" then + out "Since you are obviously from the future \z + I assume you already know the repository state." + else + out "Everything up to date. \z + Luaotfload is in sync with upstream." + end + return false + end + + local gh_catchup = function (current, latest) + local compared = gh_compare (latest, current) + local ahead_by = tonumber (compared.ahead_by) + if ahead_by > 0 then + local permalink_url = compared.permalink_url + out ("Your Luaotfload is %d revisions \z + behind the most recent release.", + ahead_by) + out ("To view the commit log, visit <%s>.", + permalink_url) + out ("You can grab an up to date tarball at <%s>.", + release_url) + return true + else + out "There weren't any new releases in the meantime." + out "Luaotfload is up to date." + end + return false + end + + check_upstream = function (current) + out "============= upstream repository =============" + local _succ = gh_api_checklimit () + local behind = gh_news (current) + if behind then + local latest = gh_tags () + local _behind = gh_catchup (current, + latest.commit.sha, + latest.name) + end + end + + --- trivium: diff since the first revision as pushed by Élie + --- in 2009 + --- local firstrevision = "c3ccb3ee07e0a67171c24960966ae974e0dd8e98" + --- check_upstream (firstrevision) +end +--- github api stuff end + +local print_envvar = function (var) + local val = osgetenv (var) + if val then + out ("%20s: %q", stringformat ("$%s", var), val) + return val + else + out ("%20s: ", stringformat ("$%s", var)) + end +end + +local print_path = function (var) + local val = osgetenv (var) + if val then + local paths = filesplitpath (val) + if paths then + local npaths = #paths + if npaths == 1 then + out ("%20s: %q", stringformat ("$%s", var), val) + elseif npaths > 1 then + out ("%20s: <%d items>", stringformat ("$%s", var), npaths) + for i = 1, npaths do + out (" +: %q", paths[i]) + end + else + out ("%20s: ") + end + end + else + out ("%20s: ", stringformat ("$%s", var)) + end +end + +local print_kpsevar = function (var) + var = "$" .. var + local val = kpseexpand_var (var) + if val and val ~= var then + out ("%20s: %q", var, val) + return val + else + out ("%20s: ", var) + end +end + +local print_kpsepath = function (var) + var = "$" .. var + local val = kpseexpand_path (var) + if val and val ~= "" then + local paths = filesplitpath (val) + if paths then + local npaths = #paths + if npaths == 1 then + out ("%20s: %q", var, paths[1]) + elseif npaths > 1 then + out ("%20s: <%d items>", var, npaths) + for i = 1, npaths do + out (" +: %q", paths[i]) + end + else + out ("%20s: ") + end + end + else + out ("%20s: ", var) + end +end + +--- this test first if a variable is set and then expands the +--- paths; this is necessitated by the fact that expand-path will +--- return the empty string both if the variable is unset and if +--- the directory does not exist + +local print_kpsepathvar = function (var) + local vvar = "$" .. var + local val = kpseexpand_var (vvar) + if val and val ~= vvar then + out ("%20s: %q", vvar, val) + print_kpsepath (var) + else + out ("%20s: ", var) + end +end + +local check_environment = function (errcnt) + out "============ environment settings =============" + out ("system: %s/%s", ostype, osname) + if ostype == "unix" and io.popen then + local chan = io.popen ("uname -a", "r") + if chan then + out ("info: %s", chan:read "*all") + chan:close () + end + end + + out "1) *shell environment*" + print_envvar "SHELL" + print_path "PATH" + print_path "OSFONTDIR" + print_envvar "USER" + if ostype == "windows" then + print_envvar "WINDIR" + print_envvar "CD" + print_path "TEMP" + elseif ostype == "unix" then + print_envvar "HOME" + print_envvar "PWD" + print_path "TMPDIR" + end + + out "2) *kpathsea*" + print_kpsepathvar "OPENTYPEFONTS" + print_kpsepathvar "TTFONTS" + + print_kpsepathvar "TEXMFCACHE" + print_kpsepathvar "TEXMFVAR" + + --- the expansion of these can be quite large; as they aren’t + --- usually essential to luaotfload, we won’t dump every single + --- path + print_kpsevar "LUAINPUTS" + print_kpsevar "CLUAINPUTS" + + return errcnt +end + +local anamneses = { + "environment", + "files", + "index", + "repository", + "permissions" +} + +local diagnose = function (job) + local errcnt = 0 + local asked = job.asked_diagnostics + if asked == "all" or asked == "thorough" then + asked = tabletohash (anamneses, true) + else + asked = lpegmatch (splitcomma, asked) + asked = tabletohash (asked, true) + end + + if asked.index == true then + errcnt = check_index (errcnt) + asked.index = nil + end + + if asked.environment == true then + errcnt = check_environment (errcnt) + asked.environment = nil + end + + if asked.files == true then + errcnt = verify_files (errcnt, status) + asked.files = nil + end + + if asked.permissions == true then + errcnt = check_permissions (errcnt) + asked.permissions = nil + end + + if asked.repository == true then + check_upstream (status.notes.revision) + asked.repository = nil + end + + local rest = next (asked) + if rest ~= nil then --> something unknown + out ("Unknown diagnostic %q.", rest) + end + if errcnt == 0 then --> success + out ("Everything appears to be in order, \z + you may sleep well.") + return true, false + end + out ( [[=============================================== + WARNING + =============================================== + + The diagnostic detected %d errors. + + This version of luaotfload may have been + tampered with. Modified versions of the + luaotfload source are unsupported. Read the log + carefully and get a clean version from CTAN or + github: + + × http://ctan.org/tex-archive/macros/luatex/generic/luaotfload + × https://github.com/lualatex/luaotfload/releases + + If you are uncertain as to how to proceed, then + ask on the lualatex mailing list: + + http://www.tug.org/mailman/listinfo/lualatex-dev + + =============================================== +]], errcnt) + return true, false +end + +return diagnose + +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua new file mode 100644 index 0000000..4237d71 --- /dev/null +++ b/src/luaotfload-features.lua @@ -0,0 +1,1276 @@ +if not modules then modules = { } end modules ["features"] = { + version = "2.5", + comment = "companion to luaotfload-main.lua", + author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local type, next = type, next +local tonumber = tonumber +local tostring = tostring + +local lpeg = require "lpeg" +local lpegmatch = lpeg.match +local P = lpeg.P +local R = lpeg.R +local C = lpeg.C + +---[[ begin included font-ltx.lua ]] +--- this appears to be based in part on luatex-fonts-def.lua + +local fonts = fonts +local definers = fonts.definers +local handlers = fonts.handlers + +local as_script, normalize + +if handlers then + normalize = handlers.otf.features.normalize +else + normalize = function () end + as_script = true +end + + +--HH A bit of tuning for definitions. + +if fonts.constructors then + fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload +end + +--[[HH-- + tricky: we sort of bypass the parser and directly feed all into + the sub parser +--HH]]-- + +function fonts.definers.getspecification(str) + return "", str, "", ":", str +end + +local log = luaotfload.log +local report = log.report + +local stringfind = string.find +local stringlower = string.lower +local stringgsub = string.gsub +local stringsub = string.sub +local stringformat = string.format +local stringis_empty = string.is_empty +local mathceil = math.ceil + +local defaults = { + dflt = { + "ccmp", "locl", "rlig", "liga", "clig", + "kern", "mark", "mkmk", 'itlc', + }, + arab = { + "ccmp", "locl", "isol", "fina", "fin2", + "fin3", "medi", "med2", "init", "rlig", + "calt", "liga", "cswh", "mset", "curs", + "kern", "mark", "mkmk", + }, + deva = { + "ccmp", "locl", "init", "nukt", "akhn", + "rphf", "blwf", "half", "pstf", "vatu", + "pres", "blws", "abvs", "psts", "haln", + "calt", "blwm", "abvm", "dist", "kern", + "mark", "mkmk", + }, + khmr = { + "ccmp", "locl", "pref", "blwf", "abvf", + "pstf", "pres", "blws", "abvs", "psts", + "clig", "calt", "blwm", "abvm", "dist", + "kern", "mark", "mkmk", + }, + thai = { + "ccmp", "locl", "liga", "kern", "mark", + "mkmk", + }, + hang = { + "ccmp", "ljmo", "vjmo", "tjmo", + }, +} + +local global_defaults = { mode = "node" } + +defaults.beng = defaults.deva +defaults.guru = defaults.deva +defaults.gujr = defaults.deva +defaults.orya = defaults.deva +defaults.taml = defaults.deva +defaults.telu = defaults.deva +defaults.knda = defaults.deva +defaults.mlym = defaults.deva +defaults.sinh = defaults.deva + +defaults.syrc = defaults.arab +defaults.mong = defaults.arab +defaults.nko = defaults.arab + +defaults.tibt = defaults.khmr + +defaults.lao = defaults.thai + +---[[ begin excerpt from font-ott.lua ]] + +local scripts = { + ['arab'] = 'arabic', + ['armn'] = 'armenian', + ['bali'] = 'balinese', + ['beng'] = 'bengali', + ['bopo'] = 'bopomofo', + ['brai'] = 'braille', + ['bugi'] = 'buginese', + ['buhd'] = 'buhid', + ['byzm'] = 'byzantine music', + ['cans'] = 'canadian syllabics', + ['cher'] = 'cherokee', + ['copt'] = 'coptic', + ['cprt'] = 'cypriot syllabary', + ['cyrl'] = 'cyrillic', + ['deva'] = 'devanagari', + ['dsrt'] = 'deseret', + ['ethi'] = 'ethiopic', + ['geor'] = 'georgian', + ['glag'] = 'glagolitic', + ['goth'] = 'gothic', + ['grek'] = 'greek', + ['gujr'] = 'gujarati', + ['guru'] = 'gurmukhi', + ['hang'] = 'hangul', + ['hani'] = 'cjk ideographic', + ['hano'] = 'hanunoo', + ['hebr'] = 'hebrew', + ['ital'] = 'old italic', + ['jamo'] = 'hangul jamo', + ['java'] = 'javanese', + ['kana'] = 'hiragana and katakana', + ['khar'] = 'kharosthi', + ['khmr'] = 'khmer', + ['knda'] = 'kannada', + ['lao' ] = 'lao', + ['latn'] = 'latin', + ['limb'] = 'limbu', + ['linb'] = 'linear b', + ['math'] = 'mathematical alphanumeric symbols', + ['mlym'] = 'malayalam', + ['mong'] = 'mongolian', + ['musc'] = 'musical symbols', + ['mymr'] = 'myanmar', + ['nko' ] = "n'ko", + ['ogam'] = 'ogham', + ['orya'] = 'oriya', + ['osma'] = 'osmanya', + ['phag'] = 'phags-pa', + ['phnx'] = 'phoenician', + ['runr'] = 'runic', + ['shaw'] = 'shavian', + ['sinh'] = 'sinhala', + ['sylo'] = 'syloti nagri', + ['syrc'] = 'syriac', + ['tagb'] = 'tagbanwa', + ['tale'] = 'tai le', + ['talu'] = 'tai lu', + ['taml'] = 'tamil', + ['telu'] = 'telugu', + ['tfng'] = 'tifinagh', + ['tglg'] = 'tagalog', + ['thaa'] = 'thaana', + ['thai'] = 'thai', + ['tibt'] = 'tibetan', + ['ugar'] = 'ugaritic cuneiform', + ['xpeo'] = 'old persian cuneiform', + ['xsux'] = 'sumero-akkadian cuneiform', + ['yi' ] = 'yi', +} + +local languages = { + ['aba'] = 'abaza', + ['abk'] = 'abkhazian', + ['ady'] = 'adyghe', + ['afk'] = 'afrikaans', + ['afr'] = 'afar', + ['agw'] = 'agaw', + ['als'] = 'alsatian', + ['alt'] = 'altai', + ['amh'] = 'amharic', + ['ara'] = 'arabic', + ['ari'] = 'aari', + ['ark'] = 'arakanese', + ['asm'] = 'assamese', + ['ath'] = 'athapaskan', + ['avr'] = 'avar', + ['awa'] = 'awadhi', + ['aym'] = 'aymara', + ['aze'] = 'azeri', + ['bad'] = 'badaga', + ['bag'] = 'baghelkhandi', + ['bal'] = 'balkar', + ['bau'] = 'baule', + ['bbr'] = 'berber', + ['bch'] = 'bench', + ['bcr'] = 'bible cree', + ['bel'] = 'belarussian', + ['bem'] = 'bemba', + ['ben'] = 'bengali', + ['bgr'] = 'bulgarian', + ['bhi'] = 'bhili', + ['bho'] = 'bhojpuri', + ['bik'] = 'bikol', + ['bil'] = 'bilen', + ['bkf'] = 'blackfoot', + ['bli'] = 'balochi', + ['bln'] = 'balante', + ['blt'] = 'balti', + ['bmb'] = 'bambara', + ['bml'] = 'bamileke', + ['bos'] = 'bosnian', + ['bre'] = 'breton', + ['brh'] = 'brahui', + ['bri'] = 'braj bhasha', + ['brm'] = 'burmese', + ['bsh'] = 'bashkir', + ['bti'] = 'beti', + ['cat'] = 'catalan', + ['ceb'] = 'cebuano', + ['che'] = 'chechen', + ['chg'] = 'chaha gurage', + ['chh'] = 'chattisgarhi', + ['chi'] = 'chichewa', + ['chk'] = 'chukchi', + ['chp'] = 'chipewyan', + ['chr'] = 'cherokee', + ['chu'] = 'chuvash', + ['cmr'] = 'comorian', + ['cop'] = 'coptic', + ['cos'] = 'corsican', + ['cre'] = 'cree', + ['crr'] = 'carrier', + ['crt'] = 'crimean tatar', + ['csl'] = 'church slavonic', + ['csy'] = 'czech', + ['dan'] = 'danish', + ['dar'] = 'dargwa', + ['dcr'] = 'woods cree', + ['deu'] = 'german', + ['dgr'] = 'dogri', + ['div'] = 'divehi', + ['djr'] = 'djerma', + ['dng'] = 'dangme', + ['dnk'] = 'dinka', + ['dri'] = 'dari', + ['dun'] = 'dungan', + ['dzn'] = 'dzongkha', + ['ebi'] = 'ebira', + ['ecr'] = 'eastern cree', + ['edo'] = 'edo', + ['efi'] = 'efik', + ['ell'] = 'greek', + ['eng'] = 'english', + ['erz'] = 'erzya', + ['esp'] = 'spanish', + ['eti'] = 'estonian', + ['euq'] = 'basque', + ['evk'] = 'evenki', + ['evn'] = 'even', + ['ewe'] = 'ewe', + ['fan'] = 'french antillean', + ['far'] = 'farsi', + ['fin'] = 'finnish', + ['fji'] = 'fijian', + ['fle'] = 'flemish', + ['fne'] = 'forest nenets', + ['fon'] = 'fon', + ['fos'] = 'faroese', + ['fra'] = 'french', + ['fri'] = 'frisian', + ['frl'] = 'friulian', + ['fta'] = 'futa', + ['ful'] = 'fulani', + ['gad'] = 'ga', + ['gae'] = 'gaelic', + ['gag'] = 'gagauz', + ['gal'] = 'galician', + ['gar'] = 'garshuni', + ['gaw'] = 'garhwali', + ['gez'] = "ge'ez", + ['gil'] = 'gilyak', + ['gmz'] = 'gumuz', + ['gon'] = 'gondi', + ['grn'] = 'greenlandic', + ['gro'] = 'garo', + ['gua'] = 'guarani', + ['guj'] = 'gujarati', + ['hai'] = 'haitian', + ['hal'] = 'halam', + ['har'] = 'harauti', + ['hau'] = 'hausa', + ['haw'] = 'hawaiin', + ['hbn'] = 'hammer-banna', + ['hil'] = 'hiligaynon', + ['hin'] = 'hindi', + ['hma'] = 'high mari', + ['hnd'] = 'hindko', + ['ho'] = 'ho', + ['hri'] = 'harari', + ['hrv'] = 'croatian', + ['hun'] = 'hungarian', + ['hye'] = 'armenian', + ['ibo'] = 'igbo', + ['ijo'] = 'ijo', + ['ilo'] = 'ilokano', + ['ind'] = 'indonesian', + ['ing'] = 'ingush', + ['inu'] = 'inuktitut', + ['iri'] = 'irish', + ['irt'] = 'irish traditional', + ['isl'] = 'icelandic', + ['ism'] = 'inari sami', + ['ita'] = 'italian', + ['iwr'] = 'hebrew', + ['jan'] = 'japanese', + ['jav'] = 'javanese', + ['jii'] = 'yiddish', + ['jud'] = 'judezmo', + ['jul'] = 'jula', + ['kab'] = 'kabardian', + ['kac'] = 'kachchi', + ['kal'] = 'kalenjin', + ['kan'] = 'kannada', + ['kar'] = 'karachay', + ['kat'] = 'georgian', + ['kaz'] = 'kazakh', + ['keb'] = 'kebena', + ['kge'] = 'khutsuri georgian', + ['kha'] = 'khakass', + ['khk'] = 'khanty-kazim', + ['khm'] = 'khmer', + ['khs'] = 'khanty-shurishkar', + ['khv'] = 'khanty-vakhi', + ['khw'] = 'khowar', + ['kik'] = 'kikuyu', + ['kir'] = 'kirghiz', + ['kis'] = 'kisii', + ['kkn'] = 'kokni', + ['klm'] = 'kalmyk', + ['kmb'] = 'kamba', + ['kmn'] = 'kumaoni', + ['kmo'] = 'komo', + ['kms'] = 'komso', + ['knr'] = 'kanuri', + ['kod'] = 'kodagu', + ['koh'] = 'korean old hangul', + ['kok'] = 'konkani', + ['kon'] = 'kikongo', + ['kop'] = 'komi-permyak', + ['kor'] = 'korean', + ['koz'] = 'komi-zyrian', + ['kpl'] = 'kpelle', + ['kri'] = 'krio', + ['krk'] = 'karakalpak', + ['krl'] = 'karelian', + ['krm'] = 'karaim', + ['krn'] = 'karen', + ['krt'] = 'koorete', + ['ksh'] = 'kashmiri', + ['ksi'] = 'khasi', + ['ksm'] = 'kildin sami', + ['kui'] = 'kui', + ['kul'] = 'kulvi', + ['kum'] = 'kumyk', + ['kur'] = 'kurdish', + ['kuu'] = 'kurukh', + ['kuy'] = 'kuy', + ['kyk'] = 'koryak', + ['lad'] = 'ladin', + ['lah'] = 'lahuli', + ['lak'] = 'lak', + ['lam'] = 'lambani', + ['lao'] = 'lao', + ['lat'] = 'latin', + ['laz'] = 'laz', + ['lcr'] = 'l-cree', + ['ldk'] = 'ladakhi', + ['lez'] = 'lezgi', + ['lin'] = 'lingala', + ['lma'] = 'low mari', + ['lmb'] = 'limbu', + ['lmw'] = 'lomwe', + ['lsb'] = 'lower sorbian', + ['lsm'] = 'lule sami', + ['lth'] = 'lithuanian', + ['ltz'] = 'luxembourgish', + ['lub'] = 'luba', + ['lug'] = 'luganda', + ['luh'] = 'luhya', + ['luo'] = 'luo', + ['lvi'] = 'latvian', + ['maj'] = 'majang', + ['mak'] = 'makua', + ['mal'] = 'malayalam traditional', + ['man'] = 'mansi', + ['map'] = 'mapudungun', + ['mar'] = 'marathi', + ['maw'] = 'marwari', + ['mbn'] = 'mbundu', + ['mch'] = 'manchu', + ['mcr'] = 'moose cree', + ['mde'] = 'mende', + ['men'] = "me'en", + ['miz'] = 'mizo', + ['mkd'] = 'macedonian', + ['mle'] = 'male', + ['mlg'] = 'malagasy', + ['mln'] = 'malinke', + ['mlr'] = 'malayalam reformed', + ['mly'] = 'malay', + ['mnd'] = 'mandinka', + ['mng'] = 'mongolian', + ['mni'] = 'manipuri', + ['mnk'] = 'maninka', + ['mnx'] = 'manx gaelic', + ['moh'] = 'mohawk', + ['mok'] = 'moksha', + ['mol'] = 'moldavian', + ['mon'] = 'mon', + ['mor'] = 'moroccan', + ['mri'] = 'maori', + ['mth'] = 'maithili', + ['mts'] = 'maltese', + ['mun'] = 'mundari', + ['nag'] = 'naga-assamese', + ['nan'] = 'nanai', + ['nas'] = 'naskapi', + ['ncr'] = 'n-cree', + ['ndb'] = 'ndebele', + ['ndg'] = 'ndonga', + ['nep'] = 'nepali', + ['new'] = 'newari', + ['ngr'] = 'nagari', + ['nhc'] = 'norway house cree', + ['nis'] = 'nisi', + ['niu'] = 'niuean', + ['nkl'] = 'nkole', + ['nko'] = "n'ko", + ['nld'] = 'dutch', + ['nog'] = 'nogai', + ['nor'] = 'norwegian', + ['nsm'] = 'northern sami', + ['nta'] = 'northern tai', + ['nto'] = 'esperanto', + ['nyn'] = 'nynorsk', + ['oci'] = 'occitan', + ['ocr'] = 'oji-cree', + ['ojb'] = 'ojibway', + ['ori'] = 'oriya', + ['oro'] = 'oromo', + ['oss'] = 'ossetian', + ['paa'] = 'palestinian aramaic', + ['pal'] = 'pali', + ['pan'] = 'punjabi', + ['pap'] = 'palpa', + ['pas'] = 'pashto', + ['pgr'] = 'polytonic greek', + ['pil'] = 'pilipino', + ['plg'] = 'palaung', + ['plk'] = 'polish', + ['pro'] = 'provencal', + ['ptg'] = 'portuguese', + ['qin'] = 'chin', + ['raj'] = 'rajasthani', + ['rbu'] = 'russian buriat', + ['rcr'] = 'r-cree', + ['ria'] = 'riang', + ['rms'] = 'rhaeto-romanic', + ['rom'] = 'romanian', + ['roy'] = 'romany', + ['rsy'] = 'rusyn', + ['rua'] = 'ruanda', + ['rus'] = 'russian', + ['sad'] = 'sadri', + ['san'] = 'sanskrit', + ['sat'] = 'santali', + ['say'] = 'sayisi', + ['sek'] = 'sekota', + ['sel'] = 'selkup', + ['sgo'] = 'sango', + ['shn'] = 'shan', + ['sib'] = 'sibe', + ['sid'] = 'sidamo', + ['sig'] = 'silte gurage', + ['sks'] = 'skolt sami', + ['sky'] = 'slovak', + ['sla'] = 'slavey', + ['slv'] = 'slovenian', + ['sml'] = 'somali', + ['smo'] = 'samoan', + ['sna'] = 'sena', + ['snd'] = 'sindhi', + ['snh'] = 'sinhalese', + ['snk'] = 'soninke', + ['sog'] = 'sodo gurage', + ['sot'] = 'sotho', + ['sqi'] = 'albanian', + ['srb'] = 'serbian', + ['srk'] = 'saraiki', + ['srr'] = 'serer', + ['ssl'] = 'south slavey', + ['ssm'] = 'southern sami', + ['sur'] = 'suri', + ['sva'] = 'svan', + ['sve'] = 'swedish', + ['swa'] = 'swadaya aramaic', + ['swk'] = 'swahili', + ['swz'] = 'swazi', + ['sxt'] = 'sutu', + ['syr'] = 'syriac', + ['tab'] = 'tabasaran', + ['taj'] = 'tajiki', + ['tam'] = 'tamil', + ['tat'] = 'tatar', + ['tcr'] = 'th-cree', + ['tel'] = 'telugu', + ['tgn'] = 'tongan', + ['tgr'] = 'tigre', + ['tgy'] = 'tigrinya', + ['tha'] = 'thai', + ['tht'] = 'tahitian', + ['tib'] = 'tibetan', + ['tkm'] = 'turkmen', + ['tmn'] = 'temne', + ['tna'] = 'tswana', + ['tne'] = 'tundra nenets', + ['tng'] = 'tonga', + ['tod'] = 'todo', + ['trk'] = 'turkish', + ['tsg'] = 'tsonga', + ['tua'] = 'turoyo aramaic', + ['tul'] = 'tulu', + ['tuv'] = 'tuvin', + ['twi'] = 'twi', + ['udm'] = 'udmurt', + ['ukr'] = 'ukrainian', + ['urd'] = 'urdu', + ['usb'] = 'upper sorbian', + ['uyg'] = 'uyghur', + ['uzb'] = 'uzbek', + ['ven'] = 'venda', + ['vit'] = 'vietnamese', + ['wa' ] = 'wa', + ['wag'] = 'wagdi', + ['wcr'] = 'west-cree', + ['wel'] = 'welsh', + ['wlf'] = 'wolof', + ['xbd'] = 'tai lue', + ['xhs'] = 'xhosa', + ['yak'] = 'yakut', + ['yba'] = 'yoruba', + ['ycr'] = 'y-cree', + ['yic'] = 'yi classic', + ['yim'] = 'yi modern', + ['zhh'] = 'chinese hong kong', + ['zhp'] = 'chinese phonetic', + ['zhs'] = 'chinese simplified', + ['zht'] = 'chinese traditional', + ['znd'] = 'zande', + ['zul'] = 'zulu' +} + +local features = { + ['aalt'] = 'access all alternates', + ['abvf'] = 'above-base forms', + ['abvm'] = 'above-base mark positioning', + ['abvs'] = 'above-base substitutions', + ['afrc'] = 'alternative fractions', + ['akhn'] = 'akhands', + ['blwf'] = 'below-base forms', + ['blwm'] = 'below-base mark positioning', + ['blws'] = 'below-base substitutions', + ['c2pc'] = 'petite capitals from capitals', + ['c2sc'] = 'small capitals from capitals', + ['calt'] = 'contextual alternates', + ['case'] = 'case-sensitive forms', + ['ccmp'] = 'glyph composition/decomposition', + ['cjct'] = 'conjunct forms', + ['clig'] = 'contextual ligatures', + ['cpsp'] = 'capital spacing', + ['cswh'] = 'contextual swash', + ['curs'] = 'cursive positioning', + ['dflt'] = 'default processing', + ['dist'] = 'distances', + ['dlig'] = 'discretionary ligatures', + ['dnom'] = 'denominators', + ['dtls'] = 'dotless forms', -- math + ['expt'] = 'expert forms', + ['falt'] = 'final glyph alternates', + ['fin2'] = 'terminal forms #2', + ['fin3'] = 'terminal forms #3', + ['fina'] = 'terminal forms', + ['flac'] = 'flattened accents over capitals', -- math + ['frac'] = 'fractions', + ['fwid'] = 'full width', + ['half'] = 'half forms', + ['haln'] = 'halant forms', + ['halt'] = 'alternate half width', + ['hist'] = 'historical forms', + ['hkna'] = 'horizontal kana alternates', + ['hlig'] = 'historical ligatures', + ['hngl'] = 'hangul', + ['hojo'] = 'hojo kanji forms', + ['hwid'] = 'half width', + ['init'] = 'initial forms', + ['isol'] = 'isolated forms', + ['ital'] = 'italics', + ['jalt'] = 'justification alternatives', + ['jp04'] = 'jis2004 forms', + ['jp78'] = 'jis78 forms', + ['jp83'] = 'jis83 forms', + ['jp90'] = 'jis90 forms', + ['kern'] = 'kerning', + ['lfbd'] = 'left bounds', + ['liga'] = 'standard ligatures', + ['ljmo'] = 'leading jamo forms', + ['lnum'] = 'lining figures', + ['locl'] = 'localized forms', + ['mark'] = 'mark positioning', + ['med2'] = 'medial forms #2', + ['medi'] = 'medial forms', + ['mgrk'] = 'mathematical greek', + ['mkmk'] = 'mark to mark positioning', + ['mset'] = 'mark positioning via substitution', + ['nalt'] = 'alternate annotation forms', + ['nlck'] = 'nlc kanji forms', + ['nukt'] = 'nukta forms', + ['numr'] = 'numerators', + ['onum'] = 'old style figures', + ['opbd'] = 'optical bounds', + ['ordn'] = 'ordinals', + ['ornm'] = 'ornaments', + ['palt'] = 'proportional alternate width', + ['pcap'] = 'petite capitals', + ['pnum'] = 'proportional figures', + ['pref'] = 'pre-base forms', + ['pres'] = 'pre-base substitutions', + ['pstf'] = 'post-base forms', + ['psts'] = 'post-base substitutions', + ['pwid'] = 'proportional widths', + ['qwid'] = 'quarter widths', + ['rand'] = 'randomize', + ['rkrf'] = 'rakar forms', + ['rlig'] = 'required ligatures', + ['rphf'] = 'reph form', + ['rtbd'] = 'right bounds', + ['rtla'] = 'right-to-left alternates', + ['rtlm'] = 'right to left math', -- math + ['ruby'] = 'ruby notation forms', + ['salt'] = 'stylistic alternates', + ['sinf'] = 'scientific inferiors', + ['size'] = 'optical size', + ['smcp'] = 'small capitals', + ['smpl'] = 'simplified forms', + -- ['ss01'] = 'stylistic set 1', + -- ['ss02'] = 'stylistic set 2', + -- ['ss03'] = 'stylistic set 3', + -- ['ss04'] = 'stylistic set 4', + -- ['ss05'] = 'stylistic set 5', + -- ['ss06'] = 'stylistic set 6', + -- ['ss07'] = 'stylistic set 7', + -- ['ss08'] = 'stylistic set 8', + -- ['ss09'] = 'stylistic set 9', + -- ['ss10'] = 'stylistic set 10', + -- ['ss11'] = 'stylistic set 11', + -- ['ss12'] = 'stylistic set 12', + -- ['ss13'] = 'stylistic set 13', + -- ['ss14'] = 'stylistic set 14', + -- ['ss15'] = 'stylistic set 15', + -- ['ss16'] = 'stylistic set 16', + -- ['ss17'] = 'stylistic set 17', + -- ['ss18'] = 'stylistic set 18', + -- ['ss19'] = 'stylistic set 19', + -- ['ss20'] = 'stylistic set 20', + ['ssty'] = 'script style', -- math + ['subs'] = 'subscript', + ['sups'] = 'superscript', + ['swsh'] = 'swash', + ['titl'] = 'titling', + ['tjmo'] = 'trailing jamo forms', + ['tnam'] = 'traditional name forms', + ['tnum'] = 'tabular figures', + ['trad'] = 'traditional forms', + ['twid'] = 'third widths', + ['unic'] = 'unicase', + ['valt'] = 'alternate vertical metrics', + ['vatu'] = 'vattu variants', + ['vert'] = 'vertical writing', + ['vhal'] = 'alternate vertical half metrics', + ['vjmo'] = 'vowel jamo forms', + ['vkna'] = 'vertical kana alternates', + ['vkrn'] = 'vertical kerning', + ['vpal'] = 'proportional alternate vertical metrics', + ['vrt2'] = 'vertical rotation', + ['zero'] = 'slashed zero', + + ['trep'] = 'traditional tex replacements', + ['tlig'] = 'traditional tex ligatures', + + ['ss..'] = 'stylistic set ..', + ['cv..'] = 'character variant ..', + ['js..'] = 'justification ..', + + ["dv.."] = "devanagari ..", +} + +local baselines = { + ['hang'] = 'hanging baseline', + ['icfb'] = 'ideographic character face bottom edge baseline', + ['icft'] = 'ideographic character face tope edige baseline', + ['ideo'] = 'ideographic em-box bottom edge baseline', + ['idtp'] = 'ideographic em-box top edge baseline', + ['math'] = 'mathmatical centered baseline', + ['romn'] = 'roman baseline' +} + +local swapped = function (h) + local r = { } + for k, v in next, h do + r[stringgsub(v,"[^a-z0-9]","")] = k -- is already lower + end + return r +end + +local verbosescripts = swapped(scripts ) +local verboselanguages = swapped(languages) +local verbosefeatures = swapped(features ) +local verbosebaselines = swapped(baselines) + +---[[ end excerpt from font-ott.lua ]] + +--[[doc-- + + As discussed, we will issue a warning because of incomplete support + when one of the scripts below is requested. + + Reference: https://github.com/lualatex/luaotfload/issues/31 + +--doc]]-- + +local support_incomplete = table.tohash({ + "deva", "beng", "guru", "gujr", + "orya", "taml", "telu", "knda", + "mlym", "sinh", +}, true) + +--[[doc-- + + Which features are active by default depends on the script + requested. + +--doc]]-- + +--- (string, string) dict -> (string, string) dict +local set_default_features = function (speclist) + speclist = speclist or { } + speclist[""] = nil --- invalid options stub + + --- handle language tag + local language = speclist.language + if language then --- already lowercase at this point + language = stringgsub(language, "[^a-z0-9]", "") + language = rawget(verboselanguages, language) -- srsly, rawget? + or (languages[language] and language) + or "dflt" + else + language = "dflt" + end + speclist.language = language + + --- handle script tag + local script = speclist.script + if script then + script = stringgsub(script, "[^a-z0-9]","") + script = rawget(verbosescripts, script) + or (scripts[script] and script) + or "dflt" + if support_incomplete[script] then + report("log", 0, "load", + "support for the requested script: " + .. "%q may be incomplete", script) + end + else + script = "dflt" + end + speclist.script = script + + report("log", 0, "load", + "auto-selecting default features for script: %s", + script) + + local requested = defaults[script] + if not requested then + report("log", 0, "load", + "no defaults for script %q, falling back to \"dflt\"", + script) + requested = defaults.dflt + end + + for i=1, #requested do + local feat = requested[i] + if speclist[feat] ~= false then speclist[feat] = true end + end + + for feat, state in next, global_defaults do + --- This is primarily intended for setting node + --- mode unless “base” is requested, as stated + --- in the manual. + if not speclist[feat] then speclist[feat] = state end + end + return speclist +end + +local import_values = { + --- That’s what the 1.x parser did, not quite as graciously, + --- with an array of branch expressions. + -- "style", "optsize",--> from slashed notation; handled otherwise + { "lookup", false }, + { "sub", false }, + { "mode", true }, +} + +local lookup_types = { "anon", "file", "kpse", "my", "name", "path" } + +local select_lookup = function (request) + for i=1, #lookup_types do + local lookup = lookup_types[i] + local value = request[lookup] + if value then + return lookup, value + end + end +end + +local supported = { + b = "b", + i = "i", + bi = "bi", + aat = false, + icu = false, + gr = false, +} + +--- (string | (string * string) | bool) list -> (string * number) +local handle_slashed = function (modifiers) + local style, optsize + for i=1, #modifiers do + local mod = modifiers[i] + if type(mod) == "table" and mod[1] == "optsize" then --> optical size + optsize = tonumber(mod[2]) + elseif mod == false then + --- ignore + report("log", 0, + "load", "unsupported font option: %s", v) + elseif supported[mod] then + style = supported[mod] + elseif not stringis_empty(mod) then + style = stringgsub(mod, "[^%a%d]", "") + end + end + return style, optsize +end + +local extract_subfont +do + local eof = P(-1) + local digit = R"09" + --- Theoretically a valid subfont address can be up to ten + --- digits long. + local sub_expr = P"(" * C(digit^1) * P")" * eof + local full_path = C(P(1 - sub_expr)^1) + extract_subfont = full_path * sub_expr +end + +--- spec -> spec +local handle_request = function (specification) + local request = lpegmatch(luaotfload.parsers.font_request, + specification.specification) + if not request then + --- happens when called with an absolute path + --- in an anonymous lookup; + --- we try to behave as friendly as possible + --- just go with it ... + report("log", 1, "load", "invalid request %q of type anon", + specification.specification) + report("log", 1, "load", + "use square bracket syntax or consult the documentation.") + --- The result of \fontname must be re-feedable into \font + --- which is expected by the Latex font mechanism. Now this + --- is complicated with TTC fonts that need to pass the + --- number of the requested subfont along with the file name. + --- Thus we test whether the request is a bare path only or + --- ends in a subfont expression (decimal digits inside + --- parentheses). + --- https://github.com/lualatex/luaotfload/issues/57 + local fullpath, sub = lpegmatch(extract_subfont, + specification.specification) + if fullpath and sub then + specification.sub = tonumber(sub) + specification.name = fullpath + else + specification.name = specification.specification + end + specification.lookup = "path" + return specification + end + local lookup, name = select_lookup(request) + request.features = set_default_features(request.features) + + if name then + specification.name = name + specification.lookup = lookup or specification.lookup + end + + if request.modifiers then + local style, optsize = handle_slashed(request.modifiers) + specification.style, specification.optsize = style, optsize + end + + for n=1, #import_values do + local feat = import_values[n][1] + local keep = import_values[n][2] + local newvalue = request.features[feat] + if newvalue then + specification[feat] = request.features[feat] + if not keep then + request.features[feat] = nil + end + end + end + + --- The next line sets the “rand” feature to “random”; I haven’t + --- investigated it any further (luatex-fonts-ext), so it will + --- just stay here. + specification.features.normal = normalize (request.features) + return specification +end + +if as_script == true then --- skip the remainder of the file + fonts.names.handle_request = handle_request + report ("log", 5, "load", + "Exiting early from luaotfload-features.lua.") + return +else + local registersplit = definers.registersplit + registersplit (":", handle_request, "cryptic") + registersplit ("", handle_request, "more cryptic") -- catches \font\text=[names] +end + +---[[ end included font-ltx.lua ]] + +--[[doc-- +This uses the code from luatex-fonts-merged (<- font-otc.lua) instead +of the removed luaotfload-font-otc.lua. + +TODO find out how far we get setting features without these lines, +relying on luatex-fonts only (it *does* handle features somehow, after +all). +--doc]]-- + +-- we assume that the other otf stuff is loaded already + +---[[ begin snippet from font-otc.lua ]] +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +local report_otf = logs.reporter("fonts","otf loading") + +local otf = fonts.handlers.otf +local registerotffeature = otf.features.register +local setmetatableindex = table.setmetatableindex + +--[[HH-- + + In the userdata interface we can not longer tweak the loaded font as + conveniently as before. For instance, instead of pushing extra data in + in the table using the original structure, we now have to operate on + the mkiv representation. And as the fontloader interface is modelled + after fontforge we cannot change that one too much either. + +--HH]]-- + +local types = { + substitution = "gsub_single", + ligature = "gsub_ligature", + alternate = "gsub_alternate", +} + +setmetatableindex(types, function(t,k) t[k] = k return k end) -- "key" + +local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } } +local noflags = { } + +local function addfeature(data,feature,specifications) + local descriptions = data.descriptions + local resources = data.resources + local lookups = resources.lookups + local gsubfeatures = resources.features.gsub + if gsubfeatures and gsubfeatures[feature] then + -- already present + else + local sequences = resources.sequences + local fontfeatures = resources.features + local unicodes = resources.unicodes + local lookuptypes = resources.lookuptypes + local splitter = lpeg.splitter(" ",unicodes) + local done = 0 + local skip = 0 + if not specifications[1] then + -- so we accept a one entry specification + specifications = { specifications } + end + -- subtables are tables themselves but we also accept flattened singular subtables + for s=1,#specifications do + local specification = specifications[s] + local valid = specification.valid + if not valid or valid(data,specification,feature) then + local initialize = specification.initialize + if initialize then + -- when false is returned we initialize only once + specification.initialize = initialize(specification) and initialize or nil + end + local askedfeatures = specification.features or everywhere + local subtables = specification.subtables or { specification.data } or { } + local featuretype = types[specification.type or "substitution"] + local featureflags = specification.flags or noflags + local added = false + local featurename = stringformat("ctx_%s_%s",feature,s) + local st = { } + for t=1,#subtables do + local list = subtables[t] + local full = stringformat("%s_%s",featurename,t) + st[t] = full + if featuretype == "gsub_ligature" then + lookuptypes[full] = "ligature" + for code, ligature in next, list do + local unicode = tonumber(code) or unicodes[code] + local description = descriptions[unicode] + if description then + local slookups = description.slookups + if type(ligature) == "string" then + ligature = { lpegmatch(splitter,ligature) } + end + local present = true + for i=1,#ligature do + if not descriptions[ligature[i]] then + present = false + break + end + end + if present then + if slookups then + slookups[full] = ligature + else + description.slookups = { [full] = ligature } + end + done, added = done + 1, true + else + skip = skip + 1 + end + end + end + elseif featuretype == "gsub_single" then + lookuptypes[full] = "substitution" + for code, replacement in next, list do + local unicode = tonumber(code) or unicodes[code] + local description = descriptions[unicode] + if description then + local slookups = description.slookups + replacement = tonumber(replacement) or unicodes[replacement] + if descriptions[replacement] then + if slookups then + slookups[full] = replacement + else + description.slookups = { [full] = replacement } + end + done, added = done + 1, true + end + end + end + end + end + if added then + -- script = { lang1, lang2, lang3 } or script = { lang1 = true, ... } + for k, v in next, askedfeatures do + if v[1] then + askedfeatures[k] = table.tohash(v) + end + end + sequences[#sequences+1] = { + chain = 0, + features = { [feature] = askedfeatures }, + flags = featureflags, + name = featurename, + subtables = st, + type = featuretype, + } + -- register in metadata (merge as there can be a few) + if not gsubfeatures then + gsubfeatures = { } + fontfeatures.gsub = gsubfeatures + end + local k = gsubfeatures[feature] + if not k then + k = { } + gsubfeatures[feature] = k + end + for script, languages in next, askedfeatures do + local kk = k[script] + if not kk then + kk = { } + k[script] = kk + end + for language, value in next, languages do + kk[language] = value + end + end + end + end + end + if trace_loading then + report_otf("registering feature %a, affected glyphs %a, skipped glyphs %a",feature,done,skip) + end + end +end + +otf.enhancers.addfeature = addfeature + +local extrafeatures = { } + +function otf.addfeature(name,specification) + extrafeatures[name] = specification +end + +local function enhance(data,filename,raw) + for feature, specification in next, extrafeatures do + addfeature(data,feature,specification) + end +end + +otf.enhancers.register("check extra features",enhance) + +---[[ end snippet from font-otc.lua ]] + +local tlig = { + { + type = "substitution", + features = everywhere, + data = { + [0x0022] = 0x201D, -- quotedblright + [0x0027] = 0x2019, -- quoteleft + [0x0060] = 0x2018, -- quoteright + }, + flags = { }, + }, + { + type = "ligature", + features = everywhere, + data = { + [0x2013] = {0x002D, 0x002D}, -- endash + [0x2014] = {0x002D, 0x002D, 0x002D}, -- emdash + [0x201C] = {0x2018, 0x2018}, -- quotedblleft + [0x201D] = {0x2019, 0x2019}, -- quotedblright + [0x00A1] = {0x0021, 0x2018}, -- exclamdown + [0x00BF] = {0x003F, 0x2018}, -- questiondown + --- next three originate in T1 encoding; Xetex applies + --- them too + [0x201E] = {0x002C, 0x002C}, -- quotedblbase + [0x00AB] = {0x003C, 0x003C}, -- LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + [0x00BB] = {0x003E, 0x003E}, -- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + }, + flags = { }, + }, + { + type = "ligature", + features = everywhere, + data = { + [0x201C] = {0x0060, 0x0060}, -- quotedblleft + [0x201D] = {0x0027, 0x0027}, -- quotedblright + [0x00A1] = {0x0021, 0x0060}, -- exclamdown + [0x00BF] = {0x003F, 0x0060}, -- questiondown + }, + flags = { }, + }, +} + +otf.addfeature ("tlig", tlig) +otf.addfeature ("trep", { }) + +local anum_arabic = { --- these are the same as in font-otc + [0x0030] = 0x0660, + [0x0031] = 0x0661, + [0x0032] = 0x0662, + [0x0033] = 0x0663, + [0x0034] = 0x0664, + [0x0035] = 0x0665, + [0x0036] = 0x0666, + [0x0037] = 0x0667, + [0x0038] = 0x0668, + [0x0039] = 0x0669, +} + +local anum_persian = {--- these are the same as in font-otc + [0x0030] = 0x06F0, + [0x0031] = 0x06F1, + [0x0032] = 0x06F2, + [0x0033] = 0x06F3, + [0x0034] = 0x06F4, + [0x0035] = 0x06F5, + [0x0036] = 0x06F6, + [0x0037] = 0x06F7, + [0x0038] = 0x06F8, + [0x0039] = 0x06F9, +} + +local function valid(data) + local features = data.resources.features + if features then + for k, v in next, features do + for k, v in next, v do + if v.arab then + return true + end + end + end + end +end + +local anum_specification = { + { + type = "substitution", + features = { arab = { far = true, urd = true, snd = true } }, + data = anum_persian, + flags = { }, + valid = valid, + }, + { + type = "substitution", + features = { arab = { ["*"] = true } }, + data = anum_arabic, + flags = { }, + valid = valid, + }, +} + +--[[doc-- + + Below the specifications as given in the removed font-otc.lua. + The rest was identical to what this file had from the beginning. + Both make the “anum.tex” test pass anyways. + +--doc]]-- + +otf.addfeature("anum",anum_specification) + +registerotffeature { + name = 'anum', + description = 'arabic digits', +} + +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua new file mode 100644 index 0000000..8c31750 --- /dev/null +++ b/src/luaotfload-fontloader.lua @@ -0,0 +1,13617 @@ +-- merged file : luatex-fonts-merged.lua +-- parent file : luatex-fonts.lua +-- merge date : 02/07/14 00:57:35 + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-lua']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local major,minor=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") +_MAJORVERSION=tonumber(major) or 5 +_MINORVERSION=tonumber(minor) or 1 +_LUAVERSION=_MAJORVERSION+_MINORVERSION/10 +if not lpeg then + lpeg=require("lpeg") +end +if loadstring then + local loadnormal=load + function load(first,...) + if type(first)=="string" then + return loadstring(first,...) + else + return loadnormal(first,...) + end + end +else + loadstring=load +end +if not ipairs then + local function iterate(a,i) + i=i+1 + local v=a[i] + if v~=nil then + return i,v + end + end + function ipairs(a) + return iterate,a,0 + end +end +if not pairs then + function pairs(t) + return next,t + end +end +if not table.unpack then + table.unpack=_G.unpack +elseif not unpack then + _G.unpack=table.unpack +end +if not package.loaders then + package.loaders=package.searchers +end +local print,select,tostring=print,select,tostring +local inspectors={} +function setinspector(inspector) + inspectors[#inspectors+1]=inspector +end +function inspect(...) + for s=1,select("#",...) do + local value=select(s,...) + local done=false + for i=1,#inspectors do + done=inspectors[i](value) + if done then + break + end + end + if not done then + print(tostring(value)) + end + end +end +local dummy=function() end +function optionalrequire(...) + local ok,result=xpcall(require,dummy,...) + if ok then + return result + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-lpeg']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +lpeg=require("lpeg") +if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end +local type,next,tostring=type,next,tostring +local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format +local floor=math.floor +local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt +local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print +if setinspector then + setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +end +lpeg.patterns=lpeg.patterns or {} +local patterns=lpeg.patterns +local anything=P(1) +local endofstring=P(-1) +local alwaysmatched=P(true) +patterns.anything=anything +patterns.endofstring=endofstring +patterns.beginofstring=alwaysmatched +patterns.alwaysmatched=alwaysmatched +local sign=S('+-') +local zero=P('0') +local digit=R('09') +local octdigit=R("07") +local lowercase=R("az") +local uppercase=R("AZ") +local underscore=P("_") +local hexdigit=digit+lowercase+uppercase +local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") +local newline=crlf+S("\r\n") +local escaped=P("\\")*anything +local squote=P("'") +local dquote=P('"') +local space=P(" ") +local period=P(".") +local comma=P(",") +local utfbom_32_be=P('\000\000\254\255') +local utfbom_32_le=P('\255\254\000\000') +local utfbom_16_be=P('\254\255') +local utfbom_16_le=P('\255\254') +local utfbom_8=P('\239\187\191') +local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8 +local utftype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")+alwaysmatched*Cc("utf-8") +local utfstricttype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8") +local utfoffset=utfbom_32_be*Cc(4)+utfbom_32_le*Cc(4)+utfbom_16_be*Cc(2)+utfbom_16_le*Cc(2)+utfbom_8*Cc(3)+Cc(0) +local utf8next=R("\128\191") +patterns.utfbom_32_be=utfbom_32_be +patterns.utfbom_32_le=utfbom_32_le +patterns.utfbom_16_be=utfbom_16_be +patterns.utfbom_16_le=utfbom_16_le +patterns.utfbom_8=utfbom_8 +patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") +patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf8one=R("\000\127") +patterns.utf8two=R("\194\223")*utf8next +patterns.utf8three=R("\224\239")*utf8next*utf8next +patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next +patterns.utfbom=utfbom +patterns.utftype=utftype +patterns.utfstricttype=utfstricttype +patterns.utfoffset=utfoffset +local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four +local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false) +local utf8character=P(1)*R("\128\191")^0 +patterns.utf8=utf8char +patterns.utf8char=utf8char +patterns.utf8character=utf8character +patterns.validutf8=validutf8char +patterns.validutf8char=validutf8char +local eol=S("\n\r") +local spacer=S(" \t\f\v") +local whitespace=eol+spacer +local nonspacer=1-spacer +local nonwhitespace=1-whitespace +patterns.eol=eol +patterns.spacer=spacer +patterns.whitespace=whitespace +patterns.nonspacer=nonspacer +patterns.nonwhitespace=nonwhitespace +local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0)) +patterns.stripper=stripper +patterns.collapser=collapser +patterns.lowercase=lowercase +patterns.uppercase=uppercase +patterns.letter=patterns.lowercase+patterns.uppercase +patterns.space=space +patterns.tab=P("\t") +patterns.spaceortab=patterns.space+patterns.tab +patterns.newline=newline +patterns.emptyline=newline^1 +patterns.equal=P("=") +patterns.comma=comma +patterns.commaspacer=comma*spacer^0 +patterns.period=period +patterns.colon=P(":") +patterns.semicolon=P(";") +patterns.underscore=underscore +patterns.escaped=escaped +patterns.squote=squote +patterns.dquote=dquote +patterns.nosquote=(escaped+(1-squote))^0 +patterns.nodquote=(escaped+(1-dquote))^0 +patterns.unsingle=(squote/"")*patterns.nosquote*(squote/"") +patterns.undouble=(dquote/"")*patterns.nodquote*(dquote/"") +patterns.unquoted=patterns.undouble+patterns.unsingle +patterns.unspacer=((patterns.spacer^1)/"")^0 +patterns.singlequoted=squote*patterns.nosquote*squote +patterns.doublequoted=dquote*patterns.nodquote*dquote +patterns.quoted=patterns.doublequoted+patterns.singlequoted +patterns.digit=digit +patterns.octdigit=octdigit +patterns.hexdigit=hexdigit +patterns.sign=sign +patterns.cardinal=digit^1 +patterns.integer=sign^-1*digit^1 +patterns.unsigned=digit^0*period*digit^1 +patterns.float=sign^-1*patterns.unsigned +patterns.cunsigned=digit^0*comma*digit^1 +patterns.cfloat=sign^-1*patterns.cunsigned +patterns.number=patterns.float+patterns.integer +patterns.cnumber=patterns.cfloat+patterns.integer +patterns.oct=zero*octdigit^1 +patterns.octal=patterns.oct +patterns.HEX=zero*P("X")*(digit+uppercase)^1 +patterns.hex=zero*P("x")*(digit+lowercase)^1 +patterns.hexadecimal=zero*S("xX")*hexdigit^1 +patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigit^1+hexdigit^1*period*hexdigit^0+hexdigit^1)*(S("pP")*sign^-1*hexdigit^1)^-1 +patterns.decafloat=sign^-1*(digit^0*period*digit^1+digit^1*period*digit^0+digit^1)*S("eE")*sign^-1*digit^1 +patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring +patterns.somecontent=(anything-newline-space)^1 +patterns.beginline=#(1-newline) +patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(P(-1)+Cc(" ")))^0)) +local function anywhere(pattern) + return P { P(pattern)+1*V(1) } +end +lpeg.anywhere=anywhere +function lpeg.instringchecker(p) + p=anywhere(p) + return function(str) + return lpegmatch(p,str) and true or false + end +end +function lpeg.splitter(pattern,action) + return (((1-P(pattern))^1)/action+1)^0 +end +function lpeg.tsplitter(pattern,action) + return Ct((((1-P(pattern))^1)/action+1)^0) +end +local splitters_s,splitters_m,splitters_t={},{},{} +local function splitat(separator,single) + local splitter=(single and splitters_s[separator]) or splitters_m[separator] + if not splitter then + separator=P(separator) + local other=C((1-separator)^0) + if single then + local any=anything + splitter=other*(separator*C(any^0)+"") + splitters_s[separator]=splitter + else + splitter=other*(separator*other)^0 + splitters_m[separator]=splitter + end + end + return splitter +end +local function tsplitat(separator) + local splitter=splitters_t[separator] + if not splitter then + splitter=Ct(splitat(separator)) + splitters_t[separator]=splitter + end + return splitter +end +lpeg.splitat=splitat +lpeg.tsplitat=tsplitat +function string.splitup(str,separator) + if not separator then + separator="," + end + return lpegmatch(splitters_m[separator] or splitat(separator),str) +end +local cache={} +function lpeg.split(separator,str) + local c=cache[separator] + if not c then + c=tsplitat(separator) + cache[separator]=c + end + return lpegmatch(c,str) +end +function string.split(str,separator) + if separator then + local c=cache[separator] + if not c then + c=tsplitat(separator) + cache[separator]=c + end + return lpegmatch(c,str) + else + return { str } + end +end +local spacing=patterns.spacer^0*newline +local empty=spacing*Cc("") +local nonempty=Cs((1-spacing)^1)*spacing^-1 +local content=(empty+nonempty)^1 +patterns.textline=content +local linesplitter=tsplitat(newline) +patterns.linesplitter=linesplitter +function string.splitlines(str) + return lpegmatch(linesplitter,str) +end +local cache={} +function lpeg.checkedsplit(separator,str) + local c=cache[separator] + if not c then + separator=P(separator) + local other=C((1-separator)^1) + c=Ct(separator^0*other*(separator^1*other)^0) + cache[separator]=c + end + return lpegmatch(c,str) +end +function string.checkedsplit(str,separator) + local c=cache[separator] + if not c then + separator=P(separator) + local other=C((1-separator)^1) + c=Ct(separator^0*other*(separator^1*other)^0) + cache[separator]=c + end + return lpegmatch(c,str) +end +local function f2(s) local c1,c2=byte(s,1,2) return c1*64+c2-12416 end +local function f3(s) local c1,c2,c3=byte(s,1,3) return (c1*64+c2)*64+c3-925824 end +local function f4(s) local c1,c2,c3,c4=byte(s,1,4) return ((c1*64+c2)*64+c3)*64+c4-63447168 end +local utf8byte=patterns.utf8one/byte+patterns.utf8two/f2+patterns.utf8three/f3+patterns.utf8four/f4 +patterns.utf8byte=utf8byte +local cache={} +function lpeg.stripper(str) + if type(str)=="string" then + local s=cache[str] + if not s then + s=Cs(((S(str)^1)/""+1)^0) + cache[str]=s + end + return s + else + return Cs(((str^1)/""+1)^0) + end +end +local cache={} +function lpeg.keeper(str) + if type(str)=="string" then + local s=cache[str] + if not s then + s=Cs((((1-S(str))^1)/""+1)^0) + cache[str]=s + end + return s + else + return Cs((((1-str)^1)/""+1)^0) + end +end +function lpeg.frontstripper(str) + return (P(str)+P(true))*Cs(anything^0) +end +function lpeg.endstripper(str) + return Cs((1-P(str)*endofstring)^0) +end +function lpeg.replacer(one,two,makefunction,isutf) + local pattern + local u=isutf and utf8char or 1 + if type(one)=="table" then + local no=#one + local p=P(false) + if no==0 then + for k,v in next,one do + p=p+P(k)/v + end + pattern=Cs((p+u)^0) + elseif no==1 then + local o=one[1] + one,two=P(o[1]),o[2] + pattern=Cs((one/two+u)^0) + else + for i=1,no do + local o=one[i] + p=p+P(o[1])/o[2] + end + pattern=Cs((p+u)^0) + end + else + pattern=Cs((P(one)/(two or "")+u)^0) + end + if makefunction then + return function(str) + return lpegmatch(pattern,str) + end + else + return pattern + end +end +function lpeg.finder(lst,makefunction,isutf) + local pattern + if type(lst)=="table" then + pattern=P(false) + if #lst==0 then + for k,v in next,lst do + pattern=pattern+P(k) + end + else + for i=1,#lst do + pattern=pattern+P(lst[i]) + end + end + else + pattern=P(lst) + end + if isutf then + pattern=((utf8char or 1)-pattern)^0*pattern + else + pattern=(1-pattern)^0*pattern + end + if makefunction then + return function(str) + return lpegmatch(pattern,str) + end + else + return pattern + end +end +local splitters_f,splitters_s={},{} +function lpeg.firstofsplit(separator) + local splitter=splitters_f[separator] + if not splitter then + local pattern=P(separator) + splitter=C((1-pattern)^0) + splitters_f[separator]=splitter + end + return splitter +end +function lpeg.secondofsplit(separator) + local splitter=splitters_s[separator] + if not splitter then + local pattern=P(separator) + splitter=(1-pattern)^0*pattern*C(anything^0) + splitters_s[separator]=splitter + end + return splitter +end +local splitters_s,splitters_p={},{} +function lpeg.beforesuffix(separator) + local splitter=splitters_s[separator] + if not splitter then + local pattern=P(separator) + splitter=C((1-pattern)^0)*pattern*endofstring + splitters_s[separator]=splitter + end + return splitter +end +function lpeg.afterprefix(separator) + local splitter=splitters_p[separator] + if not splitter then + local pattern=P(separator) + splitter=pattern*C(anything^0) + splitters_p[separator]=splitter + end + return splitter +end +function lpeg.balancer(left,right) + left,right=P(left),P(right) + return P { left*((1-left-right)+V(1))^0*right } +end +local nany=utf8char/"" +function lpeg.counter(pattern) + pattern=Cs((P(pattern)/" "+nany)^0) + return function(str) + return #lpegmatch(pattern,str) + end +end +utf=utf or (unicode and unicode.utf8) or {} +local utfcharacters=utf and utf.characters or string.utfcharacters +local utfgmatch=utf and utf.gmatch +local utfchar=utf and utf.char +lpeg.UP=lpeg.P +if utfcharacters then + function lpeg.US(str) + local p=P(false) + for uc in utfcharacters(str) do + p=p+P(uc) + end + return p + end +elseif utfgmatch then + function lpeg.US(str) + local p=P(false) + for uc in utfgmatch(str,".") do + p=p+P(uc) + end + return p + end +else + function lpeg.US(str) + local p=P(false) + local f=function(uc) + p=p+P(uc) + end + lpegmatch((utf8char/f)^0,str) + return p + end +end +local range=utf8byte*utf8byte+Cc(false) +function lpeg.UR(str,more) + local first,last + if type(str)=="number" then + first=str + last=more or first + else + first,last=lpegmatch(range,str) + if not last then + return P(str) + end + end + if first==last then + return P(str) + elseif utfchar and (last-first<8) then + local p=P(false) + for i=first,last do + p=p+P(utfchar(i)) + end + return p + else + local f=function(b) + return b>=first and b<=last + end + return utf8byte/f + end +end +function lpeg.is_lpeg(p) + return p and lpegtype(p)=="pattern" +end +function lpeg.oneof(list,...) + if type(list)~="table" then + list={ list,... } + end + local p=P(list[1]) + for l=2,#list do + p=p+P(list[l]) + end + return p +end +local sort=table.sort +local function copyindexed(old) + local new={} + for i=1,#old do + new[i]=old + end + return new +end +local function sortedkeys(tab) + local keys,s={},0 + for key,_ in next,tab do + s=s+1 + keys[s]=key + end + sort(keys) + return keys +end +function lpeg.append(list,pp,delayed,checked) + local p=pp + if #list>0 then + local keys=copyindexed(list) + sort(keys) + for i=#keys,1,-1 do + local k=keys[i] + if p then + p=P(k)+p + else + p=P(k) + end + end + elseif delayed then + local keys=sortedkeys(list) + if p then + for i=1,#keys,1 do + local k=keys[i] + local v=list[k] + p=P(k)/list+p + end + else + for i=1,#keys do + local k=keys[i] + local v=list[k] + if p then + p=P(k)+p + else + p=P(k) + end + end + if p then + p=p/list + end + end + elseif checked then + local keys=sortedkeys(list) + for i=1,#keys do + local k=keys[i] + local v=list[k] + if p then + if k==v then + p=P(k)+p + else + p=P(k)/v+p + end + else + if k==v then + p=P(k) + else + p=P(k)/v + end + end + end + else + local keys=sortedkeys(list) + for i=1,#keys do + local k=keys[i] + local v=list[k] + if p then + p=P(k)/v+p + else + p=P(k)/v + end + end + end + return p +end +local function make(t) + local p + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + local v=t[k] + if not p then + if next(v) then + p=P(k)*make(v) + else + p=P(k) + end + else + if next(v) then + p=p+P(k)*make(v) + else + p=p+P(k) + end + end + end + return p +end +function lpeg.utfchartabletopattern(list) + local tree={} + for i=1,#list do + local t=tree + for c in gmatch(list[i],".") do + if not t[c] then + t[c]={} + end + t=t[c] + end + end + return make(tree) +end +patterns.containseol=lpeg.finder(eol) +local function nextstep(n,step,result) + local m=n%step + local d=floor(n/step) + if d>0 then + local v=V(tostring(step)) + local s=result.start + for i=1,d do + if s then + s=v*s + else + s=v + end + end + result.start=s + end + if step>1 and result.start then + local v=V(tostring(step/2)) + result[tostring(step)]=v*v + end + if step>0 then + return nextstep(m,step/2,result) + else + return result + end +end +function lpeg.times(pattern,n) + return P(nextstep(n,2^16,{ "start",["1"]=pattern })) +end +local trailingzeros=zero^0*-digit +local case_1=period*trailingzeros/"" +local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"") +local number=digit^1*(case_1+case_2) +local stripper=Cs((number+1)^0) +lpeg.patterns.stripzeros=stripper + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-functions']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +functions=functions or {} +function functions.dummy() end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-string']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local string=string +local sub,gmatch,format,char,byte,rep,lower=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower +local lpegmatch,patterns=lpeg.match,lpeg.patterns +local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs +local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote +function string.unquoted(str) + return lpegmatch(unquoted,str) or str +end +function string.quoted(str) + return format("%q",str) +end +function string.count(str,pattern) + local n=0 + for _ in gmatch(str,pattern) do + n=n+1 + end + return n +end +function string.limit(str,n,sentinel) + if #str>n then + sentinel=sentinel or "..." + return sub(str,1,(n-#sentinel))..sentinel + else + return str + end +end +local stripper=patterns.stripper +local collapser=patterns.collapser +local longtostring=patterns.longtostring +function string.strip(str) + return lpegmatch(stripper,str) or "" +end +function string.collapsespaces(str) + return lpegmatch(collapser,str) or "" +end +function string.longtostring(str) + return lpegmatch(longtostring,str) or "" +end +local pattern=P(" ")^0*P(-1) +function string.is_empty(str) + if str=="" then + return true + else + return lpegmatch(pattern,str) and true or false + end +end +local anything=patterns.anything +local allescapes=Cc("%")*S(".-+%?()[]*") +local someescapes=Cc("%")*S(".-+%()[]") +local matchescapes=Cc(".")*S("*?") +local pattern_a=Cs ((allescapes+anything )^0 ) +local pattern_b=Cs ((someescapes+matchescapes+anything )^0 ) +local pattern_c=Cs (Cc("^")*(someescapes+matchescapes+anything )^0*Cc("$") ) +function string.escapedpattern(str,simple) + return lpegmatch(simple and pattern_b or pattern_a,str) +end +function string.topattern(str,lowercase,strict) + if str=="" or type(str)~="string" then + return ".*" + elseif strict then + str=lpegmatch(pattern_c,str) + else + str=lpegmatch(pattern_b,str) + end + if lowercase then + return lower(str) + else + return str + end +end +function string.valid(str,default) + return (type(str)=="string" and str~="" and str) or default or nil +end +string.itself=function(s) return s end +local pattern=Ct(C(1)^0) +function string.totable(str) + return lpegmatch(pattern,str) +end +local replacer=lpeg.replacer("@","%%") +function string.tformat(fmt,...) + return format(lpegmatch(replacer,fmt),...) +end +string.quote=string.quoted +string.unquote=string.unquoted + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-table']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local type,next,tostring,tonumber,ipairs,select=type,next,tostring,tonumber,ipairs,select +local table,string=table,string +local concat,sort,insert,remove=table.concat,table.sort,table.insert,table.remove +local format,lower,dump=string.format,string.lower,string.dump +local getmetatable,setmetatable=getmetatable,setmetatable +local getinfo=debug.getinfo +local lpegmatch,patterns=lpeg.match,lpeg.patterns +local floor=math.floor +local stripper=patterns.stripper +function table.strip(tab) + local lst,l={},0 + for i=1,#tab do + local s=lpegmatch(stripper,tab[i]) or "" + if s=="" then + else + l=l+1 + lst[l]=s + end + end + return lst +end +function table.keys(t) + if t then + local keys,k={},0 + for key,_ in next,t do + k=k+1 + keys[k]=key + end + return keys + else + return {} + end +end +local function compare(a,b) + local ta,tb=type(a),type(b) + if ta==tb then + return a0 then + local n=0 + for _,v in next,t do + n=n+1 + end + if n==#t then + local tt,nt={},0 + for i=1,#t do + local v=t[i] + local tv=type(v) + if tv=="number" then + nt=nt+1 + if hexify then + tt[nt]=format("0x%04X",v) + else + tt[nt]=tostring(v) + end + elseif tv=="string" then + nt=nt+1 + tt[nt]=format("%q",v) + elseif tv=="boolean" then + nt=nt+1 + tt[nt]=v and "true" or "false" + else + tt=nil + break + end + end + return tt + end + end + return nil +end +local propername=patterns.propername +local function dummy() end +local function do_serialize(root,name,depth,level,indexed) + if level>0 then + depth=depth.." " + if indexed then + handle(format("%s{",depth)) + else + local tn=type(name) + if tn=="number" then + if hexify then + handle(format("%s[0x%04X]={",depth,name)) + else + handle(format("%s[%s]={",depth,name)) + end + elseif tn=="string" then + if noquotes and not reserved[name] and lpegmatch(propername,name) then + handle(format("%s%s={",depth,name)) + else + handle(format("%s[%q]={",depth,name)) + end + elseif tn=="boolean" then + handle(format("%s[%s]={",depth,name and "true" or "false")) + else + handle(format("%s{",depth)) + end + end + end + if root and next(root) then + local first,last=nil,0 + if compact then + last=#root + for k=1,last do + if root[k]==nil then + last=k-1 + break + end + end + if last>0 then + first=1 + end + end + local sk=sortedkeys(root) + for i=1,#sk do + local k=sk[i] + local v=root[k] + local tv,tk=type(v),type(k) + if compact and first and tk=="number" and k>=first and k<=last then + if tv=="number" then + if hexify then + handle(format("%s 0x%04X,",depth,v)) + else + handle(format("%s %s,",depth,v)) + end + elseif tv=="string" then + if reduce and tonumber(v) then + handle(format("%s %s,",depth,v)) + else + handle(format("%s %q,",depth,v)) + end + elseif tv=="table" then + if not next(v) then + handle(format("%s {},",depth)) + elseif inline then + local st=simple_table(v) + if st then + handle(format("%s { %s },",depth,concat(st,", "))) + else + do_serialize(v,k,depth,level+1,true) + end + else + do_serialize(v,k,depth,level+1,true) + end + elseif tv=="boolean" then + handle(format("%s %s,",depth,v and "true" or "false")) + elseif tv=="function" then + if functions then + handle(format('%s load(%q),',depth,dump(v))) + else + handle(format('%s "function",',depth)) + end + else + handle(format("%s %q,",depth,tostring(v))) + end + elseif k=="__p__" then + if false then + handle(format("%s __p__=nil,",depth)) + end + elseif tv=="number" then + if tk=="number" then + if hexify then + handle(format("%s [0x%04X]=0x%04X,",depth,k,v)) + else + handle(format("%s [%s]=%s,",depth,k,v)) + end + elseif tk=="boolean" then + if hexify then + handle(format("%s [%s]=0x%04X,",depth,k and "true" or "false",v)) + else + handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) + end + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + if hexify then + handle(format("%s %s=0x%04X,",depth,k,v)) + else + handle(format("%s %s=%s,",depth,k,v)) + end + else + if hexify then + handle(format("%s [%q]=0x%04X,",depth,k,v)) + else + handle(format("%s [%q]=%s,",depth,k,v)) + end + end + elseif tv=="string" then + if reduce and tonumber(v) then + if tk=="number" then + if hexify then + handle(format("%s [0x%04X]=%s,",depth,k,v)) + else + handle(format("%s [%s]=%s,",depth,k,v)) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%s,",depth,k,v)) + else + handle(format("%s [%q]=%s,",depth,k,v)) + end + else + if tk=="number" then + if hexify then + handle(format("%s [0x%04X]=%q,",depth,k,v)) + else + handle(format("%s [%s]=%q,",depth,k,v)) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%q,",depth,k and "true" or "false",v)) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%q,",depth,k,v)) + else + handle(format("%s [%q]=%q,",depth,k,v)) + end + end + elseif tv=="table" then + if not next(v) then + if tk=="number" then + if hexify then + handle(format("%s [0x%04X]={},",depth,k)) + else + handle(format("%s [%s]={},",depth,k)) + end + elseif tk=="boolean" then + handle(format("%s [%s]={},",depth,k and "true" or "false")) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s={},",depth,k)) + else + handle(format("%s [%q]={},",depth,k)) + end + elseif inline then + local st=simple_table(v) + if st then + if tk=="number" then + if hexify then + handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", "))) + else + handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) + end + elseif tk=="boolean" then + handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", "))) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s={ %s },",depth,k,concat(st,", "))) + else + handle(format("%s [%q]={ %s },",depth,k,concat(st,", "))) + end + else + do_serialize(v,k,depth,level+1) + end + else + do_serialize(v,k,depth,level+1) + end + elseif tv=="boolean" then + if tk=="number" then + if hexify then + handle(format("%s [0x%04X]=%s,",depth,k,v and "true" or "false")) + else + handle(format("%s [%s]=%s,",depth,k,v and "true" or "false")) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false")) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%s,",depth,k,v and "true" or "false")) + else + handle(format("%s [%q]=%s,",depth,k,v and "true" or "false")) + end + elseif tv=="function" then + if functions then + local f=getinfo(v).what=="C" and dump(dummy) or dump(v) + if tk=="number" then + if hexify then + handle(format("%s [0x%04X]=load(%q),",depth,k,f)) + else + handle(format("%s [%s]=load(%q),",depth,k,f)) + end + elseif tk=="boolean" then + handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f)) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=load(%q),",depth,k,f)) + else + handle(format("%s [%q]=load(%q),",depth,k,f)) + end + end + else + if tk=="number" then + if hexify then + handle(format("%s [0x%04X]=%q,",depth,k,tostring(v))) + else + handle(format("%s [%s]=%q,",depth,k,tostring(v))) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v))) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%q,",depth,k,tostring(v))) + else + handle(format("%s [%q]=%q,",depth,k,tostring(v))) + end + end + end + end + if level>0 then + handle(format("%s},",depth)) + end +end +local function serialize(_handle,root,name,specification) + local tname=type(name) + if type(specification)=="table" then + noquotes=specification.noquotes + hexify=specification.hexify + handle=_handle or specification.handle or print + reduce=specification.reduce or false + functions=specification.functions + compact=specification.compact + inline=specification.inline and compact + if functions==nil then + functions=true + end + if compact==nil then + compact=true + end + if inline==nil then + inline=compact + end + else + noquotes=false + hexify=false + handle=_handle or print + reduce=false + compact=true + inline=true + functions=true + end + if tname=="string" then + if name=="return" then + handle("return {") + else + handle(name.."={") + end + elseif tname=="number" then + if hexify then + handle(format("[0x%04X]={",name)) + else + handle("["..name.."]={") + end + elseif tname=="boolean" then + if name then + handle("return {") + else + handle("{") + end + else + handle("t={") + end + if root then + if getmetatable(root) then + local dummy=root._w_h_a_t_e_v_e_r_ + root._w_h_a_t_e_v_e_r_=nil + end + if next(root) then + do_serialize(root,name,"",0) + end + end + handle("}") +end +function table.serialize(root,name,specification) + local t,n={},0 + local function flush(s) + n=n+1 + t[n]=s + end + serialize(flush,root,name,specification) + return concat(t,"\n") +end +table.tohandle=serialize +local maxtab=2*1024 +function table.tofile(filename,root,name,specification) + local f=io.open(filename,'w') + if f then + if maxtab>1 then + local t,n={},0 + local function flush(s) + n=n+1 + t[n]=s + if n>maxtab then + f:write(concat(t,"\n"),"\n") + t,n={},0 + end + end + serialize(flush,root,name,specification) + f:write(concat(t,"\n"),"\n") + else + local function flush(s) + f:write(s,"\n") + end + serialize(flush,root,name,specification) + end + f:close() + io.flush() + end +end +local function flattened(t,f,depth) + if f==nil then + f={} + depth=0xFFFF + elseif tonumber(f) then + depth=f + f={} + elseif not depth then + depth=0xFFFF + end + for k,v in next,t do + if type(k)~="number" then + if depth>0 and type(v)=="table" then + flattened(v,f,depth-1) + else + f[#f+1]=v + end + end + end + for k=1,#t do + local v=t[k] + if depth>0 and type(v)=="table" then + flattened(v,f,depth-1) + else + f[#f+1]=v + end + end + return f +end +table.flattened=flattened +local function unnest(t,f) + if not f then + f={} + end + for i=1,#t do + local v=t[i] + if type(v)=="table" then + if type(v[1])=="table" then + unnest(v,f) + else + f[#f+1]=v + end + else + f[#f+1]=v + end + end + return f +end +function table.unnest(t) + return unnest(t) +end +local function are_equal(a,b,n,m) + if a and b and #a==#b then + n=n or 1 + m=m or #a + for i=n,m do + local ai,bi=a[i],b[i] + if ai==bi then + elseif type(ai)=="table" and type(bi)=="table" then + if not are_equal(ai,bi) then + return false + end + else + return false + end + end + return true + else + return false + end +end +local function identical(a,b) + for ka,va in next,a do + local vb=b[ka] + if va==vb then + elseif type(va)=="table" and type(vb)=="table" then + if not identical(va,vb) then + return false + end + else + return false + end + end + return true +end +table.identical=identical +table.are_equal=are_equal +function table.compact(t) + if t then + for k,v in next,t do + if not next(v) then + t[k]=nil + end + end + end +end +function table.contains(t,v) + if t then + for i=1,#t do + if t[i]==v then + return i + end + end + end + return false +end +function table.count(t) + local n=0 + for k,v in next,t do + n=n+1 + end + return n +end +function table.swapped(t,s) + local n={} + if s then + for k,v in next,s do + n[k]=v + end + end + for k,v in next,t do + n[v]=k + end + return n +end +function table.mirrored(t) + local n={} + for k,v in next,t do + n[v]=k + n[k]=v + end + return n +end +function table.reversed(t) + if t then + local tt,tn={},#t + if tn>0 then + local ttn=0 + for i=tn,1,-1 do + ttn=ttn+1 + tt[ttn]=t[i] + end + end + return tt + end +end +function table.reverse(t) + if t then + local n=#t + for i=1,floor(n/2) do + local j=n-i+1 + t[i],t[j]=t[j],t[i] + end + return t + end +end +function table.sequenced(t,sep,simple) + if not t then + return "" + end + local n=#t + local s={} + if n>0 then + for i=1,n do + s[i]=tostring(t[i]) + end + else + n=0 + for k,v in sortedhash(t) do + if simple then + if v==true then + n=n+1 + s[n]=k + elseif v and v~="" then + n=n+1 + s[n]=k.."="..tostring(v) + end + else + n=n+1 + s[n]=k.."="..tostring(v) + end + end + end + return concat(s,sep or " | ") +end +function table.print(t,...) + if type(t)~="table" then + print(tostring(t)) + else + serialize(print,t,...) + end +end +if setinspector then + setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +end +function table.sub(t,i,j) + return { unpack(t,i,j) } +end +function table.is_empty(t) + return not t or not next(t) +end +function table.has_one_entry(t) + return t and not next(t,next(t)) +end +function table.loweredkeys(t) + local l={} + for k,v in next,t do + l[lower(k)]=v + end + return l +end +function table.unique(old) + local hash={} + local new={} + local n=0 + for i=1,#old do + local oi=old[i] + if not hash[oi] then + n=n+1 + new[n]=oi + hash[oi]=true + end + end + return new +end +function table.sorted(t,...) + sort(t,...) + return t +end +function table.values(t,s) + if t then + local values,keys,v={},{},0 + for key,value in next,t do + if not keys[value] then + v=v+1 + values[v]=value + keys[k]=key + end + end + if s then + sort(values) + end + return values + else + return {} + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-io']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local io=io +local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format +local concat=table.concat +local floor=math.floor +local type=type +if string.find(os.getenv("PATH"),";") then + io.fileseparator,io.pathseparator="\\",";" +else + io.fileseparator,io.pathseparator="/",":" +end +local function readall(f) + return f:read("*all") +end +local function readall(f) + local size=f:seek("end") + if size==0 then + return "" + elseif size<1024*1024 then + f:seek("set",0) + return f:read('*all') + else + local done=f:seek("set",0) + local step + if size<1024*1024 then + step=1024*1024 + elseif size>16*1024*1024 then + step=16*1024*1024 + else + step=floor(size/(1024*1024))*1024*1024/8 + end + local data={} + while true do + local r=f:read(step) + if not r then + return concat(data) + else + data[#data+1]=r + end + end + end +end +io.readall=readall +function io.loaddata(filename,textmode) + local f=io.open(filename,(textmode and 'r') or 'rb') + if f then + local data=readall(f) + f:close() + if #data>0 then + return data + end + end +end +function io.savedata(filename,data,joiner) + local f=io.open(filename,"wb") + if f then + if type(data)=="table" then + f:write(concat(data,joiner or "")) + elseif type(data)=="function" then + data(f) + else + f:write(data or "") + end + f:close() + io.flush() + return true + else + return false + end +end +function io.loadlines(filename,n) + local f=io.open(filename,'r') + if not f then + elseif n then + local lines={} + for i=1,n do + local line=f:read("*lines") + if line then + lines[#lines+1]=line + else + break + end + end + f:close() + lines=concat(lines,"\n") + if #lines>0 then + return lines + end + else + local line=f:read("*line") or "" + f:close() + if #line>0 then + return line + end + end +end +function io.loadchunk(filename,n) + local f=io.open(filename,'rb') + if f then + local data=f:read(n or 1024) + f:close() + if #data>0 then + return data + end + end +end +function io.exists(filename) + local f=io.open(filename) + if f==nil then + return false + else + f:close() + return true + end +end +function io.size(filename) + local f=io.open(filename) + if f==nil then + return 0 + else + local s=f:seek("end") + f:close() + return s + end +end +function io.noflines(f) + if type(f)=="string" then + local f=io.open(filename) + if f then + local n=f and io.noflines(f) or 0 + f:close() + return n + else + return 0 + end + else + local n=0 + for _ in f:lines() do + n=n+1 + end + f:seek('set',0) + return n + end +end +local nextchar={ + [ 4]=function(f) + return f:read(1,1,1,1) + end, + [ 2]=function(f) + return f:read(1,1) + end, + [ 1]=function(f) + return f:read(1) + end, + [-2]=function(f) + local a,b=f:read(1,1) + return b,a + end, + [-4]=function(f) + local a,b,c,d=f:read(1,1,1,1) + return d,c,b,a + end +} +function io.characters(f,n) + if f then + return nextchar[n or 1],f + end +end +local nextbyte={ + [4]=function(f) + local a,b,c,d=f:read(1,1,1,1) + if d then + return byte(a),byte(b),byte(c),byte(d) + end + end, + [3]=function(f) + local a,b,c=f:read(1,1,1) + if b then + return byte(a),byte(b),byte(c) + end + end, + [2]=function(f) + local a,b=f:read(1,1) + if b then + return byte(a),byte(b) + end + end, + [1]=function (f) + local a=f:read(1) + if a then + return byte(a) + end + end, + [-2]=function (f) + local a,b=f:read(1,1) + if b then + return byte(b),byte(a) + end + end, + [-3]=function(f) + local a,b,c=f:read(1,1,1) + if b then + return byte(c),byte(b),byte(a) + end + end, + [-4]=function(f) + local a,b,c,d=f:read(1,1,1,1) + if d then + return byte(d),byte(c),byte(b),byte(a) + end + end +} +function io.bytes(f,n) + if f then + return nextbyte[n or 1],f + else + return nil,nil + end +end +function io.ask(question,default,options) + while true do + io.write(question) + if options then + io.write(format(" [%s]",concat(options,"|"))) + end + if default then + io.write(format(" [%s]",default)) + end + io.write(format(" ")) + io.flush() + local answer=io.read() + answer=gsub(answer,"^%s*(.*)%s*$","%1") + if answer=="" and default then + return default + elseif not options then + return answer + else + for k=1,#options do + if options[k]==answer then + return answer + end + end + local pattern="^"..answer + for k=1,#options do + local v=options[k] + if find(v,pattern) then + return v + end + end + end + end +end +local function readnumber(f,n,m) + if m then + f:seek("set",n) + n=m + end + if n==1 then + return byte(f:read(1)) + elseif n==2 then + local a,b=byte(f:read(2),1,2) + return 256*a+b + elseif n==3 then + local a,b,c=byte(f:read(3),1,3) + return 256*256*a+256*b+c + elseif n==4 then + local a,b,c,d=byte(f:read(4),1,4) + return 256*256*256*a+256*256*b+256*c+d + elseif n==8 then + local a,b=readnumber(f,4),readnumber(f,4) + return 256*a+b + elseif n==12 then + local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4) + return 256*256*a+256*b+c + elseif n==-2 then + local b,a=byte(f:read(2),1,2) + return 256*a+b + elseif n==-3 then + local c,b,a=byte(f:read(3),1,3) + return 256*256*a+256*b+c + elseif n==-4 then + local d,c,b,a=byte(f:read(4),1,4) + return 256*256*256*a+256*256*b+256*c+d + elseif n==-8 then + local h,g,f,e,d,c,b,a=byte(f:read(8),1,8) + return 256*256*256*256*256*256*256*a+256*256*256*256*256*256*b+256*256*256*256*256*c+256*256*256*256*d+256*256*256*e+256*256*f+256*g+h + else + return 0 + end +end +io.readnumber=readnumber +function io.readstring(f,n,m) + if m then + f:seek("set",n) + n=m + end + local str=gsub(f:read(n),"\000","") + return str +end +if not io.i_limiter then function io.i_limiter() end end +if not io.o_limiter then function io.o_limiter() end end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-file']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +file=file or {} +local file=file +if not lfs then + lfs=optionalrequire("lfs") +end +if not lfs then + lfs={ + getcurrentdir=function() + return "." + end, + attributes=function() + return nil + end, + isfile=function(name) + local f=io.open(name,'rb') + if f then + f:close() + return true + end + end, + isdir=function(name) + print("you need to load lfs") + return false + end + } +elseif not lfs.isfile then + local attributes=lfs.attributes + function lfs.isdir(name) + return attributes(name,"mode")=="directory" + end + function lfs.isfile(name) + return attributes(name,"mode")=="file" + end +end +local insert,concat=table.insert,table.concat +local match,find,gmatch=string.match,string.find,string.gmatch +local lpegmatch=lpeg.match +local getcurrentdir,attributes=lfs.currentdir,lfs.attributes +local checkedsplit=string.checkedsplit +local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct +local colon=P(":") +local period=P(".") +local periods=P("..") +local fwslash=P("/") +local bwslash=P("\\") +local slashes=S("\\/") +local noperiod=1-period +local noslashes=1-slashes +local name=noperiod^1 +local suffix=period/""*(1-period-slashes)^1*-1 +local pattern=C((1-(slashes^1*noslashes^1*-1))^1)*P(1) +local function pathpart(name,default) + return name and lpegmatch(pattern,name) or default or "" +end +local pattern=(noslashes^0*slashes)^1*C(noslashes^1)*-1 +local function basename(name) + return name and lpegmatch(pattern,name) or name +end +local pattern=(noslashes^0*slashes^1)^0*Cs((1-suffix)^1)*suffix^0 +local function nameonly(name) + return name and lpegmatch(pattern,name) or name +end +local pattern=(noslashes^0*slashes)^0*(noperiod^1*period)^1*C(noperiod^1)*-1 +local function suffixonly(name) + return name and lpegmatch(pattern,name) or "" +end +local pattern=(noslashes^0*slashes)^0*noperiod^1*((period*C(noperiod^1))^1)*-1+Cc("") +local function suffixesonly(name) + if name then + return lpegmatch(pattern,name) + else + return "" + end +end +file.pathpart=pathpart +file.basename=basename +file.nameonly=nameonly +file.suffixonly=suffixonly +file.suffix=suffixonly +file.suffixesonly=suffixesonly +file.suffixes=suffixesonly +file.dirname=pathpart +file.extname=suffixonly +local drive=C(R("az","AZ"))*colon +local path=C((noslashes^0*slashes)^0) +local suffix=period*C(P(1-period)^0*P(-1)) +local base=C((1-suffix)^0) +local rest=C(P(1)^0) +drive=drive+Cc("") +path=path+Cc("") +base=base+Cc("") +suffix=suffix+Cc("") +local pattern_a=drive*path*base*suffix +local pattern_b=path*base*suffix +local pattern_c=C(drive*path)*C(base*suffix) +local pattern_d=path*rest +function file.splitname(str,splitdrive) + if not str then + elseif splitdrive then + return lpegmatch(pattern_a,str) + else + return lpegmatch(pattern_b,str) + end +end +function file.splitbase(str) + if str then + return lpegmatch(pattern_d,str) + else + return "",str + end +end +function file.nametotable(str,splitdrive) + if str then + local path,drive,subpath,name,base,suffix=lpegmatch(pattern_c,str) + if splitdrive then + return { + path=path, + drive=drive, + subpath=subpath, + name=name, + base=base, + suffix=suffix, + } + else + return { + path=path, + name=name, + base=base, + suffix=suffix, + } + end + end +end +local pattern=Cs(((period*(1-period-slashes)^1*-1)/""+1)^1) +function file.removesuffix(name) + return name and lpegmatch(pattern,name) +end +local suffix=period/""*(1-period-slashes)^1*-1 +local pattern=Cs((noslashes^0*slashes^1)^0*((1-suffix)^1))*Cs(suffix) +function file.addsuffix(filename,suffix,criterium) + if not filename or not suffix or suffix=="" then + return filename + elseif criterium==true then + return filename.."."..suffix + elseif not criterium then + local n,s=lpegmatch(pattern,filename) + if not s or s=="" then + return filename.."."..suffix + else + return filename + end + else + local n,s=lpegmatch(pattern,filename) + if s and s~="" then + local t=type(criterium) + if t=="table" then + for i=1,#criterium do + if s==criterium[i] then + return filename + end + end + elseif t=="string" then + if s==criterium then + return filename + end + end + end + return (n or filename).."."..suffix + end +end +local suffix=period*(1-period-slashes)^1*-1 +local pattern=Cs((1-suffix)^0) +function file.replacesuffix(name,suffix) + if name and suffix and suffix~="" then + return lpegmatch(pattern,name).."."..suffix + else + return name + end +end +local reslasher=lpeg.replacer(P("\\"),"/") +function file.reslash(str) + return str and lpegmatch(reslasher,str) +end +function file.is_writable(name) + if not name then + elseif lfs.isdir(name) then + name=name.."/m_t_x_t_e_s_t.tmp" + local f=io.open(name,"wb") + if f then + f:close() + os.remove(name) + return true + end + elseif lfs.isfile(name) then + local f=io.open(name,"ab") + if f then + f:close() + return true + end + else + local f=io.open(name,"ab") + if f then + f:close() + os.remove(name) + return true + end + end + return false +end +local readable=P("r")*Cc(true) +function file.is_readable(name) + if name then + local a=attributes(name) + return a and lpegmatch(readable,a.permissions) or false + else + return false + end +end +file.isreadable=file.is_readable +file.iswritable=file.is_writable +function file.size(name) + if name then + local a=attributes(name) + return a and a.size or 0 + else + return 0 + end +end +function file.splitpath(str,separator) + return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator) +end +function file.joinpath(tab,separator) + return tab and concat(tab,separator or io.pathseparator) +end +local someslash=S("\\/") +local stripper=Cs(P(fwslash)^0/""*reslasher) +local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon +local isroot=fwslash^1*-1 +local hasroot=fwslash^1 +local reslasher=lpeg.replacer(S("\\/"),"/") +local deslasher=lpeg.replacer(S("\\/")^1,"/") +function file.join(...) + local lst={... } + local one=lst[1] + if lpegmatch(isnetwork,one) then + local one=lpegmatch(reslasher,one) + local two=lpegmatch(deslasher,concat(lst,"/",2)) + if lpegmatch(hasroot,two) then + return one..two + else + return one.."/"..two + end + elseif lpegmatch(isroot,one) then + local two=lpegmatch(deslasher,concat(lst,"/",2)) + if lpegmatch(hasroot,two) then + return two + else + return "/"..two + end + elseif one=="" then + return lpegmatch(stripper,concat(lst,"/",2)) + else + return lpegmatch(deslasher,concat(lst,"/")) + end +end +local drivespec=R("az","AZ")^1*colon +local anchors=fwslash+drivespec +local untouched=periods+(1-period)^1*P(-1) +local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0) +local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//") +local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1)) +local absolute=fwslash +function file.collapsepath(str,anchor) + if not str then + return + end + if anchor==true and not lpegmatch(anchors,str) then + str=getcurrentdir().."/"..str + end + if str=="" or str=="." then + return "." + elseif lpegmatch(untouched,str) then + return lpegmatch(reslasher,str) + end + local starter,oldelements=lpegmatch(splitstarter,str) + local newelements={} + local i=#oldelements + while i>0 do + local element=oldelements[i] + if element=='.' then + elseif element=='..' then + local n=i-1 + while n>0 do + local element=oldelements[n] + if element~='..' and element~='.' then + oldelements[n]='.' + break + else + n=n-1 + end + end + if n<1 then + insert(newelements,1,'..') + end + elseif element~="" then + insert(newelements,1,element) + end + i=i-1 + end + if #newelements==0 then + return starter or "." + elseif starter then + return starter..concat(newelements,'/') + elseif lpegmatch(absolute,str) then + return "/"..concat(newelements,'/') + else + newelements=concat(newelements,'/') + if anchor=="." and find(str,"^%./") then + return "./"..newelements + else + return newelements + end + end +end +local validchars=R("az","09","AZ","--","..") +local pattern_a=lpeg.replacer(1-validchars) +local pattern_a=Cs((validchars+P(1)/"-")^1) +local whatever=P("-")^0/"" +local pattern_b=Cs(whatever*(1-whatever*-1)^1) +function file.robustname(str,strict) + if str then + str=lpegmatch(pattern_a,str) or str + if strict then + return lpegmatch(pattern_b,str) or str + else + return str + end + end +end +file.readdata=io.loaddata +file.savedata=io.savedata +function file.copy(oldname,newname) + if oldname and newname then + local data=io.loaddata(oldname) + if data and data~="" then + file.savedata(newname,data) + end + end +end +local letter=R("az","AZ")+S("_-+") +local separator=P("://") +local qualified=period^0*fwslash+letter*colon+letter^1*separator+letter^1*fwslash +local rootbased=fwslash+letter*colon +lpeg.patterns.qualified=qualified +lpeg.patterns.rootbased=rootbased +function file.is_qualified_path(filename) + return filename and lpegmatch(qualified,filename)~=nil +end +function file.is_rootbased_path(filename) + return filename and lpegmatch(rootbased,filename)~=nil +end +function file.strip(name,dir) + if name then + local b,a=match(name,"^(.-)"..dir.."(.*)$") + return a~="" and a or name + end +end +function lfs.mkdirs(path) + local full="" + for sub in gmatch(path,"(/*[^\\/]+)") do + full=full..sub + lfs.mkdir(full) + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-boolean']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local type,tonumber=type,tonumber +boolean=boolean or {} +local boolean=boolean +function boolean.tonumber(b) + if b then return 1 else return 0 end +end +function toboolean(str,tolerant) + if str==nil then + return false + elseif str==false then + return false + elseif str==true then + return true + elseif str=="true" then + return true + elseif str=="false" then + return false + elseif not tolerant then + return false + elseif str==0 then + return false + elseif (tonumber(str) or 0)>0 then + return true + else + return str=="yes" or str=="on" or str=="t" + end +end +string.toboolean=toboolean +function string.booleanstring(str) + if str=="0" then + return false + elseif str=="1" then + return true + elseif str=="" then + return false + elseif str=="false" then + return false + elseif str=="true" then + return true + elseif (tonumber(str) or 0)>0 then + return true + else + return str=="yes" or str=="on" or str=="t" + end +end +function string.is_boolean(str,default) + if type(str)=="string" then + if str=="true" or str=="yes" or str=="on" or str=="t" or str=="1" then + return true + elseif str=="false" or str=="no" or str=="off" or str=="f" or str=="0" then + return false + end + end + return default +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-math']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local floor,sin,cos,tan=math.floor,math.sin,math.cos,math.tan +if not math.round then + function math.round(x) return floor(x+0.5) end +end +if not math.div then + function math.div(n,m) return floor(n/m) end +end +if not math.mod then + function math.mod(n,m) return n%m end +end +local pipi=2*math.pi/360 +if not math.sind then + function math.sind(d) return sin(d*pipi) end + function math.cosd(d) return cos(d*pipi) end + function math.tand(d) return tan(d*pipi) end +end +if not math.odd then + function math.odd (n) return n%2~=0 end + function math.even(n) return n%2==0 end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['util-str']={ + version=1.001, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +utilities=utilities or {} +utilities.strings=utilities.strings or {} +local strings=utilities.strings +local format,gsub,rep,sub=string.format,string.gsub,string.rep,string.sub +local load,dump=load,string.dump +local tonumber,type,tostring=tonumber,type,tostring +local unpack,concat=table.unpack,table.concat +local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc +local patterns,lpegmatch=lpeg.patterns,lpeg.match +local utfchar,utfbyte=utf.char,utf.byte +local loadstripped=function(str,shortcuts) + if shortcuts then + return load(dump(load(str),true),nil,nil,shortcuts) + else + return load(dump(load(str),true)) + end +end +if not number then number={} end +local stripper=patterns.stripzeros +local function points(n) + return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) +end +local function basepoints(n) + return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536)) +end +number.points=points +number.basepoints=basepoints +local rubish=patterns.spaceortab^0*patterns.newline +local anyrubish=patterns.spaceortab+patterns.newline +local anything=patterns.anything +local stripped=(patterns.spaceortab^1/"")*patterns.newline +local leading=rubish^0/"" +local trailing=(anyrubish^1*patterns.endofstring)/"" +local redundant=rubish^3/"\n" +local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0) +function strings.collapsecrlf(str) + return lpegmatch(pattern,str) +end +local repeaters={} +function strings.newrepeater(str,offset) + offset=offset or 0 + local s=repeaters[str] + if not s then + s={} + repeaters[str]=s + end + local t=s[offset] + if t then + return t + end + t={} + setmetatable(t,{ __index=function(t,k) + if not k then + return "" + end + local n=k+offset + local s=n>0 and rep(str,n) or "" + t[k]=s + return s + end }) + s[offset]=t + return t +end +local extra,tab,start=0,0,4,0 +local nspaces=strings.newrepeater(" ") +string.nspaces=nspaces +local pattern=Carg(1)/function(t) + extra,tab,start=0,t or 7,1 + end*Cs(( + Cp()*patterns.tab/function(position) + local current=(position-start+1)+extra + local spaces=tab-(current-1)%tab + if spaces>0 then + extra=extra+spaces-1 + return nspaces[spaces] + else + return "" + end + end+patterns.newline*Cp()/function(position) + extra,start=0,position + end+patterns.anything + )^1) +function strings.tabtospace(str,tab) + return lpegmatch(pattern,str,1,tab or 7) +end +function strings.striplong(str) + str=gsub(str,"^%s*","") + str=gsub(str,"[\n\r]+ *","\n") + return str +end +function strings.nice(str) + str=gsub(str,"[:%-+_]+"," ") + return str +end +local n=0 +local sequenced=table.sequenced +function string.autodouble(s,sep) + if s==nil then + return '""' + end + local t=type(s) + if t=="number" then + return tostring(s) + end + if t=="table" then + return ('"'..sequenced(s,sep or ",")..'"') + end + return ('"'..tostring(s)..'"') +end +function string.autosingle(s,sep) + if s==nil then + return "''" + end + local t=type(s) + if t=="number" then + return tostring(s) + end + if t=="table" then + return ("'"..sequenced(s,sep or ",").."'") + end + return ("'"..tostring(s).."'") +end +local tracedchars={} +string.tracedchars=tracedchars +strings.tracers=tracedchars +function string.tracedchar(b) + if type(b)=="number" then + return tracedchars[b] or (utfchar(b).." (U+"..format('%05X',b)..")") + else + local c=utfbyte(b) + return tracedchars[c] or (b.." (U+"..format('%05X',c)..")") + end +end +function number.signed(i) + if i>0 then + return "+",i + else + return "-",-i + end +end +local zero=P("0")^1/"" +local plus=P("+")/"" +local minus=P("-") +local separator=S(".") +local digit=R("09") +local trailing=zero^1*#S("eE") +local exponent=(S("eE")*(plus+Cs((minus*zero^0*P(-1))/"")+minus)*zero^0*(P(-1)*Cc("0")+P(1)^1)) +local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent) +local pattern_b=Cs((exponent+P(1))^0) +function number.sparseexponent(f,n) + if not n then + n=f + f="%e" + end + local tn=type(n) + if tn=="string" then + local m=tonumber(n) + if m then + return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m)) + end + elseif tn=="number" then + return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n)) + end + return tostring(n) +end +local template=[[ +%s +%s +return function(%s) return %s end +]] +local environment={ + global=global or _G, + lpeg=lpeg, + type=type, + tostring=tostring, + tonumber=tonumber, + format=string.format, + concat=table.concat, + signed=number.signed, + points=number.points, + basepoints=number.basepoints, + utfchar=utf.char, + utfbyte=utf.byte, + lpegmatch=lpeg.match, + nspaces=string.nspaces, + tracedchar=string.tracedchar, + autosingle=string.autosingle, + autodouble=string.autodouble, + sequenced=table.sequenced, + formattednumber=number.formatted, + sparseexponent=number.sparseexponent, +} +local preamble="" +local arguments={ "a1" } +setmetatable(arguments,{ __index=function(t,k) + local v=t[k-1]..",a"..k + t[k]=v + return v + end +}) +local prefix_any=C((S("+- .")+R("09"))^0) +local prefix_tab=P("{")*C((1-P("}"))^0)*P("}")+C((1-R("az","AZ","09","%%"))^0) +local format_s=function(f) + n=n+1 + if f and f~="" then + return format("format('%%%ss',a%s)",f,n) + else + return format("(a%s or '')",n) + end +end +local format_S=function(f) + n=n+1 + if f and f~="" then + return format("format('%%%ss',tostring(a%s))",f,n) + else + return format("tostring(a%s)",n) + end +end +local format_q=function() + n=n+1 + return format("(a%s and format('%%q',a%s) or '')",n,n) +end +local format_Q=function() + n=n+1 + return format("format('%%q',tostring(a%s))",n) +end +local format_i=function(f) + n=n+1 + if f and f~="" then + return format("format('%%%si',a%s)",f,n) + else + return format("format('%%i',a%s)",n) + end +end +local format_d=format_i +local format_I=function(f) + n=n+1 + return format("format('%%s%%%si',signed(a%s))",f,n) +end +local format_f=function(f) + n=n+1 + return format("format('%%%sf',a%s)",f,n) +end +local format_g=function(f) + n=n+1 + return format("format('%%%sg',a%s)",f,n) +end +local format_G=function(f) + n=n+1 + return format("format('%%%sG',a%s)",f,n) +end +local format_e=function(f) + n=n+1 + return format("format('%%%se',a%s)",f,n) +end +local format_E=function(f) + n=n+1 + return format("format('%%%sE',a%s)",f,n) +end +local format_j=function(f) + n=n+1 + return format("sparseexponent('%%%se',a%s)",f,n) +end +local format_J=function(f) + n=n+1 + return format("sparseexponent('%%%sE',a%s)",f,n) +end +local format_x=function(f) + n=n+1 + return format("format('%%%sx',a%s)",f,n) +end +local format_X=function(f) + n=n+1 + return format("format('%%%sX',a%s)",f,n) +end +local format_o=function(f) + n=n+1 + return format("format('%%%so',a%s)",f,n) +end +local format_c=function() + n=n+1 + return format("utfchar(a%s)",n) +end +local format_C=function() + n=n+1 + return format("tracedchar(a%s)",n) +end +local format_r=function(f) + n=n+1 + return format("format('%%%s.0f',a%s)",f,n) +end +local format_h=function(f) + n=n+1 + if f=="-" then + f=sub(f,2) + return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + else + return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + end +end +local format_H=function(f) + n=n+1 + if f=="-" then + f=sub(f,2) + return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + else + return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + end +end +local format_u=function(f) + n=n+1 + if f=="-" then + f=sub(f,2) + return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + else + return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + end +end +local format_U=function(f) + n=n+1 + if f=="-" then + f=sub(f,2) + return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + else + return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) + end +end +local format_p=function() + n=n+1 + return format("points(a%s)",n) +end +local format_b=function() + n=n+1 + return format("basepoints(a%s)",n) +end +local format_t=function(f) + n=n+1 + if f and f~="" then + return format("concat(a%s,%q)",n,f) + else + return format("concat(a%s)",n) + end +end +local format_T=function(f) + n=n+1 + if f and f~="" then + return format("sequenced(a%s,%q)",n,f) + else + return format("sequenced(a%s)",n) + end +end +local format_l=function() + n=n+1 + return format("(a%s and 'true' or 'false')",n) +end +local format_L=function() + n=n+1 + return format("(a%s and 'TRUE' or 'FALSE')",n) +end +local format_N=function() + n=n+1 + return format("tostring(tonumber(a%s) or a%s)",n,n) +end +local format_a=function(f) + n=n+1 + if f and f~="" then + return format("autosingle(a%s,%q)",n,f) + else + return format("autosingle(a%s)",n) + end +end +local format_A=function(f) + n=n+1 + if f and f~="" then + return format("autodouble(a%s,%q)",n,f) + else + return format("autodouble(a%s)",n) + end +end +local format_w=function(f) + n=n+1 + f=tonumber(f) + if f then + return format("nspaces[%s+a%s]",f,n) + else + return format("nspaces[a%s]",n) + end +end +local format_W=function(f) + return format("nspaces[%s]",tonumber(f) or 0) +end +local digit=patterns.digit +local period=patterns.period +local three=digit*digit*digit +local splitter=Cs ( + (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2) +) +patterns.formattednumber=splitter +function number.formatted(n,sep1,sep2) + local s=type(s)=="string" and n or format("%0.2f",n) + if sep1==true then + return lpegmatch(splitter,s,1,".",",") + elseif sep1=="." then + return lpegmatch(splitter,s,1,sep1,sep2 or ",") + elseif sep1=="," then + return lpegmatch(splitter,s,1,sep1,sep2 or ".") + else + return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") + end +end +local format_m=function(f) + n=n+1 + if not f or f=="" then + f="," + end + return format([[formattednumber(a%s,%q,".")]],n,f) +end +local format_M=function(f) + n=n+1 + if not f or f=="" then + f="." + end + return format([[formattednumber(a%s,%q,",")]],n,f) +end +local format_z=function(f) + n=n+(tonumber(f) or 1) + return "''" +end +local format_rest=function(s) + return format("%q",s) +end +local format_extension=function(extensions,f,name) + local extension=extensions[name] or "tostring(%s)" + local f=tonumber(f) or 1 + if f==0 then + return extension + elseif f==1 then + n=n+1 + local a="a"..n + return format(extension,a,a) + elseif f<0 then + local a="a"..(n+f+1) + return format(extension,a,a) + else + local t={} + for i=1,f do + n=n+1 + t[#t+1]="a"..n + end + return format(extension,unpack(t)) + end +end +local builder=Cs { "start", + start=( + ( + P("%")/""*( + V("!") ++V("s")+V("q")+V("i")+V("d")+V("f")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") ++V("c")+V("C")+V("S") ++V("Q") ++V("N") ++V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w") ++V("W") ++V("a") ++V("A") ++V("j")+V("J") ++V("m")+V("M") ++V("z") ++V("*") + )+V("*") + )*(P(-1)+Carg(1)) + )^0, + ["s"]=(prefix_any*P("s"))/format_s, + ["q"]=(prefix_any*P("q"))/format_q, + ["i"]=(prefix_any*P("i"))/format_i, + ["d"]=(prefix_any*P("d"))/format_d, + ["f"]=(prefix_any*P("f"))/format_f, + ["g"]=(prefix_any*P("g"))/format_g, + ["G"]=(prefix_any*P("G"))/format_G, + ["e"]=(prefix_any*P("e"))/format_e, + ["E"]=(prefix_any*P("E"))/format_E, + ["x"]=(prefix_any*P("x"))/format_x, + ["X"]=(prefix_any*P("X"))/format_X, + ["o"]=(prefix_any*P("o"))/format_o, + ["S"]=(prefix_any*P("S"))/format_S, + ["Q"]=(prefix_any*P("Q"))/format_S, + ["N"]=(prefix_any*P("N"))/format_N, + ["c"]=(prefix_any*P("c"))/format_c, + ["C"]=(prefix_any*P("C"))/format_C, + ["r"]=(prefix_any*P("r"))/format_r, + ["h"]=(prefix_any*P("h"))/format_h, + ["H"]=(prefix_any*P("H"))/format_H, + ["u"]=(prefix_any*P("u"))/format_u, + ["U"]=(prefix_any*P("U"))/format_U, + ["p"]=(prefix_any*P("p"))/format_p, + ["b"]=(prefix_any*P("b"))/format_b, + ["t"]=(prefix_tab*P("t"))/format_t, + ["T"]=(prefix_tab*P("T"))/format_T, + ["l"]=(prefix_any*P("l"))/format_l, + ["L"]=(prefix_any*P("L"))/format_L, + ["I"]=(prefix_any*P("I"))/format_I, + ["w"]=(prefix_any*P("w"))/format_w, + ["W"]=(prefix_any*P("W"))/format_W, + ["j"]=(prefix_any*P("j"))/format_j, + ["J"]=(prefix_any*P("J"))/format_J, + ["m"]=(prefix_tab*P("m"))/format_m, + ["M"]=(prefix_tab*P("M"))/format_M, + ["z"]=(prefix_any*P("z"))/format_z, + ["a"]=(prefix_any*P("a"))/format_a, + ["A"]=(prefix_any*P("A"))/format_A, + ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, + ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, +} +local direct=Cs ( + P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]] +) +local function make(t,str) + local f + local p + local p=lpegmatch(direct,str) + if p then + f=loadstripped(p)() + else + n=0 + p=lpegmatch(builder,str,1,"..",t._extensions_) + if n>0 then + p=format(template,preamble,t._preamble_,arguments[n],p) + f=loadstripped(p,t._environment_)() + else + f=function() return str end + end + end + t[str]=f + return f +end +local function use(t,fmt,...) + return t[fmt](...) +end +strings.formatters={} +function strings.formatters.new() + local e={} + for k,v in next,environment do + e[k]=v + end + local t={ _extensions_={},_preamble_="",_environment_=e,_type_="formatter" } + setmetatable(t,{ __index=make,__call=use }) + return t +end +local formatters=strings.formatters.new() +string.formatters=formatters +string.formatter=function(str,...) return formatters[str](...) end +local function add(t,name,template,preamble) + if type(t)=="table" and t._type_=="formatter" then + t._extensions_[name]=template or "%s" + if type(preamble)=="string" then + t._preamble_=preamble.."\n"..t._preamble_ + elseif type(preamble)=="table" then + for k,v in next,preamble do + t._environment_[k]=v + end + end + end +end +strings.formatters.add=add +patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"""+P(1))^0) +patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) +patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) +patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) +add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) +add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) +add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luat-basics-gen']={ + version=1.100, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +local dummyfunction=function() +end +local dummyreporter=function(c) + return function(...) + (texio.reporter or texio.write_nl)(c.." : "..string.formatters(...)) + end +end +statistics={ + register=dummyfunction, + starttiming=dummyfunction, + stoptiming=dummyfunction, + elapsedtime=nil, +} +directives={ + register=dummyfunction, + enable=dummyfunction, + disable=dummyfunction, +} +trackers={ + register=dummyfunction, + enable=dummyfunction, + disable=dummyfunction, +} +experiments={ + register=dummyfunction, + enable=dummyfunction, + disable=dummyfunction, +} +storage={ + register=dummyfunction, + shared={}, +} +logs={ + new=dummyreporter, + reporter=dummyreporter, + messenger=dummyreporter, + report=dummyfunction, +} +callbacks={ + register=function(n,f) return callback.register(n,f) end, +} +utilities={ + storage={ + allocate=function(t) return t or {} end, + mark=function(t) return t or {} end, + }, +} +characters=characters or { + data={} +} +texconfig.kpse_init=true +resolvers=resolvers or {} +local remapper={ + otf="opentype fonts", + ttf="truetype fonts", + ttc="truetype fonts", + dfont="truetype fonts", + cid="cid maps", + cidmap="cid maps", + fea="font feature files", + pfa="type1 fonts", + pfb="type1 fonts", + afm="afm", +} +function resolvers.findfile(name,fileformat) + name=string.gsub(name,"\\","/") + if not fileformat or fileformat=="" then + fileformat=file.suffix(name) + if fileformat=="" then + fileformat="tex" + end + end + fileformat=string.lower(fileformat) + fileformat=remapper[fileformat] or fileformat + local found=kpse.find_file(name,fileformat) + if not found or found=="" then + found=kpse.find_file(name,"other text files") + end + return found +end +resolvers.findbinfile=resolvers.findfile +function resolvers.loadbinfile(filename,filetype) + local data=io.loaddata(filename) + return true,data,#data +end +function resolvers.resolve(s) + return s +end +function resolvers.unresolve(s) + return s +end +caches={} +local writable=nil +local readables={} +local usingjit=jit +if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then + caches.namespace='generic' +end +do + local cachepaths=kpse.expand_var('$TEXMFCACHE') or "" + if cachepaths=="" or cachepaths=="$TEXMFCACHE" then + cachepaths=kpse.expand_var('$TEXMFVAR') or "" + end + if cachepaths=="" or cachepaths=="$TEXMFVAR" then + cachepaths=kpse.expand_var('$VARTEXMF') or "" + end + if cachepaths=="" then + local fallbacks={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" } + for i=1,#fallbacks do + cachepaths=os.getenv(fallbacks[i]) or "" + if cachepath~="" and lfs.isdir(cachepath) then + break + end + end + end + if cachepaths=="" then + cachepaths="." + end + cachepaths=string.split(cachepaths,os.type=="windows" and ";" or ":") + for i=1,#cachepaths do + local cachepath=cachepaths[i] + if not lfs.isdir(cachepath) then + lfs.mkdirs(cachepath) + if lfs.isdir(cachepath) then + texio.write(string.format("(created cache path: %s)",cachepath)) + end + end + if file.is_writable(cachepath) then + writable=file.join(cachepath,"luatex-cache") + lfs.mkdir(writable) + writable=file.join(writable,caches.namespace) + lfs.mkdir(writable) + break + end + end + for i=1,#cachepaths do + if file.is_readable(cachepaths[i]) then + readables[#readables+1]=file.join(cachepaths[i],"luatex-cache",caches.namespace) + end + end + if not writable then + texio.write_nl("quiting: fix your writable cache path") + os.exit() + elseif #readables==0 then + texio.write_nl("quiting: fix your readable cache path") + os.exit() + elseif #readables==1 and readables[1]==writable then + texio.write(string.format("(using cache: %s)",writable)) + else + texio.write(string.format("(using write cache: %s)",writable)) + texio.write(string.format("(using read cache: %s)",table.concat(readables," "))) + end +end +function caches.getwritablepath(category,subcategory) + local path=file.join(writable,category) + lfs.mkdir(path) + path=file.join(path,subcategory) + lfs.mkdir(path) + return path +end +function caches.getreadablepaths(category,subcategory) + local t={} + for i=1,#readables do + t[i]=file.join(readables[i],category,subcategory) + end + return t +end +local function makefullname(path,name) + if path and path~="" then + return file.addsuffix(file.join(path,name),"lua"),file.addsuffix(file.join(path,name),usingjit and "lub" or "luc") + end +end +function caches.is_writable(path,name) + local fullname=makefullname(path,name) + return fullname and file.is_writable(fullname) +end +function caches.loaddata(paths,name) + for i=1,#paths do + local data=false + local luaname,lucname=makefullname(paths[i],name) + if lucname and lfs.isfile(lucname) then + texio.write(string.format("(load luc: %s)",lucname)) + data=loadfile(lucname) + if data then + data=data() + end + if data then + return data + else + texio.write(string.format("(loading failed: %s)",lucname)) + end + end + if luaname and lfs.isfile(luaname) then + texio.write(string.format("(load lua: %s)",luaname)) + data=loadfile(luaname) + if data then + data=data() + end + if data then + return data + end + end + end +end +function caches.savedata(path,name,data) + local luaname,lucname=makefullname(path,name) + if luaname then + texio.write(string.format("(save: %s)",luaname)) + table.tofile(luaname,data,true) + if lucname and type(caches.compile)=="function" then + os.remove(lucname) + texio.write(string.format("(save: %s)",lucname)) + caches.compile(data,luaname,lucname) + end + end +end +function caches.compile(data,luaname,lucname) + local d=io.loaddata(luaname) + if not d or d=="" then + d=table.serialize(data,true) + end + if d and d~="" then + local f=io.open(lucname,'wb') + if f then + local s=loadstring(d) + if s then + f:write(string.dump(s,true)) + end + f:close() + end + end +end +function table.setmetatableindex(t,f) + setmetatable(t,{ __index=f }) +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['data-con']={ + version=1.100, + comment="companion to luat-lib.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local format,lower,gsub=string.format,string.lower,string.gsub +local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end) +local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end) +local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end) +containers=containers or {} +local containers=containers +containers.usecache=true +local report_containers=logs.reporter("resolvers","containers") +local allocated={} +local mt={ + __index=function(t,k) + if k=="writable" then + local writable=caches.getwritablepath(t.category,t.subcategory) or { "." } + t.writable=writable + return writable + elseif k=="readables" then + local readables=caches.getreadablepaths(t.category,t.subcategory) or { "." } + t.readables=readables + return readables + end + end, + __storage__=true +} +function containers.define(category,subcategory,version,enabled) + if category and subcategory then + local c=allocated[category] + if not c then + c={} + allocated[category]=c + end + local s=c[subcategory] + if not s then + s={ + category=category, + subcategory=subcategory, + storage={}, + enabled=enabled, + version=version or math.pi, + trace=false, + } + setmetatable(s,mt) + c[subcategory]=s + end + return s + end +end +function containers.is_usable(container,name) + return container.enabled and caches and caches.is_writable(container.writable,name) +end +function containers.is_valid(container,name) + if name and name~="" then + local storage=container.storage[name] + return storage and storage.cache_version==container.version + else + return false + end +end +function containers.read(container,name) + local storage=container.storage + local stored=storage[name] + if not stored and container.enabled and caches and containers.usecache then + stored=caches.loaddata(container.readables,name) + if stored and stored.cache_version==container.version then + if trace_cache or trace_containers then + report_containers("action %a, category %a, name %a","load",container.subcategory,name) + end + else + stored=nil + end + storage[name]=stored + elseif stored then + if trace_cache or trace_containers then + report_containers("action %a, category %a, name %a","reuse",container.subcategory,name) + end + end + return stored +end +function containers.write(container,name,data) + if data then + data.cache_version=container.version + if container.enabled and caches then + local unique,shared=data.unique,data.shared + data.unique,data.shared=nil,nil + caches.savedata(container.writable,name,data) + if trace_cache or trace_containers then + report_containers("action %a, category %a, name %a","save",container.subcategory,name) + end + data.unique,data.shared=unique,shared + end + if trace_cache or trace_containers then + report_containers("action %a, category %a, name %a","store",container.subcategory,name) + end + container.storage[name]=data + end + return data +end +function containers.content(container,name) + return container.storage[name] +end +function containers.cleanname(name) + return (gsub(lower(name),"[^%w\128-\255]+","-")) +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-fonts-nod']={ + version=1.001, + comment="companion to luatex-fonts.lua", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +if tex.attribute[0]~=0 then + texio.write_nl("log","!") + texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") + texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") + texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.") + texio.write_nl("log","!") + tex.attribute[0]=0 +end +attributes=attributes or {} +attributes.unsetvalue=-0x7FFFFFFF +local numbers,last={},127 +attributes.private=attributes.private or function(name) + local number=numbers[name] + if not number then + if last<255 then + last=last+1 + end + number=last + numbers[name]=number + end + return number +end +nodes={} +nodes.pool={} +nodes.handlers={} +local nodecodes={} for k,v in next,node.types () do nodecodes[string.gsub(v,"_","")]=k end +local whatcodes={} for k,v in next,node.whatsits() do whatcodes[string.gsub(v,"_","")]=k end +local glyphcodes={ [0]="character","glyph","ligature","ghost","left","right" } +local disccodes={ [0]="discretionary","explicit","automatic","regular","first","second" } +nodes.nodecodes=nodecodes +nodes.whatcodes=whatcodes +nodes.whatsitcodes=whatcodes +nodes.glyphcodes=glyphcodes +nodes.disccodes=disccodes +local free_node=node.free +local remove_node=node.remove +local new_node=node.new +local traverse_id=node.traverse_id +local math_code=nodecodes.math +nodes.handlers.protectglyphs=node.protect_glyphs +nodes.handlers.unprotectglyphs=node.unprotect_glyphs +function nodes.remove(head,current,free_too) + local t=current + head,current=remove_node(head,current) + if t then + if free_too then + free_node(t) + t=nil + else + t.next,t.prev=nil,nil + end + end + return head,current,t +end +function nodes.delete(head,current) + return nodes.remove(head,current,true) +end +function nodes.pool.kern(k) + local n=new_node("kern",1) + n.kern=k + return n +end +local getfield=node.getfield or function(n,tag) return n[tag] end +local setfield=node.setfield or function(n,tag,value) n[tag]=value end +nodes.getfield=getfield +nodes.setfield=setfield +nodes.getattr=getfield +nodes.setattr=setfield +if node.getid then nodes.getid=node.getid else function nodes.getid (n) return getfield(n,"id") end end +if node.getsubtype then nodes.getsubtype=node.getsubtype else function nodes.getsubtype(n) return getfield(n,"subtype") end end +if node.getnext then nodes.getnext=node.getnext else function nodes.getnext (n) return getfield(n,"next") end end +if node.getprev then nodes.getprev=node.getprev else function nodes.getprev (n) return getfield(n,"prev") end end +if node.getchar then nodes.getchar=node.getchar else function nodes.getchar (n) return getfield(n,"char") end end +if node.getfont then nodes.getfont=node.getfont else function nodes.getfont (n) return getfield(n,"font") end end +if node.getlist then nodes.getlist=node.getlist else function nodes.getlist (n) return getfield(n,"list") end end +function nodes.tonut (n) return n end +function nodes.tonode(n) return n end +nodes.tostring=node.tostring or tostring +nodes.copy=node.copy +nodes.copy_list=node.copy_list +nodes.delete=node.delete +nodes.dimensions=node.dimensions +nodes.end_of_math=node.end_of_math +nodes.flush_list=node.flush_list +nodes.flush_node=node.flush_node +nodes.free=node.free +nodes.insert_after=node.insert_after +nodes.insert_before=node.insert_before +nodes.hpack=node.hpack +nodes.new=node.new +nodes.tail=node.tail +nodes.traverse=node.traverse +nodes.traverse_id=node.traverse_id +nodes.slide=node.slide +nodes.vpack=node.vpack +nodes.first_glyph=node.first_glyph +nodes.first_character=node.first_character +nodes.has_glyph=node.has_glyph or node.first_glyph +nodes.current_attr=node.current_attr +nodes.do_ligature_n=node.do_ligature_n +nodes.has_field=node.has_field +nodes.last_node=node.last_node +nodes.usedlist=node.usedlist +nodes.protrusion_skippable=node.protrusion_skippable +nodes.write=node.write +nodes.has_attribute=node.has_attribute +nodes.set_attribute=node.set_attribute +nodes.unset_attribute=node.unset_attribute +nodes.protect_glyphs=node.protect_glyphs +nodes.unprotect_glyphs=node.unprotect_glyphs +nodes.kerning=node.kerning +nodes.ligaturing=node.ligaturing +nodes.mlist_to_hlist=node.mlist_to_hlist +nodes.nuts=nodes + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-ini']={ + 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 allocate=utilities.storage.allocate +local report_defining=logs.reporter("fonts","defining") +fonts=fonts or {} +local fonts=fonts +fonts.hashes={ identifiers=allocate() } +fonts.tables=fonts.tables or {} +fonts.helpers=fonts.helpers or {} +fonts.tracers=fonts.tracers or {} +fonts.specifiers=fonts.specifiers or {} +fonts.analyzers={} +fonts.readers={} +fonts.definers={ methods={} } +fonts.loggers={ register=function() end } +fontloader.totable=fontloader.to_table + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-con']={ + 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 next,tostring,rawget=next,tostring,rawget +local format,match,lower,gsub=string.format,string.match,string.lower,string.gsub +local utfbyte=utf.byte +local sort,insert,concat,sortedkeys,serialize,fastcopy=table.sort,table.insert,table.concat,table.sortedkeys,table.serialize,table.fastcopy +local derivetable=table.derive +local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) +local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end) +local report_defining=logs.reporter("fonts","defining") +local fonts=fonts +local constructors=fonts.constructors or {} +fonts.constructors=constructors +local handlers=fonts.handlers or {} +fonts.handlers=handlers +local allocate=utilities.storage.allocate +local setmetatableindex=table.setmetatableindex +constructors.dontembed=allocate() +constructors.autocleanup=true +constructors.namemode="fullpath" +constructors.version=1.01 +constructors.cache=containers.define("fonts","constructors",constructors.version,false) +constructors.privateoffset=0xF0000 +constructors.keys={ + properties={ + encodingbytes="number", + embedding="number", + cidinfo={}, + format="string", + fontname="string", + fullname="string", + filename="filename", + psname="string", + name="string", + virtualized="boolean", + hasitalics="boolean", + autoitalicamount="basepoints", + nostackmath="boolean", + noglyphnames="boolean", + mode="string", + hasmath="boolean", + mathitalics="boolean", + textitalics="boolean", + finalized="boolean", + }, + parameters={ + mathsize="number", + scriptpercentage="float", + scriptscriptpercentage="float", + units="cardinal", + designsize="scaledpoints", + expansion={ + stretch="integerscale", + shrink="integerscale", + step="integerscale", + auto="boolean", + }, + protrusion={ + auto="boolean", + }, + slantfactor="float", + extendfactor="float", + factor="float", + hfactor="float", + vfactor="float", + size="scaledpoints", + units="scaledpoints", + scaledpoints="scaledpoints", + slantperpoint="scaledpoints", + spacing={ + width="scaledpoints", + stretch="scaledpoints", + shrink="scaledpoints", + extra="scaledpoints", + }, + xheight="scaledpoints", + quad="scaledpoints", + ascender="scaledpoints", + descender="scaledpoints", + synonyms={ + space="spacing.width", + spacestretch="spacing.stretch", + spaceshrink="spacing.shrink", + extraspace="spacing.extra", + x_height="xheight", + space_stretch="spacing.stretch", + space_shrink="spacing.shrink", + extra_space="spacing.extra", + em="quad", + ex="xheight", + slant="slantperpoint", + }, + }, + description={ + width="basepoints", + height="basepoints", + depth="basepoints", + boundingbox={}, + }, + character={ + width="scaledpoints", + height="scaledpoints", + depth="scaledpoints", + italic="scaledpoints", + }, +} +local designsizes=allocate() +constructors.designsizes=designsizes +local loadedfonts=allocate() +constructors.loadedfonts=loadedfonts +local factors={ + pt=65536.0, + bp=65781.8, +} +function constructors.setfactor(f) + constructors.factor=factors[f or 'pt'] or factors.pt +end +constructors.setfactor() +function constructors.scaled(scaledpoints,designsize) + if scaledpoints<0 then + if designsize then + local factor=constructors.factor + if designsize>factor then + return (- scaledpoints/1000)*designsize + else + return (- scaledpoints/1000)*designsize*factor + end + else + return (- scaledpoints/1000)*10*factor + end + else + return scaledpoints + end +end +function constructors.cleanuptable(tfmdata) + if constructors.autocleanup and tfmdata.properties.virtualized then + for k,v in next,tfmdata.characters do + if v.commands then v.commands=nil end + end + end +end +function constructors.calculatescale(tfmdata,scaledpoints) + local parameters=tfmdata.parameters + if scaledpoints<0 then + scaledpoints=(- scaledpoints/1000)*(tfmdata.designsize or parameters.designsize) + end + return scaledpoints,scaledpoints/(parameters.units or 1000) +end +local unscaled={ + ScriptPercentScaleDown=true, + ScriptScriptPercentScaleDown=true, + RadicalDegreeBottomRaisePercent=true +} +function constructors.assignmathparameters(target,original) + local mathparameters=original.mathparameters + if mathparameters and next(mathparameters) then + local targetparameters=target.parameters + local targetproperties=target.properties + local targetmathparameters={} + local factor=targetproperties.math_is_scaled and 1 or targetparameters.factor + for name,value in next,mathparameters do + if unscaled[name] then + targetmathparameters[name]=value + else + targetmathparameters[name]=value*factor + end + end + if not targetmathparameters.FractionDelimiterSize then + targetmathparameters.FractionDelimiterSize=1.01*targetparameters.size + end + if not mathparameters.FractionDelimiterDisplayStyleSize then + targetmathparameters.FractionDelimiterDisplayStyleSize=2.40*targetparameters.size + end + target.mathparameters=targetmathparameters + end +end +function constructors.beforecopyingcharacters(target,original) +end +function constructors.aftercopyingcharacters(target,original) +end +constructors.sharefonts=false +constructors.nofsharedfonts=0 +local sharednames={} +function constructors.trytosharefont(target,tfmdata) + if constructors.sharefonts then + local characters=target.characters + local n=1 + local t={ target.psname } + local u=sortedkeys(characters) + for i=1,#u do + n=n+1;t[n]=k + n=n+1;t[n]=characters[u[i]].index or k + end + local h=md5.HEX(concat(t," ")) + local s=sharednames[h] + if s then + if trace_defining then + report_defining("font %a uses backend resources of font %a",target.fullname,s) + end + target.fullname=s + constructors.nofsharedfonts=constructors.nofsharedfonts+1 + target.properties.sharedwith=s + else + sharednames[h]=target.fullname + end + end +end +function constructors.enhanceparameters(parameters) + local xheight=parameters.x_height + local quad=parameters.quad + local space=parameters.space + local stretch=parameters.space_stretch + local shrink=parameters.space_shrink + local extra=parameters.extra_space + local slant=parameters.slant + parameters.xheight=xheight + parameters.spacestretch=stretch + parameters.spaceshrink=shrink + parameters.extraspace=extra + parameters.em=quad + parameters.ex=xheight + parameters.slantperpoint=slant + parameters.spacing={ + width=space, + stretch=stretch, + shrink=shrink, + extra=extra, + } +end +function constructors.scale(tfmdata,specification) + local target={} + if tonumber(specification) then + specification={ size=specification } + end + target.specification=specification + local scaledpoints=specification.size + local relativeid=specification.relativeid + local properties=tfmdata.properties or {} + local goodies=tfmdata.goodies or {} + local resources=tfmdata.resources or {} + local descriptions=tfmdata.descriptions or {} + local characters=tfmdata.characters or {} + local changed=tfmdata.changed or {} + local shared=tfmdata.shared or {} + local parameters=tfmdata.parameters or {} + local mathparameters=tfmdata.mathparameters or {} + local targetcharacters={} + local targetdescriptions=derivetable(descriptions) + local targetparameters=derivetable(parameters) + local targetproperties=derivetable(properties) + local targetgoodies=goodies + target.characters=targetcharacters + target.descriptions=targetdescriptions + target.parameters=targetparameters + target.properties=targetproperties + target.goodies=targetgoodies + target.shared=shared + target.resources=resources + target.unscaled=tfmdata + local mathsize=tonumber(specification.mathsize) or 0 + local textsize=tonumber(specification.textsize) or scaledpoints + local forcedsize=tonumber(parameters.mathsize ) or 0 + local extrafactor=tonumber(specification.factor ) or 1 + if (mathsize==2 or forcedsize==2) and parameters.scriptpercentage then + scaledpoints=parameters.scriptpercentage*textsize/100 + elseif (mathsize==3 or forcedsize==3) and parameters.scriptscriptpercentage then + scaledpoints=parameters.scriptscriptpercentage*textsize/100 + elseif forcedsize>1000 then + scaledpoints=forcedsize + end + targetparameters.mathsize=mathsize + targetparameters.textsize=textsize + targetparameters.forcedsize=forcedsize + targetparameters.extrafactor=extrafactor + local tounicode=resources.tounicode + local defaultwidth=resources.defaultwidth or 0 + local defaultheight=resources.defaultheight or 0 + local defaultdepth=resources.defaultdepth or 0 + local units=parameters.units or 1000 + if target.fonts then + target.fonts=fastcopy(target.fonts) + end + targetproperties.language=properties.language or "dflt" + targetproperties.script=properties.script or "dflt" + targetproperties.mode=properties.mode or "base" + local askedscaledpoints=scaledpoints + local scaledpoints,delta=constructors.calculatescale(tfmdata,scaledpoints,nil,specification) + local hdelta=delta + local vdelta=delta + target.designsize=parameters.designsize + target.units_per_em=units + local direction=properties.direction or tfmdata.direction or 0 + target.direction=direction + properties.direction=direction + target.size=scaledpoints + target.encodingbytes=properties.encodingbytes or 1 + target.embedding=properties.embedding or "subset" + target.tounicode=1 + target.cidinfo=properties.cidinfo + target.format=properties.format + local fontname=properties.fontname or tfmdata.fontname + local fullname=properties.fullname or tfmdata.fullname + local filename=properties.filename or tfmdata.filename + local psname=properties.psname or tfmdata.psname + local name=properties.name or tfmdata.name + if not psname or psname=="" then + psname=fontname or (fullname and fonts.names.cleanname(fullname)) + end + target.fontname=fontname + target.fullname=fullname + target.filename=filename + target.psname=psname + target.name=name + properties.fontname=fontname + properties.fullname=fullname + properties.filename=filename + properties.psname=psname + properties.name=name + local expansion=parameters.expansion + if expansion then + target.stretch=expansion.stretch + target.shrink=expansion.shrink + target.step=expansion.step + target.auto_expand=expansion.auto + end + local protrusion=parameters.protrusion + if protrusion then + target.auto_protrude=protrusion.auto + end + local extendfactor=parameters.extendfactor or 0 + if extendfactor~=0 and extendfactor~=1 then + hdelta=hdelta*extendfactor + target.extend=extendfactor*1000 + else + target.extend=1000 + end + local slantfactor=parameters.slantfactor or 0 + if slantfactor~=0 then + target.slant=slantfactor*1000 + else + target.slant=0 + end + targetparameters.factor=delta + targetparameters.hfactor=hdelta + targetparameters.vfactor=vdelta + targetparameters.size=scaledpoints + targetparameters.units=units + targetparameters.scaledpoints=askedscaledpoints + local isvirtual=properties.virtualized or tfmdata.type=="virtual" + local hasquality=target.auto_expand or target.auto_protrude + local hasitalics=properties.hasitalics + local autoitalicamount=properties.autoitalicamount + local stackmath=not properties.nostackmath + local nonames=properties.noglyphnames + local nodemode=properties.mode=="node" + if changed and not next(changed) then + changed=false + end + target.type=isvirtual and "virtual" or "real" + target.postprocessors=tfmdata.postprocessors + local targetslant=(parameters.slant or parameters[1] or 0)*factors.pt + local targetspace=(parameters.space or parameters[2] or 0)*hdelta + local targetspace_stretch=(parameters.space_stretch or parameters[3] or 0)*hdelta + local targetspace_shrink=(parameters.space_shrink or parameters[4] or 0)*hdelta + local targetx_height=(parameters.x_height or parameters[5] or 0)*vdelta + local targetquad=(parameters.quad or parameters[6] or 0)*hdelta + local targetextra_space=(parameters.extra_space or parameters[7] or 0)*hdelta + targetparameters.slant=targetslant + targetparameters.space=targetspace + targetparameters.space_stretch=targetspace_stretch + targetparameters.space_shrink=targetspace_shrink + targetparameters.x_height=targetx_height + targetparameters.quad=targetquad + targetparameters.extra_space=targetextra_space + local ascender=parameters.ascender + if ascender then + targetparameters.ascender=delta*ascender + end + local descender=parameters.descender + if descender then + targetparameters.descender=delta*descender + end + constructors.enhanceparameters(targetparameters) + local protrusionfactor=(targetquad~=0 and 1000/targetquad) or 0 + local scaledwidth=defaultwidth*hdelta + local scaledheight=defaultheight*vdelta + local scaleddepth=defaultdepth*vdelta + local hasmath=(properties.hasmath or next(mathparameters)) and true + if hasmath then + constructors.assignmathparameters(target,tfmdata) + properties.hasmath=true + target.nomath=false + target.MathConstants=target.mathparameters + else + properties.hasmath=false + target.nomath=true + target.mathparameters=nil + end + local italickey="italic" + local useitalics=true + if hasmath then + autoitalicamount=false + elseif properties.textitalics then + italickey="italic_correction" + useitalics=false + if properties.delaytextitalics then + autoitalicamount=false + end + end + if trace_defining then + report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a", + name,fullname,filename,hdelta,vdelta, + hasmath and "enabled" or "disabled",useitalics and "enabled" or "disabled") + end + constructors.beforecopyingcharacters(target,tfmdata) + local sharedkerns={} + for unicode,character in next,characters do + local chr,description,index,touni + if changed then + local c=changed[unicode] + if c then + description=descriptions[c] or descriptions[unicode] or character + character=characters[c] or character + index=description.index or c + if tounicode then + touni=tounicode[index] + if not touni then + local d=descriptions[unicode] or characters[unicode] + local i=d.index or unicode + touni=tounicode[i] + end + end + else + description=descriptions[unicode] or character + index=description.index or unicode + if tounicode then + touni=tounicode[index] + end + end + else + description=descriptions[unicode] or character + index=description.index or unicode + if tounicode then + touni=tounicode[index] + end + end + local width=description.width + local height=description.height + local depth=description.depth + if width then width=hdelta*width else width=scaledwidth end + if height then height=vdelta*height else height=scaledheight end + if depth and depth~=0 then + depth=delta*depth + if nonames then + chr={ + index=index, + height=height, + depth=depth, + width=width, + } + else + chr={ + name=description.name, + index=index, + height=height, + depth=depth, + width=width, + } + end + else + if nonames then + chr={ + index=index, + height=height, + width=width, + } + else + chr={ + name=description.name, + index=index, + height=height, + width=width, + } + end + end + if touni then + chr.tounicode=touni + end + if hasquality then + local ve=character.expansion_factor + if ve then + chr.expansion_factor=ve*1000 + end + local vl=character.left_protruding + if vl then + chr.left_protruding=protrusionfactor*width*vl + end + local vr=character.right_protruding + if vr then + chr.right_protruding=protrusionfactor*width*vr + end + end + if autoitalicamount then + local vi=description.italic + if not vi then + local vi=description.boundingbox[3]-description.width+autoitalicamount + if vi>0 then + chr[italickey]=vi*hdelta + end + elseif vi~=0 then + chr[italickey]=vi*hdelta + end + elseif hasitalics then + local vi=description.italic + if vi and vi~=0 then + chr[italickey]=vi*hdelta + end + end + if hasmath then + local vn=character.next + if vn then + chr.next=vn + else + local vv=character.vert_variants + if vv then + local t={} + for i=1,#vv do + local vvi=vv[i] + t[i]={ + ["start"]=(vvi["start"] or 0)*vdelta, + ["end"]=(vvi["end"] or 0)*vdelta, + ["advance"]=(vvi["advance"] or 0)*vdelta, + ["extender"]=vvi["extender"], + ["glyph"]=vvi["glyph"], + } + end + chr.vert_variants=t + else + local hv=character.horiz_variants + if hv then + local t={} + for i=1,#hv do + local hvi=hv[i] + t[i]={ + ["start"]=(hvi["start"] or 0)*hdelta, + ["end"]=(hvi["end"] or 0)*hdelta, + ["advance"]=(hvi["advance"] or 0)*hdelta, + ["extender"]=hvi["extender"], + ["glyph"]=hvi["glyph"], + } + end + chr.horiz_variants=t + end + end + end + local va=character.top_accent + if va then + chr.top_accent=vdelta*va + end + if stackmath then + local mk=character.mathkerns + if mk then + local kerns={} + local v=mk.top_right if v then local k={} for i=1,#v do local vi=v[i] + k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } + end kerns.top_right=k end + local v=mk.top_left if v then local k={} for i=1,#v do local vi=v[i] + k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } + end kerns.top_left=k end + local v=mk.bottom_left if v then local k={} for i=1,#v do local vi=v[i] + k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } + end kerns.bottom_left=k end + local v=mk.bottom_right if v then local k={} for i=1,#v do local vi=v[i] + k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern } + end kerns.bottom_right=k end + chr.mathkern=kerns + end + end + end + if not nodemode then + local vk=character.kerns + if vk then + local s=sharedkerns[vk] + if not s then + s={} + for k,v in next,vk do s[k]=v*hdelta end + sharedkerns[vk]=s + end + chr.kerns=s + end + local vl=character.ligatures + if vl then + if true then + chr.ligatures=vl + else + local tt={} + for i,l in next,vl do + tt[i]=l + end + chr.ligatures=tt + end + end + end + if isvirtual then + local vc=character.commands + if vc then + local ok=false + for i=1,#vc do + local key=vc[i][1] + if key=="right" or key=="down" then + ok=true + break + end + end + if ok then + local tt={} + for i=1,#vc do + local ivc=vc[i] + local key=ivc[1] + if key=="right" then + tt[i]={ key,ivc[2]*hdelta } + elseif key=="down" then + tt[i]={ key,ivc[2]*vdelta } + elseif key=="rule" then + tt[i]={ key,ivc[2]*vdelta,ivc[3]*hdelta } + else + tt[i]=ivc + end + end + chr.commands=tt + else + chr.commands=vc + end + chr.index=nil + end + end + targetcharacters[unicode]=chr + end + constructors.aftercopyingcharacters(target,tfmdata) + constructors.trytosharefont(target,tfmdata) + return target +end +function constructors.finalize(tfmdata) + if tfmdata.properties and tfmdata.properties.finalized then + return + end + if not tfmdata.characters then + return nil + end + if not tfmdata.goodies then + tfmdata.goodies={} + end + local parameters=tfmdata.parameters + if not parameters then + return nil + end + if not parameters.expansion then + parameters.expansion={ + stretch=tfmdata.stretch or 0, + shrink=tfmdata.shrink or 0, + step=tfmdata.step or 0, + auto=tfmdata.auto_expand or false, + } + end + if not parameters.protrusion then + parameters.protrusion={ + auto=auto_protrude + } + end + if not parameters.size then + parameters.size=tfmdata.size + end + if not parameters.extendfactor then + parameters.extendfactor=tfmdata.extend or 0 + end + if not parameters.slantfactor then + parameters.slantfactor=tfmdata.slant or 0 + end + if not parameters.designsize then + parameters.designsize=tfmdata.designsize or (factors.pt*10) + end + if not parameters.units then + parameters.units=tfmdata.units_per_em or 1000 + end + if not tfmdata.descriptions then + local descriptions={} + setmetatableindex(descriptions,function(t,k) local v={} t[k]=v return v end) + tfmdata.descriptions=descriptions + end + local properties=tfmdata.properties + if not properties then + properties={} + tfmdata.properties=properties + end + if not properties.virtualized then + properties.virtualized=tfmdata.type=="virtual" + end + if not tfmdata.properties then + tfmdata.properties={ + fontname=tfmdata.fontname, + filename=tfmdata.filename, + fullname=tfmdata.fullname, + name=tfmdata.name, + psname=tfmdata.psname, + encodingbytes=tfmdata.encodingbytes or 1, + embedding=tfmdata.embedding or "subset", + tounicode=tfmdata.tounicode or 1, + cidinfo=tfmdata.cidinfo or nil, + format=tfmdata.format or "type1", + direction=tfmdata.direction or 0, + } + end + if not tfmdata.resources then + tfmdata.resources={} + end + if not tfmdata.shared then + tfmdata.shared={} + end + if not properties.hasmath then + properties.hasmath=not tfmdata.nomath + end + tfmdata.MathConstants=nil + tfmdata.postprocessors=nil + tfmdata.fontname=nil + tfmdata.filename=nil + tfmdata.fullname=nil + tfmdata.name=nil + tfmdata.psname=nil + tfmdata.encodingbytes=nil + tfmdata.embedding=nil + tfmdata.tounicode=nil + tfmdata.cidinfo=nil + tfmdata.format=nil + tfmdata.direction=nil + tfmdata.type=nil + tfmdata.nomath=nil + tfmdata.designsize=nil + tfmdata.size=nil + tfmdata.stretch=nil + tfmdata.shrink=nil + tfmdata.step=nil + tfmdata.auto_expand=nil + tfmdata.auto_protrude=nil + tfmdata.extend=nil + tfmdata.slant=nil + tfmdata.units_per_em=nil + properties.finalized=true + return tfmdata +end +local hashmethods={} +constructors.hashmethods=hashmethods +function constructors.hashfeatures(specification) + local features=specification.features + if features then + local t,tn={},0 + for category,list in next,features do + if next(list) then + local hasher=hashmethods[category] + if hasher then + local hash=hasher(list) + if hash then + tn=tn+1 + t[tn]=category..":"..hash + end + end + end + end + if tn>0 then + return concat(t," & ") + end + end + return "unknown" +end +hashmethods.normal=function(list) + local s={} + local n=0 + for k,v in next,list do + if not k then + elseif k=="number" or k=="features" then + else + n=n+1 + s[n]=k + end + end + if n>0 then + sort(s) + for i=1,n do + local k=s[i] + s[i]=k..'='..tostring(list[k]) + end + return concat(s,"+") + end +end +function constructors.hashinstance(specification,force) + local hash,size,fallbacks=specification.hash,specification.size,specification.fallbacks + if force or not hash then + hash=constructors.hashfeatures(specification) + specification.hash=hash + end + if size<1000 and designsizes[hash] then + size=math.round(constructors.scaled(size,designsizes[hash])) + specification.size=size + end + if fallbacks then + return hash..' @ '..tostring(size)..' @ '..fallbacks + else + return hash..' @ '..tostring(size) + end +end +function constructors.setname(tfmdata,specification) + if constructors.namemode=="specification" then + local specname=specification.specification + if specname then + tfmdata.properties.name=specname + if trace_defining then + report_otf("overloaded fontname %a",specname) + end + end + end +end +function constructors.checkedfilename(data) + local foundfilename=data.foundfilename + if not foundfilename then + local askedfilename=data.filename or "" + if askedfilename~="" then + askedfilename=resolvers.resolve(askedfilename) + foundfilename=resolvers.findbinfile(askedfilename,"") or "" + if foundfilename=="" then + report_defining("source file %a is not found",askedfilename) + foundfilename=resolvers.findbinfile(file.basename(askedfilename),"") or "" + if foundfilename~="" then + report_defining("using source file %a due to cache mismatch",foundfilename) + end + end + end + data.foundfilename=foundfilename + end + return foundfilename +end +local formats=allocate() +fonts.formats=formats +setmetatableindex(formats,function(t,k) + local l=lower(k) + if rawget(t,k) then + t[k]=l + return l + end + return rawget(t,file.suffix(l)) +end) +local locations={} +local function setindeed(mode,target,group,name,action,position) + local t=target[mode] + if not t then + report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode) + os.exit() + elseif position then + insert(t,position,{ name=name,action=action }) + else + for i=1,#t do + local ti=t[i] + if ti.name==name then + ti.action=action + return + end + end + insert(t,{ name=name,action=action }) + end +end +local function set(group,name,target,source) + target=target[group] + if not target then + report_defining("fatal target error in setting feature %a, group %a",name,group) + os.exit() + end + local source=source[group] + if not source then + report_defining("fatal source error in setting feature %a, group %a",name,group) + os.exit() + end + local node=source.node + local base=source.base + local position=source.position + if node then + setindeed("node",target,group,name,node,position) + end + if base then + setindeed("base",target,group,name,base,position) + end +end +local function register(where,specification) + local name=specification.name + if name and name~="" then + local default=specification.default + local description=specification.description + local initializers=specification.initializers + local processors=specification.processors + local manipulators=specification.manipulators + local modechecker=specification.modechecker + if default then + where.defaults[name]=default + end + if description and description~="" then + where.descriptions[name]=description + end + if initializers then + set('initializers',name,where,specification) + end + if processors then + set('processors',name,where,specification) + end + if manipulators then + set('manipulators',name,where,specification) + end + if modechecker then + where.modechecker=modechecker + end + end +end +constructors.registerfeature=register +function constructors.getfeatureaction(what,where,mode,name) + what=handlers[what].features + if what then + where=what[where] + if where then + mode=where[mode] + if mode then + for i=1,#mode do + local m=mode[i] + if m.name==name then + return m.action + end + end + end + end + end +end +function constructors.newhandler(what) + local handler=handlers[what] + if not handler then + handler={} + handlers[what]=handler + end + return handler +end +function constructors.newfeatures(what) + local handler=handlers[what] + local features=handler.features + if not features then + local tables=handler.tables + local statistics=handler.statistics + features=allocate { + defaults={}, + descriptions=tables and tables.features or {}, + used=statistics and statistics.usedfeatures or {}, + initializers={ base={},node={} }, + processors={ base={},node={} }, + manipulators={ base={},node={} }, + } + features.register=function(specification) return register(features,specification) end + handler.features=features + end + return features +end +function constructors.checkedfeatures(what,features) + local defaults=handlers[what].features.defaults + if features and next(features) then + features=fastcopy(features) + for key,value in next,defaults do + if features[key]==nil then + features[key]=value + end + end + return features + else + return fastcopy(defaults) + end +end +function constructors.initializefeatures(what,tfmdata,features,trace,report) + if features and next(features) then + local properties=tfmdata.properties or {} + local whathandler=handlers[what] + local whatfeatures=whathandler.features + local whatinitializers=whatfeatures.initializers + local whatmodechecker=whatfeatures.modechecker + local mode=properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base" + properties.mode=mode + features.mode=mode + local done={} + while true do + local redo=false + local initializers=whatfeatures.initializers[mode] + if initializers then + for i=1,#initializers do + local step=initializers[i] + local feature=step.name + local value=features[feature] + if not value then + elseif done[feature] then + else + local action=step.action + if trace then + report("initializing feature %a to %a for mode %a for font %a",feature, + value,mode,tfmdata.properties.fullname) + end + action(tfmdata,value,features) + if mode~=properties.mode or mode~=features.mode then + if whatmodechecker then + properties.mode=whatmodechecker(tfmdata,features,properties.mode) + features.mode=properties.mode + end + if mode~=properties.mode then + mode=properties.mode + redo=true + end + end + done[feature]=true + end + if redo then + break + end + end + if not redo then + break + end + else + break + end + end + properties.mode=mode + return true + else + return false + end +end +function constructors.collectprocessors(what,tfmdata,features,trace,report) + local processes,nofprocesses={},0 + if features and next(features) then + local properties=tfmdata.properties + local whathandler=handlers[what] + local whatfeatures=whathandler.features + local whatprocessors=whatfeatures.processors + local mode=properties.mode + local processors=whatprocessors[mode] + if processors then + for i=1,#processors do + local step=processors[i] + local feature=step.name + if features[feature] then + local action=step.action + if trace then + report("installing feature processor %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname) + end + if action then + nofprocesses=nofprocesses+1 + processes[nofprocesses]=action + end + end + end + elseif trace then + report("no feature processors for mode %a for font %a",mode,properties.fullname) + end + end + return processes +end +function constructors.applymanipulators(what,tfmdata,features,trace,report) + if features and next(features) then + local properties=tfmdata.properties + local whathandler=handlers[what] + local whatfeatures=whathandler.features + local whatmanipulators=whatfeatures.manipulators + local mode=properties.mode + local manipulators=whatmanipulators[mode] + if manipulators then + for i=1,#manipulators do + local step=manipulators[i] + local feature=step.name + local value=features[feature] + if value then + local action=step.action + if trace then + report("applying feature manipulator %a for mode %a for font %a",feature,mode,properties.fullname) + end + if action then + action(tfmdata,feature,value) + end + end + end + end + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-font-enc']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +local fonts=fonts +fonts.encodings={} +fonts.encodings.agl={} +setmetatable(fonts.encodings.agl,{ __index=function(t,k) + if k=="unicodes" then + texio.write(" ") + local unicodes=dofile(resolvers.findfile("font-age.lua")) + fonts.encodings.agl={ unicodes=unicodes } + return unicodes + else + return nil + end +end }) + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-cid']={ + version=1.001, + comment="companion to font-otf.lua (cidmaps)", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local format,match,lower=string.format,string.match,string.lower +local tonumber=tonumber +local P,S,R,C,V,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.match +local fonts,logs,trackers=fonts,logs,trackers +local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) +local report_otf=logs.reporter("fonts","otf loading") +local cid={} +fonts.cid=cid +local cidmap={} +local cidmax=10 +local number=C(R("09","af","AF")^1) +local space=S(" \n\r\t") +local spaces=space^0 +local period=P(".") +local periods=period*period +local name=P("/")*C((1-space)^1) +local unicodes,names={},{} +local function do_one(a,b) + unicodes[tonumber(a)]=tonumber(b,16) +end +local function do_range(a,b,c) + c=tonumber(c,16) + for i=tonumber(a),tonumber(b) do + unicodes[i]=c + c=c+1 + end +end +local function do_name(a,b) + names[tonumber(a)]=b +end +local grammar=P { "start", + start=number*spaces*number*V("series"), + series=(spaces*(V("one")+V("range")+V("named")))^1, + one=(number*spaces*number)/do_one, + range=(number*periods*number*spaces*number)/do_range, + named=(number*spaces*name)/do_name +} +local function loadcidfile(filename) + local data=io.loaddata(filename) + if data then + unicodes,names={},{} + lpegmatch(grammar,data) + local supplement,registry,ordering=match(filename,"^(.-)%-(.-)%-()%.(.-)$") + return { + supplement=supplement, + registry=registry, + ordering=ordering, + filename=filename, + unicodes=unicodes, + names=names + } + end +end +cid.loadfile=loadcidfile +local template="%s-%s-%s.cidmap" +local function locate(registry,ordering,supplement) + local filename=format(template,registry,ordering,supplement) + local hashname=lower(filename) + local found=cidmap[hashname] + if not found then + if trace_loading then + report_otf("checking cidmap, registry %a, ordering %a, supplement %a, filename %a",registry,ordering,supplement,filename) + end + local fullname=resolvers.findfile(filename,'cid') or "" + if fullname~="" then + found=loadcidfile(fullname) + if found then + if trace_loading then + report_otf("using cidmap file %a",filename) + end + cidmap[hashname]=found + found.usedname=file.basename(filename) + end + end + end + return found +end +function cid.getmap(specification) + if not specification then + report_otf("invalid cidinfo specification, table expected") + return + end + local registry=specification.registry + local ordering=specification.ordering + local supplement=specification.supplement + local filename=format(registry,ordering,supplement) + local found=cidmap[lower(filename)] + if found then + return found + end + if trace_loading then + report_otf("cidmap needed, registry %a, ordering %a, supplement %a",registry,ordering,supplement) + end + found=locate(registry,ordering,supplement) + if not found then + local supnum=tonumber(supplement) + local cidnum=nil + if supnum0 then + for s=supnum-1,0,-1 do + local c=locate(registry,ordering,s) + if c then + found,cidnum=c,s + break + end + end + end + registry=lower(registry) + ordering=lower(ordering) + if found and cidnum>0 then + for s=0,cidnum-1 do + local filename=format(template,registry,ordering,s) + if not cidmap[filename] then + cidmap[filename]=found + end + end + end + end + return found +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-map']={ + 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=tonumber +local match,format,find,concat,gsub,lower=string.match,string.format,string.find,table.concat,string.gsub,string.lower +local P,R,S,C,Ct,Cc,lpegmatch=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.match +local utfbyte=utf.byte +local floor=math.floor +local trace_loading=false trackers.register("fonts.loading",function(v) trace_loading=v end) +local trace_mapping=false trackers.register("fonts.mapping",function(v) trace_unimapping=v end) +local report_fonts=logs.reporter("fonts","loading") +local fonts=fonts or {} +local mappings=fonts.mappings or {} +fonts.mappings=mappings +local function loadlumtable(filename) + local lumname=file.replacesuffix(file.basename(filename),"lum") + local lumfile=resolvers.findfile(lumname,"map") or "" + if lumfile~="" and lfs.isfile(lumfile) then + if trace_loading or trace_mapping then + report_fonts("loading map table %a",lumfile) + end + lumunic=dofile(lumfile) + return lumunic,lumfile + end +end +local hex=R("AF","09") +local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end +local hexsix=(hex*hex*hex*hex*hex*hex)/function(s) return tonumber(s,16) end +local dec=(R("09")^1)/tonumber +local period=P(".") +local unicode=P("uni")*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true)) +local ucode=P("u")*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true)) +local index=P("index")*dec*Cc(false) +local parser=unicode+ucode+index +local parsers={} +local function makenameparser(str) + if not str or str=="" then + return parser + else + local p=parsers[str] + if not p then + p=P(str)*period*dec*Cc(false) + parsers[str]=p + end + return p + end +end +local function tounicode16(unicode,name) + if unicode<0x10000 then + return format("%04X",unicode) + elseif unicode<0x1FFFFFFFFF then + return format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00) + else + report_fonts("can't convert %a in %a into tounicode",unicode,name) + end +end +local function tounicode16sequence(unicodes,name) + local t={} + for l=1,#unicodes do + local unicode=unicodes[l] + if unicode<0x10000 then + t[l]=format("%04X",unicode) + elseif unicode<0x1FFFFFFFFF then + t[l]=format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00) + else + report_fonts ("can't convert %a in %a into tounicode",unicode,name) + end + end + return concat(t) +end +local function fromunicode16(str) + if #str==4 then + return tonumber(str,16) + else + local l,r=match(str,"(....)(....)") + return (tonumber(l,16))*0x400+tonumber(r,16)-0xDC00 + end +end +mappings.loadlumtable=loadlumtable +mappings.makenameparser=makenameparser +mappings.tounicode16=tounicode16 +mappings.tounicode16sequence=tounicode16sequence +mappings.fromunicode16=fromunicode16 +local ligseparator=P("_") +local varseparator=P(".") +local namesplitter=Ct(C((1-ligseparator-varseparator)^1)*(ligseparator*C((1-ligseparator-varseparator)^1))^0) +function mappings.addtounicode(data,filename) + local resources=data.resources + local properties=data.properties + local descriptions=data.descriptions + local unicodes=resources.unicodes + if not unicodes then + return + end + unicodes['space']=unicodes['space'] or 32 + unicodes['hyphen']=unicodes['hyphen'] or 45 + unicodes['zwj']=unicodes['zwj'] or 0x200D + unicodes['zwnj']=unicodes['zwnj'] or 0x200C + local private=fonts.constructors.privateoffset + local unknown=format("%04X",utfbyte("?")) + local unicodevector=fonts.encodings.agl.unicodes + local tounicode={} + local originals={} + resources.tounicode=tounicode + resources.originals=originals + local lumunic,uparser,oparser + local cidinfo,cidnames,cidcodes,usedmap + if false then + lumunic=loadlumtable(filename) + lumunic=lumunic and lumunic.tounicode + end + cidinfo=properties.cidinfo + usedmap=cidinfo and fonts.cid.getmap(cidinfo) + if usedmap then + oparser=usedmap and makenameparser(cidinfo.ordering) + cidnames=usedmap.names + cidcodes=usedmap.unicodes + end + uparser=makenameparser() + local ns,nl=0,0 + for unic,glyph in next,descriptions do + local index=glyph.index + local name=glyph.name + if unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then + local unicode=lumunic and lumunic[name] or unicodevector[name] + if unicode then + originals[index]=unicode + tounicode[index]=tounicode16(unicode,name) + ns=ns+1 + end + if (not unicode) and usedmap then + local foundindex=lpegmatch(oparser,name) + if foundindex then + unicode=cidcodes[foundindex] + if unicode then + originals[index]=unicode + tounicode[index]=tounicode16(unicode,name) + ns=ns+1 + else + local reference=cidnames[foundindex] + if reference then + local foundindex=lpegmatch(oparser,reference) + if foundindex then + unicode=cidcodes[foundindex] + if unicode then + originals[index]=unicode + tounicode[index]=tounicode16(unicode,name) + ns=ns+1 + end + end + if not unicode or unicode=="" then + local foundcodes,multiple=lpegmatch(uparser,reference) + if foundcodes then + originals[index]=foundcodes + if multiple then + tounicode[index]=tounicode16sequence(foundcodes) + nl=nl+1 + unicode=true + else + tounicode[index]=tounicode16(foundcodes,name) + ns=ns+1 + unicode=foundcodes + end + end + end + end + end + end + end + if not unicode or unicode=="" then + local split=lpegmatch(namesplitter,name) + local nsplit=split and #split or 0 + local t,n={},0 + unicode=true + for l=1,nsplit do + local base=split[l] + local u=unicodes[base] or unicodevector[base] + if not u then + break + elseif type(u)=="table" then + if u[1]>=private then + unicode=false + break + end + n=n+1 + t[n]=u[1] + else + if u>=private then + unicode=false + break + end + n=n+1 + t[n]=u + end + end + if n==0 then + elseif n==1 then + originals[index]=t[1] + tounicode[index]=tounicode16(t[1],name) + else + originals[index]=t + tounicode[index]=tounicode16sequence(t) + end + nl=nl+1 + end + if not unicode or unicode=="" then + local foundcodes,multiple=lpegmatch(uparser,name) + if foundcodes then + if multiple then + originals[index]=foundcodes + tounicode[index]=tounicode16sequence(foundcodes,name) + nl=nl+1 + unicode=true + else + originals[index]=foundcodes + tounicode[index]=tounicode16(foundcodes,name) + ns=ns+1 + unicode=foundcodes + end + end + end + end + end + if trace_mapping then + for unic,glyph in table.sortedhash(descriptions) do + local name=glyph.name + local index=glyph.index + local toun=tounicode[index] + if toun then + report_fonts("internal slot %U, name %a, unicode %U, tounicode %a",index,name,unic,toun) + else + report_fonts("internal slot %U, name %a, unicode %U",index,name,unic) + end + end + end + if trace_loading and (ns>0 or nl>0) then + report_fonts("%s tounicode entries added, ligatures %s",nl+ns,ns) + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-fonts-syn']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +local fonts=fonts +fonts.names=fonts.names or {} +fonts.names.version=1.001 +fonts.names.basename="luatex-fonts-names" +fonts.names.new_to_old={} +fonts.names.old_to_new={} +fonts.names.cache=containers.define("fonts","data",fonts.names.version,true) +local data,loaded=nil,false +local fileformats={ "lua","tex","other text files" } +function fonts.names.reportmissingbase() + texio.write("") + fonts.names.reportmissingbase=nil +end +function fonts.names.reportmissingname() + texio.write("") + fonts.names.reportmissingname=nil +end +function fonts.names.resolve(name,sub) + if not loaded then + local basename=fonts.names.basename + if basename and basename~="" then + data=containers.read(fonts.names.cache,basename) + if not data then + basename=file.addsuffix(basename,"lua") + for i=1,#fileformats do + local format=fileformats[i] + local foundname=resolvers.findfile(basename,format) or "" + if foundname~="" then + data=dofile(foundname) + texio.write("") + break + end + end + end + end + loaded=true + end + if type(data)=="table" and data.version==fonts.names.version then + local condensed=string.gsub(string.lower(name),"[^%a%d]","") + local found=data.mappings and data.mappings[condensed] + if found then + local fontname,filename,subfont=found[1],found[2],found[3] + if subfont then + return filename,fontname + else + return filename,false + end + elseif fonts.names.reportmissingname then + fonts.names.reportmissingname() + return name,false + end + elseif fonts.names.reportmissingbase then + fonts.names.reportmissingbase() + end +end +fonts.names.resolvespec=fonts.names.resolve +function fonts.names.getfilename(askedname,suffix) + return "" +end +function fonts.names.ignoredfile(filename) + return false +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-tfm']={ + 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 next=next +local match=string.match +local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) +local trace_features=false trackers.register("tfm.features",function(v) trace_features=v end) +local report_defining=logs.reporter("fonts","defining") +local report_tfm=logs.reporter("fonts","tfm loading") +local findbinfile=resolvers.findbinfile +local fonts=fonts +local handlers=fonts.handlers +local readers=fonts.readers +local constructors=fonts.constructors +local encodings=fonts.encodings +local tfm=constructors.newhandler("tfm") +local tfmfeatures=constructors.newfeatures("tfm") +local registertfmfeature=tfmfeatures.register +constructors.resolvevirtualtoo=false +fonts.formats.tfm="type1" +function tfm.setfeatures(tfmdata,features) + local okay=constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm) + if okay then + return constructors.collectprocessors("tfm",tfmdata,features,trace_features,report_tfm) + else + return {} + end +end +local function read_from_tfm(specification) + local filename=specification.filename + local size=specification.size + if trace_defining then + report_defining("loading tfm file %a at size %s",filename,size) + end + local tfmdata=font.read_tfm(filename,size) + if tfmdata then + local features=specification.features and specification.features.normal or {} + local resources=tfmdata.resources or {} + local properties=tfmdata.properties or {} + local parameters=tfmdata.parameters or {} + local shared=tfmdata.shared or {} + properties.name=tfmdata.name + properties.fontname=tfmdata.fontname + properties.psname=tfmdata.psname + properties.filename=specification.filename + parameters.size=size + shared.rawdata={} + shared.features=features + shared.processes=next(features) and tfm.setfeatures(tfmdata,features) or nil + tfmdata.properties=properties + tfmdata.resources=resources + tfmdata.parameters=parameters + tfmdata.shared=shared + parameters.slant=parameters.slant or parameters[1] or 0 + parameters.space=parameters.space or parameters[2] or 0 + parameters.space_stretch=parameters.space_stretch or parameters[3] or 0 + parameters.space_shrink=parameters.space_shrink or parameters[4] or 0 + parameters.x_height=parameters.x_height or parameters[5] or 0 + parameters.quad=parameters.quad or parameters[6] or 0 + parameters.extra_space=parameters.extra_space or parameters[7] or 0 + constructors.enhanceparameters(parameters) + if constructors.resolvevirtualtoo then + fonts.loggers.register(tfmdata,file.suffix(filename),specification) + local vfname=findbinfile(specification.name,'ovf') + if vfname and vfname~="" then + local vfdata=font.read_vf(vfname,size) + if vfdata then + local chars=tfmdata.characters + for k,v in next,vfdata.characters do + chars[k].commands=v.commands + end + properties.virtualized=true + tfmdata.fonts=vfdata.fonts + end + end + end + local allfeatures=tfmdata.shared.features or specification.features.normal + constructors.applymanipulators("tfm",tfmdata,allfeatures.normal,trace_features,report_tfm) + if not features.encoding then + local encoding,filename=match(properties.filename,"^(.-)%-(.*)$") + if filename and encoding and encodings.known and encodings.known[encoding] then + features.encoding=encoding + end + end + return tfmdata + end +end +local function check_tfm(specification,fullname) + local foundname=findbinfile(fullname,'tfm') or "" + if foundname=="" then + foundname=findbinfile(fullname,'ofm') or "" + end + if foundname=="" then + foundname=fonts.names.getfilename(fullname,"tfm") or "" + end + if foundname~="" then + specification.filename=foundname + specification.format="ofm" + return read_from_tfm(specification) + elseif trace_defining then + report_defining("loading tfm with name %a fails",specification.name) + end +end +readers.check_tfm=check_tfm +function readers.tfm(specification) + local fullname=specification.filename or "" + if fullname=="" then + local forced=specification.forced or "" + if forced~="" then + fullname=specification.name.."."..forced + else + fullname=specification.name + end + end + return check_tfm(specification,fullname) +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-afm']={ + 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 fonts,logs,trackers,containers,resolvers=fonts,logs,trackers,containers,resolvers +local next,type,tonumber=next,type,tonumber +local format,match,gmatch,lower,gsub,strip=string.format,string.match,string.gmatch,string.lower,string.gsub,string.strip +local abs=math.abs +local P,S,C,R,lpegmatch,patterns=lpeg.P,lpeg.S,lpeg.C,lpeg.R,lpeg.match,lpeg.patterns +local derivetable=table.derive +local trace_features=false trackers.register("afm.features",function(v) trace_features=v end) +local trace_indexing=false trackers.register("afm.indexing",function(v) trace_indexing=v end) +local trace_loading=false trackers.register("afm.loading",function(v) trace_loading=v end) +local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) +local report_afm=logs.reporter("fonts","afm loading") +local findbinfile=resolvers.findbinfile +local definers=fonts.definers +local readers=fonts.readers +local constructors=fonts.constructors +local afm=constructors.newhandler("afm") +local pfb=constructors.newhandler("pfb") +local afmfeatures=constructors.newfeatures("afm") +local registerafmfeature=afmfeatures.register +afm.version=1.410 +afm.cache=containers.define("fonts","afm",afm.version,true) +afm.autoprefixed=true +afm.helpdata={} +afm.syncspace=true +afm.addligatures=true +afm.addtexligatures=true +afm.addkerns=true +local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes +local function setmode(tfmdata,value) + if value then + tfmdata.properties.mode=lower(value) + end +end +registerafmfeature { + name="mode", + description="mode", + initializers={ + base=setmode, + node=setmode, + } +} +local comment=P("Comment") +local spacing=patterns.spacer +local lineend=patterns.newline +local words=C((1-lineend)^1) +local number=C((R("09")+S("."))^1)/tonumber*spacing^0 +local data=lpeg.Carg(1) +local pattern=( + comment*spacing*( + data*( + ("CODINGSCHEME"*spacing*words )/function(fd,a) end+("DESIGNSIZE"*spacing*number*words )/function(fd,a) fd[ 1]=a end+("CHECKSUM"*spacing*number*words )/function(fd,a) fd[ 2]=a end+("SPACE"*spacing*number*"plus"*number*"minus"*number)/function(fd,a,b,c) fd[ 3],fd[ 4],fd[ 5]=a,b,c end+("QUAD"*spacing*number )/function(fd,a) fd[ 6]=a end+("EXTRASPACE"*spacing*number )/function(fd,a) fd[ 7]=a end+("NUM"*spacing*number*number*number )/function(fd,a,b,c) fd[ 8],fd[ 9],fd[10]=a,b,c end+("DENOM"*spacing*number*number )/function(fd,a,b ) fd[11],fd[12]=a,b end+("SUP"*spacing*number*number*number )/function(fd,a,b,c) fd[13],fd[14],fd[15]=a,b,c end+("SUB"*spacing*number*number )/function(fd,a,b) fd[16],fd[17]=a,b end+("SUPDROP"*spacing*number )/function(fd,a) fd[18]=a end+("SUBDROP"*spacing*number )/function(fd,a) fd[19]=a end+("DELIM"*spacing*number*number )/function(fd,a,b) fd[20],fd[21]=a,b end+("AXISHEIGHT"*spacing*number )/function(fd,a) fd[22]=a end + )+(1-lineend)^0 + )+(1-comment)^1 +)^0 +local function scan_comment(str) + local fd={} + lpegmatch(pattern,str,1,fd) + return fd +end +local keys={} +function keys.FontName (data,line) data.metadata.fontname=strip (line) + data.metadata.fullname=strip (line) end +function keys.ItalicAngle (data,line) data.metadata.italicangle=tonumber (line) end +function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch=toboolean(line,true) end +function keys.CharWidth (data,line) data.metadata.charwidth=tonumber (line) end +function keys.XHeight (data,line) data.metadata.xheight=tonumber (line) end +function keys.Descender (data,line) data.metadata.descender=tonumber (line) end +function keys.Ascender (data,line) data.metadata.ascender=tonumber (line) end +function keys.Comment (data,line) + line=lower(line) + local designsize=match(line,"designsize[^%d]*(%d+)") + if designsize then data.metadata.designsize=tonumber(designsize) end +end +local function get_charmetrics(data,charmetrics,vector) + local characters=data.characters + local chr,ind={},0 + for k,v in gmatch(charmetrics,"([%a]+) +(.-) *;") do + if k=='C' then + v=tonumber(v) + if v<0 then + ind=ind+1 + else + ind=v + end + chr={ + index=ind + } + elseif k=='WX' then + chr.width=tonumber(v) + elseif k=='N' then + characters[v]=chr + elseif k=='B' then + local llx,lly,urx,ury=match(v,"^ *(.-) +(.-) +(.-) +(.-)$") + chr.boundingbox={ tonumber(llx),tonumber(lly),tonumber(urx),tonumber(ury) } + elseif k=='L' then + local plus,becomes=match(v,"^(.-) +(.-)$") + local ligatures=chr.ligatures + if ligatures then + ligatures[plus]=becomes + else + chr.ligatures={ [plus]=becomes } + end + end + end +end +local function get_kernpairs(data,kernpairs) + local characters=data.characters + for one,two,value in gmatch(kernpairs,"KPX +(.-) +(.-) +(.-)\n") do + local chr=characters[one] + if chr then + local kerns=chr.kerns + if kerns then + kerns[two]=tonumber(value) + else + chr.kerns={ [two]=tonumber(value) } + end + end + end +end +local function get_variables(data,fontmetrics) + for key,rest in gmatch(fontmetrics,"(%a+) *(.-)[\n\r]") do + local keyhandler=keys[key] + if keyhandler then + keyhandler(data,rest) + end + end +end +local function get_indexes(data,pfbname) + data.resources.filename=resolvers.unresolve(pfbname) + local pfbblob=fontloader.open(pfbname) + if pfbblob then + local characters=data.characters + local pfbdata=fontloader.to_table(pfbblob) + if pfbdata then + local glyphs=pfbdata.glyphs + if glyphs then + if trace_loading then + report_afm("getting index data from %a",pfbname) + end + for index,glyph in next,glyphs do + local name=glyph.name + if name then + local char=characters[name] + if char then + if trace_indexing then + report_afm("glyph %a has index %a",name,index) + end + char.index=index + end + end + end + elseif trace_loading then + report_afm("no glyph data in pfb file %a",pfbname) + end + elseif trace_loading then + report_afm("no data in pfb file %a",pfbname) + end + fontloader.close(pfbblob) + elseif trace_loading then + report_afm("invalid pfb file %a",pfbname) + end +end +local function readafm(filename) + local ok,afmblob,size=resolvers.loadbinfile(filename) + if ok and afmblob then + local data={ + resources={ + filename=resolvers.unresolve(filename), + version=afm.version, + creator="context mkiv", + }, + properties={ + hasitalics=false, + }, + goodies={}, + metadata={ + filename=file.removesuffix(file.basename(filename)) + }, + characters={ + }, + descriptions={ + }, + } + afmblob=gsub(afmblob,"StartCharMetrics(.-)EndCharMetrics",function(charmetrics) + if trace_loading then + report_afm("loading char metrics") + end + get_charmetrics(data,charmetrics,vector) + return "" + end) + afmblob=gsub(afmblob,"StartKernPairs(.-)EndKernPairs",function(kernpairs) + if trace_loading then + report_afm("loading kern pairs") + end + get_kernpairs(data,kernpairs) + return "" + end) + afmblob=gsub(afmblob,"StartFontMetrics%s+([%d%.]+)(.-)EndFontMetrics",function(version,fontmetrics) + if trace_loading then + report_afm("loading variables") + end + data.afmversion=version + get_variables(data,fontmetrics) + data.fontdimens=scan_comment(fontmetrics) + return "" + end) + return data + else + if trace_loading then + report_afm("no valid afm file %a",filename) + end + return nil + end +end +local addkerns,addligatures,addtexligatures,unify,normalize +function afm.load(filename) + filename=resolvers.findfile(filename,'afm') or "" + if filename~="" and not fonts.names.ignoredfile(filename) then + local name=file.removesuffix(file.basename(filename)) + local data=containers.read(afm.cache,name) + local attr=lfs.attributes(filename) + local size,time=attr.size or 0,attr.modification or 0 + local pfbfile=file.replacesuffix(name,"pfb") + local pfbname=resolvers.findfile(pfbfile,"pfb") or "" + if pfbname=="" then + pfbname=resolvers.findfile(file.basename(pfbfile),"pfb") or "" + end + local pfbsize,pfbtime=0,0 + if pfbname~="" then + local attr=lfs.attributes(pfbname) + pfbsize=attr.size or 0 + pfbtime=attr.modification or 0 + end + if not data or data.size~=size or data.time~=time or data.pfbsize~=pfbsize or data.pfbtime~=pfbtime then + report_afm("reading %a",filename) + data=readafm(filename) + if data then + if pfbname~="" then + get_indexes(data,pfbname) + elseif trace_loading then + report_afm("no pfb file for %a",filename) + end + report_afm("unifying %a",filename) + unify(data,filename) + if afm.addligatures then + report_afm("add ligatures") + addligatures(data) + end + if afm.addtexligatures then + report_afm("add tex ligatures") + addtexligatures(data) + end + if afm.addkerns then + report_afm("add extra kerns") + addkerns(data) + end + normalize(data) + report_afm("add tounicode data") + fonts.mappings.addtounicode(data,filename) + data.size=size + data.time=time + data.pfbsize=pfbsize + data.pfbtime=pfbtime + report_afm("saving %a in cache",name) + data=containers.write(afm.cache,name,data) + data=containers.read(afm.cache,name) + end + if applyruntimefixes and data then + applyruntimefixes(filename,data) + end + end + return data + else + return nil + end +end +local uparser=fonts.mappings.makenameparser() +unify=function(data,filename) + local unicodevector=fonts.encodings.agl.unicodes + local unicodes,names={},{} + local private=constructors.privateoffset + local descriptions=data.descriptions + for name,blob in next,data.characters do + local code=unicodevector[name] + if not code then + code=lpegmatch(uparser,name) + if not code then + code=private + private=private+1 + report_afm("assigning private slot %U for unknown glyph name %a",code,name) + end + end + local index=blob.index + unicodes[name]=code + names[name]=index + blob.name=name + descriptions[code]={ + boundingbox=blob.boundingbox, + width=blob.width, + kerns=blob.kerns, + index=index, + name=name, + } + end + for unicode,description in next,descriptions do + local kerns=description.kerns + if kerns then + local krn={} + for name,kern in next,kerns do + local unicode=unicodes[name] + if unicode then + krn[unicode]=kern + else + print(unicode,name) + end + end + description.kerns=krn + end + end + data.characters=nil + local resources=data.resources + local filename=resources.filename or file.removesuffix(file.basename(filename)) + resources.filename=resolvers.unresolve(filename) + resources.unicodes=unicodes + resources.marks={} + resources.names=names + resources.private=private +end +normalize=function(data) +end +local addthem=function(rawdata,ligatures) + if ligatures then + local descriptions=rawdata.descriptions + local resources=rawdata.resources + local unicodes=resources.unicodes + local names=resources.names + for ligname,ligdata in next,ligatures do + local one=descriptions[unicodes[ligname]] + if one then + for _,pair in next,ligdata do + local two,three=unicodes[pair[1]],unicodes[pair[2]] + if two and three then + local ol=one.ligatures + if ol then + if not ol[two] then + ol[two]=three + end + else + one.ligatures={ [two]=three } + end + end + end + end + end + end +end +addligatures=function(rawdata) addthem(rawdata,afm.helpdata.ligatures ) end +addtexligatures=function(rawdata) addthem(rawdata,afm.helpdata.texligatures) end +addkerns=function(rawdata) + local descriptions=rawdata.descriptions + local resources=rawdata.resources + local unicodes=resources.unicodes + local function do_it_left(what) + if what then + for unicode,description in next,descriptions do + local kerns=description.kerns + if kerns then + local extrakerns + for complex,simple in next,what do + complex=unicodes[complex] + simple=unicodes[simple] + if complex and simple then + local ks=kerns[simple] + if ks and not kerns[complex] then + if extrakerns then + extrakerns[complex]=ks + else + extrakerns={ [complex]=ks } + end + end + end + end + if extrakerns then + description.extrakerns=extrakerns + end + end + end + end + end + local function do_it_copy(what) + if what then + for complex,simple in next,what do + complex=unicodes[complex] + simple=unicodes[simple] + if complex and simple then + local complexdescription=descriptions[complex] + if complexdescription then + local simpledescription=descriptions[complex] + if simpledescription then + local extrakerns + local kerns=simpledescription.kerns + if kerns then + for unicode,kern in next,kerns do + if extrakerns then + extrakerns[unicode]=kern + else + extrakerns={ [unicode]=kern } + end + end + end + local extrakerns=simpledescription.extrakerns + if extrakerns then + for unicode,kern in next,extrakerns do + if extrakerns then + extrakerns[unicode]=kern + else + extrakerns={ [unicode]=kern } + end + end + end + if extrakerns then + complexdescription.extrakerns=extrakerns + end + end + end + end + end + end + end + do_it_left(afm.helpdata.leftkerned) + do_it_left(afm.helpdata.bothkerned) + do_it_copy(afm.helpdata.bothkerned) + do_it_copy(afm.helpdata.rightkerned) +end +local function adddimensions(data) + if data then + for unicode,description in next,data.descriptions do + local bb=description.boundingbox + if bb then + local ht,dp=bb[4],-bb[2] + if ht==0 or ht<0 then + else + description.height=ht + end + if dp==0 or dp<0 then + else + description.depth=dp + end + end + end + end +end +local function copytotfm(data) + if data and data.descriptions then + local metadata=data.metadata + local resources=data.resources + local properties=derivetable(data.properties) + local descriptions=derivetable(data.descriptions) + local goodies=derivetable(data.goodies) + local characters={} + local parameters={} + local unicodes=resources.unicodes + for unicode,description in next,data.descriptions do + characters[unicode]={} + end + local filename=constructors.checkedfilename(resources) + local fontname=metadata.fontname or metadata.fullname + local fullname=metadata.fullname or metadata.fontname + local endash=unicodes['space'] + local emdash=unicodes['emdash'] + local spacer="space" + local spaceunits=500 + local monospaced=metadata.isfixedpitch + local charwidth=metadata.charwidth + local italicangle=metadata.italicangle + local charxheight=metadata.xheight and metadata.xheight>0 and metadata.xheight + properties.monospaced=monospaced + parameters.italicangle=italicangle + parameters.charwidth=charwidth + parameters.charxheight=charxheight + if properties.monospaced then + if descriptions[endash] then + spaceunits,spacer=descriptions[endash].width,"space" + end + if not spaceunits and descriptions[emdash] then + spaceunits,spacer=descriptions[emdash].width,"emdash" + end + if not spaceunits and charwidth then + spaceunits,spacer=charwidth,"charwidth" + end + else + if descriptions[endash] then + spaceunits,spacer=descriptions[endash].width,"space" + end + if not spaceunits and charwidth then + spaceunits,spacer=charwidth,"charwidth" + end + end + spaceunits=tonumber(spaceunits) + if spaceunits<200 then + end + parameters.slant=0 + parameters.space=spaceunits + parameters.space_stretch=500 + parameters.space_shrink=333 + parameters.x_height=400 + parameters.quad=1000 + if italicangle and italicangle~=0 then + parameters.italicangle=italicangle + parameters.italicfactor=math.cos(math.rad(90+italicangle)) + parameters.slant=- math.tan(italicangle*math.pi/180) + end + if monospaced then + parameters.space_stretch=0 + parameters.space_shrink=0 + elseif afm.syncspace then + parameters.space_stretch=spaceunits/2 + parameters.space_shrink=spaceunits/3 + end + parameters.extra_space=parameters.space_shrink + if charxheight then + parameters.x_height=charxheight + else + local x=unicodes['x'] + if x then + local x=descriptions[x] + if x then + parameters.x_height=x.height + end + end + end + local fd=data.fontdimens + if fd and fd[8] and fd[9] and fd[10] then + for k,v in next,fd do + parameters[k]=v + end + end + parameters.designsize=(metadata.designsize or 10)*65536 + parameters.ascender=abs(metadata.ascender or 0) + parameters.descender=abs(metadata.descender or 0) + parameters.units=1000 + properties.spacer=spacer + properties.encodingbytes=2 + properties.format=fonts.formats[filename] or "type1" + properties.filename=filename + properties.fontname=fontname + properties.fullname=fullname + properties.psname=fullname + properties.name=filename or fullname or fontname + if next(characters) then + return { + characters=characters, + descriptions=descriptions, + parameters=parameters, + resources=resources, + properties=properties, + goodies=goodies, + } + end + end + return nil +end +function afm.setfeatures(tfmdata,features) + local okay=constructors.initializefeatures("afm",tfmdata,features,trace_features,report_afm) + if okay then + return constructors.collectprocessors("afm",tfmdata,features,trace_features,report_afm) + else + return {} + end +end +local function checkfeatures(specification) +end +local function afmtotfm(specification) + local afmname=specification.filename or specification.name + if specification.forced=="afm" or specification.format=="afm" then + if trace_loading then + report_afm("forcing afm format for %a",afmname) + end + else + local tfmname=findbinfile(afmname,"ofm") or "" + if tfmname~="" then + if trace_loading then + report_afm("fallback from afm to tfm for %a",afmname) + end + return + end + end + if afmname~="" then + local features=constructors.checkedfeatures("afm",specification.features.normal) + specification.features.normal=features + constructors.hashinstance(specification,true) + specification=definers.resolve(specification) + local cache_id=specification.hash + local tfmdata=containers.read(constructors.cache,cache_id) + if not tfmdata then + local rawdata=afm.load(afmname) + if rawdata and next(rawdata) then + adddimensions(rawdata) + tfmdata=copytotfm(rawdata) + if tfmdata and next(tfmdata) then + local shared=tfmdata.shared + if not shared then + shared={} + tfmdata.shared=shared + end + shared.rawdata=rawdata + shared.features=features + shared.processes=afm.setfeatures(tfmdata,features) + end + elseif trace_loading then + report_afm("no (valid) afm file found with name %a",afmname) + end + tfmdata=containers.write(constructors.cache,cache_id,tfmdata) + end + return tfmdata + end +end +local function read_from_afm(specification) + local tfmdata=afmtotfm(specification) + if tfmdata then + tfmdata.properties.name=specification.name + tfmdata=constructors.scale(tfmdata,specification) + local allfeatures=tfmdata.shared.features or specification.features.normal + constructors.applymanipulators("afm",tfmdata,allfeatures,trace_features,report_afm) + fonts.loggers.register(tfmdata,'afm',specification) + end + return tfmdata +end +local function prepareligatures(tfmdata,ligatures,value) + if value then + local descriptions=tfmdata.descriptions + for unicode,character in next,tfmdata.characters do + local description=descriptions[unicode] + local dligatures=description.ligatures + if dligatures then + local cligatures=character.ligatures + if not cligatures then + cligatures={} + character.ligatures=cligatures + end + for unicode,ligature in next,dligatures do + cligatures[unicode]={ + char=ligature, + type=0 + } + end + end + end + end +end +local function preparekerns(tfmdata,kerns,value) + if value then + local rawdata=tfmdata.shared.rawdata + local resources=rawdata.resources + local unicodes=resources.unicodes + local descriptions=tfmdata.descriptions + for u,chr in next,tfmdata.characters do + local d=descriptions[u] + local newkerns=d[kerns] + if newkerns then + local kerns=chr.kerns + if not kerns then + kerns={} + chr.kerns=kerns + end + for k,v in next,newkerns do + local uk=unicodes[k] + if uk then + kerns[uk]=v + end + end + end + end + end +end +local list={ + [0x0027]=0x2019, +} +local function texreplacements(tfmdata,value) + local descriptions=tfmdata.descriptions + local characters=tfmdata.characters + for k,v in next,list do + characters [k]=characters [v] + descriptions[k]=descriptions[v] + end +end +local function ligatures (tfmdata,value) prepareligatures(tfmdata,'ligatures',value) end +local function texligatures(tfmdata,value) prepareligatures(tfmdata,'texligatures',value) end +local function kerns (tfmdata,value) preparekerns (tfmdata,'kerns',value) end +local function extrakerns (tfmdata,value) preparekerns (tfmdata,'extrakerns',value) end +registerafmfeature { + name="liga", + description="traditional ligatures", + initializers={ + base=ligatures, + node=ligatures, + } +} +registerafmfeature { + name="kern", + description="intercharacter kerning", + initializers={ + base=kerns, + node=kerns, + } +} +registerafmfeature { + name="extrakerns", + description="additional intercharacter kerning", + initializers={ + base=extrakerns, + node=extrakerns, + } +} +registerafmfeature { + name='tlig', + description='tex ligatures', + initializers={ + base=texligatures, + node=texligatures, + } +} +registerafmfeature { + name='trep', + description='tex replacements', + initializers={ + base=texreplacements, + node=texreplacements, + } +} +local check_tfm=readers.check_tfm +fonts.formats.afm="type1" +fonts.formats.pfb="type1" +local function check_afm(specification,fullname) + local foundname=findbinfile(fullname,'afm') or "" + if foundname=="" then + foundname=fonts.names.getfilename(fullname,"afm") or "" + end + if foundname=="" and afm.autoprefixed then + local encoding,shortname=match(fullname,"^(.-)%-(.*)$") + if encoding and shortname and fonts.encodings.known[encoding] then + shortname=findbinfile(shortname,'afm') or "" + if shortname~="" then + foundname=shortname + if trace_defining then + report_afm("stripping encoding prefix from filename %a",afmname) + end + end + end + end + if foundname~="" then + specification.filename=foundname + specification.format="afm" + return read_from_afm(specification) + end +end +function readers.afm(specification,method) + local fullname,tfmdata=specification.filename or "",nil + if fullname=="" then + local forced=specification.forced or "" + if forced~="" then + tfmdata=check_afm(specification,specification.name.."."..forced) + end + if not tfmdata then + method=method or definers.method or "afm or tfm" + if method=="tfm" then + tfmdata=check_tfm(specification,specification.name) + elseif method=="afm" then + tfmdata=check_afm(specification,specification.name) + elseif method=="tfm or afm" then + tfmdata=check_tfm(specification,specification.name) or check_afm(specification,specification.name) + else + tfmdata=check_afm(specification,specification.name) or check_tfm(specification,specification.name) + end + end + else + tfmdata=check_afm(specification,fullname) + end + return tfmdata +end +function readers.pfb(specification,method) + local original=specification.specification + if trace_defining then + report_afm("using afm reader for %a",original) + end + specification.specification=gsub(original,"%.pfb",".afm") + specification.forced="afm" + return readers.afm(specification,method) +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-afk']={ + version=1.001, + comment="companion to font-afm.lua", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files", + dataonly=true, +} +local allocate=utilities.storage.allocate +fonts.handlers.afm.helpdata={ + ligatures=allocate { + ['f']={ + { 'f','ff' }, + { 'i','fi' }, + { 'l','fl' }, + }, + ['ff']={ + { 'i','ffi' } + }, + ['fi']={ + { 'i','fii' } + }, + ['fl']={ + { 'i','fli' } + }, + ['s']={ + { 't','st' } + }, + ['i']={ + { 'j','ij' } + }, + }, + texligatures=allocate { + ['quoteleft']={ + { 'quoteleft','quotedblleft' } + }, + ['quoteright']={ + { 'quoteright','quotedblright' } + }, + ['hyphen']={ + { 'hyphen','endash' } + }, + ['endash']={ + { 'hyphen','emdash' } + } + }, + leftkerned=allocate { + AEligature="A",aeligature="a", + OEligature="O",oeligature="o", + IJligature="I",ijligature="i", + AE="A",ae="a", + OE="O",oe="o", + IJ="I",ij="i", + Ssharp="S",ssharp="s", + }, + rightkerned=allocate { + AEligature="E",aeligature="e", + OEligature="E",oeligature="e", + IJligature="J",ijligature="j", + AE="E",ae="e", + OE="E",oe="e", + IJ="J",ij="j", + Ssharp="S",ssharp="s", + }, + bothkerned=allocate { + Acircumflex="A",acircumflex="a", + Ccircumflex="C",ccircumflex="c", + Ecircumflex="E",ecircumflex="e", + Gcircumflex="G",gcircumflex="g", + Hcircumflex="H",hcircumflex="h", + Icircumflex="I",icircumflex="i", + Jcircumflex="J",jcircumflex="j", + Ocircumflex="O",ocircumflex="o", + Scircumflex="S",scircumflex="s", + Ucircumflex="U",ucircumflex="u", + Wcircumflex="W",wcircumflex="w", + Ycircumflex="Y",ycircumflex="y", + Agrave="A",agrave="a", + Egrave="E",egrave="e", + Igrave="I",igrave="i", + Ograve="O",ograve="o", + Ugrave="U",ugrave="u", + Ygrave="Y",ygrave="y", + Atilde="A",atilde="a", + Itilde="I",itilde="i", + Otilde="O",otilde="o", + Utilde="U",utilde="u", + Ntilde="N",ntilde="n", + Adiaeresis="A",adiaeresis="a",Adieresis="A",adieresis="a", + Ediaeresis="E",ediaeresis="e",Edieresis="E",edieresis="e", + Idiaeresis="I",idiaeresis="i",Idieresis="I",idieresis="i", + Odiaeresis="O",odiaeresis="o",Odieresis="O",odieresis="o", + Udiaeresis="U",udiaeresis="u",Udieresis="U",udieresis="u", + Ydiaeresis="Y",ydiaeresis="y",Ydieresis="Y",ydieresis="y", + Aacute="A",aacute="a", + Cacute="C",cacute="c", + Eacute="E",eacute="e", + Iacute="I",iacute="i", + Lacute="L",lacute="l", + Nacute="N",nacute="n", + Oacute="O",oacute="o", + Racute="R",racute="r", + Sacute="S",sacute="s", + Uacute="U",uacute="u", + Yacute="Y",yacute="y", + Zacute="Z",zacute="z", + Dstroke="D",dstroke="d", + Hstroke="H",hstroke="h", + Tstroke="T",tstroke="t", + Cdotaccent="C",cdotaccent="c", + Edotaccent="E",edotaccent="e", + Gdotaccent="G",gdotaccent="g", + Idotaccent="I",idotaccent="i", + Zdotaccent="Z",zdotaccent="z", + Amacron="A",amacron="a", + Emacron="E",emacron="e", + Imacron="I",imacron="i", + Omacron="O",omacron="o", + Umacron="U",umacron="u", + Ccedilla="C",ccedilla="c", + Kcedilla="K",kcedilla="k", + Lcedilla="L",lcedilla="l", + Ncedilla="N",ncedilla="n", + Rcedilla="R",rcedilla="r", + Scedilla="S",scedilla="s", + Tcedilla="T",tcedilla="t", + Ohungarumlaut="O",ohungarumlaut="o", + Uhungarumlaut="U",uhungarumlaut="u", + Aogonek="A",aogonek="a", + Eogonek="E",eogonek="e", + Iogonek="I",iogonek="i", + Uogonek="U",uogonek="u", + Aring="A",aring="a", + Uring="U",uring="u", + Abreve="A",abreve="a", + Ebreve="E",ebreve="e", + Gbreve="G",gbreve="g", + Ibreve="I",ibreve="i", + Obreve="O",obreve="o", + Ubreve="U",ubreve="u", + Ccaron="C",ccaron="c", + Dcaron="D",dcaron="d", + Ecaron="E",ecaron="e", + Lcaron="L",lcaron="l", + Ncaron="N",ncaron="n", + Rcaron="R",rcaron="r", + Scaron="S",scaron="s", + Tcaron="T",tcaron="t", + Zcaron="Z",zcaron="z", + dotlessI="I",dotlessi="i", + dotlessJ="J",dotlessj="j", + AEligature="AE",aeligature="ae",AE="AE",ae="ae", + OEligature="OE",oeligature="oe",OE="OE",oe="oe", + IJligature="IJ",ijligature="ij",IJ="IJ",ij="ij", + Lstroke="L",lstroke="l",Lslash="L",lslash="l", + Ostroke="O",ostroke="o",Oslash="O",oslash="o", + Ssharp="SS",ssharp="ss", + Aumlaut="A",aumlaut="a", + Eumlaut="E",eumlaut="e", + Iumlaut="I",iumlaut="i", + Oumlaut="O",oumlaut="o", + Uumlaut="U",uumlaut="u", + } +} + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-fonts-tfm']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +local fonts=fonts +local tfm={} +fonts.handlers.tfm=tfm +fonts.formats.tfm="type1" +function fonts.readers.tfm(specification) + local fullname=specification.filename or "" + if fullname=="" then + local forced=specification.forced or "" + if forced~="" then + fullname=specification.name.."."..forced + else + fullname=specification.name + end + end + local foundname=resolvers.findbinfile(fullname,'tfm') or "" + if foundname=="" then + foundname=resolvers.findbinfile(fullname,'ofm') or "" + end + if foundname~="" then + specification.filename=foundname + specification.format="ofm" + return font.read_tfm(specification.filename,specification.size) + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-oti']={ + 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 lower=string.lower +local fonts=fonts +local constructors=fonts.constructors +local otf=constructors.newhandler("otf") +local otffeatures=constructors.newfeatures("otf") +local otftables=otf.tables +local registerotffeature=otffeatures.register +local allocate=utilities.storage.allocate +registerotffeature { + name="features", + description="initialization of feature handler", + default=true, +} +local function setmode(tfmdata,value) + if value then + tfmdata.properties.mode=lower(value) + end +end +local function setlanguage(tfmdata,value) + if value then + local cleanvalue=lower(value) + local languages=otftables and otftables.languages + local properties=tfmdata.properties + if not languages then + properties.language=cleanvalue + elseif languages[value] then + properties.language=cleanvalue + else + properties.language="dflt" + end + end +end +local function setscript(tfmdata,value) + if value then + local cleanvalue=lower(value) + local scripts=otftables and otftables.scripts + local properties=tfmdata.properties + if not scripts then + properties.script=cleanvalue + elseif scripts[value] then + properties.script=cleanvalue + else + properties.script="dflt" + end + end +end +registerotffeature { + name="mode", + description="mode", + initializers={ + base=setmode, + node=setmode, + } +} +registerotffeature { + name="language", + description="language", + initializers={ + base=setlanguage, + node=setlanguage, + } +} +registerotffeature { + name="script", + description="script", + initializers={ + base=setscript, + node=setscript, + } +} + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otf']={ + 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 utfbyte=utf.byte +local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip +local type,next,tonumber,tostring=type,next,tonumber,tostring +local abs=math.abs +local insert=table.insert +local lpegmatch=lpeg.match +local reversed,concat,remove,sortedkeys=table.reversed,table.concat,table.remove,table.sortedkeys +local ioflush=io.flush +local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive +local formatters=string.formatters +local allocate=utilities.storage.allocate +local registertracker=trackers.register +local registerdirective=directives.register +local starttiming=statistics.starttiming +local stoptiming=statistics.stoptiming +local elapsedtime=statistics.elapsedtime +local findbinfile=resolvers.findbinfile +local trace_private=false registertracker("otf.private",function(v) trace_private=v end) +local trace_loading=false registertracker("otf.loading",function(v) trace_loading=v end) +local trace_features=false registertracker("otf.features",function(v) trace_features=v end) +local trace_dynamics=false registertracker("otf.dynamics",function(v) trace_dynamics=v end) +local trace_sequences=false registertracker("otf.sequences",function(v) trace_sequences=v end) +local trace_markwidth=false registertracker("otf.markwidth",function(v) trace_markwidth=v end) +local trace_defining=false registertracker("fonts.defining",function(v) trace_defining=v end) +local report_otf=logs.reporter("fonts","otf loading") +local fonts=fonts +local otf=fonts.handlers.otf +otf.glists={ "gsub","gpos" } +otf.version=2.751 +otf.cache=containers.define("fonts","otf",otf.version,true) +local fontdata=fonts.hashes.identifiers +local chardata=characters and characters.data +local otffeatures=fonts.constructors.newfeatures("otf") +local registerotffeature=otffeatures.register +local enhancers=allocate() +otf.enhancers=enhancers +local patches={} +enhancers.patches=patches +local definers=fonts.definers +local readers=fonts.readers +local constructors=fonts.constructors +local forceload=false +local cleanup=0 +local usemetatables=false +local packdata=true +local syncspace=true +local forcenotdef=false +local includesubfonts=false +local overloadkerns=false +local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes +local wildcard="*" +local default="dflt" +local fontloaderfields=fontloader.fields +local mainfields=nil +local glyphfields=nil +local formats=fonts.formats +formats.otf="opentype" +formats.ttf="truetype" +formats.ttc="truetype" +formats.dfont="truetype" +registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or (v and 1) or 0 end) +registerdirective("fonts.otf.loader.force",function(v) forceload=v end) +registerdirective("fonts.otf.loader.usemetatables",function(v) usemetatables=v end) +registerdirective("fonts.otf.loader.pack",function(v) packdata=v end) +registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end) +registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end) +registerdirective("fonts.otf.loader.overloadkerns",function(v) overloadkerns=v end) +function otf.fileformat(filename) + local leader=lower(io.loadchunk(filename,4)) + local suffix=lower(file.suffix(filename)) + if leader=="otto" then + return formats.otf,suffix=="otf" + elseif leader=="ttcf" then + return formats.ttc,suffix=="ttc" + elseif suffix=="ttc" then + return formats.ttc,true + elseif suffix=="dfont" then + return formats.dfont,true + else + return formats.ttf,suffix=="ttf" + end +end +local function otf_format(filename) + local format,okay=otf.fileformat(filename) + if not okay then + report_otf("font %a is actually an %a file",filename,format) + end + return format +end +local function load_featurefile(raw,featurefile) + if featurefile and featurefile~="" then + if trace_loading then + report_otf("using featurefile %a",featurefile) + end + fontloader.apply_featurefile(raw,featurefile) + end +end +local function showfeatureorder(rawdata,filename) + local sequences=rawdata.resources.sequences + if sequences and #sequences>0 then + if trace_loading then + report_otf("font %a has %s sequences",filename,#sequences) + report_otf(" ") + end + for nos=1,#sequences do + local sequence=sequences[nos] + local typ=sequence.type or "no-type" + local name=sequence.name or "no-name" + local subtables=sequence.subtables or { "no-subtables" } + local features=sequence.features + if trace_loading then + report_otf("%3i %-15s %-20s [% t]",nos,name,typ,subtables) + end + if features then + for feature,scripts in next,features do + local tt={} + if type(scripts)=="table" then + for script,languages in next,scripts do + local ttt={} + for language,_ in next,languages do + ttt[#ttt+1]=language + end + tt[#tt+1]=formatters["[%s: % t]"](script,ttt) + end + if trace_loading then + report_otf(" %s: % t",feature,tt) + end + else + if trace_loading then + report_otf(" %s: %S",feature,scripts) + end + end + end + end + end + if trace_loading then + report_otf("\n") + end + elseif trace_loading then + report_otf("font %a has no sequences",filename) + end +end +local valid_fields=table.tohash { + "ascent", + "cidinfo", + "copyright", + "descent", + "design_range_bottom", + "design_range_top", + "design_size", + "encodingchanged", + "extrema_bound", + "familyname", + "fontname", + "fontname", + "fontstyle_id", + "fontstyle_name", + "fullname", + "hasvmetrics", + "horiz_base", + "issans", + "isserif", + "italicangle", + "macstyle", + "onlybitmaps", + "origname", + "os2_version", + "pfminfo", + "serifcheck", + "sfd_version", + "strokedfont", + "strokewidth", + "table_version", + "ttf_tables", + "uni_interp", + "uniqueid", + "units_per_em", + "upos", + "use_typo_metrics", + "uwidth", + "validation_state", + "version", + "vert_base", + "weight", + "weight_width_slope_only", +} +local ordered_enhancers={ + "prepare tables", + "prepare glyphs", + "prepare lookups", + "analyze glyphs", + "analyze math", + "prepare tounicode", + "reorganize lookups", + "reorganize mark classes", + "reorganize anchor classes", + "reorganize glyph kerns", + "reorganize glyph lookups", + "reorganize glyph anchors", + "merge kern classes", + "reorganize features", + "reorganize subtables", + "check glyphs", + "check metadata", + "check extra features", + "check encoding", + "add duplicates", + "cleanup tables", +} +local actions=allocate() +local before=allocate() +local after=allocate() +patches.before=before +patches.after=after +local function enhance(name,data,filename,raw) + local enhancer=actions[name] + if enhancer then + if trace_loading then + report_otf("apply enhancement %a to file %a",name,filename) + ioflush() + end + enhancer(data,filename,raw) + else + end +end +function enhancers.apply(data,filename,raw) + local basename=file.basename(lower(filename)) + if trace_loading then + report_otf("%s enhancing file %a","start",filename) + end + ioflush() + for e=1,#ordered_enhancers do + local enhancer=ordered_enhancers[e] + local b=before[enhancer] + if b then + for pattern,action in next,b do + if find(basename,pattern) then + action(data,filename,raw) + end + end + end + enhance(enhancer,data,filename,raw) + local a=after[enhancer] + if a then + for pattern,action in next,a do + if find(basename,pattern) then + action(data,filename,raw) + end + end + end + ioflush() + end + if trace_loading then + report_otf("%s enhancing file %a","stop",filename) + end + ioflush() +end +function patches.register(what,where,pattern,action) + local pw=patches[what] + if pw then + local ww=pw[where] + if ww then + ww[pattern]=action + else + pw[where]={ [pattern]=action} + end + end +end +function patches.report(fmt,...) + if trace_loading then + report_otf("patching: %s",formatters[fmt](...)) + end +end +function enhancers.register(what,action) + actions[what]=action +end +function otf.load(filename,sub,featurefile) + local base=file.basename(file.removesuffix(filename)) + local name=file.removesuffix(base) + local attr=lfs.attributes(filename) + local size=attr and attr.size or 0 + local time=attr and attr.modification or 0 + if featurefile then + name=name.."@"..file.removesuffix(file.basename(featurefile)) + end + if sub=="" then + sub=false + end + local hash=name + if sub then + hash=hash.."-"..sub + end + hash=containers.cleanname(hash) + local featurefiles + if featurefile then + featurefiles={} + for s in gmatch(featurefile,"[^,]+") do + local name=resolvers.findfile(file.addsuffix(s,'fea'),'fea') or "" + if name=="" then + report_otf("loading error, no featurefile %a",s) + else + local attr=lfs.attributes(name) + featurefiles[#featurefiles+1]={ + name=name, + size=attr and attr.size or 0, + time=attr and attr.modification or 0, + } + end + end + if #featurefiles==0 then + featurefiles=nil + end + end + local data=containers.read(otf.cache,hash) + local reload=not data or data.size~=size or data.time~=time + if forceload then + report_otf("forced reload of %a due to hard coded flag",filename) + reload=true + end + if not reload then + local featuredata=data.featuredata + if featurefiles then + if not featuredata or #featuredata~=#featurefiles then + reload=true + else + for i=1,#featurefiles do + local fi,fd=featurefiles[i],featuredata[i] + if fi.name~=fd.name or fi.size~=fd.size or fi.time~=fd.time then + reload=true + break + end + end + end + elseif featuredata then + reload=true + end + if reload then + report_otf("loading: forced reload due to changed featurefile specification %a",featurefile) + end + end + if reload then + report_otf("loading %a, hash %a",filename,hash) + local fontdata,messages + if sub then + fontdata,messages=fontloader.open(filename,sub) + else + fontdata,messages=fontloader.open(filename) + end + if fontdata then + mainfields=mainfields or (fontloaderfields and fontloaderfields(fontdata)) + end + if trace_loading and messages and #messages>0 then + if type(messages)=="string" then + report_otf("warning: %s",messages) + else + for m=1,#messages do + report_otf("warning: %S",messages[m]) + end + end + else + report_otf("loading done") + end + if fontdata then + if featurefiles then + for i=1,#featurefiles do + load_featurefile(fontdata,featurefiles[i].name) + end + end + local unicodes={ + } + local splitter=lpeg.splitter(" ",unicodes) + data={ + size=size, + time=time, + format=otf_format(filename), + featuredata=featurefiles, + resources={ + filename=resolvers.unresolve(filename), + version=otf.version, + creator="context mkiv", + unicodes=unicodes, + indices={ + }, + duplicates={ + }, + variants={ + }, + lookuptypes={}, + }, + metadata={ + }, + properties={ + }, + descriptions={}, + goodies={}, + helpers={ + tounicodelist=splitter, + tounicodetable=lpeg.Ct(splitter), + }, + } + starttiming(data) + report_otf("file size: %s",size) + enhancers.apply(data,filename,fontdata) + local packtime={} + if packdata then + if cleanup>0 then + collectgarbage("collect") + end + starttiming(packtime) + enhance("pack",data,filename,nil) + stoptiming(packtime) + end + report_otf("saving %a in cache",filename) + data=containers.write(otf.cache,hash,data) + if cleanup>1 then + collectgarbage("collect") + end + stoptiming(data) + if elapsedtime then + report_otf("preprocessing and caching time %s, packtime %s", + elapsedtime(data),packdata and elapsedtime(packtime) or 0) + end + fontloader.close(fontdata) + if cleanup>3 then + collectgarbage("collect") + end + data=containers.read(otf.cache,hash) + if cleanup>2 then + collectgarbage("collect") + end + else + data=nil + report_otf("loading failed due to read error") + end + end + if data then + if trace_defining then + report_otf("loading from cache using hash %a",hash) + end + enhance("unpack",data,filename,nil,false) + if applyruntimefixes then + applyruntimefixes(filename,data) + end + enhance("add dimensions",data,filename,nil,false) + if trace_sequences then + showfeatureorder(data,filename) + end + end + return data +end +local mt={ + __index=function(t,k) + if k=="height" then + local ht=t.boundingbox[4] + return ht<0 and 0 or ht + elseif k=="depth" then + local dp=-t.boundingbox[2] + return dp<0 and 0 or dp + elseif k=="width" then + return 0 + elseif k=="name" then + return forcenotdef and ".notdef" + end + end +} +actions["prepare tables"]=function(data,filename,raw) + data.properties.hasitalics=false +end +actions["add dimensions"]=function(data,filename) + if data then + local descriptions=data.descriptions + local resources=data.resources + local defaultwidth=resources.defaultwidth or 0 + local defaultheight=resources.defaultheight or 0 + local defaultdepth=resources.defaultdepth or 0 + local basename=trace_markwidth and file.basename(filename) + if usemetatables then + for _,d in next,descriptions do + local wd=d.width + if not wd then + d.width=defaultwidth + elseif trace_markwidth and wd~=0 and d.class=="mark" then + report_otf("mark %a with width %b found in %a",d.name or "",wd,basename) + end + setmetatable(d,mt) + end + else + for _,d in next,descriptions do + local bb,wd=d.boundingbox,d.width + if not wd then + d.width=defaultwidth + elseif trace_markwidth and wd~=0 and d.class=="mark" then + report_otf("mark %a with width %b found in %a",d.name or "",wd,basename) + end + if bb then + local ht,dp=bb[4],-bb[2] + if ht==0 or ht<0 then + else + d.height=ht + end + if dp==0 or dp<0 then + else + d.depth=dp + end + end + end + end + end +end +local function somecopy(old) + if old then + local new={} + if type(old)=="table" then + for k,v in next,old do + if k=="glyphs" then + elseif type(v)=="table" then + new[k]=somecopy(v) + else + new[k]=v + end + end + else + for i=1,#mainfields do + local k=mainfields[i] + local v=old[k] + if k=="glyphs" then + elseif type(v)=="table" then + new[k]=somecopy(v) + else + new[k]=v + end + end + end + return new + else + return {} + end +end +actions["prepare glyphs"]=function(data,filename,raw) + local rawglyphs=raw.glyphs + local rawsubfonts=raw.subfonts + local rawcidinfo=raw.cidinfo + local criterium=constructors.privateoffset + local private=criterium + local resources=data.resources + local metadata=data.metadata + local properties=data.properties + local descriptions=data.descriptions + local unicodes=resources.unicodes + local indices=resources.indices + local duplicates=resources.duplicates + local variants=resources.variants + if rawsubfonts then + metadata.subfonts=includesubfonts and {} + properties.cidinfo=rawcidinfo + if rawcidinfo.registry then + local cidmap=fonts.cid.getmap(rawcidinfo) + if cidmap then + rawcidinfo.usedname=cidmap.usedname + local nofnames,nofunicodes=0,0 + local cidunicodes,cidnames=cidmap.unicodes,cidmap.names + for cidindex=1,#rawsubfonts do + local subfont=rawsubfonts[cidindex] + local cidglyphs=subfont.glyphs + if includesubfonts then + metadata.subfonts[cidindex]=somecopy(subfont) + end + for index=0,subfont.glyphcnt-1 do + local glyph=cidglyphs[index] + if glyph then + local unicode=glyph.unicode + local name=glyph.name or cidnames[index] + if not unicode or unicode==-1 or unicode>=criterium then + unicode=cidunicodes[index] + end + if unicode and descriptions[unicode] then + report_otf("preventing glyph %a at index %H to overload unicode %U",name or "noname",index,unicode) + unicode=-1 + end + if not unicode or unicode==-1 or unicode>=criterium then + if not name then + name=format("u%06X",private) + end + unicode=private + unicodes[name]=private + if trace_private then + report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private) + end + private=private+1 + nofnames=nofnames+1 + else + if not name then + name=format("u%06X",unicode) + end + unicodes[name]=unicode + nofunicodes=nofunicodes+1 + end + indices[index]=unicode + local description={ + boundingbox=glyph.boundingbox, + name=glyph.name or name or "unknown", + cidindex=cidindex, + index=index, + glyph=glyph, + } + descriptions[unicode]=description + else + end + end + end + if trace_loading then + report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes,nofnames,nofunicodes+nofnames) + end + elseif trace_loading then + report_otf("unable to remap cid font, missing cid file for %a",filename) + end + elseif trace_loading then + report_otf("font %a has no glyphs",filename) + end + else + for index=0,raw.glyphcnt-1 do + local glyph=rawglyphs[index] + if glyph then + local unicode=glyph.unicode + local name=glyph.name + if not unicode or unicode==-1 or unicode>=criterium then + unicode=private + unicodes[name]=private + if trace_private then + report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private) + end + private=private+1 + else + unicodes[name]=unicode + end + indices[index]=unicode + if not name then + name=format("u%06X",unicode) + end + descriptions[unicode]={ + boundingbox=glyph.boundingbox, + name=name, + index=index, + glyph=glyph, + } + local altuni=glyph.altuni + if altuni then + for i=1,#altuni do + local a=altuni[i] + local u=a.unicode + local v=a.variant + if v then + local vv=variants[v] + if vv then + vv[u]=unicode + else + vv={ [u]=unicode } + variants[v]=vv + end + end + end + end + else + report_otf("potential problem: glyph %U is used but empty",index) + end + end + end + resources.private=private +end +actions["check encoding"]=function(data,filename,raw) + local descriptions=data.descriptions + local resources=data.resources + local properties=data.properties + local unicodes=resources.unicodes + local indices=resources.indices + local duplicates=resources.duplicates + local mapdata=raw.map or {} + local unicodetoindex=mapdata and mapdata.map or {} + local indextounicode=mapdata and mapdata.backmap or {} + local encname=lower(data.enc_name or mapdata.enc_name or "") + local criterium=0xFFFF + if find(encname,"unicode") then + if trace_loading then + report_otf("checking embedded unicode map %a",encname) + end + local hash={} + for index,unicode in next,indices do + hash[index]=descriptions[unicode] + end + local reported={} + for unicode,index in next,unicodetoindex do + if not descriptions[unicode] then + local d=hash[index] + if d then + if d.unicode~=unicode then + local c=d.copies + if c then + c[unicode]=true + else + d.copies={ [unicode]=true } + end + end + elseif not reported[i] then + report_otf("missing index %i",index) + reported[i]=true + end + end + end + for index,data in next,hash do + data.copies=sortedkeys(data.copies) + end + for index,unicode in next,indices do + local description=hash[index] + local copies=description.copies + if copies then + duplicates[unicode]=copies + description.copies=nil + else + report_otf("copies but no unicode parent %U",unicode) + end + end + elseif properties.cidinfo then + report_otf("warning: no unicode map, used cidmap %a",properties.cidinfo.usedname) + else + report_otf("warning: non unicode map %a, only using glyph unicode data",encname or "whatever") + end + if mapdata then + mapdata.map={} + mapdata.backmap={} + end +end +actions["add duplicates"]=function(data,filename,raw) + local descriptions=data.descriptions + local resources=data.resources + local properties=data.properties + local unicodes=resources.unicodes + local indices=resources.indices + local duplicates=resources.duplicates + for unicode,d in next,duplicates do + local nofduplicates=#d + if nofduplicates>4 then + if trace_loading then + report_otf("ignoring excessive duplicates of %U (n=%s)",unicode,nofduplicates) + end + else + for i=1,nofduplicates do + local u=d[i] + if not descriptions[u] then + local description=descriptions[unicode] + local n=0 + for _,description in next,descriptions do + if kerns then + local kerns=description.kerns + for _,k in next,kerns do + local ku=k[unicode] + if ku then + k[u]=ku + n=n+1 + end + end + end + end + if u>0 then + local duplicate=table.copy(description) + duplicate.comment=format("copy of U+%05X",unicode) + descriptions[u]=duplicate + if trace_loading then + report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n) + end + end + end + end + end + end +end +actions["analyze glyphs"]=function(data,filename,raw) + local descriptions=data.descriptions + local resources=data.resources + local metadata=data.metadata + local properties=data.properties + local hasitalics=false + local widths={} + local marks={} + for unicode,description in next,descriptions do + local glyph=description.glyph + local italic=glyph.italic_correction + if not italic then + elseif italic==0 then + else + description.italic=italic + hasitalics=true + end + local width=glyph.width + widths[width]=(widths[width] or 0)+1 + local class=glyph.class + if class then + if class=="mark" then + marks[unicode]=true + end + description.class=class + end + end + properties.hasitalics=hasitalics + resources.marks=marks + local wd,most=0,1 + for k,v in next,widths do + if v>most then + wd,most=k,v + end + end + if most>1000 then + if trace_loading then + report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most) + end + for unicode,description in next,descriptions do + if description.width==wd then + else + description.width=description.glyph.width + end + end + resources.defaultwidth=wd + else + for unicode,description in next,descriptions do + description.width=description.glyph.width + end + end +end +actions["reorganize mark classes"]=function(data,filename,raw) + local mark_classes=raw.mark_classes + if mark_classes then + local resources=data.resources + local unicodes=resources.unicodes + local markclasses={} + resources.markclasses=markclasses + for name,class in next,mark_classes do + local t={} + for s in gmatch(class,"[^ ]+") do + t[unicodes[s]]=true + end + markclasses[name]=t + end + end +end +actions["reorganize features"]=function(data,filename,raw) + local features={} + data.resources.features=features + for k,what in next,otf.glists do + local dw=raw[what] + if dw then + local f={} + features[what]=f + for i=1,#dw do + local d=dw[i] + local dfeatures=d.features + if dfeatures then + for i=1,#dfeatures do + local df=dfeatures[i] + local tag=strip(lower(df.tag)) + local ft=f[tag] + if not ft then + ft={} + f[tag]=ft + end + local dscripts=df.scripts + for i=1,#dscripts do + local d=dscripts[i] + local languages=d.langs + local script=strip(lower(d.script)) + local fts=ft[script] if not fts then fts={} ft[script]=fts end + for i=1,#languages do + fts[strip(lower(languages[i]))]=true + end + end + end + end + end + end + end +end +actions["reorganize anchor classes"]=function(data,filename,raw) + local resources=data.resources + local anchor_to_lookup={} + local lookup_to_anchor={} + resources.anchor_to_lookup=anchor_to_lookup + resources.lookup_to_anchor=lookup_to_anchor + local classes=raw.anchor_classes + if classes then + for c=1,#classes do + local class=classes[c] + local anchor=class.name + local lookups=class.lookup + if type(lookups)~="table" then + lookups={ lookups } + end + local a=anchor_to_lookup[anchor] + if not a then + a={} + anchor_to_lookup[anchor]=a + end + for l=1,#lookups do + local lookup=lookups[l] + local l=lookup_to_anchor[lookup] + if l then + l[anchor]=true + else + l={ [anchor]=true } + lookup_to_anchor[lookup]=l + end + a[lookup]=true + end + end + end +end +actions["prepare tounicode"]=function(data,filename,raw) + fonts.mappings.addtounicode(data,filename) +end +local g_directions={ + gsub_contextchain=1, + gpos_contextchain=1, + gsub_reversecontextchain=-1, + gpos_reversecontextchain=-1, +} +actions["reorganize subtables"]=function(data,filename,raw) + local resources=data.resources + local sequences={} + local lookups={} + local chainedfeatures={} + resources.sequences=sequences + resources.lookups=lookups + for _,what in next,otf.glists do + local dw=raw[what] + if dw then + for k=1,#dw do + local gk=dw[k] + local features=gk.features + local typ=gk.type + local chain=g_directions[typ] or 0 + local subtables=gk.subtables + if subtables then + local t={} + for s=1,#subtables do + t[s]=subtables[s].name + end + subtables=t + end + local flags,markclass=gk.flags,nil + if flags then + local t={ + (flags.ignorecombiningmarks and "mark") or false, + (flags.ignoreligatures and "ligature") or false, + (flags.ignorebaseglyphs and "base") or false, + flags.r2l or false, + } + markclass=flags.mark_class + if markclass then + markclass=resources.markclasses[markclass] + end + flags=t + end + local name=gk.name + if not name then + report_otf("skipping weird lookup number %s",k) + elseif features then + local f={} + for i=1,#features do + local df=features[i] + local tag=strip(lower(df.tag)) + local ft=f[tag] if not ft then ft={} f[tag]=ft end + local dscripts=df.scripts + for i=1,#dscripts do + local d=dscripts[i] + local languages=d.langs + local script=strip(lower(d.script)) + local fts=ft[script] if not fts then fts={} ft[script]=fts end + for i=1,#languages do + fts[strip(lower(languages[i]))]=true + end + end + end + sequences[#sequences+1]={ + type=typ, + chain=chain, + flags=flags, + name=name, + subtables=subtables, + markclass=markclass, + features=f, + } + else + lookups[name]={ + type=typ, + chain=chain, + flags=flags, + subtables=subtables, + markclass=markclass, + } + end + end + end + end +end +actions["prepare lookups"]=function(data,filename,raw) + local lookups=raw.lookups + if lookups then + data.lookups=lookups + end +end +local function t_uncover(splitter,cache,covers) + local result={} + for n=1,#covers do + local cover=covers[n] + local uncovered=cache[cover] + if not uncovered then + uncovered=lpegmatch(splitter,cover) + cache[cover]=uncovered + end + result[n]=uncovered + end + return result +end +local function s_uncover(splitter,cache,cover) + if cover=="" then + return nil + else + local uncovered=cache[cover] + if not uncovered then + uncovered=lpegmatch(splitter,cover) + cache[cover]=uncovered + end + return { uncovered } + end +end +local function t_hashed(t,cache) + if t then + local ht={} + for i=1,#t do + local ti=t[i] + local tih=cache[ti] + if not tih then + tih={} + for i=1,#ti do + tih[ti[i]]=true + end + cache[ti]=tih + end + ht[i]=tih + end + return ht + else + return nil + end +end +local function s_hashed(t,cache) + if t then + local ht={} + local tf=t[1] + for i=1,#tf do + ht[i]={ [tf[i]]=true } + end + return ht + else + return nil + end +end +local function r_uncover(splitter,cache,cover,replacements) + if cover=="" then + return nil + else + local uncovered=cover[1] + local replaced=cache[replacements] + if not replaced then + replaced=lpegmatch(splitter,replacements) + cache[replacements]=replaced + end + local nu,nr=#uncovered,#replaced + local r={} + if nu==nr then + for i=1,nu do + r[uncovered[i]]=replaced[i] + end + end + return r + end +end +actions["reorganize lookups"]=function(data,filename,raw) + if data.lookups then + local splitter=data.helpers.tounicodetable + local t_u_cache={} + local s_u_cache=t_u_cache + local t_h_cache={} + local s_h_cache=t_h_cache + local r_u_cache={} + for _,lookup in next,data.lookups do + local rules=lookup.rules + if rules then + local format=lookup.format + if format=="class" then + local before_class=lookup.before_class + if before_class then + before_class=t_uncover(splitter,t_u_cache,reversed(before_class)) + end + local current_class=lookup.current_class + if current_class then + current_class=t_uncover(splitter,t_u_cache,current_class) + end + local after_class=lookup.after_class + if after_class then + after_class=t_uncover(splitter,t_u_cache,after_class) + end + for i=1,#rules do + local rule=rules[i] + local class=rule.class + local before=class.before + if before then + for i=1,#before do + before[i]=before_class[before[i]] or {} + end + rule.before=t_hashed(before,t_h_cache) + end + local current=class.current + local lookups=rule.lookups + if current then + for i=1,#current do + current[i]=current_class[current[i]] or {} + if lookups and not lookups[i] then + lookups[i]="" + end + end + rule.current=t_hashed(current,t_h_cache) + end + local after=class.after + if after then + for i=1,#after do + after[i]=after_class[after[i]] or {} + end + rule.after=t_hashed(after,t_h_cache) + end + rule.class=nil + end + lookup.before_class=nil + lookup.current_class=nil + lookup.after_class=nil + lookup.format="coverage" + elseif format=="coverage" then + for i=1,#rules do + local rule=rules[i] + local coverage=rule.coverage + if coverage then + local before=coverage.before + if before then + before=t_uncover(splitter,t_u_cache,reversed(before)) + rule.before=t_hashed(before,t_h_cache) + end + local current=coverage.current + if current then + current=t_uncover(splitter,t_u_cache,current) + local lookups=rule.lookups + if lookups then + for i=1,#current do + if not lookups[i] then + lookups[i]="" + end + end + end + rule.current=t_hashed(current,t_h_cache) + end + local after=coverage.after + if after then + after=t_uncover(splitter,t_u_cache,after) + rule.after=t_hashed(after,t_h_cache) + end + rule.coverage=nil + end + end + elseif format=="reversecoverage" then + for i=1,#rules do + local rule=rules[i] + local reversecoverage=rule.reversecoverage + if reversecoverage then + local before=reversecoverage.before + if before then + before=t_uncover(splitter,t_u_cache,reversed(before)) + rule.before=t_hashed(before,t_h_cache) + end + local current=reversecoverage.current + if current then + current=t_uncover(splitter,t_u_cache,current) + rule.current=t_hashed(current,t_h_cache) + end + local after=reversecoverage.after + if after then + after=t_uncover(splitter,t_u_cache,after) + rule.after=t_hashed(after,t_h_cache) + end + local replacements=reversecoverage.replacements + if replacements then + rule.replacements=r_uncover(splitter,r_u_cache,current,replacements) + end + rule.reversecoverage=nil + end + end + elseif format=="glyphs" then + for i=1,#rules do + local rule=rules[i] + local glyphs=rule.glyphs + if glyphs then + local fore=glyphs.fore + if fore and fore~="" then + fore=s_uncover(splitter,s_u_cache,fore) + rule.before=s_hashed(fore,s_h_cache) + end + local back=glyphs.back + if back then + back=s_uncover(splitter,s_u_cache,back) + rule.after=s_hashed(back,s_h_cache) + end + local names=glyphs.names + if names then + names=s_uncover(splitter,s_u_cache,names) + rule.current=s_hashed(names,s_h_cache) + end + rule.glyphs=nil + end + end + end + end + end + end +end +local function check_variants(unicode,the_variants,splitter,unicodes) + local variants=the_variants.variants + if variants then + local glyphs=lpegmatch(splitter,variants) + local done={ [unicode]=true } + local n=0 + for i=1,#glyphs do + local g=glyphs[i] + if done[g] then + report_otf("skipping cyclic reference %U in math variant %U",g,unicode) + else + if n==0 then + n=1 + variants={ g } + else + n=n+1 + variants[n]=g + end + done[g]=true + end + end + if n==0 then + variants=nil + end + end + local parts=the_variants.parts + if parts then + local p=#parts + if p>0 then + for i=1,p do + local pi=parts[i] + pi.glyph=unicodes[pi.component] or 0 + pi.component=nil + end + else + parts=nil + end + end + local italic_correction=the_variants.italic_correction + if italic_correction and italic_correction==0 then + italic_correction=nil + end + return variants,parts,italic_correction +end +actions["analyze math"]=function(data,filename,raw) + if raw.math then + data.metadata.math=raw.math + local unicodes=data.resources.unicodes + local splitter=data.helpers.tounicodetable + for unicode,description in next,data.descriptions do + local glyph=description.glyph + local mathkerns=glyph.mathkern + local horiz_variants=glyph.horiz_variants + local vert_variants=glyph.vert_variants + local top_accent=glyph.top_accent + if mathkerns or horiz_variants or vert_variants or top_accent then + local math={} + if top_accent then + math.top_accent=top_accent + end + if mathkerns then + for k,v in next,mathkerns do + if not next(v) then + mathkerns[k]=nil + else + for k,v in next,v do + if v==0 then + k[v]=nil + end + end + end + end + math.kerns=mathkerns + end + if horiz_variants then + math.horiz_variants,math.horiz_parts,math.horiz_italic_correction=check_variants(unicode,horiz_variants,splitter,unicodes) + end + if vert_variants then + math.vert_variants,math.vert_parts,math.vert_italic_correction=check_variants(unicode,vert_variants,splitter,unicodes) + end + local italic_correction=description.italic + if italic_correction and italic_correction~=0 then + math.italic_correction=italic_correction + end + description.math=math + end + end + end +end +actions["reorganize glyph kerns"]=function(data,filename,raw) + local descriptions=data.descriptions + local resources=data.resources + local unicodes=resources.unicodes + for unicode,description in next,descriptions do + local kerns=description.glyph.kerns + if kerns then + local newkerns={} + for k,kern in next,kerns do + local name=kern.char + local offset=kern.off + local lookup=kern.lookup + if name and offset and lookup then + local unicode=unicodes[name] + if unicode then + if type(lookup)=="table" then + for l=1,#lookup do + local lookup=lookup[l] + local lookupkerns=newkerns[lookup] + if lookupkerns then + lookupkerns[unicode]=offset + else + newkerns[lookup]={ [unicode]=offset } + end + end + else + local lookupkerns=newkerns[lookup] + if lookupkerns then + lookupkerns[unicode]=offset + else + newkerns[lookup]={ [unicode]=offset } + end + end + elseif trace_loading then + report_otf("problems with unicode %a of kern %a of glyph %U",name,k,unicode) + end + end + end + description.kerns=newkerns + end + end +end +actions["merge kern classes"]=function(data,filename,raw) + local gposlist=raw.gpos + if gposlist then + local descriptions=data.descriptions + local resources=data.resources + local unicodes=resources.unicodes + local splitter=data.helpers.tounicodetable + local ignored=0 + local blocked=0 + for gp=1,#gposlist do + local gpos=gposlist[gp] + local subtables=gpos.subtables + if subtables then + local first_done={} + local split={} + for s=1,#subtables do + local subtable=subtables[s] + local kernclass=subtable.kernclass + local lookup=subtable.lookup or subtable.name + if kernclass then + if #kernclass>0 then + kernclass=kernclass[1] + lookup=type(kernclass.lookup)=="string" and kernclass.lookup or lookup + report_otf("fixing kernclass table of lookup %a",lookup) + end + local firsts=kernclass.firsts + local seconds=kernclass.seconds + local offsets=kernclass.offsets + for n,s in next,firsts do + split[s]=split[s] or lpegmatch(splitter,s) + end + local maxseconds=0 + for n,s in next,seconds do + if n>maxseconds then + maxseconds=n + end + split[s]=split[s] or lpegmatch(splitter,s) + end + for fk=1,#firsts do + local fv=firsts[fk] + local splt=split[fv] + if splt then + local extrakerns={} + local baseoffset=(fk-1)*maxseconds + for sk=2,maxseconds do + local sv=seconds[sk] + local splt=split[sv] + if splt then + local offset=offsets[baseoffset+sk] + if offset then + for i=1,#splt do + extrakerns[splt[i]]=offset + end + end + end + end + for i=1,#splt do + local first_unicode=splt[i] + if first_done[first_unicode] then + report_otf("lookup %a: ignoring further kerns of %C",lookup,first_unicode) + blocked=blocked+1 + else + first_done[first_unicode]=true + local description=descriptions[first_unicode] + if description then + local kerns=description.kerns + if not kerns then + kerns={} + description.kerns=kerns + end + local lookupkerns=kerns[lookup] + if not lookupkerns then + lookupkerns={} + kerns[lookup]=lookupkerns + end + if overloadkerns then + for second_unicode,kern in next,extrakerns do + lookupkerns[second_unicode]=kern + end + else + for second_unicode,kern in next,extrakerns do + local k=lookupkerns[second_unicode] + if not k then + lookupkerns[second_unicode]=kern + elseif k~=kern then + if trace_loading then + report_otf("lookup %a: ignoring overload of kern between %C and %C, rejecting %a, keeping %a",lookup,first_unicode,second_unicode,k,kern) + end + ignored=ignored+1 + end + end + end + elseif trace_loading then + report_otf("no glyph data for %U",first_unicode) + end + end + end + end + end + subtable.kernclass={} + end + end + end + end + if ignored>0 then + report_otf("%s kern overloads ignored",ignored) + end + if blocked>0 then + report_otf("%s succesive kerns blocked",blocked) + end + end +end +actions["check glyphs"]=function(data,filename,raw) + for unicode,description in next,data.descriptions do + description.glyph=nil + end +end +actions["check metadata"]=function(data,filename,raw) + local metadata=data.metadata + for _,k in next,mainfields do + if valid_fields[k] then + local v=raw[k] + if not metadata[k] then + metadata[k]=v + end + end + end + local ttftables=metadata.ttf_tables + if ttftables then + for i=1,#ttftables do + ttftables[i].data="deleted" + end + end + if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then + local name=file.nameonly(filename) + metadata.fontname="bad-fontname-"..name + metadata.fullname="bad-fullname-"..name + end +end +actions["cleanup tables"]=function(data,filename,raw) + data.resources.indices=nil + data.helpers=nil +end +actions["reorganize glyph lookups"]=function(data,filename,raw) + local resources=data.resources + local unicodes=resources.unicodes + local descriptions=data.descriptions + local splitter=data.helpers.tounicodelist + local lookuptypes=resources.lookuptypes + for unicode,description in next,descriptions do + local lookups=description.glyph.lookups + if lookups then + for tag,lookuplist in next,lookups do + for l=1,#lookuplist do + local lookup=lookuplist[l] + local specification=lookup.specification + local lookuptype=lookup.type + local lt=lookuptypes[tag] + if not lt then + lookuptypes[tag]=lookuptype + elseif lt~=lookuptype then + report_otf("conflicting lookuptypes, %a points to %a and %a",tag,lt,lookuptype) + end + if lookuptype=="ligature" then + lookuplist[l]={ lpegmatch(splitter,specification.components) } + elseif lookuptype=="alternate" then + lookuplist[l]={ lpegmatch(splitter,specification.components) } + elseif lookuptype=="substitution" then + lookuplist[l]=unicodes[specification.variant] + elseif lookuptype=="multiple" then + lookuplist[l]={ lpegmatch(splitter,specification.components) } + elseif lookuptype=="position" then + lookuplist[l]={ + specification.x or 0, + specification.y or 0, + specification.h or 0, + specification.v or 0 + } + elseif lookuptype=="pair" then + local one=specification.offsets[1] + local two=specification.offsets[2] + local paired=unicodes[specification.paired] + if one then + if two then + lookuplist[l]={ paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 },{ two.x or 0,two.y or 0,two.h or 0,two.v or 0 } } + else + lookuplist[l]={ paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 } } + end + else + if two then + lookuplist[l]={ paired,{},{ two.x or 0,two.y or 0,two.h or 0,two.v or 0} } + else + lookuplist[l]={ paired } + end + end + end + end + end + local slookups,mlookups + for tag,lookuplist in next,lookups do + if #lookuplist==1 then + if slookups then + slookups[tag]=lookuplist[1] + else + slookups={ [tag]=lookuplist[1] } + end + else + if mlookups then + mlookups[tag]=lookuplist + else + mlookups={ [tag]=lookuplist } + end + end + end + if slookups then + description.slookups=slookups + end + if mlookups then + description.mlookups=mlookups + end + end + end +end +actions["reorganize glyph anchors"]=function(data,filename,raw) + local descriptions=data.descriptions + for unicode,description in next,descriptions do + local anchors=description.glyph.anchors + if anchors then + for class,data in next,anchors do + if class=="baselig" then + for tag,specification in next,data do + for i=1,#specification do + local si=specification[i] + specification[i]={ si.x or 0,si.y or 0 } + end + end + else + for tag,specification in next,data do + data[tag]={ specification.x or 0,specification.y or 0 } + end + end + end + description.anchors=anchors + end + end +end +function otf.setfeatures(tfmdata,features) + local okay=constructors.initializefeatures("otf",tfmdata,features,trace_features,report_otf) + if okay then + return constructors.collectprocessors("otf",tfmdata,features,trace_features,report_otf) + else + return {} + end +end +local function copytotfm(data,cache_id) + if data then + local metadata=data.metadata + local resources=data.resources + local properties=derivetable(data.properties) + local descriptions=derivetable(data.descriptions) + local goodies=derivetable(data.goodies) + local characters={} + local parameters={} + local mathparameters={} + local pfminfo=metadata.pfminfo or {} + local resources=data.resources + local unicodes=resources.unicodes + local spaceunits=500 + local spacer="space" + local designsize=metadata.designsize or metadata.design_size or 100 + local mathspecs=metadata.math + if designsize==0 then + designsize=100 + end + if mathspecs then + for name,value in next,mathspecs do + mathparameters[name]=value + end + end + for unicode,_ in next,data.descriptions do + characters[unicode]={} + end + if mathspecs then + for unicode,character in next,characters do + local d=descriptions[unicode] + local m=d.math + if m then + local variants=m.horiz_variants + local parts=m.horiz_parts + if variants then + local c=character + for i=1,#variants do + local un=variants[i] + c.next=un + c=characters[un] + end + c.horiz_variants=parts + elseif parts then + character.horiz_variants=parts + end + local variants=m.vert_variants + local parts=m.vert_parts + if variants then + local c=character + for i=1,#variants do + local un=variants[i] + c.next=un + c=characters[un] + end + c.vert_variants=parts + elseif parts then + character.vert_variants=parts + end + local italic_correction=m.vert_italic_correction + if italic_correction then + character.vert_italic_correction=italic_correction + end + local top_accent=m.top_accent + if top_accent then + character.top_accent=top_accent + end + local kerns=m.kerns + if kerns then + character.mathkerns=kerns + end + end + end + end + local filename=constructors.checkedfilename(resources) + local fontname=metadata.fontname + local fullname=metadata.fullname or fontname + local units=metadata.units_per_em or 1000 + if units==0 then + units=1000 + metadata.units_per_em=1000 + report_otf("changing %a units to %a",0,units) + end + local monospaced=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced") + local charwidth=pfminfo.avgwidth + local charxheight=pfminfo.os2_xheight and pfminfo.os2_xheight>0 and pfminfo.os2_xheight + local italicangle=metadata.italicangle + properties.monospaced=monospaced + parameters.italicangle=italicangle + parameters.charwidth=charwidth + parameters.charxheight=charxheight + local space=0x0020 + local emdash=0x2014 + if monospaced then + if descriptions[space] then + spaceunits,spacer=descriptions[space].width,"space" + end + if not spaceunits and descriptions[emdash] then + spaceunits,spacer=descriptions[emdash].width,"emdash" + end + if not spaceunits and charwidth then + spaceunits,spacer=charwidth,"charwidth" + end + else + if descriptions[space] then + spaceunits,spacer=descriptions[space].width,"space" + end + if not spaceunits and descriptions[emdash] then + spaceunits,spacer=descriptions[emdash].width/2,"emdash/2" + end + if not spaceunits and charwidth then + spaceunits,spacer=charwidth,"charwidth" + end + end + spaceunits=tonumber(spaceunits) or 500 + parameters.slant=0 + parameters.space=spaceunits + parameters.space_stretch=units/2 + parameters.space_shrink=1*units/3 + parameters.x_height=2*units/5 + parameters.quad=units + if spaceunits<2*units/5 then + end + if italicangle and italicangle~=0 then + parameters.italicangle=italicangle + parameters.italicfactor=math.cos(math.rad(90+italicangle)) + parameters.slant=- math.tan(italicangle*math.pi/180) + end + if monospaced then + parameters.space_stretch=0 + parameters.space_shrink=0 + elseif syncspace then + parameters.space_stretch=spaceunits/2 + parameters.space_shrink=spaceunits/3 + end + parameters.extra_space=parameters.space_shrink + if charxheight then + parameters.x_height=charxheight + else + local x=0x78 + if x then + local x=descriptions[x] + if x then + parameters.x_height=x.height + end + end + end + parameters.designsize=(designsize/10)*65536 + parameters.ascender=abs(metadata.ascent or 0) + parameters.descender=abs(metadata.descent or 0) + parameters.units=units + properties.space=spacer + properties.encodingbytes=2 + properties.format=data.format or otf_format(filename) or formats.otf + properties.noglyphnames=true + properties.filename=filename + properties.fontname=fontname + properties.fullname=fullname + properties.psname=fontname or fullname + properties.name=filename or fullname + return { + characters=characters, + descriptions=descriptions, + parameters=parameters, + mathparameters=mathparameters, + resources=resources, + properties=properties, + goodies=goodies, + } + end +end +local function otftotfm(specification) + local cache_id=specification.hash + local tfmdata=containers.read(constructors.cache,cache_id) + if not tfmdata then + local name=specification.name + local sub=specification.sub + local filename=specification.filename + local features=specification.features.normal + local rawdata=otf.load(filename,sub,features and features.featurefile) + if rawdata and next(rawdata) then + local descriptions=rawdata.descriptions + local duplicates=rawdata.resources.duplicates + if duplicates then + local nofduplicates,nofduplicated=0,0 + for parent,list in next,duplicates do + for i=1,#list do + local unicode=list[i] + if not descriptions[unicode] then + descriptions[unicode]=descriptions[parent] + nofduplicated=nofduplicated+1 + end + end + nofduplicates=nofduplicates+#list + end + if trace_otf and nofduplicated~=nofduplicates then + report_otf("%i extra duplicates copied out of %i",nofduplicated,nofduplicates) + end + end + rawdata.lookuphash={} + tfmdata=copytotfm(rawdata,cache_id) + if tfmdata and next(tfmdata) then + local features=constructors.checkedfeatures("otf",features) + local shared=tfmdata.shared + if not shared then + shared={} + tfmdata.shared=shared + end + shared.rawdata=rawdata + shared.dynamics={} + tfmdata.changed={} + shared.features=features + shared.processes=otf.setfeatures(tfmdata,features) + end + end + containers.write(constructors.cache,cache_id,tfmdata) + end + return tfmdata +end +local function read_from_otf(specification) + local tfmdata=otftotfm(specification) + if tfmdata then + tfmdata.properties.name=specification.name + tfmdata.properties.sub=specification.sub + tfmdata=constructors.scale(tfmdata,specification) + local allfeatures=tfmdata.shared.features or specification.features.normal + constructors.applymanipulators("otf",tfmdata,allfeatures,trace_features,report_otf) + constructors.setname(tfmdata,specification) + fonts.loggers.register(tfmdata,file.suffix(specification.filename),specification) + end + return tfmdata +end +local function checkmathsize(tfmdata,mathsize) + local mathdata=tfmdata.shared.rawdata.metadata.math + local mathsize=tonumber(mathsize) + if mathdata then + local parameters=tfmdata.parameters + parameters.scriptpercentage=mathdata.ScriptPercentScaleDown + parameters.scriptscriptpercentage=mathdata.ScriptScriptPercentScaleDown + parameters.mathsize=mathsize + end +end +registerotffeature { + name="mathsize", + description="apply mathsize specified in the font", + initializers={ + base=checkmathsize, + node=checkmathsize, + } +} +function otf.collectlookups(rawdata,kind,script,language) + local sequences=rawdata.resources.sequences + if sequences then + local featuremap,featurelist={},{} + for s=1,#sequences do + local sequence=sequences[s] + local features=sequence.features + features=features and features[kind] + features=features and (features[script] or features[default] or features[wildcard]) + features=features and (features[language] or features[default] or features[wildcard]) + if features then + local subtables=sequence.subtables + if subtables then + for s=1,#subtables do + local ss=subtables[s] + if not featuremap[s] then + featuremap[ss]=true + featurelist[#featurelist+1]=ss + end + end + end + end + end + if #featurelist>0 then + return featuremap,featurelist + end + end + return nil,nil +end +local function check_otf(forced,specification,suffix) + local name=specification.name + if forced then + name=specification.forcedname + end + local fullname=findbinfile(name,suffix) or "" + if fullname=="" then + fullname=fonts.names.getfilename(name,suffix) or "" + end + if fullname~="" and not fonts.names.ignoredfile(fullname) then + specification.filename=fullname + return read_from_otf(specification) + end +end +local function opentypereader(specification,suffix) + local forced=specification.forced or "" + if formats[forced] then + return check_otf(true,specification,forced) + else + return check_otf(false,specification,suffix) + end +end +readers.opentype=opentypereader +function readers.otf (specification) return opentypereader(specification,"otf") end +function readers.ttf (specification) return opentypereader(specification,"ttf") end +function readers.ttc (specification) return opentypereader(specification,"ttf") end +function readers.dfont(specification) return opentypereader(specification,"ttf") end +function otf.scriptandlanguage(tfmdata,attr) + local properties=tfmdata.properties + return properties.script or "dflt",properties.language or "dflt" +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otb']={ + 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 concat=table.concat +local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip +local type,next,tonumber,tostring=type,next,tonumber,tostring +local lpegmatch=lpeg.match +local utfchar=utf.char +local trace_baseinit=false trackers.register("otf.baseinit",function(v) trace_baseinit=v end) +local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end) +local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end) +local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end) +local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end) +local trace_ligatures_detail=false trackers.register("otf.ligatures.detail",function(v) trace_ligatures_detail=v end) +local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end) +local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end) +local report_prepare=logs.reporter("fonts","otf prepare") +local fonts=fonts +local otf=fonts.handlers.otf +local otffeatures=otf.features +local registerotffeature=otffeatures.register +otf.defaultbasealternate="none" +local wildcard="*" +local default="dflt" +local formatters=string.formatters +local f_unicode=formatters["%U"] +local f_uniname=formatters["%U (%s)"] +local f_unilist=formatters["% t (% t)"] +local function gref(descriptions,n) + if type(n)=="number" then + local name=descriptions[n].name + if name then + return f_uniname(n,name) + else + return f_unicode(n) + end + elseif n then + local num,nam={},{} + for i=2,#n do + local ni=n[i] + if tonumber(ni) then + local di=descriptions[ni] + num[i]=f_unicode(ni) + nam[i]=di and di.name or "-" + end + end + return f_unilist(num,nam) + else + return "" + end +end +local function cref(feature,lookupname) + if lookupname then + return formatters["feature %a, lookup %a"](feature,lookupname) + else + return formatters["feature %a"](feature) + end +end +local function report_alternate(feature,lookupname,descriptions,unicode,replacement,value,comment) + report_prepare("%s: base alternate %s => %s (%S => %S)", + cref(feature,lookupname), + gref(descriptions,unicode), + replacement and gref(descriptions,replacement), + value, + comment) +end +local function report_substitution(feature,lookupname,descriptions,unicode,substitution) + report_prepare("%s: base substitution %s => %S", + cref(feature,lookupname), + gref(descriptions,unicode), + gref(descriptions,substitution)) +end +local function report_ligature(feature,lookupname,descriptions,unicode,ligature) + report_prepare("%s: base ligature %s => %S", + cref(feature,lookupname), + gref(descriptions,ligature), + gref(descriptions,unicode)) +end +local function report_kern(feature,lookupname,descriptions,unicode,otherunicode,value) + report_prepare("%s: base kern %s + %s => %S", + cref(feature,lookupname), + gref(descriptions,unicode), + gref(descriptions,otherunicode), + value) +end +local basemethods={} +local basemethod="" +local function applybasemethod(what,...) + local m=basemethods[basemethod][what] + if m then + return m(...) + end +end +local basehash,basehashes,applied={},1,{} +local function registerbasehash(tfmdata) + local properties=tfmdata.properties + local hash=concat(applied," ") + local base=basehash[hash] + if not base then + basehashes=basehashes+1 + base=basehashes + basehash[hash]=base + end + properties.basehash=base + properties.fullname=properties.fullname.."-"..base + applied={} +end +local function registerbasefeature(feature,value) + applied[#applied+1]=feature.."="..tostring(value) +end +local trace=false +local function finalize_ligatures(tfmdata,ligatures) + local nofligatures=#ligatures + if nofligatures>0 then + local characters=tfmdata.characters + local descriptions=tfmdata.descriptions + local resources=tfmdata.resources + local unicodes=resources.unicodes + local private=resources.private + local alldone=false + while not alldone do + local done=0 + for i=1,nofligatures do + local ligature=ligatures[i] + if ligature then + local unicode,lookupdata=ligature[1],ligature[2] + if trace then + trace_ligatures_detail("building % a into %a",lookupdata,unicode) + end + local size=#lookupdata + local firstcode=lookupdata[1] + local firstdata=characters[firstcode] + local okay=false + if firstdata then + local firstname="ctx_"..firstcode + for i=1,size-1 do + local firstdata=characters[firstcode] + if not firstdata then + firstcode=private + if trace then + trace_ligatures_detail("defining %a as %a",firstname,firstcode) + end + unicodes[firstname]=firstcode + firstdata={ intermediate=true,ligatures={} } + characters[firstcode]=firstdata + descriptions[firstcode]={ name=firstname } + private=private+1 + end + local target + local secondcode=lookupdata[i+1] + local secondname=firstname.."_"..secondcode + if i==size-1 then + target=unicode + if not unicodes[secondname] then + unicodes[secondname]=unicode + end + okay=true + else + target=unicodes[secondname] + if not target then + break + end + end + if trace then + trace_ligatures_detail("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target) + end + local firstligs=firstdata.ligatures + if firstligs then + firstligs[secondcode]={ char=target } + else + firstdata.ligatures={ [secondcode]={ char=target } } + end + firstcode=target + firstname=secondname + end + end + if okay then + ligatures[i]=false + done=done+1 + end + end + end + alldone=done==0 + end + if trace then + for k,v in next,characters do + if v.ligatures then table.print(v,k) end + end + end + tfmdata.resources.private=private + end +end +local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) + local characters=tfmdata.characters + local descriptions=tfmdata.descriptions + local resources=tfmdata.resources + local changed=tfmdata.changed + local unicodes=resources.unicodes + local lookuphash=resources.lookuphash + local lookuptypes=resources.lookuptypes + local ligatures={} + local alternate=tonumber(value) + local defaultalt=otf.defaultbasealternate + local trace_singles=trace_baseinit and trace_singles + local trace_alternatives=trace_baseinit and trace_alternatives + local trace_ligatures=trace_baseinit and trace_ligatures + local actions={ + substitution=function(lookupdata,lookupname,description,unicode) + if trace_singles then + report_substitution(feature,lookupname,descriptions,unicode,lookupdata) + end + changed[unicode]=lookupdata + end, + alternate=function(lookupdata,lookupname,description,unicode) + local replacement=lookupdata[alternate] + if replacement then + changed[unicode]=replacement + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal") + end + elseif defaultalt=="first" then + replacement=lookupdata[1] + changed[unicode]=replacement + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) + end + elseif defaultalt=="last" then + replacement=lookupdata[#data] + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) + end + else + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown") + end + end + end, + ligature=function(lookupdata,lookupname,description,unicode) + if trace_ligatures then + report_ligature(feature,lookupname,descriptions,unicode,lookupdata) + end + ligatures[#ligatures+1]={ unicode,lookupdata } + end, + } + for unicode,character in next,characters do + local description=descriptions[unicode] + local lookups=description.slookups + if lookups then + for l=1,#lookuplist do + local lookupname=lookuplist[l] + local lookupdata=lookups[lookupname] + if lookupdata then + local lookuptype=lookuptypes[lookupname] + local action=actions[lookuptype] + if action then + action(lookupdata,lookupname,description,unicode) + end + end + end + end + local lookups=description.mlookups + if lookups then + for l=1,#lookuplist do + local lookupname=lookuplist[l] + local lookuplist=lookups[lookupname] + if lookuplist then + local lookuptype=lookuptypes[lookupname] + local action=actions[lookuptype] + if action then + for i=1,#lookuplist do + action(lookuplist[i],lookupname,description,unicode) + end + end + end + end + end + end + finalize_ligatures(tfmdata,ligatures) +end +local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) + local characters=tfmdata.characters + local descriptions=tfmdata.descriptions + local resources=tfmdata.resources + local unicodes=resources.unicodes + local sharedkerns={} + local traceindeed=trace_baseinit and trace_kerns + for unicode,character in next,characters do + local description=descriptions[unicode] + local rawkerns=description.kerns + if rawkerns then + local s=sharedkerns[rawkerns] + if s==false then + elseif s then + character.kerns=s + else + local newkerns=character.kerns + local done=false + for l=1,#lookuplist do + local lookup=lookuplist[l] + local kerns=rawkerns[lookup] + if kerns then + for otherunicode,value in next,kerns do + if value==0 then + elseif not newkerns then + newkerns={ [otherunicode]=value } + done=true + if traceindeed then + report_kern(feature,lookup,descriptions,unicode,otherunicode,value) + end + elseif not newkerns[otherunicode] then + newkerns[otherunicode]=value + done=true + if traceindeed then + report_kern(feature,lookup,descriptions,unicode,otherunicode,value) + end + end + end + end + end + if done then + sharedkerns[rawkerns]=newkerns + character.kerns=newkerns + else + sharedkerns[rawkerns]=false + end + end + end + end +end +basemethods.independent={ + preparesubstitutions=preparesubstitutions, + preparepositionings=preparepositionings, +} +local function makefake(tfmdata,name,present) + local resources=tfmdata.resources + local private=resources.private + local character={ intermediate=true,ligatures={} } + resources.unicodes[name]=private + tfmdata.characters[private]=character + tfmdata.descriptions[private]={ name=name } + resources.private=private+1 + present[name]=private + return character +end +local function make_1(present,tree,name) + for k,v in next,tree do + if k=="ligature" then + present[name]=v + else + make_1(present,v,name.."_"..k) + end + end +end +local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done,lookupname) + for k,v in next,tree do + if k=="ligature" then + local character=characters[preceding] + if not character then + if trace_baseinit then + report_prepare("weird ligature in lookup %a, current %C, preceding %C",lookupname,v,preceding) + end + character=makefake(tfmdata,name,present) + end + local ligatures=character.ligatures + if ligatures then + ligatures[unicode]={ char=v } + else + character.ligatures={ [unicode]={ char=v } } + end + if done then + local d=done[lookupname] + if not d then + done[lookupname]={ "dummy",v } + else + d[#d+1]=v + end + end + else + local code=present[name] or unicode + local name=name.."_"..k + make_2(present,tfmdata,characters,v,name,code,k,done,lookupname) + end + end +end +local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) + local characters=tfmdata.characters + local descriptions=tfmdata.descriptions + local resources=tfmdata.resources + local changed=tfmdata.changed + local lookuphash=resources.lookuphash + local lookuptypes=resources.lookuptypes + local ligatures={} + local alternate=tonumber(value) + local defaultalt=otf.defaultbasealternate + local trace_singles=trace_baseinit and trace_singles + local trace_alternatives=trace_baseinit and trace_alternatives + local trace_ligatures=trace_baseinit and trace_ligatures + for l=1,#lookuplist do + local lookupname=lookuplist[l] + local lookupdata=lookuphash[lookupname] + local lookuptype=lookuptypes[lookupname] + for unicode,data in next,lookupdata do + if lookuptype=="substitution" then + if trace_singles then + report_substitution(feature,lookupname,descriptions,unicode,data) + end + changed[unicode]=data + elseif lookuptype=="alternate" then + local replacement=data[alternate] + if replacement then + changed[unicode]=replacement + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal") + end + elseif defaultalt=="first" then + replacement=data[1] + changed[unicode]=replacement + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) + end + elseif defaultalt=="last" then + replacement=data[#data] + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) + end + else + if trace_alternatives then + report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown") + end + end + elseif lookuptype=="ligature" then + ligatures[#ligatures+1]={ unicode,data,lookupname } + if trace_ligatures then + report_ligature(feature,lookupname,descriptions,unicode,data) + end + end + end + end + local nofligatures=#ligatures + if nofligatures>0 then + local characters=tfmdata.characters + local present={} + local done=trace_baseinit and trace_ligatures and {} + for i=1,nofligatures do + local ligature=ligatures[i] + local unicode,tree=ligature[1],ligature[2] + make_1(present,tree,"ctx_"..unicode) + end + for i=1,nofligatures do + local ligature=ligatures[i] + local unicode,tree,lookupname=ligature[1],ligature[2],ligature[3] + make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,lookupname) + end + end +end +local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) + local characters=tfmdata.characters + local descriptions=tfmdata.descriptions + local resources=tfmdata.resources + local lookuphash=resources.lookuphash + local traceindeed=trace_baseinit and trace_kerns + for l=1,#lookuplist do + local lookupname=lookuplist[l] + local lookupdata=lookuphash[lookupname] + for unicode,data in next,lookupdata do + local character=characters[unicode] + local kerns=character.kerns + if not kerns then + kerns={} + character.kerns=kerns + end + if traceindeed then + for otherunicode,kern in next,data do + if not kerns[otherunicode] and kern~=0 then + kerns[otherunicode]=kern + report_kern(feature,lookup,descriptions,unicode,otherunicode,kern) + end + end + else + for otherunicode,kern in next,data do + if not kerns[otherunicode] and kern~=0 then + kerns[otherunicode]=kern + end + end + end + end + end +end +local function initializehashes(tfmdata) + nodeinitializers.features(tfmdata) +end +basemethods.shared={ + initializehashes=initializehashes, + preparesubstitutions=preparesubstitutions, + preparepositionings=preparepositionings, +} +basemethod="independent" +local function featuresinitializer(tfmdata,value) + if true then + local t=trace_preparing and os.clock() + local features=tfmdata.shared.features + if features then + applybasemethod("initializehashes",tfmdata) + local collectlookups=otf.collectlookups + local rawdata=tfmdata.shared.rawdata + local properties=tfmdata.properties + local script=properties.script + local language=properties.language + local basesubstitutions=rawdata.resources.features.gsub + local basepositionings=rawdata.resources.features.gpos + if basesubstitutions then + for feature,data in next,basesubstitutions do + local value=features[feature] + if value then + local validlookups,lookuplist=collectlookups(rawdata,feature,script,language) + if validlookups then + applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist) + registerbasefeature(feature,value) + end + end + end + end + if basepositionings then + for feature,data in next,basepositionings do + local value=features[feature] + if value then + local validlookups,lookuplist=collectlookups(rawdata,feature,script,language) + if validlookups then + applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist) + registerbasefeature(feature,value) + end + end + end + end + registerbasehash(tfmdata) + end + if trace_preparing then + report_prepare("preparation time is %0.3f seconds for %a",os.clock()-t,tfmdata.properties.fullname) + end + end +end +registerotffeature { + name="features", + description="features", + default=true, + initializers={ + base=featuresinitializer, + } +} +directives.register("fonts.otf.loader.basemethod",function(v) + if basemethods[v] then + basemethod=v + end +end) + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['node-inj']={ + version=1.001, + comment="companion to node-ini.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files", +} +local next=next +local utfchar=utf.char +local trace_injections=false trackers.register("nodes.injections",function(v) trace_injections=v end) +local report_injections=logs.reporter("nodes","injections") +local attributes,nodes,node=attributes,nodes,node +fonts=fonts +local fontdata=fonts.hashes.identifiers +nodes.injections=nodes.injections or {} +local injections=nodes.injections +local nodecodes=nodes.nodecodes +local glyph_code=nodecodes.glyph +local kern_code=nodecodes.kern +local nodepool=nodes.pool +local newkern=nodepool.kern +local traverse_id=node.traverse_id +local insert_node_before=node.insert_before +local insert_node_after=node.insert_after +local a_kernpair=attributes.private('kernpair') +local a_ligacomp=attributes.private('ligacomp') +local a_markbase=attributes.private('markbase') +local a_markmark=attributes.private('markmark') +local a_markdone=attributes.private('markdone') +local a_cursbase=attributes.private('cursbase') +local a_curscurs=attributes.private('curscurs') +local a_cursdone=attributes.private('cursdone') +function injections.installnewkern(nk) + newkern=nk or newkern +end +local cursives={} +local marks={} +local kerns={} +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) + local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2]) + local ws,wn=tfmstart.width,tfmnext.width + local bound=#cursives+1 + start[a_cursbase]=bound + nxt[a_curscurs]=bound + cursives[bound]={ rlmode,dx,dy,ws,wn } + return dx,dy,bound +end +function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) + local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4] + if x~=0 or w~=0 or y~=0 or h~=0 then + local bound=current[a_kernpair] + if bound then + local kb=kerns[bound] + kb[2],kb[3],kb[4],kb[5]=(kb[2] or 0)+x,(kb[3] or 0)+y,(kb[4] or 0)+w,(kb[5] or 0)+h + else + bound=#kerns+1 + current[a_kernpair]=bound + kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width } + end + return x,y,w,h,bound + end + return x,y,w,h +end +function injections.setkern(current,factor,rlmode,x,tfmchr) + local dx=factor*x + if dx~=0 then + local bound=#kerns+1 + current[a_kernpair]=bound + kerns[bound]={ rlmode,dx } + return dx,bound + else + return 0,0 + end +end +function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) + local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) + local bound=base[a_markbase] + local index=1 + if bound then + local mb=marks[bound] + if mb then + index=#mb+1 + mb[index]={ dx,dy,rlmode } + start[a_markmark]=bound + start[a_markdone]=index + return dx,dy,bound + else + report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound) + end + end + index=index or 1 + bound=#marks+1 + base[a_markbase]=bound + start[a_markmark]=bound + start[a_markdone]=index + marks[bound]={ [index]={ dx,dy,rlmode,baseismark } } + return dx,dy,bound +end +local function dir(n) + return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" +end +local function trace(head) + report_injections("begin run") + for n in traverse_id(glyph_code,head) do + if n.subtype<256 then + local kp=n[a_kernpair] + local mb=n[a_markbase] + local mm=n[a_markmark] + local md=n[a_markdone] + local cb=n[a_cursbase] + local cc=n[a_curscurs] + local char=n.char + report_injections("font %s, char %U, glyph %c",n.font,char,char) + if kp then + local k=kerns[kp] + if k[3] then + report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5]) + else + report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2]) + end + end + if mb then + report_injections(" markbase: bound %a",mb) + end + if mm then + local m=marks[mm] + if mb then + local m=m[mb] + if m then + report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2]) + else + report_injections(" markmark: bound %a, missing index",mm) + end + else + m=m[1] + report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2]) + end + end + if cb then + report_injections(" cursbase: bound %a",cb) + end + if cc then + local c=cursives[cc] + report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3]) + end + end + end + report_injections("end run") +end +local function show_result(head) + local current=head + local skipping=false + while current do + local id=current.id + if id==glyph_code then + report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset) + skipping=false + elseif id==kern_code then + report_injections("kern: %p",current.kern) + skipping=false + elseif not skipping then + report_injections() + skipping=true + end + current=current.next + end +end +function injections.handler(head,where,keep) + local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns) + if has_marks or has_cursives then + if trace_injections then + trace(head) + end + local done,ky,rl,valid,cx,wx,mk,nofvalid=false,{},{},{},{},{},{},0 + if has_kerns then + local nf,tm=nil,nil + for n in traverse_id(glyph_code,head) do + if n.subtype<256 then + nofvalid=nofvalid+1 + valid[nofvalid]=n + if n.font~=nf then + nf=n.font + tm=fontdata[nf].resources.marks + end + if tm then + mk[n]=tm[n.char] + end + local k=n[a_kernpair] + if k then + local kk=kerns[k] + if kk then + local x,y,w,h=kk[2] or 0,kk[3] or 0,kk[4] or 0,kk[5] or 0 + local dy=y-h + if dy~=0 then + ky[n]=dy + end + if w~=0 or x~=0 then + wx[n]=kk + end + rl[n]=kk[1] + end + end + end + end + else + local nf,tm=nil,nil + for n in traverse_id(glyph_code,head) do + if n.subtype<256 then + nofvalid=nofvalid+1 + valid[nofvalid]=n + if n.font~=nf then + nf=n.font + tm=fontdata[nf].resources.marks + end + if tm then + mk[n]=tm[n.char] + end + end + end + end + if nofvalid>0 then + local cx={} + if has_kerns and next(ky) then + for n,k in next,ky do + n.yoffset=k + end + end + if has_cursives then + local p_cursbase,p=nil,nil + local t,d,maxt={},{},0 + for i=1,nofvalid do + local n=valid[i] + if not mk[n] then + local n_cursbase=n[a_cursbase] + if p_cursbase then + local n_curscurs=n[a_curscurs] + if p_cursbase==n_curscurs then + local c=cursives[n_curscurs] + if c then + local rlmode,dx,dy,ws,wn=c[1],c[2],c[3],c[4],c[5] + if rlmode>=0 then + dx=dx-ws + else + dx=dx+wn + end + if dx~=0 then + cx[n]=dx + rl[n]=rlmode + end + dy=-dy + maxt=maxt+1 + t[maxt]=p + d[maxt]=dy + else + maxt=0 + end + end + elseif maxt>0 then + local ny=n.yoffset + for i=maxt,1,-1 do + ny=ny+d[i] + local ti=t[i] + ti.yoffset=ti.yoffset+ny + end + maxt=0 + end + if not n_cursbase and maxt>0 then + local ny=n.yoffset + for i=maxt,1,-1 do + ny=ny+d[i] + local ti=t[i] + ti.yoffset=ny + end + maxt=0 + end + p_cursbase,p=n_cursbase,n + end + end + if maxt>0 then + local ny=n.yoffset + for i=maxt,1,-1 do + ny=ny+d[i] + local ti=t[i] + ti.yoffset=ny + end + maxt=0 + end + if not keep then + cursives={} + end + end + if has_marks then + for i=1,nofvalid do + local p=valid[i] + local p_markbase=p[a_markbase] + if p_markbase then + local mrks=marks[p_markbase] + local nofmarks=#mrks + for n in traverse_id(glyph_code,p.next) do + local n_markmark=n[a_markmark] + if p_markbase==n_markmark then + local index=n[a_markdone] or 1 + local d=mrks[index] + if d then + local rlmode=d[3] + local k=wx[p] + if k then + local x=k[2] + local w=k[4] + if w then + if rlmode and rlmode>=0 then + n.xoffset=p.xoffset-p.width+d[1]-(w-x) + else + n.xoffset=p.xoffset-d[1]-x + end + else + if rlmode and rlmode>=0 then + n.xoffset=p.xoffset-p.width+d[1] + else + n.xoffset=p.xoffset-d[1]-x + end + end + else + if rlmode and rlmode>=0 then + n.xoffset=p.xoffset-p.width+d[1] + else + n.xoffset=p.xoffset-d[1] + end + local w=n.width + if w~=0 then + insert_node_before(head,n,newkern(-w/2)) + insert_node_after(head,n,newkern(-w/2)) + end + end + if mk[p] then + n.yoffset=p.yoffset+d[2] + else + n.yoffset=n.yoffset+p.yoffset+d[2] + end + if nofmarks==1 then + break + else + nofmarks=nofmarks-1 + end + end + else + end + end + end + end + if not keep then + marks={} + end + end + if next(wx) then + for n,k in next,wx do + local x=k[2] + local w=k[4] + if w then + local rl=k[1] + local wx=w-x + if rl<0 then + if wx~=0 then + insert_node_before(head,n,newkern(wx)) + end + if x~=0 then + insert_node_after (head,n,newkern(x)) + end + else + if x~=0 then + insert_node_before(head,n,newkern(x)) + end + if wx~=0 then + insert_node_after (head,n,newkern(wx)) + end + end + elseif x~=0 then + insert_node_before(head,n,newkern(x)) + end + end + end + if next(cx) then + for n,k in next,cx do + if k~=0 then + local rln=rl[n] + if rln and rln<0 then + insert_node_before(head,n,newkern(-k)) + else + insert_node_before(head,n,newkern(k)) + end + end + end + end + if not keep then + kerns={} + end + return head,true + elseif not keep then + kerns,cursives,marks={},{},{} + end + elseif has_kerns then + if trace_injections then + trace(head) + end + for n in traverse_id(glyph_code,head) do + if n.subtype<256 then + local k=n[a_kernpair] + if k then + local kk=kerns[k] + if kk then + local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4] + if y and y~=0 then + n.yoffset=y + end + if w then + local wx=w-x + if rl<0 then + if wx~=0 then + insert_node_before(head,n,newkern(wx)) + end + if x~=0 then + insert_node_after (head,n,newkern(x)) + end + else + if x~=0 then + insert_node_before(head,n,newkern(x)) + end + if wx~=0 then + insert_node_after(head,n,newkern(wx)) + end + end + else + if x~=0 then + insert_node_before(head,n,newkern(x)) + end + end + end + end + end + end + if not keep then + kerns={} + end + return head,true + else + end + return head,false +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-ota']={ + version=1.001, + comment="companion to font-otf.lua (analysing)", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local type=type +if not trackers then trackers={ register=function() end } end +local fonts,nodes,node=fonts,nodes,node +local allocate=utilities.storage.allocate +local otf=fonts.handlers.otf +local analyzers=fonts.analyzers +local initializers=allocate() +local methods=allocate() +analyzers.initializers=initializers +analyzers.methods=methods +analyzers.useunicodemarks=false +local a_state=attributes.private('state') +local nodecodes=nodes.nodecodes +local glyph_code=nodecodes.glyph +local disc_code=nodecodes.disc +local math_code=nodecodes.math +local traverse_id=node.traverse_id +local traverse_node_list=node.traverse +local end_of_math=node.end_of_math +local fontdata=fonts.hashes.identifiers +local categories=characters and characters.categories or {} +local otffeatures=fonts.constructors.newfeatures("otf") +local registerotffeature=otffeatures.register +local s_init=1 local s_rphf=7 +local s_medi=2 local s_half=8 +local s_fina=3 local s_pref=9 +local s_isol=4 local s_blwf=10 +local s_mark=5 local s_pstf=11 +local s_rest=6 +local states={ + init=s_init, + medi=s_medi, + fina=s_fina, + isol=s_isol, + mark=s_mark, + rest=s_rest, + rphf=s_rphf, + half=s_half, + pref=s_pref, + blwf=s_blwf, + pstf=s_pstf, +} +local features={ + init=s_init, + medi=s_medi, + fina=s_fina, + isol=s_isol, + rphf=s_rphf, + half=s_half, + pref=s_pref, + blwf=s_blwf, + pstf=s_pstf, +} +analyzers.states=states +analyzers.features=features +function analyzers.setstate(head,font) + local useunicodemarks=analyzers.useunicodemarks + local tfmdata=fontdata[font] + local descriptions=tfmdata.descriptions + local first,last,current,n,done=nil,nil,head,0,false + while current do + local id=current.id + if id==glyph_code and current.font==font then + done=true + local char=current.char + local d=descriptions[char] + if d then + if d.class=="mark" or (useunicodemarks and categories[char]=="mn") then + done=true + current[a_state]=s_mark + elseif n==0 then + first,last,n=current,current,1 + current[a_state]=s_init + else + last,n=current,n+1 + current[a_state]=s_medi + end + else + if first and first==last then + last[a_state]=s_isol + elseif last then + last[a_state]=s_fina + end + first,last,n=nil,nil,0 + end + elseif id==disc_code then + current[a_state]=s_medi + last=current + else + if first and first==last then + last[a_state]=s_isol + elseif last then + last[a_state]=s_fina + end + first,last,n=nil,nil,0 + if id==math_code then + current=end_of_math(current) + end + end + current=current.next + end + if first and first==last then + last[a_state]=s_isol + elseif last then + last[a_state]=s_fina + end + return head,done +end +local function analyzeinitializer(tfmdata,value) + local script,language=otf.scriptandlanguage(tfmdata) + local action=initializers[script] + if not action then + elseif type(action)=="function" then + return action(tfmdata,value) + else + local action=action[language] + if action then + return action(tfmdata,value) + end + end +end +local function analyzeprocessor(head,font,attr) + local tfmdata=fontdata[font] + local script,language=otf.scriptandlanguage(tfmdata,attr) + local action=methods[script] + if not action then + elseif type(action)=="function" then + return action(head,font,attr) + else + action=action[language] + if action then + return action(head,font,attr) + end + end + return head,false +end +registerotffeature { + name="analyze", + description="analysis of character classes", + default=true, + initializers={ + node=analyzeinitializer, + }, + processors={ + position=1, + node=analyzeprocessor, + } +} +methods.latn=analyzers.setstate +local tatweel=0x0640 +local zwnj=0x200C +local zwj=0x200D +local isolated={ + [0x0600]=true,[0x0601]=true,[0x0602]=true,[0x0603]=true, + [0x0604]=true, + [0x0608]=true,[0x060B]=true,[0x0621]=true,[0x0674]=true, + [0x06DD]=true, + [0x0856]=true,[0x0858]=true,[0x0857]=true, + [0x07FA]=true, + [zwnj]=true, +} +local final={ + [0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true, + [0x0627]=true,[0x0629]=true,[0x062F]=true,[0x0630]=true, + [0x0631]=true,[0x0632]=true,[0x0648]=true,[0x0671]=true, + [0x0672]=true,[0x0673]=true,[0x0675]=true,[0x0676]=true, + [0x0677]=true,[0x0688]=true,[0x0689]=true,[0x068A]=true, + [0x068B]=true,[0x068C]=true,[0x068D]=true,[0x068E]=true, + [0x068F]=true,[0x0690]=true,[0x0691]=true,[0x0692]=true, + [0x0693]=true,[0x0694]=true,[0x0695]=true,[0x0696]=true, + [0x0697]=true,[0x0698]=true,[0x0699]=true,[0x06C0]=true, + [0x06C3]=true,[0x06C4]=true,[0x06C5]=true,[0x06C6]=true, + [0x06C7]=true,[0x06C8]=true,[0x06C9]=true,[0x06CA]=true, + [0x06CB]=true,[0x06CD]=true,[0x06CF]=true,[0x06D2]=true, + [0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true, + [0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true, + [0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true, + [0x0778]=true,[0x0779]=true, + [0x08AA]=true,[0x08AB]=true,[0x08AC]=true, + [0xFEF5]=true,[0xFEF7]=true,[0xFEF9]=true,[0xFEFB]=true, + [0x0710]=true,[0x0715]=true,[0x0716]=true,[0x0717]=true, + [0x0718]=true,[0x0719]=true,[0x0728]=true,[0x072A]=true, + [0x072C]=true,[0x071E]=true, + [0x072F]=true,[0x074D]=true, + [0x0840]=true,[0x0849]=true,[0x0854]=true,[0x0846]=true, + [0x084F]=true +} +local medial={ + [0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true, + [0x062C]=true,[0x062D]=true,[0x062E]=true,[0x0633]=true, + [0x0634]=true,[0x0635]=true,[0x0636]=true,[0x0637]=true, + [0x0638]=true,[0x0639]=true,[0x063A]=true,[0x063B]=true, + [0x063C]=true,[0x063D]=true,[0x063E]=true,[0x063F]=true, + [0x0641]=true,[0x0642]=true,[0x0643]=true, + [0x0644]=true,[0x0645]=true,[0x0646]=true,[0x0647]=true, + [0x0649]=true,[0x064A]=true,[0x066E]=true,[0x066F]=true, + [0x0678]=true,[0x0679]=true,[0x067A]=true,[0x067B]=true, + [0x067C]=true,[0x067D]=true,[0x067E]=true,[0x067F]=true, + [0x0680]=true,[0x0681]=true,[0x0682]=true,[0x0683]=true, + [0x0684]=true,[0x0685]=true,[0x0686]=true,[0x0687]=true, + [0x069A]=true,[0x069B]=true,[0x069C]=true,[0x069D]=true, + [0x069E]=true,[0x069F]=true,[0x06A0]=true,[0x06A1]=true, + [0x06A2]=true,[0x06A3]=true,[0x06A4]=true,[0x06A5]=true, + [0x06A6]=true,[0x06A7]=true,[0x06A8]=true,[0x06A9]=true, + [0x06AA]=true,[0x06AB]=true,[0x06AC]=true,[0x06AD]=true, + [0x06AE]=true,[0x06AF]=true,[0x06B0]=true,[0x06B1]=true, + [0x06B2]=true,[0x06B3]=true,[0x06B4]=true,[0x06B5]=true, + [0x06B6]=true,[0x06B7]=true,[0x06B8]=true,[0x06B9]=true, + [0x06BA]=true,[0x06BB]=true,[0x06BC]=true,[0x06BD]=true, + [0x06BE]=true,[0x06BF]=true,[0x06C1]=true,[0x06C2]=true, + [0x06CC]=true,[0x06CE]=true,[0x06D0]=true,[0x06D1]=true, + [0x06FA]=true,[0x06FB]=true,[0x06FC]=true,[0x06FF]=true, + [0x0750]=true,[0x0751]=true,[0x0752]=true,[0x0753]=true, + [0x0754]=true,[0x0755]=true,[0x0756]=true,[0x0757]=true, + [0x0758]=true,[0x075C]=true,[0x075D]=true,[0x075E]=true, + [0x075F]=true,[0x0760]=true,[0x0761]=true,[0x0762]=true, + [0x0763]=true,[0x0764]=true,[0x0765]=true,[0x0766]=true, + [0x0767]=true,[0x0768]=true,[0x0769]=true,[0x076A]=true, + [0x076D]=true,[0x076E]=true,[0x076F]=true,[0x0770]=true, + [0x0772]=true,[0x0775]=true,[0x0776]=true,[0x0777]=true, + [0x077A]=true,[0x077B]=true,[0x077C]=true,[0x077D]=true, + [0x077E]=true,[0x077F]=true, + [0x08A0]=true,[0x08A2]=true,[0x08A4]=true,[0x08A5]=true, + [0x08A6]=true,[0x0620]=true,[0x08A8]=true,[0x08A9]=true, + [0x08A7]=true,[0x08A3]=true, + [0x0712]=true,[0x0713]=true,[0x0714]=true,[0x071A]=true, + [0x071B]=true,[0x071C]=true,[0x071D]=true,[0x071F]=true, + [0x0720]=true,[0x0721]=true,[0x0722]=true,[0x0723]=true, + [0x0724]=true,[0x0725]=true,[0x0726]=true,[0x0727]=true, + [0x0729]=true,[0x072B]=true,[0x072D]=true,[0x072E]=true, + [0x074E]=true,[0x074F]=true, + [0x0841]=true,[0x0842]=true,[0x0843]=true,[0x0844]=true, + [0x0845]=true,[0x0847]=true,[0x0848]=true,[0x0855]=true, + [0x0851]=true,[0x084E]=true,[0x084D]=true,[0x084A]=true, + [0x084B]=true,[0x084C]=true,[0x0850]=true,[0x0852]=true, + [0x0853]=true, + [0x07D7]=true,[0x07E8]=true,[0x07D9]=true,[0x07EA]=true, + [0x07CA]=true,[0x07DB]=true,[0x07CC]=true,[0x07DD]=true, + [0x07CE]=true,[0x07DF]=true,[0x07D4]=true,[0x07E5]=true, + [0x07E9]=true,[0x07E7]=true,[0x07E3]=true,[0x07E2]=true, + [0x07E0]=true,[0x07E1]=true,[0x07DE]=true,[0x07DC]=true, + [0x07D1]=true,[0x07DA]=true,[0x07D8]=true,[0x07D6]=true, + [0x07D2]=true,[0x07D0]=true,[0x07CF]=true,[0x07CD]=true, + [0x07CB]=true,[0x07D3]=true,[0x07E4]=true,[0x07D5]=true, + [0x07E6]=true, + [tatweel]=true, + [zwj]=true, +} +local arab_warned={} +local function warning(current,what) + local char=current.char + if not arab_warned[char] then + log.report("analyze","arab: character %C has no %a class",char,what) + arab_warned[char]=true + end +end +local function finish(first,last) + if last then + if first==last then + local fc=first.char + if medial[fc] or final[fc] then + first[a_state]=s_isol + else + warning(first,"isol") + first[a_state]=s_error + end + else + local lc=last.char + if medial[lc] or final[lc] then + last[a_state]=s_fina + else + warning(last,"fina") + last[a_state]=s_error + end + end + first,last=nil,nil + elseif first then + local fc=first.char + if medial[fc] or final[fc] then + first[a_state]=s_isol + else + warning(first,"isol") + first[a_state]=s_error + end + first=nil + end + return first,last +end +function methods.arab(head,font,attr) + local useunicodemarks=analyzers.useunicodemarks + local tfmdata=fontdata[font] + local marks=tfmdata.resources.marks + local first,last,current,done=nil,nil,head,false + while current do + local id=current.id + if id==glyph_code and current.font==font and current.subtype<256 and not current[a_state] then + done=true + local char=current.char + if marks[char] or (useunicodemarks and categories[char]=="mn") then + current[a_state]=s_mark + elseif isolated[char] then + first,last=finish(first,last) + current[a_state]=s_isol + first,last=nil,nil + elseif not first then + if medial[char] then + current[a_state]=s_init + first,last=first or current,current + elseif final[char] then + current[a_state]=s_isol + first,last=nil,nil + else + first,last=finish(first,last) + end + elseif medial[char] then + first,last=first or current,current + current[a_state]=s_medi + elseif final[char] then + if not last[a_state]==s_init then + last[a_state]=s_medi + end + current[a_state]=s_fina + first,last=nil,nil + elseif char>=0x0600 and char<=0x06FF then + current[a_state]=s_rest + first,last=finish(first,last) + else + first,last=finish(first,last) + end + else + if first or last then + first,last=finish(first,last) + end + if id==math_code then + current=end_of_math(current) + end + end + current=current.next + end + if first or last then + finish(first,last) + end + return head,done +end +methods.syrc=methods.arab +methods.mand=methods.arab +methods.nko=methods.arab +directives.register("otf.analyze.useunicodemarks",function(v) + analyzers.useunicodemarks=v +end) + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otn']={ + 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 concat,insert,remove=table.concat,table.insert,table.remove +local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip +local type,next,tonumber,tostring=type,next,tonumber,tostring +local lpegmatch=lpeg.match +local random=math.random +local formatters=string.formatters +local logs,trackers,nodes,attributes=logs,trackers,nodes,attributes +local registertracker=trackers.register +local fonts=fonts +local otf=fonts.handlers.otf +local trace_lookups=false registertracker("otf.lookups",function(v) trace_lookups=v end) +local trace_singles=false registertracker("otf.singles",function(v) trace_singles=v end) +local trace_multiples=false registertracker("otf.multiples",function(v) trace_multiples=v end) +local trace_alternatives=false registertracker("otf.alternatives",function(v) trace_alternatives=v end) +local trace_ligatures=false registertracker("otf.ligatures",function(v) trace_ligatures=v end) +local trace_contexts=false registertracker("otf.contexts",function(v) trace_contexts=v end) +local trace_marks=false registertracker("otf.marks",function(v) trace_marks=v end) +local trace_kerns=false registertracker("otf.kerns",function(v) trace_kerns=v end) +local trace_cursive=false registertracker("otf.cursive",function(v) trace_cursive=v end) +local trace_preparing=false registertracker("otf.preparing",function(v) trace_preparing=v end) +local trace_bugs=false registertracker("otf.bugs",function(v) trace_bugs=v end) +local trace_details=false registertracker("otf.details",function(v) trace_details=v end) +local trace_applied=false registertracker("otf.applied",function(v) trace_applied=v end) +local trace_steps=false registertracker("otf.steps",function(v) trace_steps=v end) +local trace_skips=false registertracker("otf.skips",function(v) trace_skips=v end) +local trace_directions=false registertracker("otf.directions",function(v) trace_directions=v end) +local report_direct=logs.reporter("fonts","otf direct") +local report_subchain=logs.reporter("fonts","otf subchain") +local report_chain=logs.reporter("fonts","otf chain") +local report_process=logs.reporter("fonts","otf process") +local report_prepare=logs.reporter("fonts","otf prepare") +local report_warning=logs.reporter("fonts","otf warning") +registertracker("otf.verbose_chain",function(v) otf.setcontextchain(v and "verbose") end) +registertracker("otf.normal_chain",function(v) otf.setcontextchain(v and "normal") end) +registertracker("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures") +registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") +registertracker("otf.actions","otf.replacements,otf.positions") +registertracker("otf.injections","nodes.injections") +registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") +local insert_node_after=node.insert_after +local delete_node=nodes.delete +local copy_node=node.copy +local find_node_tail=node.tail or node.slide +local flush_node_list=node.flush_list +local end_of_math=node.end_of_math +local setmetatableindex=table.setmetatableindex +local zwnj=0x200C +local zwj=0x200D +local wildcard="*" +local default="dflt" +local nodecodes=nodes.nodecodes +local whatcodes=nodes.whatcodes +local glyphcodes=nodes.glyphcodes +local disccodes=nodes.disccodes +local glyph_code=nodecodes.glyph +local glue_code=nodecodes.glue +local disc_code=nodecodes.disc +local whatsit_code=nodecodes.whatsit +local math_code=nodecodes.math +local dir_code=whatcodes.dir +local localpar_code=whatcodes.localpar +local discretionary_code=disccodes.discretionary +local ligature_code=glyphcodes.ligature +local privateattribute=attributes.private +local a_state=privateattribute('state') +local a_markbase=privateattribute('markbase') +local a_markmark=privateattribute('markmark') +local a_markdone=privateattribute('markdone') +local a_cursbase=privateattribute('cursbase') +local a_curscurs=privateattribute('curscurs') +local a_cursdone=privateattribute('cursdone') +local a_kernpair=privateattribute('kernpair') +local a_ligacomp=privateattribute('ligacomp') +local injections=nodes.injections +local setmark=injections.setmark +local setcursive=injections.setcursive +local setkern=injections.setkern +local setpair=injections.setpair +local markonce=true +local cursonce=true +local kernonce=true +local fonthashes=fonts.hashes +local fontdata=fonthashes.identifiers +local otffeatures=fonts.constructors.newfeatures("otf") +local registerotffeature=otffeatures.register +local onetimemessage=fonts.loggers.onetimemessage or function() end +otf.defaultnodealternate="none" +local tfmdata=false +local characters=false +local descriptions=false +local resources=false +local marks=false +local currentfont=false +local lookuptable=false +local anchorlookups=false +local lookuptypes=false +local handlers={} +local rlmode=0 +local featurevalue=false +local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end +local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end +local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_direct(...) +end +local function logwarning(...) + report_direct(...) +end +local f_unicode=formatters["%U"] +local f_uniname=formatters["%U (%s)"] +local f_unilist=formatters["% t (% t)"] +local function gref(n) + if type(n)=="number" then + local description=descriptions[n] + local name=description and description.name + if name then + return f_uniname(n,name) + else + return f_unicode(n) + end + elseif n then + local num,nam={},{} + for i=1,#n do + local ni=n[i] + if tonumber(ni) then + local di=descriptions[ni] + num[i]=f_unicode(ni) + nam[i]=di and di.name or "-" + end + end + return f_unilist(num,nam) + else + return "" + end +end +local function cref(kind,chainname,chainlookupname,lookupname,index) + if index then + return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookupname,index) + elseif lookupname then + return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookupname) + elseif chainlookupname then + return formatters["feature %a, chain %a, sub %a"](kind,chainname,chainlookupname) + elseif chainname then + return formatters["feature %a, chain %a"](kind,chainname) + else + return formatters["feature %a"](kind) + end +end +local function pref(kind,lookupname) + return formatters["feature %a, lookup %a"](kind,lookupname) +end +local function copy_glyph(g) + local components=g.components + if components then + g.components=nil + local n=copy_node(g) + g.components=components + return n + else + return copy_node(g) + end +end +local function markstoligature(kind,lookupname,head,start,stop,char) + if start==stop and start.char==char then + return head,start + else + local prev=start.prev + local next=stop.next + start.prev=nil + stop.next=nil + local base=copy_glyph(start) + if head==start then + head=base + end + base.char=char + base.subtype=ligature_code + base.components=start + if prev then + prev.next=base + end + if next then + next.prev=base + end + base.next=next + base.prev=prev + return head,base + end +end +local function getcomponentindex(start) + if start.id~=glyph_code then + return 0 + elseif start.subtype==ligature_code then + local i=0 + local components=start.components + while components do + i=i+getcomponentindex(components) + components=components.next + end + return i + elseif not marks[start.char] then + return 1 + else + return 0 + end +end +local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) + if start==stop and start.char==char then + start.char=char + return head,start + end + local prev=start.prev + local next=stop.next + start.prev=nil + stop.next=nil + local base=copy_glyph(start) + if start==head then + head=base + end + base.char=char + base.subtype=ligature_code + base.components=start + if prev then + prev.next=base + end + if next then + next.prev=base + end + base.next=next + base.prev=prev + if not discfound then + local deletemarks=markflag~="mark" + local components=start + local baseindex=0 + local componentindex=0 + local head=base + local current=base + while start do + local char=start.char + if not marks[char] then + baseindex=baseindex+componentindex + componentindex=getcomponentindex(start) + elseif not deletemarks then + start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex) + if trace_marks then + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) + end + head,current=insert_node_after(head,current,copy_node(start)) + elseif trace_marks then + logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char)) + end + start=start.next + end + local start=current.next + while start and start.id==glyph_code do + local char=start.char + if marks[char] then + start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex) + if trace_marks then + logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) + end + else + break + end + start=start.next + end + end + return head,base +end +function handlers.gsub_single(head,start,kind,lookupname,replacement) + if trace_singles then + logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) + end + start.char=replacement + return head,start,true +end +local function get_alternative_glyph(start,alternatives,value,trace_alternatives) + local n=#alternatives + if value=="random" then + local r=random(1,n) + return alternatives[r],trace_alternatives and formatters["value %a, taking %a"](value,r) + elseif value=="first" then + return alternatives[1],trace_alternatives and formatters["value %a, taking %a"](value,1) + elseif value=="last" then + return alternatives[n],trace_alternatives and formatters["value %a, taking %a"](value,n) + else + value=tonumber(value) + if type(value)~="number" then + return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,1) + elseif value>n then + local defaultalt=otf.defaultnodealternate + if defaultalt=="first" then + return alternatives[n],trace_alternatives and formatters["invalid value %s, taking %a"](value,1) + elseif defaultalt=="last" then + return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,n) + else + return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range") + end + elseif value==0 then + return start.char,trace_alternatives and formatters["invalid value %a, %s"](value,"no change") + elseif value<1 then + return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1) + else + return alternatives[value],trace_alternatives and formatters["value %a, taking %a"](value,value) + end + end +end +local function multiple_glyphs(head,start,multiple,ignoremarks) + local nofmultiples=#multiple + if nofmultiples>0 then + start.char=multiple[1] + if nofmultiples>1 then + local sn=start.next + for k=2,nofmultiples do + local n=copy_node(start) + n.char=multiple[k] + n.next=sn + n.prev=start + if sn then + sn.prev=n + end + start.next=n + start=n + end + end + return head,start,true + else + if trace_multiples then + logprocess("no multiple for %s",gref(start.char)) + end + return head,start,false + end +end +function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence) + local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue + local choice,comment=get_alternative_glyph(start,alternative,value,trace_alternatives) + if choice then + if trace_alternatives then + logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment) + end + start.char=choice + else + if trace_alternatives then + logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment) + end + end + return head,start,true +end +function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence) + if trace_multiples then + logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) + end + return multiple_glyphs(head,start,multiple,sequence.flags[1]) +end +function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) + local s,stop,discfound=start.next,nil,false + local startchar=start.char + if marks[startchar] then + while s do + local id=s.id + if id==glyph_code and s.font==currentfont and s.subtype<256 then + local lg=ligature[s.char] + if lg then + stop=s + ligature=lg + s=s.next + else + break + end + else + break + end + end + if stop then + local lig=ligature.ligature + if lig then + if trace_ligatures then + local stopchar=stop.char + head,start=markstoligature(kind,lookupname,head,start,stop,lig) + logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) + else + head,start=markstoligature(kind,lookupname,head,start,stop,lig) + end + return head,start,true + else + end + end + else + local skipmark=sequence.flags[1] + while s do + local id=s.id + if id==glyph_code and s.subtype<256 then + if s.font==currentfont then + local char=s.char + if skipmark and marks[char] then + s=s.next + else + local lg=ligature[char] + if lg then + stop=s + ligature=lg + s=s.next + else + break + end + end + else + break + end + elseif id==disc_code then + discfound=true + s=s.next + else + break + end + end + local lig=ligature.ligature + if lig then + if stop then + if trace_ligatures then + local stopchar=stop.char + head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) + logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) + else + head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) + end + return head,start,true + else + start.char=lig + if trace_ligatures then + logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig)) + end + return head,start,true + end + else + end + end + return head,start,false +end +function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) + local markchar=start.char + if marks[markchar] then + local base=start.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char + if marks[basechar] then + while true do + base=base.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + basechar=base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return head,start,false + end + end + end + local baseanchors=descriptions[basechar] + if baseanchors then + baseanchors=baseanchors.anchors + end + if baseanchors then + local baseanchors=baseanchors['basechar'] + if baseanchors then + local al=anchorlookups[lookupname] + for anchor,ba in next,baseanchors do + if al[anchor] then + local ma=markanchors[anchor] + if ma then + local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head,start,true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + elseif trace_bugs then + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return head,start,false +end +function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence) + local markchar=start.char + if marks[markchar] then + local base=start.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char + if marks[basechar] then + while true do + base=base.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + basechar=base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return head,start,false + end + end + end + local index=start[a_ligacomp] + local baseanchors=descriptions[basechar] + if baseanchors then + baseanchors=baseanchors.anchors + if baseanchors then + local baseanchors=baseanchors['baselig'] + if baseanchors then + local al=anchorlookups[lookupname] + for anchor,ba in next,baseanchors do + if al[anchor] then + local ma=markanchors[anchor] + if ma then + ba=ba[index] + if ba then + local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", + pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) + end + return head,start,true + else + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index) + end + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return head,start,false +end +function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) + local markchar=start.char + if marks[markchar] then + local base=start.prev + local slc=start[a_ligacomp] + if slc then + while base do + local blc=base[a_ligacomp] + if blc and blc~=slc then + base=base.prev + else + break + end + end + end + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char + local baseanchors=descriptions[basechar] + if baseanchors then + baseanchors=baseanchors.anchors + if baseanchors then + baseanchors=baseanchors['basemark'] + if baseanchors then + local al=anchorlookups[lookupname] + for anchor,ba in next,baseanchors do + if al[anchor] then + local ma=markanchors[anchor] + if ma then + local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head,start,true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return head,start,false +end +function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) + local alreadydone=cursonce and start[a_cursbase] + if not alreadydone then + local done=false + local startchar=start.char + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt=start.next + while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do + local nextchar=nxt.char + if marks[nextchar] then + nxt=nxt.next + else + local entryanchors=descriptions[nextchar] + if entryanchors then + entryanchors=entryanchors.anchors + if entryanchors then + entryanchors=entryanchors['centry'] + if entryanchors then + local al=anchorlookups[lookupname] + for anchor,entry in next,entryanchors do + if al[anchor] then + local exit=exitanchors[anchor] + if exit then + local dx,dy,bound=setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + end + done=true + break + end + end + end + end + end + elseif trace_bugs then + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + end + break + end + end + end + return head,start,done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return head,start,false + end +end +function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) + local startchar=start.char + local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) + end + return head,start,false +end +function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) + local snext=start.next + if not snext then + return head,start,false + else + local prev,done=start,false + local factor=tfmdata.parameters.factor + local lookuptype=lookuptypes[lookupname] + while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do + local nextchar=snext.char + local krn=kerns[nextchar] + if not krn and marks[nextchar] then + prev=snext + snext=snext.next + else + if not krn then + elseif type(krn)=="table" then + if lookuptype=="pair" then + local a,b=krn[2],krn[3] + if a and #a>0 then + local startchar=start.char + local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b>0 then + local startchar=start.char + local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else + report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) + end + done=true + elseif krn~=0 then + local k=setkern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + end + done=true + end + break + end + end + return head,start,done + end +end +local chainmores={} +local chainprocs={} +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_subchain(...) +end +local logwarning=report_subchain +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_chain(...) +end +local logwarning=report_chain +function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) + logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return head,start,false +end +function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) + logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return head,start,false +end +function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) + local char=start.char + local replacement=replacements[char] + if replacement then + if trace_singles then + logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) + end + start.char=replacement + return head,start,true + else + return head,start,false + end +end +function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) + local current=start + local subtables=currentlookup.subtables + if #subtables>1 then + logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) + end + while current do + if current.id==glyph_code then + local currentchar=current.char + local lookupname=subtables[1] + local replacement=lookuphash[lookupname] + if not replacement then + if trace_bugs then + logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + replacement=replacement[currentchar] + if not replacement or replacement=="" then + if trace_bugs then + logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) + end + else + if trace_singles then + logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) + end + current.char=replacement + end + end + return head,start,true + elseif current==stop then + break + else + current=current.next + end + end + return head,start,false +end +chainmores.gsub_single=chainprocs.gsub_single +function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local startchar=start.char + local subtables=currentlookup.subtables + local lookupname=subtables[1] + local replacements=lookuphash[lookupname] + if not replacements then + if trace_bugs then + logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) + end + else + replacements=replacements[startchar] + if not replacements or replacement=="" then + if trace_bugs then + logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) + end + else + if trace_multiples then + logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) + end + return multiple_glyphs(head,start,replacements,currentlookup.flags[1]) + end + end + return head,start,false +end +chainmores.gsub_multiple=chainprocs.gsub_multiple +function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local current=start + local subtables=currentlookup.subtables + local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue + while current do + if current.id==glyph_code then + local currentchar=current.char + local lookupname=subtables[1] + local alternatives=lookuphash[lookupname] + if not alternatives then + if trace_bugs then + logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname)) + end + else + alternatives=alternatives[currentchar] + if alternatives then + local choice,comment=get_alternative_glyph(current,alternatives,value,trace_alternatives) + if choice then + if trace_alternatives then + logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment) + end + start.char=choice + else + if trace_alternatives then + logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) + end + end + elseif trace_bugs then + logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment) + end + end + return head,start,true + elseif current==stop then + break + else + current=current.next + end + end + return head,start,false +end +chainmores.gsub_alternate=chainprocs.gsub_alternate +function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) + local startchar=start.char + local subtables=currentlookup.subtables + local lookupname=subtables[1] + local ligatures=lookuphash[lookupname] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + ligatures=ligatures[startchar] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + end + else + local s=start.next + local discfound=false + local last=stop + local nofreplacements=0 + local skipmark=currentlookup.flags[1] + while s do + local id=s.id + if id==disc_code then + s=s.next + discfound=true + else + local schar=s.char + if skipmark and marks[schar] then + s=s.next + else + local lg=ligatures[schar] + if lg then + ligatures,last,nofreplacements=lg,s,nofreplacements+1 + if s==stop then + break + else + s=s.next + end + else + break + end + end + end + end + local l2=ligatures.ligature + if l2 then + if chainindex then + stop=last + end + if trace_ligatures then + if start==stop then + logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) + else + logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) + end + end + head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) + return head,start,true,nofreplacements + elseif trace_bugs then + if start==stop then + logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + else + logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) + end + end + end + end + return head,start,false,0 +end +chainmores.gsub_ligature=chainprocs.gsub_ligature +function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local markchar=start.char + if marks[markchar] then + local subtables=currentlookup.subtables + local lookupname=subtables[1] + local markanchors=lookuphash[lookupname] + if markanchors then + markanchors=markanchors[markchar] + end + if markanchors then + local base=start.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char + if marks[basechar] then + while true do + base=base.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + basechar=base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return head,start,false + end + end + end + local baseanchors=descriptions[basechar].anchors + if baseanchors then + local baseanchors=baseanchors['basechar'] + if baseanchors then + local al=anchorlookups[lookupname] + for anchor,ba in next,baseanchors do + if al[anchor] then + local ma=markanchors[anchor] + if ma then + local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head,start,true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return head,start,false +end +function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local markchar=start.char + if marks[markchar] then + local subtables=currentlookup.subtables + local lookupname=subtables[1] + local markanchors=lookuphash[lookupname] + if markanchors then + markanchors=markanchors[markchar] + end + if markanchors then + local base=start.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char + if marks[basechar] then + while true do + base=base.prev + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + basechar=base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) + end + return head,start,false + end + end + end + local index=start[a_ligacomp] + local baseanchors=descriptions[basechar].anchors + if baseanchors then + local baseanchors=baseanchors['baselig'] + if baseanchors then + local al=anchorlookups[lookupname] + for anchor,ba in next,baseanchors do + if al[anchor] then + local ma=markanchors[anchor] + if ma then + ba=ba[index] + if ba then + local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", + cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) + end + return head,start,true + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return head,start,false +end +function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local markchar=start.char + if marks[markchar] then + local subtables=currentlookup.subtables + local lookupname=subtables[1] + local markanchors=lookuphash[lookupname] + if markanchors then + markanchors=markanchors[markchar] + end + if markanchors then + local base=start.prev + local slc=start[a_ligacomp] + if slc then + while base do + local blc=base[a_ligacomp] + if blc and blc~=slc then + base=base.prev + else + break + end + end + end + if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then + local basechar=base.char + local baseanchors=descriptions[basechar].anchors + if baseanchors then + baseanchors=baseanchors['basemark'] + if baseanchors then + local al=anchorlookups[lookupname] + for anchor,ba in next,baseanchors do + if al[anchor] then + local ma=markanchors[anchor] + if ma then + local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head,start,true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return head,start,false +end +function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local alreadydone=cursonce and start[a_cursbase] + if not alreadydone then + local startchar=start.char + local subtables=currentlookup.subtables + local lookupname=subtables[1] + local exitanchors=lookuphash[lookupname] + if exitanchors then + exitanchors=exitanchors[startchar] + end + if exitanchors then + local done=false + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt=start.next + while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do + local nextchar=nxt.char + if marks[nextchar] then + nxt=nxt.next + else + local entryanchors=descriptions[nextchar] + if entryanchors then + entryanchors=entryanchors.anchors + if entryanchors then + entryanchors=entryanchors['centry'] + if entryanchors then + local al=anchorlookups[lookupname] + for anchor,entry in next,entryanchors do + if al[anchor] then + local exit=exitanchors[anchor] + if exit then + local dx,dy,bound=setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + end + done=true + break + end + end + end + end + end + elseif trace_bugs then + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + end + break + end + end + end + return head,start,done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return head,start,false + end + end + return head,start,false +end +function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + local startchar=start.char + local subtables=currentlookup.subtables + local lookupname=subtables[1] + local kerns=lookuphash[lookupname] + if kerns then + kerns=kerns[startchar] + if kerns then + local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) + end + end + end + return head,start,false +end +chainmores.gpos_single=chainprocs.gpos_single +function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + local snext=start.next + if snext then + local startchar=start.char + local subtables=currentlookup.subtables + local lookupname=subtables[1] + local kerns=lookuphash[lookupname] + if kerns then + kerns=kerns[startchar] + if kerns then + local lookuptype=lookuptypes[lookupname] + local prev,done=start,false + local factor=tfmdata.parameters.factor + while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do + local nextchar=snext.char + local krn=kerns[nextchar] + if not krn and marks[nextchar] then + prev=snext + snext=snext.next + else + if not krn then + elseif type(krn)=="table" then + if lookuptype=="pair" then + local a,b=krn[2],krn[3] + if a and #a>0 then + local startchar=start.char + local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b>0 then + local startchar=start.char + local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else + report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) + local a,b=krn[2],krn[6] + if a and a~=0 then + local k=setkern(snext,factor,rlmode,a) + if trace_kerns then + logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + end + if b and b~=0 then + logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) + end + end + done=true + elseif krn~=0 then + local k=setkern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + done=true + end + break + end + end + return head,start,done + end + end + end + return head,start,false +end +chainmores.gpos_pair=chainprocs.gpos_pair +local function show_skip(kind,chainname,char,ck,class) + if ck[9] then + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) + else + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) + end +end +local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) + local flags=sequence.flags + local done=false + local skipmark=flags[1] + local skipligature=flags[2] + local skipbase=flags[3] + local someskip=skipmark or skipligature or skipbase + local markclass=sequence.markclass + local skipped=false + for k=1,#contexts do + local match=true + local current=start + local last=start + local ck=contexts[k] + local seq=ck[3] + local s=#seq + if s==1 then + match=current.id==glyph_code and current.font==currentfont and current.subtype<256 and seq[1][current.char] + else + local f,l=ck[4],ck[5] + if f==1 and f==l then + else + if f==l then + else + local n=f+1 + last=last.next + while n<=l do + if last then + local id=last.id + if id==glyph_code then + if last.font==currentfont and last.subtype<256 then + local char=last.char + local ccd=descriptions[char] + if ccd then + local class=ccd.class + if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then + skipped=true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + last=last.next + elseif seq[n][char] then + if n1 then + local prev=start.prev + if prev then + local n=f-1 + while n>=1 do + if prev then + local id=prev.id + if id==glyph_code then + if prev.font==currentfont and prev.subtype<256 then + local char=prev.char + local ccd=descriptions[char] + if ccd then + local class=ccd.class + if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then + skipped=true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n=n -1 + else + match=false + break + end + else + match=false + break + end + else + match=false + break + end + elseif id==disc_code then + elseif seq[n][32] then + n=n -1 + else + match=false + break + end + prev=prev.prev + elseif seq[n][32] then + n=n -1 + else + match=false + break + end + end + elseif f==2 then + match=seq[1][32] + else + for n=f-1,1 do + if not seq[n][32] then + match=false + break + end + end + end + end + if match and s>l then + local current=last and last.next + if current then + local n=l+1 + while n<=s do + if current then + local id=current.id + if id==glyph_code then + if current.font==currentfont and current.subtype<256 then + local char=current.char + local ccd=descriptions[char] + if ccd then + local class=ccd.class + if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then + skipped=true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n=n+1 + else + match=false + break + end + else + match=false + break + end + else + match=false + break + end + elseif id==disc_code then + elseif seq[n][32] then + n=n+1 + else + match=false + break + end + current=current.next + elseif seq[n][32] then + n=n+1 + else + match=false + break + end + end + elseif s-l==1 then + match=seq[s][32] + else + for n=l+1,s do + if not seq[n][32] then + match=false + break + end + end + end + end + end + if match then + if trace_contexts then + local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5] + local char=start.char + if ck[9] then + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a", + cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) + else + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a", + cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) + end + end + local chainlookups=ck[6] + if chainlookups then + local nofchainlookups=#chainlookups + if nofchainlookups==1 then + local chainlookupname=chainlookups[1] + local chainlookup=lookuptable[chainlookupname] + if chainlookup then + local cp=chainprocs[chainlookup.type] + if cp then + local ok + head,start,ok=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + if ok then + done=true + end + else + logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + end + else + logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname)) + end + else + local i=1 + repeat + if skipped then + while true do + local char=start.char + local ccd=descriptions[char] + if ccd then + local class=ccd.class + if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then + start=start.next + else + break + end + else + break + end + end + end + local chainlookupname=chainlookups[i] + local chainlookup=lookuptable[chainlookupname] + if not chainlookup then + i=i+1 + else + local cp=chainmores[chainlookup.type] + if not cp then + logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + i=i+1 + else + local ok,n + head,start,ok,n=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) + if ok then + done=true + i=i+(n or 1) + else + i=i+1 + end + end + end + if start then + start=start.next + else + end + until i>nofchainlookups + end + else + local replacements=ck[7] + if replacements then + head,start,done=chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) + else + done=true + if trace_contexts then + logprocess("%s: skipping match",cref(kind,chainname)) + end + end + end + end + end + return head,start,done +end +local verbose_handle_contextchain=function(font,...) + logwarning("no verbose handler installed, reverting to 'normal'") + otf.setcontextchain() + return normal_handle_contextchain(...) +end +otf.chainhandlers={ + normal=normal_handle_contextchain, + verbose=verbose_handle_contextchain, +} +function otf.setcontextchain(method) + if not method or method=="normal" or not otf.chainhandlers[method] then + if handlers.contextchain then + logwarning("installing normal contextchain handler") + end + handlers.contextchain=normal_handle_contextchain + else + logwarning("installing contextchain handler %a",method) + local handler=otf.chainhandlers[method] + handlers.contextchain=function(...) + return handler(currentfont,...) + end + end + handlers.gsub_context=handlers.contextchain + handlers.gsub_contextchain=handlers.contextchain + handlers.gsub_reversecontextchain=handlers.contextchain + handlers.gpos_contextchain=handlers.contextchain + handlers.gpos_context=handlers.contextchain +end +otf.setcontextchain() +local missing={} +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_process(...) +end +local logwarning=report_process +local function report_missing_cache(typ,lookup) + local f=missing[currentfont] if not f then f={} missing[currentfont]=f end + local t=f[typ] if not t then t={} f[typ]=t end + if not t[lookup] then + t[lookup]=true + logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname) + end +end +local resolved={} +local lookuphashes={} +setmetatableindex(lookuphashes,function(t,font) + local lookuphash=fontdata[font].resources.lookuphash + if not lookuphash or not next(lookuphash) then + lookuphash=false + end + t[font]=lookuphash + return lookuphash +end) +local autofeatures=fonts.analyzers.features +local function initialize(sequence,script,language,enabled) + local features=sequence.features + if features then + for kind,scripts in next,features do + local valid=enabled[kind] + if valid then + local languages=scripts[script] or scripts[wildcard] + if languages and (languages[language] or languages[wildcard]) then + return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence } + end + end + end + end + return false +end +function otf.dataset(tfmdata,font) + local shared=tfmdata.shared + local properties=tfmdata.properties + local language=properties.language or "dflt" + local script=properties.script or "dflt" + local enabled=shared.features + local res=resolved[font] + if not res then + res={} + resolved[font]=res + end + local rs=res[script] + if not rs then + rs={} + res[script]=rs + end + local rl=rs[language] + if not rl then + rl={ + } + rs[language]=rl + local sequences=tfmdata.resources.sequences +for s=1,#sequences do + local v=enabled and initialize(sequences[s],script,language,enabled) + if v then + rl[#rl+1]=v + end +end + end + return rl +end +local function featuresprocessor(head,font,attr) + local lookuphash=lookuphashes[font] + if not lookuphash then + return head,false + end + if trace_steps then + checkstep(head) + end + tfmdata=fontdata[font] + descriptions=tfmdata.descriptions + characters=tfmdata.characters + resources=tfmdata.resources + marks=resources.marks + anchorlookups=resources.lookup_to_anchor + lookuptable=resources.lookups + lookuptypes=resources.lookuptypes + currentfont=font + rlmode=0 + local sequences=resources.sequences + local done=false + local datasets=otf.dataset(tfmdata,font,attr) + local dirstack={} + for s=1,#datasets do + local dataset=datasets[s] + featurevalue=dataset[1] + local sequence=dataset[5] + local rlparmode=0 + local topstack=0 + local success=false + local attribute=dataset[2] + local chain=dataset[3] + local typ=sequence.type + local subtables=sequence.subtables + if chain<0 then + local handler=handlers[typ] + local start=find_node_tail(head) + while start do + local id=start.id + if id==glyph_code then + if start.font==font and start.subtype<256 then + local a=start[0] + if a then + a=a==attr + else + a=true + end + if a then + for i=1,#subtables do + local lookupname=subtables[i] + local lookupcache=lookuphash[lookupname] + if lookupcache then + local lookupmatch=lookupcache[start.char] + if lookupmatch then + head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if success then + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start=start.prev end + else + start=start.prev + end + else + start=start.prev + end + else + start=start.prev + end + end + else + local handler=handlers[typ] + local ns=#subtables + local start=head + rlmode=0 + if ns==1 then + local lookupname=subtables[1] + local lookupcache=lookuphash[lookupname] + if not lookupcache then + report_missing_cache(typ,lookupname) + else + local function subrun(start) + local head=start + local done=false + while start do + local id=start.id + if id==glyph_code and start.font==font and start.subtype<256 then + local a=start[0] + if a then + a=(a==attr) and (not attribute or start[a_state]==attribute) + else + a=not attribute or start[a_state]==attribute + end + if a then + local lookupmatch=lookupcache[start.char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + done=true + end + end + if start then start=start.next end + else + start=start.next + end + else + start=start.next + end + end + if done then + success=true + return head + end + end + local function kerndisc(disc) + local prev=disc.prev + local next=disc.next + if prev and next then + prev.next=next + local a=prev[0] + if a then + a=(a==attr) and (not attribute or prev[a_state]==attribute) + else + a=not attribute or prev[a_state]==attribute + end + if a then + local lookupmatch=lookupcache[prev.char] + if lookupmatch then + local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + done=true + success=true + end + end + end + prev.next=disc + end + return next + end + while start do + local id=start.id + if id==glyph_code then + if start.font==font and start.subtype<256 then + local a=start[0] + if a then + a=(a==attr) and (not attribute or start[a_state]==attribute) + else + a=not attribute or start[a_state]==attribute + end + if a then + local lookupmatch=lookupcache[start.char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + success=true + end + end + if start then start=start.next end + else + start=start.next + end + else + start=start.next + end + elseif id==disc_code then + if start.subtype==discretionary_code then + local pre=start.pre + if pre then + local new=subrun(pre) + if new then start.pre=new end + end + local post=start.post + if post then + local new=subrun(post) + if new then start.post=new end + end + local replace=start.replace + if replace then + local new=subrun(replace) + if new then start.replace=new end + end +elseif typ=="gpos_single" or typ=="gpos_pair" then + kerndisc(start) + end + start=start.next + elseif id==whatsit_code then + local subtype=start.subtype + if subtype==dir_code then + local dir=start.dir + if dir=="+TRT" or dir=="+TLT" then + topstack=topstack+1 + dirstack[topstack]=dir + elseif dir=="-TRT" or dir=="-TLT" then + topstack=topstack-1 + end + local newdir=dirstack[topstack] + if newdir=="+TRT" then + rlmode=-1 + elseif newdir=="+TLT" then + rlmode=1 + else + rlmode=rlparmode + end + if trace_directions then + report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) + end + elseif subtype==localpar_code then + local dir=start.dir + if dir=="TRT" then + rlparmode=-1 + elseif dir=="TLT" then + rlparmode=1 + else + rlparmode=0 + end + rlmode=rlparmode + if trace_directions then + report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) + end + end + start=start.next + elseif id==math_code then + start=end_of_math(start).next + else + start=start.next + end + end + end + else + local function subrun(start) + local head=start + local done=false + while start do + local id=start.id + if id==glyph_code and start.id==font and start.subtype<256 then + local a=start[0] + if a then + a=(a==attr) and (not attribute or start[a_state]==attribute) + else + a=not attribute or start[a_state]==attribute + end + if a then + for i=1,ns do + local lookupname=subtables[i] + local lookupcache=lookuphash[lookupname] + if lookupcache then + local lookupmatch=lookupcache[start.char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + done=true + break + elseif not start then + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start=start.next end + else + start=start.next + end + else + start=start.next + end + end + if done then + success=true + return head + end + end + local function kerndisc(disc) + local prev=disc.prev + local next=disc.next + if prev and next then + prev.next=next + local a=prev[0] + if a then + a=(a==attr) and (not attribute or prev[a_state]==attribute) + else + a=not attribute or prev[a_state]==attribute + end + if a then + for i=1,ns do + local lookupname=subtables[i] + local lookupcache=lookuphash[lookupname] + if lookupcache then + local lookupmatch=lookupcache[prev.char] + if lookupmatch then + local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + done=true + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + end + prev.next=disc + end + return next + end + while start do + local id=start.id + if id==glyph_code then + if start.font==font and start.subtype<256 then + local a=start[0] + if a then + a=(a==attr) and (not attribute or start[a_state]==attribute) + else + a=not attribute or start[a_state]==attribute + end + if a then + for i=1,ns do + local lookupname=subtables[i] + local lookupcache=lookuphash[lookupname] + if lookupcache then + local lookupmatch=lookupcache[start.char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + success=true + break + elseif not start then + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start=start.next end + else + start=start.next + end + else + start=start.next + end + elseif id==disc_code then + if start.subtype==discretionary_code then + local pre=start.pre + if pre then + local new=subrun(pre) + if new then start.pre=new end + end + local post=start.post + if post then + local new=subrun(post) + if new then start.post=new end + end + local replace=start.replace + if replace then + local new=subrun(replace) + if new then start.replace=new end + end +elseif typ=="gpos_single" or typ=="gpos_pair" then + kerndisc(start) + end + start=start.next + elseif id==whatsit_code then + local subtype=start.subtype + if subtype==dir_code then + local dir=start.dir + if dir=="+TRT" or dir=="+TLT" then + topstack=topstack+1 + dirstack[topstack]=dir + elseif dir=="-TRT" or dir=="-TLT" then + topstack=topstack-1 + end + local newdir=dirstack[topstack] + if newdir=="+TRT" then + rlmode=-1 + elseif newdir=="+TLT" then + rlmode=1 + else + rlmode=rlparmode + end + if trace_directions then + report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) + end + elseif subtype==localpar_code then + local dir=start.dir + if dir=="TRT" then + rlparmode=-1 + elseif dir=="TLT" then + rlparmode=1 + else + rlparmode=0 + end + rlmode=rlparmode + if trace_directions then + report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) + end + end + start=start.next + elseif id==math_code then + start=end_of_math(start).next + else + start=start.next + end + end + end + end + if success then + done=true + end + if trace_steps then + registerstep(head) + end + end + return head,done +end +local function generic(lookupdata,lookupname,unicode,lookuphash) + local target=lookuphash[lookupname] + if target then + target[unicode]=lookupdata + else + lookuphash[lookupname]={ [unicode]=lookupdata } + end +end +local action={ + substitution=generic, + multiple=generic, + alternate=generic, + position=generic, + ligature=function(lookupdata,lookupname,unicode,lookuphash) + local target=lookuphash[lookupname] + if not target then + target={} + lookuphash[lookupname]=target + end + for i=1,#lookupdata do + local li=lookupdata[i] + local tu=target[li] + if not tu then + tu={} + target[li]=tu + end + target=tu + end + target.ligature=unicode + end, + pair=function(lookupdata,lookupname,unicode,lookuphash) + local target=lookuphash[lookupname] + if not target then + target={} + lookuphash[lookupname]=target + end + local others=target[unicode] + local paired=lookupdata[1] + if others then + others[paired]=lookupdata + else + others={ [paired]=lookupdata } + target[unicode]=others + end + end, +} +local function prepare_lookups(tfmdata) + local rawdata=tfmdata.shared.rawdata + local resources=rawdata.resources + local lookuphash=resources.lookuphash + local anchor_to_lookup=resources.anchor_to_lookup + local lookup_to_anchor=resources.lookup_to_anchor + local lookuptypes=resources.lookuptypes + local characters=tfmdata.characters + local descriptions=tfmdata.descriptions + for unicode,character in next,characters do + local description=descriptions[unicode] + if description then + local lookups=description.slookups + if lookups then + for lookupname,lookupdata in next,lookups do + action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) + end + end + local lookups=description.mlookups + if lookups then + for lookupname,lookuplist in next,lookups do + local lookuptype=lookuptypes[lookupname] + for l=1,#lookuplist do + local lookupdata=lookuplist[l] + action[lookuptype](lookupdata,lookupname,unicode,lookuphash) + end + end + end + local list=description.kerns + if list then + for lookup,krn in next,list do + local target=lookuphash[lookup] + if target then + target[unicode]=krn + else + lookuphash[lookup]={ [unicode]=krn } + end + end + end + local list=description.anchors + if list then + for typ,anchors in next,list do + if typ=="mark" or typ=="cexit" then + for name,anchor in next,anchors do + local lookups=anchor_to_lookup[name] + if lookups then + for lookup,_ in next,lookups do + local target=lookuphash[lookup] + if target then + target[unicode]=anchors + else + lookuphash[lookup]={ [unicode]=anchors } + end + end + end + end + end + end + end + end + end +end +local function split(replacement,original) + local result={} + for i=1,#replacement do + result[original[i]]=replacement[i] + end + return result +end +local valid={ + coverage={ chainsub=true,chainpos=true,contextsub=true }, + reversecoverage={ reversesub=true }, + glyphs={ chainsub=true,chainpos=true }, +} +local function prepare_contextchains(tfmdata) + local rawdata=tfmdata.shared.rawdata + local resources=rawdata.resources + local lookuphash=resources.lookuphash + local lookups=rawdata.lookups + if lookups then + for lookupname,lookupdata in next,rawdata.lookups do + local lookuptype=lookupdata.type + if lookuptype then + local rules=lookupdata.rules + if rules then + local format=lookupdata.format + local validformat=valid[format] + if not validformat then + report_prepare("unsupported format %a",format) + elseif not validformat[lookuptype] then + report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookupname) + else + local contexts=lookuphash[lookupname] + if not contexts then + contexts={} + lookuphash[lookupname]=contexts + end + local t,nt={},0 + for nofrules=1,#rules do + local rule=rules[nofrules] + local current=rule.current + local before=rule.before + local after=rule.after + local replacements=rule.replacements + local sequence={} + local nofsequences=0 + if before then + for n=1,#before do + nofsequences=nofsequences+1 + sequence[nofsequences]=before[n] + end + end + local start=nofsequences+1 + for n=1,#current do + nofsequences=nofsequences+1 + sequence[nofsequences]=current[n] + end + local stop=nofsequences + if after then + for n=1,#after do + nofsequences=nofsequences+1 + sequence[nofsequences]=after[n] + end + end + if sequence[1] then + nt=nt+1 + t[nt]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements } + for unic,_ in next,sequence[start] do + local cu=contexts[unic] + if not cu then + contexts[unic]=t + end + end + end + end + end + else + end + else + report_prepare("missing lookuptype for lookupname %a",lookupname) + end + end + end +end +local function featuresinitializer(tfmdata,value) + if true then + local rawdata=tfmdata.shared.rawdata + local properties=rawdata.properties + if not properties.initialized then + local starttime=trace_preparing and os.clock() + local resources=rawdata.resources + resources.lookuphash=resources.lookuphash or {} + prepare_contextchains(tfmdata) + prepare_lookups(tfmdata) + properties.initialized=true + if trace_preparing then + report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname) + end + end + end +end +registerotffeature { + name="features", + description="features", + default=true, + initializers={ + position=1, + node=featuresinitializer, + }, + processors={ + node=featuresprocessor, + } +} +otf.handlers=handlers + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-otp']={ + version=1.001, + comment="companion to font-otf.lua (packing)", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local next,type=next,type +local sort,concat=table.sort,table.concat +local sortedhash=table.sortedhash +local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end) +local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end) +local report_otf=logs.reporter("fonts","otf loading") +fonts=fonts or {} +local handlers=fonts.handlers or {} +fonts.handlers=handlers +local otf=handlers.otf or {} +handlers.otf=otf +local enhancers=otf.enhancers or {} +otf.enhancers=enhancers +local glists=otf.glists or { "gsub","gpos" } +otf.glists=glists +local criterium=1 +local threshold=0 +local function tabstr_normal(t) + local s={} + local n=0 + for k,v in next,t do + n=n+1 + if type(v)=="table" then + s[n]=k..">"..tabstr_normal(v) + elseif v==true then + s[n]=k.."+" + elseif v then + s[n]=k.."="..v + else + s[n]=k.."-" + end + end + if n==0 then + return "" + elseif n==1 then + return s[1] + else + sort(s) + return concat(s,",") + end +end +local function tabstr_flat(t) + local s={} + local n=0 + for k,v in next,t do + n=n+1 + s[n]=k.."="..v + end + if n==0 then + return "" + elseif n==1 then + return s[1] + else + sort(s) + return concat(s,",") + end +end +local function tabstr_mixed(t) + local s={} + local n=#t + if n==0 then + return "" + elseif n==1 then + local k=t[1] + if k==true then + return "++" + elseif k==false then + return "--" + else + return tostring(k) + end + else + for i=1,n do + local k=t[i] + if k==true then + s[i]="++" + elseif k==false then + s[i]="--" + else + s[i]=k + end + end + return concat(s,",") + end +end +local function tabstr_boolean(t) + local s={} + local n=0 + for k,v in next,t do + n=n+1 + if v then + s[n]=k.."+" + else + s[n]=k.."-" + end + end + if n==0 then + return "" + elseif n==1 then + return s[1] + else + sort(s) + return concat(s,",") + end +end +local function packdata(data) + if data then + local h,t,c={},{},{} + local hh,tt,cc={},{},{} + local nt,ntt=0,0 + local function pack_normal(v) + local tag=tabstr_normal(v) + local ht=h[tag] + if ht then + c[ht]=c[ht]+1 + return ht + else + nt=nt+1 + t[nt]=v + h[tag]=nt + c[nt]=1 + return nt + end + end + local function pack_flat(v) + local tag=tabstr_flat(v) + local ht=h[tag] + if ht then + c[ht]=c[ht]+1 + return ht + else + nt=nt+1 + t[nt]=v + h[tag]=nt + c[nt]=1 + return nt + end + end + local function pack_boolean(v) + local tag=tabstr_boolean(v) + local ht=h[tag] + if ht then + c[ht]=c[ht]+1 + return ht + else + nt=nt+1 + t[nt]=v + h[tag]=nt + c[nt]=1 + return nt + end + end + local function pack_indexed(v) + local tag=concat(v," ") + local ht=h[tag] + if ht then + c[ht]=c[ht]+1 + return ht + else + nt=nt+1 + t[nt]=v + h[tag]=nt + c[nt]=1 + return nt + end + end + local function pack_mixed(v) + local tag=tabstr_mixed(v) + local ht=h[tag] + if ht then + c[ht]=c[ht]+1 + return ht + else + nt=nt+1 + t[nt]=v + h[tag]=nt + c[nt]=1 + return nt + end + end + local function pack_final(v) + if c[v]<=criterium then + return t[v] + else + local hv=hh[v] + if hv then + return hv + else + ntt=ntt+1 + tt[ntt]=t[v] + hh[v]=ntt + cc[ntt]=c[v] + return ntt + end + end + end + local function success(stage,pass) + if nt==0 then + if trace_loading or trace_packing then + report_otf("pack quality: nothing to pack") + end + return false + elseif nt>=threshold then + local one,two,rest=0,0,0 + if pass==1 then + for k,v in next,c do + if v==1 then + one=one+1 + elseif v==2 then + two=two+1 + else + rest=rest+1 + end + end + else + for k,v in next,cc do + if v>20 then + rest=rest+1 + elseif v>10 then + two=two+1 + else + one=one+1 + end + end + data.tables=tt + end + if trace_loading or trace_packing then + report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)",stage,pass,one+two+rest,one,two,rest,criterium) + end + return true + else + if trace_loading or trace_packing then + report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)",stage,pass,nt,threshold) + end + return false + end + end + local function packers(pass) + if pass==1 then + return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed + else + return pack_final,pack_final,pack_final,pack_final,pack_final + end + end + local resources=data.resources + local lookuptypes=resources.lookuptypes + for pass=1,2 do + if trace_packing then + report_otf("start packing: stage 1, pass %s",pass) + end + local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) + for unicode,description in next,data.descriptions do + local boundingbox=description.boundingbox + if boundingbox then + description.boundingbox=pack_indexed(boundingbox) + end + local slookups=description.slookups + if slookups then + for tag,slookup in next,slookups do + local what=lookuptypes[tag] + if what=="pair" then + local t=slookup[2] if t then slookup[2]=pack_indexed(t) end + local t=slookup[3] if t then slookup[3]=pack_indexed(t) end + elseif what~="substitution" then + slookups[tag]=pack_indexed(slookup) + end + end + end + local mlookups=description.mlookups + if mlookups then + for tag,mlookup in next,mlookups do + local what=lookuptypes[tag] + if what=="pair" then + for i=1,#mlookup do + local lookup=mlookup[i] + local t=lookup[2] if t then lookup[2]=pack_indexed(t) end + local t=lookup[3] if t then lookup[3]=pack_indexed(t) end + end + elseif what~="substitution" then + for i=1,#mlookup do + mlookup[i]=pack_indexed(mlookup[i]) + end + end + end + end + local kerns=description.kerns + if kerns then + for tag,kern in next,kerns do + kerns[tag]=pack_flat(kern) + end + end + local math=description.math + if math then + local kerns=math.kerns + if kerns then + for tag,kern in next,kerns do + kerns[tag]=pack_normal(kern) + end + end + end + local anchors=description.anchors + if anchors then + for what,anchor in next,anchors do + if what=="baselig" then + for _,a in next,anchor do + for k=1,#a do + a[k]=pack_indexed(a[k]) + end + end + else + for k,v in next,anchor do + anchor[k]=pack_indexed(v) + end + end + end + end + local altuni=description.altuni + if altuni then + for i=1,#altuni do + altuni[i]=pack_flat(altuni[i]) + end + end + end + local lookups=data.lookups + if lookups then + for _,lookup in next,lookups do + local rules=lookup.rules + if rules then + for i=1,#rules do + local rule=rules[i] + local r=rule.before if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end + local r=rule.after if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end + local r=rule.current if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end + local r=rule.replacements if r then rule.replacements=pack_flat (r) end + local r=rule.lookups if r then rule.lookups=pack_indexed(r) end + end + end + end + end + local anchor_to_lookup=resources.anchor_to_lookup + if anchor_to_lookup then + for anchor,lookup in next,anchor_to_lookup do + anchor_to_lookup[anchor]=pack_normal(lookup) + end + end + local lookup_to_anchor=resources.lookup_to_anchor + if lookup_to_anchor then + for lookup,anchor in next,lookup_to_anchor do + lookup_to_anchor[lookup]=pack_normal(anchor) + end + end + local sequences=resources.sequences + if sequences then + for feature,sequence in next,sequences do + local flags=sequence.flags + if flags then + sequence.flags=pack_normal(flags) + end + local subtables=sequence.subtables + if subtables then + sequence.subtables=pack_normal(subtables) + end + local features=sequence.features + if features then + for script,feature in next,features do + features[script]=pack_normal(feature) + end + end + end + end + local lookups=resources.lookups + if lookups then + for name,lookup in next,lookups do + local flags=lookup.flags + if flags then + lookup.flags=pack_normal(flags) + end + local subtables=lookup.subtables + if subtables then + lookup.subtables=pack_normal(subtables) + end + end + end + local features=resources.features + if features then + for _,what in next,glists do + local list=features[what] + if list then + for feature,spec in next,list do + list[feature]=pack_normal(spec) + end + end + end + end + if not success(1,pass) then + return + end + end + if nt>0 then + for pass=1,2 do + if trace_packing then + report_otf("start packing: stage 2, pass %s",pass) + end + local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) + for unicode,description in next,data.descriptions do + local kerns=description.kerns + if kerns then + description.kerns=pack_normal(kerns) + end + local math=description.math + if math then + local kerns=math.kerns + if kerns then + math.kerns=pack_normal(kerns) + end + end + local anchors=description.anchors + if anchors then + description.anchors=pack_normal(anchors) + end + local mlookups=description.mlookups + if mlookups then + for tag,mlookup in next,mlookups do + mlookups[tag]=pack_normal(mlookup) + end + end + local altuni=description.altuni + if altuni then + description.altuni=pack_normal(altuni) + end + end + local lookups=data.lookups + if lookups then + for _,lookup in next,lookups do + local rules=lookup.rules + if rules then + for i=1,#rules do + local rule=rules[i] + local r=rule.before if r then rule.before=pack_normal(r) end + local r=rule.after if r then rule.after=pack_normal(r) end + local r=rule.current if r then rule.current=pack_normal(r) end + end + end + end + end + local sequences=resources.sequences + if sequences then + for feature,sequence in next,sequences do + sequence.features=pack_normal(sequence.features) + end + end + if not success(2,pass) then + end + end + for pass=1,2 do + local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) + for unicode,description in next,data.descriptions do + local slookups=description.slookups + if slookups then + description.slookups=pack_normal(slookups) + end + local mlookups=description.mlookups + if mlookups then + description.mlookups=pack_normal(mlookups) + end + end + end + end + end +end +local unpacked_mt={ + __index=function(t,k) + t[k]=false + return k + end +} +local function unpackdata(data) + if data then + local tables=data.tables + if tables then + local resources=data.resources + local lookuptypes=resources.lookuptypes + local unpacked={} + setmetatable(unpacked,unpacked_mt) + for unicode,description in next,data.descriptions do + local tv=tables[description.boundingbox] + if tv then + description.boundingbox=tv + end + local slookups=description.slookups + if slookups then + local tv=tables[slookups] + if tv then + description.slookups=tv + slookups=unpacked[tv] + end + if slookups then + for tag,lookup in next,slookups do + local what=lookuptypes[tag] + if what=="pair" then + local tv=tables[lookup[2]] + if tv then + lookup[2]=tv + end + local tv=tables[lookup[3]] + if tv then + lookup[3]=tv + end + elseif what~="substitution" then + local tv=tables[lookup] + if tv then + slookups[tag]=tv + end + end + end + end + end + local mlookups=description.mlookups + if mlookups then + local tv=tables[mlookups] + if tv then + description.mlookups=tv + mlookups=unpacked[tv] + end + if mlookups then + for tag,list in next,mlookups do + local tv=tables[list] + if tv then + mlookups[tag]=tv + list=unpacked[tv] + end + if list then + local what=lookuptypes[tag] + if what=="pair" then + for i=1,#list do + local lookup=list[i] + local tv=tables[lookup[2]] + if tv then + lookup[2]=tv + end + local tv=tables[lookup[3]] + if tv then + lookup[3]=tv + end + end + elseif what~="substitution" then + for i=1,#list do + local tv=tables[list[i]] + if tv then + list[i]=tv + end + end + end + end + end + end + end + local kerns=description.kerns + if kerns then + local tm=tables[kerns] + if tm then + description.kerns=tm + kerns=unpacked[tm] + end + if kerns then + for k,kern in next,kerns do + local tv=tables[kern] + if tv then + kerns[k]=tv + end + end + end + end + local math=description.math + if math then + local kerns=math.kerns + if kerns then + local tm=tables[kerns] + if tm then + math.kerns=tm + kerns=unpacked[tm] + end + if kerns then + for k,kern in next,kerns do + local tv=tables[kern] + if tv then + kerns[k]=tv + end + end + end + end + end + local anchors=description.anchors + if anchors then + local ta=tables[anchors] + if ta then + description.anchors=ta + anchors=unpacked[ta] + end + if anchors then + for tag,anchor in next,anchors do + if tag=="baselig" then + for _,list in next,anchor do + for i=1,#list do + local tv=tables[list[i]] + if tv then + list[i]=tv + end + end + end + else + for a,data in next,anchor do + local tv=tables[data] + if tv then + anchor[a]=tv + end + end + end + end + end + end + local altuni=description.altuni + if altuni then + local altuni=tables[altuni] + if altuni then + description.altuni=altuni + for i=1,#altuni do + local tv=tables[altuni[i]] + if tv then + altuni[i]=tv + end + end + end + end + end + local lookups=data.lookups + if lookups then + for _,lookup in next,lookups do + local rules=lookup.rules + if rules then + for i=1,#rules do + local rule=rules[i] + local before=rule.before + if before then + local tv=tables[before] + if tv then + rule.before=tv + before=unpacked[tv] + end + if before then + for i=1,#before do + local tv=tables[before[i]] + if tv then + before[i]=tv + end + end + end + end + local after=rule.after + if after then + local tv=tables[after] + if tv then + rule.after=tv + after=unpacked[tv] + end + if after then + for i=1,#after do + local tv=tables[after[i]] + if tv then + after[i]=tv + end + end + end + end + local current=rule.current + if current then + local tv=tables[current] + if tv then + rule.current=tv + current=unpacked[tv] + end + if current then + for i=1,#current do + local tv=tables[current[i]] + if tv then + current[i]=tv + end + end + end + end + local replacements=rule.replacements + if replacements then + local tv=tables[replacements] + if tv then + rule.replacements=tv + end + end + local fore=rule.fore + if fore then + local tv=tables[fore] + if tv then + rule.fore=tv + end + end + local back=rule.back + if back then + local tv=tables[back] + if tv then + rule.back=tv + end + end + local names=rule.names + if names then + local tv=tables[names] + if tv then + rule.names=tv + end + end + local lookups=rule.lookups + if lookups then + local tv=tables[lookups] + if tv then + rule.lookups=tv + end + end + end + end + end + end + local anchor_to_lookup=resources.anchor_to_lookup + if anchor_to_lookup then + for anchor,lookup in next,anchor_to_lookup do + local tv=tables[lookup] + if tv then + anchor_to_lookup[anchor]=tv + end + end + end + local lookup_to_anchor=resources.lookup_to_anchor + if lookup_to_anchor then + for lookup,anchor in next,lookup_to_anchor do + local tv=tables[anchor] + if tv then + lookup_to_anchor[lookup]=tv + end + end + end + local ls=resources.sequences + if ls then + for _,feature in next,ls do + local flags=feature.flags + if flags then + local tv=tables[flags] + if tv then + feature.flags=tv + end + end + local subtables=feature.subtables + if subtables then + local tv=tables[subtables] + if tv then + feature.subtables=tv + end + end + local features=feature.features + if features then + local tv=tables[features] + if tv then + feature.features=tv + features=unpacked[tv] + end + if features then + for script,data in next,features do + local tv=tables[data] + if tv then + features[script]=tv + end + end + end + end + end + end + local lookups=resources.lookups + if lookups then + for _,lookup in next,lookups do + local flags=lookup.flags + if flags then + local tv=tables[flags] + if tv then + lookup.flags=tv + end + end + local subtables=lookup.subtables + if subtables then + local tv=tables[subtables] + if tv then + lookup.subtables=tv + end + end + end + end + local features=resources.features + if features then + for _,what in next,glists do + local feature=features[what] + if feature then + for tag,spec in next,feature do + local tv=tables[spec] + if tv then + feature[tag]=tv + end + end + end + end + end + data.tables=nil + end + end +end +if otf.enhancers.register then + otf.enhancers.register("pack",packdata) + otf.enhancers.register("unpack",unpackdata) +end +otf.enhancers.unpack=unpackdata + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-fonts-lua']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +local fonts=fonts +fonts.formats.lua="lua" +function fonts.readers.lua(specification) + local fullname=specification.filename or "" + if fullname=="" then + local forced=specification.forced or "" + if forced~="" then + fullname=specification.name.."."..forced + else + fullname=specification.name + end + end + local fullname=resolvers.findfile(fullname) or "" + if fullname~="" then + local loader=loadfile(fullname) + loader=loader and loader() + return loader and loader(specification) + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-def']={ + 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 format,gmatch,match,find,lower,gsub=string.format,string.gmatch,string.match,string.find,string.lower,string.gsub +local tostring,next=tostring,next +local lpegmatch=lpeg.match +local suffixonly,removesuffix=file.suffix,file.removesuffix +local allocate=utilities.storage.allocate +local trace_defining=false trackers .register("fonts.defining",function(v) trace_defining=v end) +local directive_embedall=false directives.register("fonts.embedall",function(v) directive_embedall=v end) +trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading") +trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*") +local report_defining=logs.reporter("fonts","defining") +local fonts=fonts +local fontdata=fonts.hashes.identifiers +local readers=fonts.readers +local definers=fonts.definers +local specifiers=fonts.specifiers +local constructors=fonts.constructors +local fontgoodies=fonts.goodies +readers.sequence=allocate { 'otf','ttf','afm','tfm','lua' } +local variants=allocate() +specifiers.variants=variants +definers.methods=definers.methods or {} +local internalized=allocate() +local lastdefined=nil +local loadedfonts=constructors.loadedfonts +local designsizes=constructors.designsizes +local resolvefile=fontgoodies and fontgoodies.filenames and fontgoodies.filenames.resolve or function(s) return s end +local splitter,splitspecifiers=nil,"" +local P,C,S,Cc=lpeg.P,lpeg.C,lpeg.S,lpeg.Cc +local left=P("(") +local right=P(")") +local colon=P(":") +local space=P(" ") +definers.defaultlookup="file" +local prefixpattern=P(false) +local function addspecifier(symbol) + splitspecifiers=splitspecifiers..symbol + local method=S(splitspecifiers) + local lookup=C(prefixpattern)*colon + local sub=left*C(P(1-left-right-method)^1)*right + local specification=C(method)*C(P(1)^1) + local name=C((1-sub-specification)^1) + splitter=P((lookup+Cc(""))*name*(sub+Cc(""))*(specification+Cc(""))) +end +local function addlookup(str,default) + prefixpattern=prefixpattern+P(str) +end +definers.addlookup=addlookup +addlookup("file") +addlookup("name") +addlookup("spec") +local function getspecification(str) + return lpegmatch(splitter,str or "") +end +definers.getspecification=getspecification +function definers.registersplit(symbol,action,verbosename) + addspecifier(symbol) + variants[symbol]=action + if verbosename then + variants[verbosename]=action + end +end +local function makespecification(specification,lookup,name,sub,method,detail,size) + size=size or 655360 + if not lookup or lookup=="" then + lookup=definers.defaultlookup + end + if trace_defining then + report_defining("specification %a, lookup %a, name %a, sub %a, method %a, detail %a", + specification,lookup,name,sub,method,detail) + end + local t={ + lookup=lookup, + specification=specification, + size=size, + name=name, + sub=sub, + method=method, + detail=detail, + resolved="", + forced="", + features={}, + } + return t +end +definers.makespecification=makespecification +function definers.analyze(specification,size) + local lookup,name,sub,method,detail=getspecification(specification or "") + return makespecification(specification,lookup,name,sub,method,detail,size) +end +definers.resolvers=definers.resolvers or {} +local resolvers=definers.resolvers +function resolvers.file(specification) + local name=resolvefile(specification.name) + local suffix=lower(suffixonly(name)) + if fonts.formats[suffix] then + specification.forced=suffix + specification.forcedname=name + specification.name=removesuffix(name) + else + specification.name=name + end +end +function resolvers.name(specification) + local resolve=fonts.names.resolve + if resolve then + local resolved,sub=resolve(specification.name,specification.sub,specification) + if resolved then + specification.resolved=resolved + specification.sub=sub + local suffix=lower(suffixonly(resolved)) + if fonts.formats[suffix] then + specification.forced=suffix + specification.forcedname=resolved + specification.name=removesuffix(resolved) + else + specification.name=resolved + end + end + else + resolvers.file(specification) + end +end +function resolvers.spec(specification) + local resolvespec=fonts.names.resolvespec + if resolvespec then + local resolved,sub=resolvespec(specification.name,specification.sub,specification) + if resolved then + specification.resolved=resolved + specification.sub=sub + specification.forced=lower(suffixonly(resolved)) + specification.forcedname=resolved + specification.name=removesuffix(resolved) + end + else + resolvers.name(specification) + end +end +function definers.resolve(specification) + if not specification.resolved or specification.resolved=="" then + local r=resolvers[specification.lookup] + if r then + r(specification) + end + end + if specification.forced=="" then + specification.forced=nil + specification.forcedname=nil + end + specification.hash=lower(specification.name..' @ '..constructors.hashfeatures(specification)) + if specification.sub and specification.sub~="" then + specification.hash=specification.sub..' @ '..specification.hash + end + return specification +end +function definers.applypostprocessors(tfmdata) + local postprocessors=tfmdata.postprocessors + if postprocessors then + local properties=tfmdata.properties + for i=1,#postprocessors do + local extrahash=postprocessors[i](tfmdata) + if type(extrahash)=="string" and extrahash~="" then + extrahash=gsub(lower(extrahash),"[^a-z]","-") + properties.fullname=format("%s-%s",properties.fullname,extrahash) + end + end + end + return tfmdata +end +local function checkembedding(tfmdata) + local properties=tfmdata.properties + local embedding + if directive_embedall then + embedding="full" + elseif properties and properties.filename and constructors.dontembed[properties.filename] then + embedding="no" + else + embedding="subset" + end + if properties then + properties.embedding=embedding + else + tfmdata.properties={ embedding=embedding } + end + tfmdata.embedding=embedding +end +function definers.loadfont(specification) + local hash=constructors.hashinstance(specification) + local tfmdata=loadedfonts[hash] + if not tfmdata then + local forced=specification.forced or "" + if forced~="" then + local reader=readers[lower(forced)] + tfmdata=reader and reader(specification) + if not tfmdata then + report_defining("forced type %a of %a not found",forced,specification.name) + end + else + local sequence=readers.sequence + for s=1,#sequence do + local reader=sequence[s] + if readers[reader] then + if trace_defining then + report_defining("trying (reader sequence driven) type %a for %a with file %a",reader,specification.name,specification.filename) + end + tfmdata=readers[reader](specification) + if tfmdata then + break + else + specification.filename=nil + end + end + end + end + if tfmdata then + tfmdata=definers.applypostprocessors(tfmdata) + checkembedding(tfmdata) + loadedfonts[hash]=tfmdata + designsizes[specification.hash]=tfmdata.parameters.designsize + end + end + if not tfmdata then + report_defining("font with asked name %a is not found using lookup %a",specification.name,specification.lookup) + end + return tfmdata +end +function constructors.checkvirtualids() +end +function constructors.readanddefine(name,size) + local specification=definers.analyze(name,size) + local method=specification.method + if method and variants[method] then + specification=variants[method](specification) + end + specification=definers.resolve(specification) + local hash=constructors.hashinstance(specification) + local id=definers.registered(hash) + if not id then + local tfmdata=definers.loadfont(specification) + if tfmdata then + tfmdata.properties.hash=hash + constructors.checkvirtualids(tfmdata) + id=font.define(tfmdata) + definers.register(tfmdata,id) + else + id=0 + end + end + return fontdata[id],id +end +function definers.current() + return lastdefined +end +function definers.registered(hash) + local id=internalized[hash] + return id,id and fontdata[id] +end +function definers.register(tfmdata,id) + if tfmdata and id then + local hash=tfmdata.properties.hash + if not hash then + report_defining("registering font, id %a, name %a, invalid hash",id,tfmdata.properties.filename or "?") + elseif not internalized[hash] then + internalized[hash]=id + if trace_defining then + report_defining("registering font, id %s, hash %a",id,hash) + end + fontdata[id]=tfmdata + end + end +end +function definers.read(specification,size,id) + statistics.starttiming(fonts) + if type(specification)=="string" then + specification=definers.analyze(specification,size) + end + local method=specification.method + if method and variants[method] then + specification=variants[method](specification) + end + specification=definers.resolve(specification) + local hash=constructors.hashinstance(specification) + local tfmdata=definers.registered(hash) + if tfmdata then + if trace_defining then + report_defining("already hashed: %s",hash) + end + else + tfmdata=definers.loadfont(specification) + if tfmdata then + if trace_defining then + report_defining("loaded and hashed: %s",hash) + end + tfmdata.properties.hash=hash + if id then + definers.register(tfmdata,id) + end + else + if trace_defining then + report_defining("not loaded and hashed: %s",hash) + end + end + end + lastdefined=tfmdata or id + if not tfmdata then + report_defining("unknown font %a, loading aborted",specification.name) + elseif trace_defining and type(tfmdata)=="table" then + local properties=tfmdata.properties or {} + local parameters=tfmdata.parameters or {} + report_defining("using %s font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a", + properties.format,id,properties.name,parameters.size,properties.encodingbytes, + properties.encodingname,properties.fullname,file.basename(properties.filename)) + end + statistics.stoptiming(fonts) + return tfmdata +end +function font.getfont(id) + return fontdata[id] +end +callbacks.register('define_font',definers.read,"definition of fonts (tfmdata preparation)") + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-font-def']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +local fonts=fonts +fonts.constructors.namemode="specification" +function fonts.definers.getspecification(str) + return "",str,"",":",str +end +local list={} +local function issome () list.lookup='name' end +local function isfile () list.lookup='file' end +local function isname () list.lookup='name' end +local function thename(s) list.name=s end +local function issub (v) list.sub=v end +local function iscrap (s) list.crap=string.lower(s) end +local function iskey (k,v) list[k]=v end +local function istrue (s) list[s]=true end +local function isfalse(s) list[s]=false end +local P,S,R,C=lpeg.P,lpeg.S,lpeg.R,lpeg.C +local spaces=P(" ")^0 +local namespec=(1-S("/:("))^0 +local crapspec=spaces*P("/")*(((1-P(":"))^0)/iscrap)*spaces +local filename_1=P("file:")/isfile*(namespec/thename) +local filename_2=P("[")*P(true)/isname*(((1-P("]"))^0)/thename)*P("]") +local fontname_1=P("name:")/isname*(namespec/thename) +local fontname_2=P(true)/issome*(namespec/thename) +local sometext=(R("az","AZ","09")+S("+-."))^1 +local truevalue=P("+")*spaces*(sometext/istrue) +local falsevalue=P("-")*spaces*(sometext/isfalse) +local keyvalue=(C(sometext)*spaces*P("=")*spaces*C(sometext))/iskey +local somevalue=sometext/istrue +local subvalue=P("(")*(C(P(1-S("()"))^1)/issub)*P(")") +local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces +local options=P(":")*spaces*(P(";")^0*option)^0 +local pattern=(filename_1+filename_2+fontname_1+fontname_2)*subvalue^0*crapspec^0*options^0 +local function colonized(specification) + list={} + lpeg.match(pattern,specification.specification) + list.crap=nil + if list.name then + specification.name=list.name + list.name=nil + end + if list.lookup then + specification.lookup=list.lookup + list.lookup=nil + end + if list.sub then + specification.sub=list.sub + list.sub=nil + end + specification.features.normal=fonts.handlers.otf.features.normalize(list) + return specification +end +fonts.definers.registersplit(":",colonized,"cryptic") +fonts.definers.registersplit("",colonized,"more cryptic") +function fonts.definers.applypostprocessors(tfmdata) + local postprocessors=tfmdata.postprocessors + if postprocessors then + for i=1,#postprocessors do + local extrahash=postprocessors[i](tfmdata) + if type(extrahash)=="string" and extrahash~="" then + extrahash=string.gsub(lower(extrahash),"[^a-z]","-") + tfmdata.properties.fullname=format("%s-%s",tfmdata.properties.fullname,extrahash) + end + end + end + return tfmdata +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-fonts-ext']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +local fonts=fonts +local otffeatures=fonts.constructors.newfeatures("otf") +local function initializeitlc(tfmdata,value) + if value then + local parameters=tfmdata.parameters + local italicangle=parameters.italicangle + if italicangle and italicangle~=0 then + local properties=tfmdata.properties + local factor=tonumber(value) or 1 + properties.hasitalics=true + properties.autoitalicamount=factor*(parameters.uwidth or 40)/2 + end + end +end +otffeatures.register { + name="itlc", + description="italic correction", + initializers={ + base=initializeitlc, + node=initializeitlc, + } +} +local function initializeslant(tfmdata,value) + value=tonumber(value) + if not value then + value=0 + elseif value>1 then + value=1 + elseif value<-1 then + value=-1 + end + tfmdata.parameters.slantfactor=value +end +otffeatures.register { + name="slant", + description="slant glyphs", + initializers={ + base=initializeslant, + node=initializeslant, + } +} +local function initializeextend(tfmdata,value) + value=tonumber(value) + if not value then + value=0 + elseif value>10 then + value=10 + elseif value<-10 then + value=-10 + end + tfmdata.parameters.extendfactor=value +end +otffeatures.register { + name="extend", + description="scale glyphs horizontally", + initializers={ + base=initializeextend, + node=initializeextend, + } +} +fonts.protrusions=fonts.protrusions or {} +fonts.protrusions.setups=fonts.protrusions.setups or {} +local setups=fonts.protrusions.setups +local function initializeprotrusion(tfmdata,value) + if value then + local setup=setups[value] + if setup then + local factor,left,right=setup.factor or 1,setup.left or 1,setup.right or 1 + local emwidth=tfmdata.parameters.quad + tfmdata.parameters.protrusion={ + auto=true, + } + for i,chr in next,tfmdata.characters do + local v,pl,pr=setup[i],nil,nil + if v then + pl,pr=v[1],v[2] + end + if pl and pl~=0 then chr.left_protruding=left*pl*factor end + if pr and pr~=0 then chr.right_protruding=right*pr*factor end + end + end + end +end +otffeatures.register { + name="protrusion", + description="shift characters into the left and or right margin", + initializers={ + base=initializeprotrusion, + node=initializeprotrusion, + } +} +fonts.expansions=fonts.expansions or {} +fonts.expansions.setups=fonts.expansions.setups or {} +local setups=fonts.expansions.setups +local function initializeexpansion(tfmdata,value) + if value then + local setup=setups[value] + if setup then + local factor=setup.factor or 1 + tfmdata.parameters.expansion={ + stretch=10*(setup.stretch or 0), + shrink=10*(setup.shrink or 0), + step=10*(setup.step or 0), + auto=true, + } + for i,chr in next,tfmdata.characters do + local v=setup[i] + if v and v~=0 then + chr.expansion_factor=v*factor + else + chr.expansion_factor=factor + end + end + end + end +end +otffeatures.register { + name="expansion", + description="apply hz optimization", + initializers={ + base=initializeexpansion, + node=initializeexpansion, + } +} +function fonts.loggers.onetimemessage() end +local byte=string.byte +fonts.expansions.setups['default']={ + stretch=2,shrink=2,step=.5,factor=1, + [byte('A')]=0.5,[byte('B')]=0.7,[byte('C')]=0.7,[byte('D')]=0.5,[byte('E')]=0.7, + [byte('F')]=0.7,[byte('G')]=0.5,[byte('H')]=0.7,[byte('K')]=0.7,[byte('M')]=0.7, + [byte('N')]=0.7,[byte('O')]=0.5,[byte('P')]=0.7,[byte('Q')]=0.5,[byte('R')]=0.7, + [byte('S')]=0.7,[byte('U')]=0.7,[byte('W')]=0.7,[byte('Z')]=0.7, + [byte('a')]=0.7,[byte('b')]=0.7,[byte('c')]=0.7,[byte('d')]=0.7,[byte('e')]=0.7, + [byte('g')]=0.7,[byte('h')]=0.7,[byte('k')]=0.7,[byte('m')]=0.7,[byte('n')]=0.7, + [byte('o')]=0.7,[byte('p')]=0.7,[byte('q')]=0.7,[byte('s')]=0.7,[byte('u')]=0.7, + [byte('w')]=0.7,[byte('z')]=0.7, + [byte('2')]=0.7,[byte('3')]=0.7,[byte('6')]=0.7,[byte('8')]=0.7,[byte('9')]=0.7, +} +fonts.protrusions.setups['default']={ + factor=1,left=1,right=1, + [0x002C]={ 0,1 }, + [0x002E]={ 0,1 }, + [0x003A]={ 0,1 }, + [0x003B]={ 0,1 }, + [0x002D]={ 0,1 }, + [0x2013]={ 0,0.50 }, + [0x2014]={ 0,0.33 }, + [0x3001]={ 0,1 }, + [0x3002]={ 0,1 }, + [0x060C]={ 0,1 }, + [0x061B]={ 0,1 }, + [0x06D4]={ 0,1 }, +} +fonts.handlers.otf.features.normalize=function(t) + if t.rand then + t.rand="random" + end + return t +end +function fonts.helpers.nametoslot(name) + local t=type(name) + if t=="string" then + local tfmdata=fonts.hashes.identifiers[currentfont()] + local shared=tfmdata and tfmdata.shared + local fntdata=shared and shared.rawdata + return fntdata and fntdata.resources.unicodes[name] + elseif t=="number" then + return n + end +end +fonts.encodings=fonts.encodings or {} +local reencodings={} +fonts.encodings.reencodings=reencodings +local function specialreencode(tfmdata,value) + local encoding=value and reencodings[value] + if encoding then + local temp={} + local char=tfmdata.characters + for k,v in next,encoding do + temp[k]=char[v] + end + for k,v in next,temp do + char[k]=temp[k] + end + return string.format("reencoded:%s",value) + end +end +local function reencode(tfmdata,value) + tfmdata.postprocessors=tfmdata.postprocessors or {} + table.insert(tfmdata.postprocessors, + function(tfmdata) + return specialreencode(tfmdata,value) + end + ) +end +otffeatures.register { + name="reencode", + description="reencode characters", + manipulators={ + base=reencode, + node=reencode, + } +} + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-fonts-cbk']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end +local fonts=fonts +local nodes=nodes +local traverse_id=node.traverse_id +local glyph_code=nodes.nodecodes.glyph +function nodes.handlers.characters(head) + local fontdata=fonts.hashes.identifiers + if fontdata then + local usedfonts,done,prevfont={},false,nil + for n in traverse_id(glyph_code,head) do + local font=n.font + if font~=prevfont then + prevfont=font + local used=usedfonts[font] + if not used then + local tfmdata=fontdata[font] + if tfmdata then + local shared=tfmdata.shared + if shared then + local processors=shared.processes + if processors and #processors>0 then + usedfonts[font]=processors + done=true + end + end + end + end + end + end + if done then + for font,processors in next,usedfonts do + for i=1,#processors do + local h,d=processors[i](head,font,0) + head,done=h or head,done or d + end + end + end + return head,true + else + return head,false + end +end +function nodes.simple_font_handler(head) + head=nodes.handlers.characters(head) + nodes.injections.handler(head) + nodes.handlers.protectglyphs(head) + head=node.ligaturing(head) + head=node.kerning(head) + return head +end + +end -- closure diff --git a/src/luaotfload-fonts-cbk.lua b/src/luaotfload-fonts-cbk.lua new file mode 100644 index 0000000..9db94f6 --- /dev/null +++ b/src/luaotfload-fonts-cbk.lua @@ -0,0 +1,68 @@ +if not modules then modules = { } end modules ['luatex-fonts-cbk'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +local nodes = nodes + +-- Fonts: (might move to node-gef.lua) + +local traverse_id = node.traverse_id +local glyph_code = nodes.nodecodes.glyph + +function nodes.handlers.characters(head) + local fontdata = fonts.hashes.identifiers + if fontdata then + local usedfonts, done, prevfont = { }, false, nil + for n in traverse_id(glyph_code,head) do + local font = n.font + if font ~= prevfont then + prevfont = font + local used = usedfonts[font] + if not used then + local tfmdata = fontdata[font] -- + if tfmdata then + local shared = tfmdata.shared -- we need to check shared, only when same features + if shared then + local processors = shared.processes + if processors and #processors > 0 then + usedfonts[font] = processors + done = true + end + end + end + end + end + end + if done then + for font, processors in next, usedfonts do + for i=1,#processors do + local h, d = processors[i](head,font,0) + head, done = h or head, done or d + end + end + end + return head, true + else + return head, false + end +end + +function nodes.simple_font_handler(head) +-- lang.hyphenate(head) + head = nodes.handlers.characters(head) + nodes.injections.handler(head) + nodes.handlers.protectglyphs(head) + head = node.ligaturing(head) + head = node.kerning(head) + return head +end diff --git a/src/luaotfload-fonts-def.lua b/src/luaotfload-fonts-def.lua new file mode 100644 index 0000000..0c2f0db --- /dev/null +++ b/src/luaotfload-fonts-def.lua @@ -0,0 +1,97 @@ +if not modules then modules = { } end modules ['luatex-font-def'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts + +-- A bit of tuning for definitions. + +fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload + +-- tricky: we sort of bypass the parser and directly feed all into +-- the sub parser + +function fonts.definers.getspecification(str) + return "", str, "", ":", str +end + +-- the generic name parser (different from context!) + +local list = { } + +local function issome () list.lookup = 'name' end -- xetex mode prefers name (not in context!) +local function isfile () list.lookup = 'file' end +local function isname () list.lookup = 'name' end +local function thename(s) list.name = s end +local function issub (v) list.sub = v end +local function iscrap (s) list.crap = string.lower(s) end +local function iskey (k,v) list[k] = v end +local function istrue (s) list[s] = true end +local function isfalse(s) list[s] = false end + +local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C + +local spaces = P(" ")^0 +local namespec = (1-S("/:("))^0 -- was: (1-S("/: ("))^0 +local crapspec = spaces * P("/") * (((1-P(":"))^0)/iscrap) * spaces +local filename_1 = P("file:")/isfile * (namespec/thename) +local filename_2 = P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]") +local fontname_1 = P("name:")/isname * (namespec/thename) +local fontname_2 = P(true)/issome * (namespec/thename) +local sometext = (R("az","AZ","09") + S("+-."))^1 +local truevalue = P("+") * spaces * (sometext/istrue) +local falsevalue = P("-") * spaces * (sometext/isfalse) +local keyvalue = (C(sometext) * spaces * P("=") * spaces * C(sometext))/iskey +local somevalue = sometext/istrue +local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim +local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces +local options = P(":") * spaces * (P(";")^0 * option)^0 + +local pattern = (filename_1 + filename_2 + fontname_1 + fontname_2) * subvalue^0 * crapspec^0 * options^0 + +local function colonized(specification) -- xetex mode + list = { } + lpeg.match(pattern,specification.specification) + list.crap = nil -- style not supported, maybe some day + if list.name then + specification.name = list.name + list.name = nil + end + if list.lookup then + specification.lookup = list.lookup + list.lookup = nil + end + if list.sub then + specification.sub = list.sub + list.sub = nil + end + specification.features.normal = fonts.handlers.otf.features.normalize(list) + return specification +end + +fonts.definers.registersplit(":",colonized,"cryptic") +fonts.definers.registersplit("", colonized,"more cryptic") -- catches \font\text=[names] + +function fonts.definers.applypostprocessors(tfmdata) + local postprocessors = tfmdata.postprocessors + if postprocessors then + for i=1,#postprocessors do + local extrahash = postprocessors[i](tfmdata) -- after scaling etc + if type(extrahash) == "string" and extrahash ~= "" then + -- e.g. a reencoding needs this + extrahash = string.gsub(lower(extrahash),"[^a-z]","-") + tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) + end + end + end + return tfmdata +end diff --git a/src/luaotfload-fonts-enc.lua b/src/luaotfload-fonts-enc.lua new file mode 100644 index 0000000..e20c3a0 --- /dev/null +++ b/src/luaotfload-fonts-enc.lua @@ -0,0 +1,28 @@ +if not modules then modules = { } end modules ['luatex-font-enc'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +fonts.encodings = { } +fonts.encodings.agl = { } + +setmetatable(fonts.encodings.agl, { __index = function(t,k) + if k == "unicodes" then + texio.write(" ") + local unicodes = dofile(resolvers.findfile("font-age.lua")) + fonts.encodings.agl = { unicodes = unicodes } + return unicodes + else + return nil + end +end }) + diff --git a/src/luaotfload-fonts-ext.lua b/src/luaotfload-fonts-ext.lua new file mode 100644 index 0000000..b60d045 --- /dev/null +++ b/src/luaotfload-fonts-ext.lua @@ -0,0 +1,272 @@ +if not modules then modules = { } end modules ['luatex-fonts-ext'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +local otffeatures = fonts.constructors.newfeatures("otf") + +-- A few generic extensions. + +local function initializeitlc(tfmdata,value) + if value then + -- the magic 40 and it formula come from Dohyun Kim but we might need another guess + local parameters = tfmdata.parameters + local italicangle = parameters.italicangle + if italicangle and italicangle ~= 0 then + local properties = tfmdata.properties + local factor = tonumber(value) or 1 + properties.hasitalics = true + properties.autoitalicamount = factor * (parameters.uwidth or 40)/2 + end + end +end + +otffeatures.register { + name = "itlc", + description = "italic correction", + initializers = { + base = initializeitlc, + node = initializeitlc, + } +} + +-- slant and extend + +local function initializeslant(tfmdata,value) + value = tonumber(value) + if not value then + value = 0 + elseif value > 1 then + value = 1 + elseif value < -1 then + value = -1 + end + tfmdata.parameters.slantfactor = value +end + +otffeatures.register { + name = "slant", + description = "slant glyphs", + initializers = { + base = initializeslant, + node = initializeslant, + } +} + +local function initializeextend(tfmdata,value) + value = tonumber(value) + if not value then + value = 0 + elseif value > 10 then + value = 10 + elseif value < -10 then + value = -10 + end + tfmdata.parameters.extendfactor = value +end + +otffeatures.register { + name = "extend", + description = "scale glyphs horizontally", + initializers = { + base = initializeextend, + node = initializeextend, + } +} + +-- expansion and protrusion + +fonts.protrusions = fonts.protrusions or { } +fonts.protrusions.setups = fonts.protrusions.setups or { } + +local setups = fonts.protrusions.setups + +local function initializeprotrusion(tfmdata,value) + if value then + local setup = setups[value] + if setup then + local factor, left, right = setup.factor or 1, setup.left or 1, setup.right or 1 + local emwidth = tfmdata.parameters.quad + tfmdata.parameters.protrusion = { + auto = true, + } + for i, chr in next, tfmdata.characters do + local v, pl, pr = setup[i], nil, nil + if v then + pl, pr = v[1], v[2] + end + if pl and pl ~= 0 then chr.left_protruding = left *pl*factor end + if pr and pr ~= 0 then chr.right_protruding = right*pr*factor end + end + end + end +end + +otffeatures.register { + name = "protrusion", + description = "shift characters into the left and or right margin", + initializers = { + base = initializeprotrusion, + node = initializeprotrusion, + } +} + +fonts.expansions = fonts.expansions or { } +fonts.expansions.setups = fonts.expansions.setups or { } + +local setups = fonts.expansions.setups + +local function initializeexpansion(tfmdata,value) + if value then + local setup = setups[value] + if setup then + local factor = setup.factor or 1 + tfmdata.parameters.expansion = { + stretch = 10 * (setup.stretch or 0), + shrink = 10 * (setup.shrink or 0), + step = 10 * (setup.step or 0), + auto = true, + } + for i, chr in next, tfmdata.characters do + local v = setup[i] + if v and v ~= 0 then + chr.expansion_factor = v*factor + else -- can be option + chr.expansion_factor = factor + end + end + end + end +end + +otffeatures.register { + name = "expansion", + description = "apply hz optimization", + initializers = { + base = initializeexpansion, + node = initializeexpansion, + } +} + +-- left over + +function fonts.loggers.onetimemessage() end + +-- example vectors + +local byte = string.byte + +fonts.expansions.setups['default'] = { + + stretch = 2, shrink = 2, step = .5, factor = 1, + + [byte('A')] = 0.5, [byte('B')] = 0.7, [byte('C')] = 0.7, [byte('D')] = 0.5, [byte('E')] = 0.7, + [byte('F')] = 0.7, [byte('G')] = 0.5, [byte('H')] = 0.7, [byte('K')] = 0.7, [byte('M')] = 0.7, + [byte('N')] = 0.7, [byte('O')] = 0.5, [byte('P')] = 0.7, [byte('Q')] = 0.5, [byte('R')] = 0.7, + [byte('S')] = 0.7, [byte('U')] = 0.7, [byte('W')] = 0.7, [byte('Z')] = 0.7, + [byte('a')] = 0.7, [byte('b')] = 0.7, [byte('c')] = 0.7, [byte('d')] = 0.7, [byte('e')] = 0.7, + [byte('g')] = 0.7, [byte('h')] = 0.7, [byte('k')] = 0.7, [byte('m')] = 0.7, [byte('n')] = 0.7, + [byte('o')] = 0.7, [byte('p')] = 0.7, [byte('q')] = 0.7, [byte('s')] = 0.7, [byte('u')] = 0.7, + [byte('w')] = 0.7, [byte('z')] = 0.7, + [byte('2')] = 0.7, [byte('3')] = 0.7, [byte('6')] = 0.7, [byte('8')] = 0.7, [byte('9')] = 0.7, +} + +fonts.protrusions.setups['default'] = { + + factor = 1, left = 1, right = 1, + + [0x002C] = { 0, 1 }, -- comma + [0x002E] = { 0, 1 }, -- period + [0x003A] = { 0, 1 }, -- colon + [0x003B] = { 0, 1 }, -- semicolon + [0x002D] = { 0, 1 }, -- hyphen + [0x2013] = { 0, 0.50 }, -- endash + [0x2014] = { 0, 0.33 }, -- emdash + [0x3001] = { 0, 1 }, -- ideographic comma 、 + [0x3002] = { 0, 1 }, -- ideographic full stop 。 + [0x060C] = { 0, 1 }, -- arabic comma ، + [0x061B] = { 0, 1 }, -- arabic semicolon ؛ + [0x06D4] = { 0, 1 }, -- arabic full stop ۔ + +} + +-- normalizer + +fonts.handlers.otf.features.normalize = function(t) + if t.rand then + t.rand = "random" + end + return t +end + +-- bonus + +function fonts.helpers.nametoslot(name) + local t = type(name) + if t == "string" then + local tfmdata = fonts.hashes.identifiers[currentfont()] + local shared = tfmdata and tfmdata.shared + local fntdata = shared and shared.rawdata + return fntdata and fntdata.resources.unicodes[name] + elseif t == "number" then + return n + end +end + +-- \font\test=file:somefont:reencode=mymessup +-- +-- fonts.encodings.reencodings.mymessup = { +-- [109] = 110, -- m +-- [110] = 109, -- n +-- } + +fonts.encodings = fonts.encodings or { } +local reencodings = { } +fonts.encodings.reencodings = reencodings + +local function specialreencode(tfmdata,value) + -- we forget about kerns as we assume symbols and we + -- could issue a message if ther are kerns but it's + -- a hack anyway so we odn't care too much here + local encoding = value and reencodings[value] + if encoding then + local temp = { } + local char = tfmdata.characters + for k, v in next, encoding do + temp[k] = char[v] + end + for k, v in next, temp do + char[k] = temp[k] + end + -- if we use the font otherwise luatex gets confused so + -- we return an additional hash component for fullname + return string.format("reencoded:%s",value) + end +end + +local function reencode(tfmdata,value) + tfmdata.postprocessors = tfmdata.postprocessors or { } + table.insert(tfmdata.postprocessors, + function(tfmdata) + return specialreencode(tfmdata,value) + end + ) +end + +otffeatures.register { + name = "reencode", + description = "reencode characters", + manipulators = { + base = reencode, + node = reencode, + } +} diff --git a/src/luaotfload-fonts-inj.lua b/src/luaotfload-fonts-inj.lua new file mode 100644 index 0000000..ae48150 --- /dev/null +++ b/src/luaotfload-fonts-inj.lua @@ -0,0 +1,526 @@ +if not modules then modules = { } end modules ['node-inj'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +-- This is very experimental (this will change when we have luatex > .50 and +-- a few pending thingies are available. Also, Idris needs to make a few more +-- test fonts. Btw, future versions of luatex will have extended glyph properties +-- that can be of help. Some optimizations can go away when we have faster machines. + +-- todo: make a special one for context + +local next = next +local utfchar = utf.char + +local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) + +local report_injections = logs.reporter("nodes","injections") + +local attributes, nodes, node = attributes, nodes, node + +fonts = fonts +local fontdata = fonts.hashes.identifiers + +nodes.injections = nodes.injections or { } +local injections = nodes.injections + +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph +local kern_code = nodecodes.kern +local nodepool = nodes.pool +local newkern = nodepool.kern + +local traverse_id = node.traverse_id +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after + +local a_kernpair = attributes.private('kernpair') +local a_ligacomp = attributes.private('ligacomp') +local a_markbase = attributes.private('markbase') +local a_markmark = attributes.private('markmark') +local a_markdone = attributes.private('markdone') +local a_cursbase = attributes.private('cursbase') +local a_curscurs = attributes.private('curscurs') +local a_cursdone = attributes.private('cursdone') + +-- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as +-- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner +-- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure +-- that this code is not 100% okay but examples are needed to figure things out. + +function injections.installnewkern(nk) + newkern = nk or newkern +end + +local cursives = { } +local marks = { } +local kerns = { } + +-- Currently we do gpos/kern in a bit inofficial way but when we have the extra fields in +-- glyphnodes to manipulate ht/dp/wd explicitly I will provide an alternative; also, we +-- can share tables. + +-- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs +-- checking with husayni (volt and fontforge). + +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) + local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) + local ws, wn = tfmstart.width, tfmnext.width + local bound = #cursives + 1 + start[a_cursbase] = bound + nxt[a_curscurs] = bound + cursives[bound] = { rlmode, dx, dy, ws, wn } + return dx, dy, bound +end + +function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) + local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] + -- dy = y - h + if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then + local bound = current[a_kernpair] + if bound then + local kb = kerns[bound] + -- inefficient but singles have less, but weird anyway, needs checking + kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h + else + bound = #kerns + 1 + current[a_kernpair] = bound + kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width } + end + return x, y, w, h, bound + end + return x, y, w, h -- no bound +end + +function injections.setkern(current,factor,rlmode,x,tfmchr) + local dx = factor*x + if dx ~= 0 then + local bound = #kerns + 1 + current[a_kernpair] = bound + kerns[bound] = { rlmode, dx } + return dx, bound + else + return 0, 0 + end +end + +function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) -- ba=baseanchor, ma=markanchor + local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) -- the index argument is no longer used but when this + local bound = base[a_markbase] -- fails again we should pass it + local index = 1 + if bound then + local mb = marks[bound] + if mb then + -- if not index then index = #mb + 1 end + index = #mb + 1 + mb[index] = { dx, dy, rlmode } + start[a_markmark] = bound + start[a_markdone] = index + return dx, dy, bound + else + report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound) + end + end +-- index = index or 1 + index = index or 1 + bound = #marks + 1 + base[a_markbase] = bound + start[a_markmark] = bound + start[a_markdone] = index + marks[bound] = { [index] = { dx, dy, rlmode, baseismark } } + return dx, dy, bound +end + +local function dir(n) + return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" +end + +local function trace(head) + report_injections("begin run") + for n in traverse_id(glyph_code,head) do + if n.subtype < 256 then + local kp = n[a_kernpair] + local mb = n[a_markbase] + local mm = n[a_markmark] + local md = n[a_markdone] + local cb = n[a_cursbase] + local cc = n[a_curscurs] + local char = n.char + report_injections("font %s, char %U, glyph %c",n.font,char,char) + if kp then + local k = kerns[kp] + if k[3] then + report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5]) + else + report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2]) + end + end + if mb then + report_injections(" markbase: bound %a",mb) + end + if mm then + local m = marks[mm] + if mb then + local m = m[mb] + if m then + report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2]) + else + report_injections(" markmark: bound %a, missing index",mm) + end + else + m = m[1] + report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2]) + end + end + if cb then + report_injections(" cursbase: bound %a",cb) + end + if cc then + local c = cursives[cc] + report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3]) + end + end + end + report_injections("end run") +end + +-- todo: reuse tables (i.e. no collection), but will be extra fields anyway +-- todo: check for attribute + +-- We can have a fast test on a font being processed, so we can check faster for marks etc +-- but I'll make a context variant anyway. + +local function show_result(head) + local current = head + local skipping = false + while current do + local id = current.id + if id == glyph_code then + report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset) + skipping = false + elseif id == kern_code then + report_injections("kern: %p",current.kern) + skipping = false + elseif not skipping then + report_injections() + skipping = true + end + current = current.next + end +end + +function injections.handler(head,where,keep) + local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) + if has_marks or has_cursives then + if trace_injections then + trace(head) + end + -- in the future variant we will not copy items but refs to tables + local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0 + if has_kerns then -- move outside loop + local nf, tm = nil, nil + for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts + if n.subtype < 256 then + nofvalid = nofvalid + 1 + valid[nofvalid] = n + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].resources.marks + end + if tm then + mk[n] = tm[n.char] + end + local k = n[a_kernpair] + if k then + local kk = kerns[k] + if kk then + local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0 + local dy = y - h + if dy ~= 0 then + ky[n] = dy + end + if w ~= 0 or x ~= 0 then + wx[n] = kk + end + rl[n] = kk[1] -- could move in test + end + end + end + end + else + local nf, tm = nil, nil + for n in traverse_id(glyph_code,head) do + if n.subtype < 256 then + nofvalid = nofvalid + 1 + valid[nofvalid] = n + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].resources.marks + end + if tm then + mk[n] = tm[n.char] + end + end + end + end + if nofvalid > 0 then + -- we can assume done == true because we have cursives and marks + local cx = { } + if has_kerns and next(ky) then + for n, k in next, ky do + n.yoffset = k + end + end + -- todo: reuse t and use maxt + if has_cursives then + local p_cursbase, p = nil, nil + -- since we need valid[n+1] we can also use a "while true do" + local t, d, maxt = { }, { }, 0 + for i=1,nofvalid do -- valid == glyphs + local n = valid[i] + if not mk[n] then + local n_cursbase = n[a_cursbase] + if p_cursbase then + local n_curscurs = n[a_curscurs] + if p_cursbase == n_curscurs then + local c = cursives[n_curscurs] + if c then + local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] + if rlmode >= 0 then + dx = dx - ws + else + dx = dx + wn + end + if dx ~= 0 then + cx[n] = dx + rl[n] = rlmode + end + -- if rlmode and rlmode < 0 then + dy = -dy + -- end + maxt = maxt + 1 + t[maxt] = p + d[maxt] = dy + else + maxt = 0 + end + end + elseif maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + local ti = t[i] + ti.yoffset = ti.yoffset + ny + end + maxt = 0 + end + if not n_cursbase and maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + local ti = t[i] + ti.yoffset = ny + end + maxt = 0 + end + p_cursbase, p = n_cursbase, n + end + end + if maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + local ti = t[i] + ti.yoffset = ny + end + maxt = 0 + end + if not keep then + cursives = { } + end + end + if has_marks then + for i=1,nofvalid do + local p = valid[i] + local p_markbase = p[a_markbase] + if p_markbase then + local mrks = marks[p_markbase] + local nofmarks = #mrks + for n in traverse_id(glyph_code,p.next) do + local n_markmark = n[a_markmark] + if p_markbase == n_markmark then + local index = n[a_markdone] or 1 + local d = mrks[index] + if d then + local rlmode = d[3] + -- + local k = wx[p] + if k then + local x = k[2] + local w = k[4] + if w then + if rlmode and rlmode >= 0 then + -- kern(x) glyph(p) kern(w-x) mark(n) + n.xoffset = p.xoffset - p.width + d[1] - (w-x) + else + -- kern(w-x) glyph(p) kern(x) mark(n) + n.xoffset = p.xoffset - d[1] - x + end + else + if rlmode and rlmode >= 0 then + -- okay for husayni + n.xoffset = p.xoffset - p.width + d[1] + else + -- needs checking: is x ok here? + n.xoffset = p.xoffset - d[1] - x + end + end + else + if rlmode and rlmode >= 0 then + n.xoffset = p.xoffset - p.width + d[1] + else + n.xoffset = p.xoffset - d[1] + end + local w = n.width + if w ~= 0 then + insert_node_before(head,n,newkern(-w/2)) + insert_node_after(head,n,newkern(-w/2)) + end + end + -- -- + if mk[p] then + n.yoffset = p.yoffset + d[2] + else + n.yoffset = n.yoffset + p.yoffset + d[2] + end + -- + if nofmarks == 1 then + break + else + nofmarks = nofmarks - 1 + end + end + else + -- KE: there can be sequences in ligatures + end + end + end + end + if not keep then + marks = { } + end + end + -- todo : combine + if next(wx) then + for n, k in next, wx do + -- only w can be nil (kernclasses), can be sped up when w == nil + local x = k[2] + local w = k[4] + if w then + local rl = k[1] -- r2l = k[6] + local wx = w - x + if rl < 0 then -- KE: don't use r2l here + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) -- type 0/2 + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) -- type 0/2 + end + else + if x ~= 0 then + insert_node_before(head,n,newkern(x)) -- type 0/2 + end + if wx ~= 0 then + insert_node_after (head,n,newkern(wx)) -- type 0/2 + end + end + elseif x ~= 0 then + -- this needs checking for rl < 0 but it is unlikely that a r2l script + -- uses kernclasses between glyphs so we're probably safe (KE has a + -- problematic font where marks interfere with rl < 0 in the previous + -- case) + insert_node_before(head,n,newkern(x)) -- a real font kern, type 0 + end + end + end + if next(cx) then + for n, k in next, cx do + if k ~= 0 then + local rln = rl[n] + if rln and rln < 0 then + insert_node_before(head,n,newkern(-k)) -- type 0/2 + else + insert_node_before(head,n,newkern(k)) -- type 0/2 + end + end + end + end + if not keep then + kerns = { } + end + -- if trace_injections then + -- show_result(head) + -- end + return head, true + elseif not keep then + kerns, cursives, marks = { }, { }, { } + end + elseif has_kerns then + if trace_injections then + trace(head) + end + for n in traverse_id(glyph_code,head) do + if n.subtype < 256 then + local k = n[a_kernpair] + if k then + local kk = kerns[k] + if kk then + local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4] + if y and y ~= 0 then + n.yoffset = y -- todo: h ? + end + if w then + -- copied from above + -- local r2l = kk[6] + local wx = w - x + if rl < 0 then -- KE: don't use r2l here + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) + end + else + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + if wx ~= 0 then + insert_node_after(head,n,newkern(wx)) + end + end + else + -- simple (e.g. kernclass kerns) + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + end + end + end + end + end + if not keep then + kerns = { } + end + -- if trace_injections then + -- show_result(head) + -- end + return head, true + else + -- no tracing needed + end + return head, false +end diff --git a/src/luaotfload-fonts-lua.lua b/src/luaotfload-fonts-lua.lua new file mode 100644 index 0000000..ec3fe38 --- /dev/null +++ b/src/luaotfload-fonts-lua.lua @@ -0,0 +1,33 @@ +if not modules then modules = { } end modules ['luatex-fonts-lua'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +fonts.formats.lua = "lua" + +function fonts.readers.lua(specification) + local fullname = specification.filename or "" + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + fullname = specification.name .. "." .. forced + else + fullname = specification.name + end + end + local fullname = resolvers.findfile(fullname) or "" + if fullname ~= "" then + local loader = loadfile(fullname) + loader = loader and loader() + return loader and loader(specification) + end +end diff --git a/src/luaotfload-fonts-otn.lua b/src/luaotfload-fonts-otn.lua new file mode 100644 index 0000000..c57be5f --- /dev/null +++ b/src/luaotfload-fonts-otn.lua @@ -0,0 +1,2848 @@ +if not modules then modules = { } end modules ['font-otn'] = { + 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", +} + +-- preprocessors = { "nodes" } + +-- this is still somewhat preliminary and it will get better in due time; +-- much functionality could only be implemented thanks to the husayni font +-- of Idris Samawi Hamid to who we dedicate this module. + +-- in retrospect it always looks easy but believe it or not, it took a lot +-- of work to get proper open type support done: buggy fonts, fuzzy specs, +-- special made testfonts, many skype sessions between taco, idris and me, +-- torture tests etc etc ... unfortunately the code does not show how much +-- time it took ... + +-- todo: +-- +-- kerning is probably not yet ok for latin around dics nodes (interesting challenge) +-- extension infrastructure (for usage out of context) +-- sorting features according to vendors/renderers +-- alternative loop quitters +-- check cursive and r2l +-- find out where ignore-mark-classes went +-- default features (per language, script) +-- handle positions (we need example fonts) +-- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) +-- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests) +-- remove some optimizations (when I have a faster machine) +-- +-- maybe redo the lot some way (more context specific) + +--[[ldx-- +

This module is a bit more split up that I'd like but since we also want to test +with plain it has to be so. This module is part of +and discussion about improvements and functionality mostly happens on the + mailing list.

+ +

The specification of OpenType is kind of vague. Apart from a lack of a proper +free specifications there's also the problem that Microsoft and Adobe +may have their own interpretation of how and in what order to apply features. +In general the Microsoft website has more detailed specifications and is a +better reference. There is also some information in the FontForge help files.

+ +

Because there is so much possible, fonts might contain bugs and/or be made to +work with certain rederers. These may evolve over time which may have the side +effect that suddenly fonts behave differently.

+ +

After a lot of experiments (mostly by Taco, me and Idris) we're now at yet another +implementation. Of course all errors are mine and of course the code can be +improved. There are quite some optimizations going on here and processing speed +is currently acceptable. Not all functions are implemented yet, often because I +lack the fonts for testing. Many scripts are not yet supported either, but I will +look into them as soon as users ask for it.

+ +

Because there are different interpretations possible, I will extend the code +with more (configureable) variants. I can also add hooks for users so that they can +write their own extensions.

+ +

Glyphs are indexed not by unicode but in their own way. This is because there is no +relationship with unicode at all, apart from the fact that a font might cover certain +ranges of characters. One character can have multiple shapes. However, at the + end we use unicode so and all extra glyphs are mapped into a private +space. This is needed because we need to access them and has to include +then in the output eventually.

+ +

The raw table as it coms from gets reorganized in to fit out needs. +In that table is packed (similar tables are shared) and cached on disk +so that successive runs can use the optimized table (after loading the table is +unpacked). The flattening code used later is a prelude to an even more compact table +format (and as such it keeps evolving).

+ +

This module is sparsely documented because it is a moving target. The table format +of the reader changes and we experiment a lot with different methods for supporting +features.

+ +

As with the code, we may decide to store more information in the + table.

+ +

Incrementing the version number will force a re-cache. We jump the number by one +when there's a fix in the library or code that +results in different tables.

+--ldx]]-- + +-- action handler chainproc chainmore comment +-- +-- gsub_single ok ok ok +-- gsub_multiple ok ok not implemented yet +-- gsub_alternate ok ok not implemented yet +-- gsub_ligature ok ok ok +-- gsub_context ok -- +-- gsub_contextchain ok -- +-- gsub_reversecontextchain ok -- +-- chainsub -- ok +-- reversesub -- ok +-- gpos_mark2base ok ok +-- gpos_mark2ligature ok ok +-- gpos_mark2mark ok ok +-- gpos_cursive ok untested +-- gpos_single ok ok +-- gpos_pair ok ok +-- gpos_context ok -- +-- gpos_contextchain ok -- +-- +-- todo: contextpos and contextsub and class stuff +-- +-- actions: +-- +-- handler : actions triggered by lookup +-- chainproc : actions triggered by contextual lookup +-- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij) +-- +-- remark: the 'not implemented yet' variants will be done when we have fonts that use them +-- remark: we need to check what to do with discretionaries + +-- We used to have independent hashes for lookups but as the tags are unique +-- we now use only one hash. If needed we can have multiple again but in that +-- case I will probably prefix (i.e. rename) the lookups in the cached font file. + +-- Todo: make plugin feature that operates on char/glyphnode arrays + +local concat, insert, remove = table.concat, table.insert, table.remove +local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip +local type, next, tonumber, tostring = type, next, tonumber, tostring +local lpegmatch = lpeg.match +local random = math.random +local formatters = string.formatters + +local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes + +local registertracker = trackers.register + +local fonts = fonts +local otf = fonts.handlers.otf + +local trace_lookups = false registertracker("otf.lookups", function(v) trace_lookups = v end) +local trace_singles = false registertracker("otf.singles", function(v) trace_singles = v end) +local trace_multiples = false registertracker("otf.multiples", function(v) trace_multiples = v end) +local trace_alternatives = false registertracker("otf.alternatives", function(v) trace_alternatives = v end) +local trace_ligatures = false registertracker("otf.ligatures", function(v) trace_ligatures = v end) +local trace_contexts = false registertracker("otf.contexts", function(v) trace_contexts = v end) +local trace_marks = false registertracker("otf.marks", function(v) trace_marks = v end) +local trace_kerns = false registertracker("otf.kerns", function(v) trace_kerns = v end) +local trace_cursive = false registertracker("otf.cursive", function(v) trace_cursive = v end) +local trace_preparing = false registertracker("otf.preparing", function(v) trace_preparing = v end) +local trace_bugs = false registertracker("otf.bugs", function(v) trace_bugs = v end) +local trace_details = false registertracker("otf.details", function(v) trace_details = v end) +local trace_applied = false registertracker("otf.applied", function(v) trace_applied = v end) +local trace_steps = false registertracker("otf.steps", function(v) trace_steps = v end) +local trace_skips = false registertracker("otf.skips", function(v) trace_skips = v end) +local trace_directions = false registertracker("otf.directions", function(v) trace_directions = v end) + +local report_direct = logs.reporter("fonts","otf direct") +local report_subchain = logs.reporter("fonts","otf subchain") +local report_chain = logs.reporter("fonts","otf chain") +local report_process = logs.reporter("fonts","otf process") +local report_prepare = logs.reporter("fonts","otf prepare") +local report_warning = logs.reporter("fonts","otf warning") + +registertracker("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) +registertracker("otf.normal_chain", function(v) otf.setcontextchain(v and "normal") end) + +registertracker("otf.replacements", "otf.singles,otf.multiples,otf.alternatives,otf.ligatures") +registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") +registertracker("otf.actions","otf.replacements,otf.positions") +registertracker("otf.injections","nodes.injections") + +registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") + +local insert_node_after = node.insert_after +local delete_node = nodes.delete +local copy_node = node.copy +local find_node_tail = node.tail or node.slide +local flush_node_list = node.flush_list +local end_of_math = node.end_of_math + +local setmetatableindex = table.setmetatableindex + +local zwnj = 0x200C +local zwj = 0x200D +local wildcard = "*" +local default = "dflt" + +local nodecodes = nodes.nodecodes +local whatcodes = nodes.whatcodes +local glyphcodes = nodes.glyphcodes +local disccodes = nodes.disccodes + +local glyph_code = nodecodes.glyph +local glue_code = nodecodes.glue +local disc_code = nodecodes.disc +local whatsit_code = nodecodes.whatsit +local math_code = nodecodes.math + +local dir_code = whatcodes.dir +local localpar_code = whatcodes.localpar + +local discretionary_code = disccodes.discretionary + +local ligature_code = glyphcodes.ligature + +local privateattribute = attributes.private + +-- Something is messed up: we have two mark / ligature indices, one at the injection +-- end and one here ... this is bases in KE's patches but there is something fishy +-- there as I'm pretty sure that for husayni we need some connection (as it's much +-- more complex than an average font) but I need proper examples of all cases, not +-- of only some. + +local a_state = privateattribute('state') +local a_markbase = privateattribute('markbase') +local a_markmark = privateattribute('markmark') +local a_markdone = privateattribute('markdone') -- assigned at the injection end +local a_cursbase = privateattribute('cursbase') +local a_curscurs = privateattribute('curscurs') +local a_cursdone = privateattribute('cursdone') +local a_kernpair = privateattribute('kernpair') +local a_ligacomp = privateattribute('ligacomp') -- assigned here (ideally it should be combined) + +local injections = nodes.injections +local setmark = injections.setmark +local setcursive = injections.setcursive +local setkern = injections.setkern +local setpair = injections.setpair + +local markonce = true +local cursonce = true +local kernonce = true + +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers + +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register + +local onetimemessage = fonts.loggers.onetimemessage or function() end + +otf.defaultnodealternate = "none" -- first last + +-- we share some vars here, after all, we have no nested lookups and less code + +local tfmdata = false +local characters = false +local descriptions = false +local resources = false +local marks = false +local currentfont = false +local lookuptable = false +local anchorlookups = false +local lookuptypes = false +local handlers = { } +local rlmode = 0 +local featurevalue = false + +-- head is always a whatsit so we can safely assume that head is not changed + +-- we use this for special testing and documentation + +local checkstep = (nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end +local registerstep = (nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end +local registermessage = (nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_direct(...) +end + +local function logwarning(...) + report_direct(...) +end + +local f_unicode = formatters["%U"] +local f_uniname = formatters["%U (%s)"] +local f_unilist = formatters["% t (% t)"] + +local function gref(n) -- currently the same as in font-otb + if type(n) == "number" then + local description = descriptions[n] + local name = description and description.name + if name then + return f_uniname(n,name) + else + return f_unicode(n) + end + elseif n then + local num, nam = { }, { } + for i=1,#n do + local ni = n[i] + if tonumber(ni) then -- later we will start at 2 + local di = descriptions[ni] + num[i] = f_unicode(ni) + nam[i] = di and di.name or "-" + end + end + return f_unilist(num,nam) + else + return "" + end +end + +local function cref(kind,chainname,chainlookupname,lookupname,index) -- not in the mood to alias f_ + if index then + return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookupname,index) + elseif lookupname then + return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookupname) + elseif chainlookupname then + return formatters["feature %a, chain %a, sub %a"](kind,chainname,chainlookupname) + elseif chainname then + return formatters["feature %a, chain %a"](kind,chainname) + else + return formatters["feature %a"](kind) + end +end + +local function pref(kind,lookupname) + return formatters["feature %a, lookup %a"](kind,lookupname) +end + +-- We can assume that languages that use marks are not hyphenated. We can also assume +-- that at most one discretionary is present. + +-- We do need components in funny kerning mode but maybe I can better reconstruct then +-- as we do have the font components info available; removing components makes the +-- previous code much simpler. Also, later on copying and freeing becomes easier. +-- However, for arabic we need to keep them around for the sake of mark placement +-- and indices. + +local function copy_glyph(g) -- next and prev are untouched ! + local components = g.components + if components then + g.components = nil + local n = copy_node(g) + g.components = components + return n + else + return copy_node(g) + end +end + +-- start is a mark and we need to keep that one + +local function markstoligature(kind,lookupname,head,start,stop,char) + if start == stop and start.char == char then + return head, start + else + local prev = start.prev + local next = stop.next + start.prev = nil + stop.next = nil + local base = copy_glyph(start) + if head == start then + head = base + end + base.char = char + base.subtype = ligature_code + base.components = start + if prev then + prev.next = base + end + if next then + next.prev = base + end + base.next = next + base.prev = prev + return head, base + end +end + +-- The next code is somewhat complicated by the fact that some fonts can have ligatures made +-- from ligatures that themselves have marks. This was identified by Kai in for instance +-- arabtype: KAF LAM SHADDA ALEF FATHA (0x0643 0x0644 0x0651 0x0627 0x064E). This becomes +-- KAF LAM-ALEF with a SHADDA on the first and a FATHA op de second component. In a next +-- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the +-- third component. + +local function getcomponentindex(start) + if start.id ~= glyph_code then + return 0 + elseif start.subtype == ligature_code then + local i = 0 + local components = start.components + while components do + i = i + getcomponentindex(components) + components = components.next + end + return i + elseif not marks[start.char] then + return 1 + else + return 0 + end +end + +-- eventually we will do positioning in an other way (needs addional w/h/d fields) + +local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head + if start == stop and start.char == char then + start.char = char + return head, start + end + local prev = start.prev + local next = stop.next + start.prev = nil + stop.next = nil + local base = copy_glyph(start) + if start == head then + head = base + end + base.char = char + base.subtype = ligature_code + base.components = start -- start can have components + if prev then + prev.next = base + end + if next then + next.prev = base + end + base.next = next + base.prev = prev + if not discfound then + local deletemarks = markflag ~= "mark" + local components = start + local baseindex = 0 + local componentindex = 0 + local head = base + local current = base + -- first we loop over the glyphs in start .. stop + while start do + local char = start.char + if not marks[char] then + baseindex = baseindex + componentindex + componentindex = getcomponentindex(start) + elseif not deletemarks then -- quite fishy + start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) + if trace_marks then + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) + end + head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components + elseif trace_marks then + logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char)) + end + start = start.next + end + -- we can have one accent as part of a lookup and another following + -- local start = components -- was wrong (component scanning was introduced when more complex ligs in devanagari was added) + local start = current.next + while start and start.id == glyph_code do + local char = start.char + if marks[char] then + start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) + if trace_marks then + logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) + end + else + break + end + start = start.next + end + end + return head, base +end + +function handlers.gsub_single(head,start,kind,lookupname,replacement) + if trace_singles then + logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) + end + start.char = replacement + return head, start, true +end + +local function get_alternative_glyph(start,alternatives,value,trace_alternatives) + local n = #alternatives + if value == "random" then + local r = random(1,n) + return alternatives[r], trace_alternatives and formatters["value %a, taking %a"](value,r) + elseif value == "first" then + return alternatives[1], trace_alternatives and formatters["value %a, taking %a"](value,1) + elseif value == "last" then + return alternatives[n], trace_alternatives and formatters["value %a, taking %a"](value,n) + else + value = tonumber(value) + if type(value) ~= "number" then + return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) + elseif value > n then + local defaultalt = otf.defaultnodealternate + if defaultalt == "first" then + return alternatives[n], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) + elseif defaultalt == "last" then + return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,n) + else + return false, trace_alternatives and formatters["invalid value %a, %s"](value,"out of range") + end + elseif value == 0 then + return start.char, trace_alternatives and formatters["invalid value %a, %s"](value,"no change") + elseif value < 1 then + return alternatives[1], trace_alternatives and formatters["invalid value %a, taking %a"](value,1) + else + return alternatives[value], trace_alternatives and formatters["value %a, taking %a"](value,value) + end + end +end + +local function multiple_glyphs(head,start,multiple,ignoremarks) + local nofmultiples = #multiple + if nofmultiples > 0 then + start.char = multiple[1] + if nofmultiples > 1 then + local sn = start.next + for k=2,nofmultiples do -- todo: use insert_node +-- untested: +-- +-- while ignoremarks and marks[sn.char] then +-- local sn = sn.next +-- end + local n = copy_node(start) -- ignore components + n.char = multiple[k] + n.next = sn + n.prev = start + if sn then + sn.prev = n + end + start.next = n + start = n + end + end + return head, start, true + else + if trace_multiples then + logprocess("no multiple for %s",gref(start.char)) + end + return head, start, false + end +end + +function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence) + local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue + local choice, comment = get_alternative_glyph(start,alternative,value,trace_alternatives) + if choice then + if trace_alternatives then + logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment) + end + start.char = choice + else + if trace_alternatives then + logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment) + end + end + return head, start, true +end + +function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence) + if trace_multiples then + logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) + end + return multiple_glyphs(head,start,multiple,sequence.flags[1]) +end + +function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) + local s, stop, discfound = start.next, nil, false + local startchar = start.char + if marks[startchar] then + while s do + local id = s.id + if id == glyph_code and s.font == currentfont and s.subtype<256 then + local lg = ligature[s.char] + if lg then + stop = s + ligature = lg + s = s.next + else + break + end + else + break + end + end + if stop then + local lig = ligature.ligature + if lig then + if trace_ligatures then + local stopchar = stop.char + head, start = markstoligature(kind,lookupname,head,start,stop,lig) + logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) + else + head, start = markstoligature(kind,lookupname,head,start,stop,lig) + end + return head, start, true + else + -- ok, goto next lookup + end + end + else + local skipmark = sequence.flags[1] + while s do + local id = s.id + if id == glyph_code and s.subtype<256 then + if s.font == currentfont then + local char = s.char + if skipmark and marks[char] then + s = s.next + else + local lg = ligature[char] + if lg then + stop = s + ligature = lg + s = s.next + else + break + end + end + else + break + end + elseif id == disc_code then + discfound = true + s = s.next + else + break + end + end + local lig = ligature.ligature + if lig then + if stop then + if trace_ligatures then + local stopchar = stop.char + head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) + logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) + else + head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) + end + return head, start, true + else + -- weird but happens (in some arabic font) + start.char = lig + if trace_ligatures then + logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig)) + end + return head, start, true + end + else + -- weird but happens + end + end + return head, start, false +end + +--[[ldx-- +

We get hits on a mark, but we're not sure if the it has to be applied so +we need to explicitly test for basechar, baselig and basemark entries.

+--ldx]]-- + +function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [start=mark] + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return head, start, false + end + end + end + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + end + if baseanchors then + local baseanchors = baseanchors['basechar'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return head, start, false +end + +function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence) + -- check chainpos variant + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [optional marks] [start=mark] + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return head, start, false + end + end + end + local index = start[a_ligacomp] + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + if baseanchors then + local baseanchors = baseanchors['baselig'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor, ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + ba = ba[index] + if ba then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index + if trace_marks then + logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", + pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) + end + return head, start, true + else + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index) + end + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return head, start, false +end + +function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [basemark] [start=mark] + local slc = start[a_ligacomp] + if slc then -- a rather messy loop ... needs checking with husayni + while base do + local blc = base[a_ligacomp] + if blc and blc ~= slc then + base = base.prev + else + break + end + end + end + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go + local basechar = base.char + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + if baseanchors then + baseanchors = baseanchors['basemark'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return head, start, false +end + +function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) -- to be checked + local alreadydone = cursonce and start[a_cursbase] + if not alreadydone then + local done = false + local startchar = start.char + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt = start.next + while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do + local nextchar = nxt.char + if marks[nextchar] then + -- should not happen (maybe warning) + nxt = nxt.next + else + local entryanchors = descriptions[nextchar] + if entryanchors then + entryanchors = entryanchors.anchors + if entryanchors then + entryanchors = entryanchors['centry'] + if entryanchors then + local al = anchorlookups[lookupname] + for anchor, entry in next, entryanchors do + if al[anchor] then + local exit = exitanchors[anchor] + if exit then + local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + end + done = true + break + end + end + end + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + end + break + end + end + end + return head, start, done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return head, start, false + end +end + +function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) + local startchar = start.char + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) + end + return head, start, false +end + +function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) + -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too + -- todo: kerns in components of ligatures + local snext = start.next + if not snext then + return head, start, false + else + local prev, done = start, false + local factor = tfmdata.parameters.factor + local lookuptype = lookuptypes[lookupname] + while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do + local nextchar = snext.char + local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = snext.next + else + if not krn then + -- skip + elseif type(krn) == "table" then + if lookuptype == "pair" then -- probably not needed + local a, b = krn[2], krn[3] + if a and #a > 0 then + local startchar = start.char + local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local startchar = start.char + local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else -- wrong ... position has different entries + report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) + -- local a, b = krn[2], krn[6] + -- if a and a ~= 0 then + -- local k = setkern(snext,factor,rlmode,a) + -- if trace_kerns then + -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + -- end + -- end + -- if b and b ~= 0 then + -- logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) + -- end + end + done = true + elseif krn ~= 0 then + local k = setkern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + end + done = true + end + break + end + end + return head, start, done + end +end + +--[[ldx-- +

I will implement multiple chain replacements once I run into a font that uses +it. It's not that complex to handle.

+--ldx]]-- + +local chainmores = { } +local chainprocs = { } + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_subchain(...) +end + +local logwarning = report_subchain + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_chain(...) +end + +local logwarning = report_chain + +-- We could share functions but that would lead to extra function calls with many +-- arguments, redundant tests and confusing messages. + +function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) + logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return head, start, false +end + +function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) + logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return head, start, false +end + +-- The reversesub is a special case, which is why we need to store the replacements +-- in a bit weird way. There is no lookup and the replacement comes from the lookup +-- itself. It is meant mostly for dealing with Urdu. + +function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) + local char = start.char + local replacement = replacements[char] + if replacement then + if trace_singles then + logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) + end + start.char = replacement + return head, start, true + else + return head, start, false + end +end + +--[[ldx-- +

This chain stuff is somewhat tricky since we can have a sequence of actions to be +applied: single, alternate, multiple or ligature where ligature can be an invalid +one in the sense that it will replace multiple by one but not neccessary one that +looks like the combination (i.e. it is the counterpart of multiple then). For +example, the following is valid:

+ + +xxxabcdexxx [single a->A][multiple b->BCD][ligature cde->E] xxxABCDExxx + + +

Therefore we we don't really do the replacement here already unless we have the +single lookup case. The efficiency of the replacements can be improved by deleting +as less as needed but that would also make the code even more messy.

+--ldx]]-- + +-- local function delete_till_stop(head,start,stop,ignoremarks) -- keeps start +-- local n = 1 +-- if start == stop then +-- -- done +-- elseif ignoremarks then +-- repeat -- start x x m x x stop => start m +-- local next = start.next +-- if not marks[next.char] then +-- local components = next.components +-- if components then -- probably not needed +-- flush_node_list(components) +-- end +-- head = delete_node(head,next) +-- end +-- n = n + 1 +-- until next == stop +-- else -- start x x x stop => start +-- repeat +-- local next = start.next +-- local components = next.components +-- if components then -- probably not needed +-- flush_node_list(components) +-- end +-- head = delete_node(head,next) +-- n = n + 1 +-- until next == stop +-- end +-- return head, n +-- end + +--[[ldx-- +

Here we replace start by a single variant, First we delete the rest of the +match.

+--ldx]]-- + +function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) + -- todo: marks ? + local current = start + local subtables = currentlookup.subtables + if #subtables > 1 then + logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) + end + while current do + if current.id == glyph_code then + local currentchar = current.char + local lookupname = subtables[1] -- only 1 + local replacement = lookuphash[lookupname] + if not replacement then + if trace_bugs then + logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + replacement = replacement[currentchar] + if not replacement or replacement == "" then + if trace_bugs then + logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) + end + else + if trace_singles then + logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) + end + current.char = replacement + end + end + return head, start, true + elseif current == stop then + break + else + current = current.next + end + end + return head, start, false +end + +chainmores.gsub_single = chainprocs.gsub_single + +--[[ldx-- +

Here we replace start by a sequence of new glyphs. First we delete the rest of +the match.

+--ldx]]-- + +function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + -- local head, n = delete_till_stop(head,start,stop) + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local replacements = lookuphash[lookupname] + if not replacements then + if trace_bugs then + logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) + end + else + replacements = replacements[startchar] + if not replacements or replacement == "" then + if trace_bugs then + logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) + end + else + if trace_multiples then + logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) + end + return multiple_glyphs(head,start,replacements,currentlookup.flags[1]) + end + end + return head, start, false +end + +chainmores.gsub_multiple = chainprocs.gsub_multiple + +--[[ldx-- +

Here we replace start by new glyph. First we delete the rest of the match.

+--ldx]]-- + +-- char_1 mark_1 -> char_x mark_1 (ignore marks) +-- char_1 mark_1 -> char_x + +-- to be checked: do we always have just one glyph? +-- we can also have alternates for marks +-- marks come last anyway +-- are there cases where we need to delete the mark + +function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local current = start + local subtables = currentlookup.subtables + local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue + while current do + if current.id == glyph_code then -- is this check needed? + local currentchar = current.char + local lookupname = subtables[1] + local alternatives = lookuphash[lookupname] + if not alternatives then + if trace_bugs then + logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname)) + end + else + alternatives = alternatives[currentchar] + if alternatives then + local choice, comment = get_alternative_glyph(current,alternatives,value,trace_alternatives) + if choice then + if trace_alternatives then + logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment) + end + start.char = choice + else + if trace_alternatives then + logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) + end + end + elseif trace_bugs then + logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment) + end + end + return head, start, true + elseif current == stop then + break + else + current = current.next + end + end + return head, start, false +end + +chainmores.gsub_alternate = chainprocs.gsub_alternate + +--[[ldx-- +

When we replace ligatures we use a helper that handles the marks. I might change +this function (move code inline and handle the marks by a separate function). We +assume rather stupid ligatures (no complex disc nodes).

+--ldx]]-- + +function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local ligatures = lookuphash[lookupname] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + ligatures = ligatures[startchar] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + end + else + local s = start.next + local discfound = false + local last = stop + local nofreplacements = 0 + local skipmark = currentlookup.flags[1] + while s do + local id = s.id + if id == disc_code then + s = s.next + discfound = true + else + local schar = s.char + if skipmark and marks[schar] then -- marks + s = s.next + else + local lg = ligatures[schar] + if lg then + ligatures, last, nofreplacements = lg, s, nofreplacements + 1 + if s == stop then + break + else + s = s.next + end + else + break + end + end + end + end + local l2 = ligatures.ligature + if l2 then + if chainindex then + stop = last + end + if trace_ligatures then + if start == stop then + logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) + else + logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) + end + end + head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) + return head, start, true, nofreplacements + elseif trace_bugs then + if start == stop then + logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + else + logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) + end + end + end + end + return head, start, false, 0 +end + +chainmores.gsub_ligature = chainprocs.gsub_ligature + +function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = lookuphash[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [start=mark] + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return head, start, false + end + end + end + local baseanchors = descriptions[basechar].anchors + if baseanchors then + local baseanchors = baseanchors['basechar'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return head, start, false +end + +function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = lookuphash[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [optional marks] [start=mark] + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) + end + return head, start, false + end + end + end + -- todo: like marks a ligatures hash + local index = start[a_ligacomp] + local baseanchors = descriptions[basechar].anchors + if baseanchors then + local baseanchors = baseanchors['baselig'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + ba = ba[index] + if ba then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", + cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) + end + return head, start, true + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return head, start, false +end + +function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + -- local alreadydone = markonce and start[a_markmark] + -- if not alreadydone then + -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = lookuphash[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [basemark] [start=mark] + local slc = start[a_ligacomp] + if slc then -- a rather messy loop ... needs checking with husayni + while base do + local blc = base[a_ligacomp] + if blc and blc ~= slc then + base = base.prev + else + break + end + end + end + if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go + local basechar = base.char + local baseanchors = descriptions[basechar].anchors + if baseanchors then + baseanchors = baseanchors['basemark'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + -- elseif trace_marks and trace_details then + -- logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) + -- end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return head, start, false +end + +function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + local alreadydone = cursonce and start[a_cursbase] + if not alreadydone then + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local exitanchors = lookuphash[lookupname] + if exitanchors then + exitanchors = exitanchors[startchar] + end + if exitanchors then + local done = false + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt = start.next + while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do + local nextchar = nxt.char + if marks[nextchar] then + -- should not happen (maybe warning) + nxt = nxt.next + else + local entryanchors = descriptions[nextchar] + if entryanchors then + entryanchors = entryanchors.anchors + if entryanchors then + entryanchors = entryanchors['centry'] + if entryanchors then + local al = anchorlookups[lookupname] + for anchor, entry in next, entryanchors do + if al[anchor] then + local exit = exitanchors[anchor] + if exit then + local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) + end + done = true + break + end + end + end + end + end + elseif trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + end + break + end + end + end + return head, start, done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return head, start, false + end + end + return head, start, false +end + +function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + -- untested .. needs checking for the new model + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = lookuphash[lookupname] + if kerns then + kerns = kerns[startchar] -- needed ? + if kerns then + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) + end + end + end + return head, start, false +end + +chainmores.gpos_single = chainprocs.gpos_single -- okay? + +-- when machines become faster i will make a shared function + +function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + local snext = start.next + if snext then + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = lookuphash[lookupname] + if kerns then + kerns = kerns[startchar] + if kerns then + local lookuptype = lookuptypes[lookupname] + local prev, done = start, false + local factor = tfmdata.parameters.factor + while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do + local nextchar = snext.char + local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = snext.next + else + if not krn then + -- skip + elseif type(krn) == "table" then + if lookuptype == "pair" then + local a, b = krn[2], krn[3] + if a and #a > 0 then + local startchar = start.char + local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local startchar = start.char + local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else + report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) + local a, b = krn[2], krn[6] + if a and a ~= 0 then + local k = setkern(snext,factor,rlmode,a) + if trace_kerns then + logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + end + if b and b ~= 0 then + logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) + end + end + done = true + elseif krn ~= 0 then + local k = setkern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + done = true + end + break + end + end + return head, start, done + end + end + end + return head, start, false +end + +chainmores.gpos_pair = chainprocs.gpos_pair -- okay? + +-- what pointer to return, spec says stop +-- to be discussed ... is bidi changer a space? +-- elseif char == zwnj and sequence[n][32] then -- brrr + +-- somehow l or f is global +-- we don't need to pass the currentcontext, saves a bit +-- make a slow variant then can be activated but with more tracing + +local function show_skip(kind,chainname,char,ck,class) + if ck[9] then + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) + else + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) + end +end + +local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) + -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] + local flags = sequence.flags + local done = false + local skipmark = flags[1] + local skipligature = flags[2] + local skipbase = flags[3] + local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) + local markclass = sequence.markclass -- todo, first we need a proper test + local skipped = false + for k=1,#contexts do + local match = true + local current = start + local last = start + local ck = contexts[k] + local seq = ck[3] + local s = #seq + -- f..l = mid string + if s == 1 then + -- never happens + match = current.id == glyph_code and current.font == currentfont and current.subtype<256 and seq[1][current.char] + else + -- maybe we need a better space check (maybe check for glue or category or combination) + -- we cannot optimize for n=2 because there can be disc nodes + local f, l = ck[4], ck[5] + -- current match + if f == 1 and f == l then -- current only + -- already a hit + -- match = true + else -- before/current/after | before/current | current/after + -- no need to test first hit (to be optimized) + if f == l then -- new, else last out of sync (f is > 1) + -- match = true + else + local n = f + 1 + last = last.next + while n <= l do + if last then + local id = last.id + if id == glyph_code then + if last.font == currentfont and last.subtype<256 then + local char = last.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + last = last.next + elseif seq[n][char] then + if n < l then + last = last.next + end + n = n + 1 + else + match = false + break + end + else + match = false + break + end + else + match = false + break + end + elseif id == disc_code then + last = last.next + else + match = false + break + end + else + match = false + break + end + end + end + end + -- before + if match and f > 1 then + local prev = start.prev + if prev then + local n = f-1 + while n >= 1 do + if prev then + local id = prev.id + if id == glyph_code then + if prev.font == currentfont and prev.subtype<256 then -- normal char + local char = prev.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n = n -1 + else + match = false + break + end + else + match = false + break + end + else + match = false + break + end + elseif id == disc_code then + -- skip 'm + elseif seq[n][32] then + n = n -1 + else + match = false + break + end + prev = prev.prev + elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces + n = n -1 + else + match = false + break + end + end + elseif f == 2 then + match = seq[1][32] + else + for n=f-1,1 do + if not seq[n][32] then + match = false + break + end + end + end + end + -- after + if match and s > l then + local current = last and last.next + if current then + -- removed optimization for s-l == 1, we have to deal with marks anyway + local n = l + 1 + while n <= s do + if current then + local id = current.id + if id == glyph_code then + if current.font == currentfont and current.subtype<256 then -- normal char + local char = current.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n = n + 1 + else + match = false + break + end + else + match = false + break + end + else + match = false + break + end + elseif id == disc_code then + -- skip 'm + elseif seq[n][32] then -- brrr + n = n + 1 + else + match = false + break + end + current = current.next + elseif seq[n][32] then + n = n + 1 + else + match = false + break + end + end + elseif s-l == 1 then + match = seq[s][32] + else + for n=l+1,s do + if not seq[n][32] then + match = false + break + end + end + end + end + end + if match then + -- ck == currentcontext + if trace_contexts then + local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5] + local char = start.char + if ck[9] then + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a", + cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) + else + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a", + cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) + end + end + local chainlookups = ck[6] + if chainlookups then + local nofchainlookups = #chainlookups + -- we can speed this up if needed + if nofchainlookups == 1 then + local chainlookupname = chainlookups[1] + local chainlookup = lookuptable[chainlookupname] + if chainlookup then + local cp = chainprocs[chainlookup.type] + if cp then + local ok + head, start, ok = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + if ok then + done = true + end + else + logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + end + else -- shouldn't happen + logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname)) + end + else + local i = 1 + repeat + if skipped then + while true do + local char = start.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + start = start.next + else + break + end + else + break + end + end + end + local chainlookupname = chainlookups[i] + local chainlookup = lookuptable[chainlookupname] + if not chainlookup then + -- okay, n matches, < n replacements + i = i + 1 + else + local cp = chainmores[chainlookup.type] + if not cp then + -- actually an error + logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + i = i + 1 + else + local ok, n + head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) + -- messy since last can be changed ! + if ok then + done = true + -- skip next one(s) if ligature + i = i + (n or 1) + else + i = i + 1 + end + end + end + if start then + start = start.next + else + -- weird + end + until i > nofchainlookups + end + else + local replacements = ck[7] + if replacements then + head, start, done = chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) -- sequence + else + done = true -- can be meant to be skipped + if trace_contexts then + logprocess("%s: skipping match",cref(kind,chainname)) + end + end + end + end + end + return head, start, done +end + +-- Because we want to keep this elsewhere (an because speed is less an issue) we +-- pass the font id so that the verbose variant can access the relevant helper tables. + +local verbose_handle_contextchain = function(font,...) + logwarning("no verbose handler installed, reverting to 'normal'") + otf.setcontextchain() + return normal_handle_contextchain(...) +end + +otf.chainhandlers = { + normal = normal_handle_contextchain, + verbose = verbose_handle_contextchain, +} + +function otf.setcontextchain(method) + if not method or method == "normal" or not otf.chainhandlers[method] then + if handlers.contextchain then -- no need for a message while making the format + logwarning("installing normal contextchain handler") + end + handlers.contextchain = normal_handle_contextchain + else + logwarning("installing contextchain handler %a",method) + local handler = otf.chainhandlers[method] + handlers.contextchain = function(...) + return handler(currentfont,...) -- hm, get rid of ... + end + end + handlers.gsub_context = handlers.contextchain + handlers.gsub_contextchain = handlers.contextchain + handlers.gsub_reversecontextchain = handlers.contextchain + handlers.gpos_contextchain = handlers.contextchain + handlers.gpos_context = handlers.contextchain +end + +otf.setcontextchain() + +local missing = { } -- we only report once + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + report_process(...) +end + +local logwarning = report_process + +local function report_missing_cache(typ,lookup) + local f = missing[currentfont] if not f then f = { } missing[currentfont] = f end + local t = f[typ] if not t then t = { } f[typ] = t end + if not t[lookup] then + t[lookup] = true + logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname) + end +end + +local resolved = { } -- we only resolve a font,script,language pair once + +-- todo: pass all these 'locals' in a table + +local lookuphashes = { } + +setmetatableindex(lookuphashes, function(t,font) + local lookuphash = fontdata[font].resources.lookuphash + if not lookuphash or not next(lookuphash) then + lookuphash = false + end + t[font] = lookuphash + return lookuphash +end) + +-- fonts.hashes.lookups = lookuphashes + +local autofeatures = fonts.analyzers.features -- was: constants + +local function initialize(sequence,script,language,enabled) + local features = sequence.features + if features then + for kind, scripts in next, features do + local valid = enabled[kind] + if valid then + local languages = scripts[script] or scripts[wildcard] + if languages and (languages[language] or languages[wildcard]) then + return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence } + end + end + end + end + return false +end + +function otf.dataset(tfmdata,font) -- generic variant, overloaded in context + local shared = tfmdata.shared + local properties = tfmdata.properties + local language = properties.language or "dflt" + local script = properties.script or "dflt" + local enabled = shared.features + local res = resolved[font] + if not res then + res = { } + resolved[font] = res + end + local rs = res[script] + if not rs then + rs = { } + res[script] = rs + end + local rl = rs[language] + if not rl then + rl = { + -- indexed but we can also add specific data by key + } + rs[language] = rl + local sequences = tfmdata.resources.sequences +-- setmetatableindex(rl, function(t,k) +-- if type(k) == "number" then +-- local v = enabled and initialize(sequences[k],script,language,enabled) +-- t[k] = v +-- return v +-- end +-- end) +for s=1,#sequences do + local v = enabled and initialize(sequences[s],script,language,enabled) + if v then + rl[#rl+1] = v + end +end + end + return rl +end + +-- elseif id == glue_code then +-- if p[5] then -- chain +-- local pc = pp[32] +-- if pc then +-- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) +-- if ok then +-- done = true +-- end +-- if start then start = start.next end +-- else +-- start = start.next +-- end +-- else +-- start = start.next +-- end + +-- there will be a new direction parser (pre-parsed etc) + +-- less bytecode: 290 -> 254 +-- +-- attr = attr or false +-- +-- local a = getattr(start,0) +-- if (a == attr and (not attribute or getattr(start,a_state) == attribute)) or (not attribute or getattr(start,a_state) == attribute) then +-- -- the action +-- end + +local function featuresprocessor(head,font,attr) + + local lookuphash = lookuphashes[font] -- we can also check sequences here + + if not lookuphash then + return head, false + end + + if trace_steps then + checkstep(head) + end + + tfmdata = fontdata[font] + descriptions = tfmdata.descriptions + characters = tfmdata.characters + resources = tfmdata.resources + + marks = resources.marks + anchorlookups = resources.lookup_to_anchor + lookuptable = resources.lookups + lookuptypes = resources.lookuptypes + + currentfont = font + rlmode = 0 + + local sequences = resources.sequences + local done = false + local datasets = otf.dataset(tfmdata,font,attr) + + local dirstack = { } -- could move outside function + + -- We could work on sub start-stop ranges instead but I wonder if there is that + -- much speed gain (experiments showed that it made not much sense) and we need + -- to keep track of directions anyway. Also at some point I want to play with + -- font interactions and then we do need the full sweeps. + + -- Keeping track of the headnode is needed for devanagari (I generalized it a bit + -- so that multiple cases are also covered.) + + for s=1,#datasets do + local dataset = datasets[s] + featurevalue = dataset[1] -- todo: pass to function instead of using a global + + local sequence = dataset[5] -- sequences[s] -- also dataset[5] + local rlparmode = 0 + local topstack = 0 + local success = false + local attribute = dataset[2] + local chain = dataset[3] -- sequence.chain or 0 + local typ = sequence.type + local subtables = sequence.subtables + if chain < 0 then + -- this is a limited case, no special treatments like 'init' etc + local handler = handlers[typ] + -- we need to get rid of this slide! probably no longer needed in latest luatex + local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo + while start do + local id = start.id + if id == glyph_code then + if start.font == font and start.subtype<256 then + local a = start[0] + if a then + a = a == attr + else + a = true + end + if a then + for i=1,#subtables do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if success then + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.prev end + else + start = start.prev + end + else + start = start.prev + end + else + start = start.prev + end + end + else + local handler = handlers[typ] + local ns = #subtables + local start = head -- local ? + rlmode = 0 -- to be checked ? + if ns == 1 then -- happens often + local lookupname = subtables[1] + local lookupcache = lookuphash[lookupname] + if not lookupcache then -- also check for empty cache + report_missing_cache(typ,lookupname) + else + + local function subrun(start) + -- mostly for gsub, gpos would demand a more clever approach + local head = start + local done = false + while start do + local id = start.id + if id == glyph_code and start.font == font and start.subtype <256 then + local a = start[0] + if a then + a = (a == attr) and (not attribute or start[a_state] == attribute) + else + a = not attribute or start[a_state] == attribute + end + if a then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- sequence kan weg + local ok + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + done = true + end + end + if start then start = start.next end + else + start = start.next + end + else + start = start.next + end + end + if done then + success = true + return head + end + end + + local function kerndisc(disc) -- we can assume that prev and next are glyphs + local prev = disc.prev + local next = disc.next + if prev and next then + prev.next = next + -- next.prev = prev + local a = prev[0] + if a then + a = (a == attr) and (not attribute or prev[a_state] == attribute) + else + a = not attribute or prev[a_state] == attribute + end + if a then + local lookupmatch = lookupcache[prev.char] + if lookupmatch then + -- sequence kan weg + local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + done = true + success = true + end + end + end + prev.next = disc + -- next.prev = disc + end + return next + end + + while start do + local id = start.id + if id == glyph_code then + if start.font == font and start.subtype<256 then + local a = start[0] + if a then + a = (a == attr) and (not attribute or start[a_state] == attribute) + else + a = not attribute or start[a_state] == attribute + end + if a then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- sequence kan weg + local ok + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + success = true + end + end + if start then start = start.next end + else + start = start.next + end + else + start = start.next + end + elseif id == disc_code then + -- mostly for gsub + if start.subtype == discretionary_code then + local pre = start.pre + if pre then + local new = subrun(pre) + if new then start.pre = new end + end + local post = start.post + if post then + local new = subrun(post) + if new then start.post = new end + end + local replace = start.replace + if replace then + local new = subrun(replace) + if new then start.replace = new end + end +elseif typ == "gpos_single" or typ == "gpos_pair" then + kerndisc(start) + end + start = start.next + elseif id == whatsit_code then -- will be function + local subtype = start.subtype + if subtype == dir_code then + local dir = start.dir + if dir == "+TRT" or dir == "+TLT" then + topstack = topstack + 1 + dirstack[topstack] = dir + elseif dir == "-TRT" or dir == "-TLT" then + topstack = topstack - 1 + end + local newdir = dirstack[topstack] + if newdir == "+TRT" then + rlmode = -1 + elseif newdir == "+TLT" then + rlmode = 1 + else + rlmode = rlparmode + end + if trace_directions then + report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) + end + elseif subtype == localpar_code then + local dir = start.dir + if dir == "TRT" then + rlparmode = -1 + elseif dir == "TLT" then + rlparmode = 1 + else + rlparmode = 0 + end + -- one might wonder if the par dir should be looked at, so we might as well drop the next line + rlmode = rlparmode + if trace_directions then + report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) + end + end + start = start.next + elseif id == math_code then + start = end_of_math(start).next + else + start = start.next + end + end + end + else + + local function subrun(start) + -- mostly for gsub, gpos would demand a more clever approach + local head = start + local done = false + while start do + local id = start.id + if id == glyph_code and start.id == font and start.subtype <256 then + local a = start[0] + if a then + a = (a == attr) and (not attribute or start[a_state] == attribute) + else + a = not attribute or start[a_state] == attribute + end + if a then + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + done = true + break + elseif not start then + -- don't ask why ... shouldn't happen + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.next end + else + start = start.next + end + else + start = start.next + end + end + if done then + success = true + return head + end + end + + local function kerndisc(disc) -- we can assume that prev and next are glyphs + local prev = disc.prev + local next = disc.next + if prev and next then + prev.next = next + -- next.prev = prev + local a = prev[0] + if a then + a = (a == attr) and (not attribute or prev[a_state] == attribute) + else + a = not attribute or prev[a_state] == attribute + end + if a then + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[prev.char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + done = true + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + end + prev.next = disc + -- next.prev = disc + end + return next + end + + while start do + local id = start.id + if id == glyph_code then + if start.font == font and start.subtype<256 then + local a = start[0] + if a then + a = (a == attr) and (not attribute or start[a_state] == attribute) + else + a = not attribute or start[a_state] == attribute + end + if a then + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + success = true + break + elseif not start then + -- don't ask why ... shouldn't happen + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.next end + else + start = start.next + end + else + start = start.next + end + elseif id == disc_code then + -- mostly for gsub + if start.subtype == discretionary_code then + local pre = start.pre + if pre then + local new = subrun(pre) + if new then start.pre = new end + end + local post = start.post + if post then + local new = subrun(post) + if new then start.post = new end + end + local replace = start.replace + if replace then + local new = subrun(replace) + if new then start.replace = new end + end +elseif typ == "gpos_single" or typ == "gpos_pair" then + kerndisc(start) + end + start = start.next + elseif id == whatsit_code then + local subtype = start.subtype + if subtype == dir_code then + local dir = start.dir + if dir == "+TRT" or dir == "+TLT" then + topstack = topstack + 1 + dirstack[topstack] = dir + elseif dir == "-TRT" or dir == "-TLT" then + topstack = topstack - 1 + end + local newdir = dirstack[topstack] + if newdir == "+TRT" then + rlmode = -1 + elseif newdir == "+TLT" then + rlmode = 1 + else + rlmode = rlparmode + end + if trace_directions then + report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) + end + elseif subtype == localpar_code then + local dir = start.dir + if dir == "TRT" then + rlparmode = -1 + elseif dir == "TLT" then + rlparmode = 1 + else + rlparmode = 0 + end + rlmode = rlparmode + if trace_directions then + report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) + end + end + start = start.next + elseif id == math_code then + start = end_of_math(start).next + else + start = start.next + end + end + end + end + if success then + done = true + end + if trace_steps then -- ? + registerstep(head) + end + end + return head, done +end + +local function generic(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if target then + target[unicode] = lookupdata + else + lookuphash[lookupname] = { [unicode] = lookupdata } + end +end + +local action = { + + substitution = generic, + multiple = generic, + alternate = generic, + position = generic, + + ligature = function(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target + end + for i=1,#lookupdata do + local li = lookupdata[i] + local tu = target[li] + if not tu then + tu = { } + target[li] = tu + end + target = tu + end + target.ligature = unicode + end, + + pair = function(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target + end + local others = target[unicode] + local paired = lookupdata[1] + if others then + others[paired] = lookupdata + else + others = { [paired] = lookupdata } + target[unicode] = others + end + end, + +} + +local function prepare_lookups(tfmdata) + + local rawdata = tfmdata.shared.rawdata + local resources = rawdata.resources + local lookuphash = resources.lookuphash + local anchor_to_lookup = resources.anchor_to_lookup + local lookup_to_anchor = resources.lookup_to_anchor + local lookuptypes = resources.lookuptypes + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + + -- we cannot free the entries in the descriptions as sometimes we access + -- then directly (for instance anchors) ... selectively freeing does save + -- much memory as it's only a reference to a table and the slot in the + -- description hash is not freed anyway + + for unicode, character in next, characters do -- we cannot loop over descriptions ! + + local description = descriptions[unicode] + + if description then + + local lookups = description.slookups + if lookups then + for lookupname, lookupdata in next, lookups do + action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) + end + end + + local lookups = description.mlookups + if lookups then + for lookupname, lookuplist in next, lookups do + local lookuptype = lookuptypes[lookupname] + for l=1,#lookuplist do + local lookupdata = lookuplist[l] + action[lookuptype](lookupdata,lookupname,unicode,lookuphash) + end + end + end + + local list = description.kerns + if list then + for lookup, krn in next, list do -- ref to glyph, saves lookup + local target = lookuphash[lookup] + if target then + target[unicode] = krn + else + lookuphash[lookup] = { [unicode] = krn } + end + end + end + + local list = description.anchors + if list then + for typ, anchors in next, list do -- types + if typ == "mark" or typ == "cexit" then -- or entry? + for name, anchor in next, anchors do + local lookups = anchor_to_lookup[name] + if lookups then + for lookup, _ in next, lookups do + local target = lookuphash[lookup] + if target then + target[unicode] = anchors + else + lookuphash[lookup] = { [unicode] = anchors } + end + end + end + end + end + end + end + + end + + end + +end + +local function split(replacement,original) + local result = { } + for i=1,#replacement do + result[original[i]] = replacement[i] + end + return result +end + +local valid = { + coverage = { chainsub = true, chainpos = true, contextsub = true }, + reversecoverage = { reversesub = true }, + glyphs = { chainsub = true, chainpos = true }, +} + +local function prepare_contextchains(tfmdata) + local rawdata = tfmdata.shared.rawdata + local resources = rawdata.resources + local lookuphash = resources.lookuphash + local lookups = rawdata.lookups + if lookups then + for lookupname, lookupdata in next, rawdata.lookups do + local lookuptype = lookupdata.type + if lookuptype then + local rules = lookupdata.rules + if rules then + local format = lookupdata.format + local validformat = valid[format] + if not validformat then + report_prepare("unsupported format %a",format) + elseif not validformat[lookuptype] then + -- todo: dejavu-serif has one (but i need to see what use it has) + report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookupname) + else + local contexts = lookuphash[lookupname] + if not contexts then + contexts = { } + lookuphash[lookupname] = contexts + end + local t, nt = { }, 0 + for nofrules=1,#rules do + local rule = rules[nofrules] + local current = rule.current + local before = rule.before + local after = rule.after + local replacements = rule.replacements + local sequence = { } + local nofsequences = 0 + -- Eventually we can store start, stop and sequence in the cached file + -- but then less sharing takes place so best not do that without a lot + -- of profiling so let's forget about it. + if before then + for n=1,#before do + nofsequences = nofsequences + 1 + sequence[nofsequences] = before[n] + end + end + local start = nofsequences + 1 + for n=1,#current do + nofsequences = nofsequences + 1 + sequence[nofsequences] = current[n] + end + local stop = nofsequences + if after then + for n=1,#after do + nofsequences = nofsequences + 1 + sequence[nofsequences] = after[n] + end + end + if sequence[1] then + -- Replacements only happen with reverse lookups as they are single only. We + -- could pack them into current (replacement value instead of true) and then + -- use sequence[start] instead but it's somewhat ugly. + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t + end + end + end + end + end + else + -- no rules + end + else + report_prepare("missing lookuptype for lookupname %a",lookupname) + end + end + end +end + +-- we can consider lookuphash == false (initialized but empty) vs lookuphash == table + +local function featuresinitializer(tfmdata,value) + if true then -- value then + -- beware we need to use the topmost properties table + local rawdata = tfmdata.shared.rawdata + local properties = rawdata.properties + if not properties.initialized then + local starttime = trace_preparing and os.clock() + local resources = rawdata.resources + resources.lookuphash = resources.lookuphash or { } + prepare_contextchains(tfmdata) + prepare_lookups(tfmdata) + properties.initialized = true + if trace_preparing then + report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname) + end + end + end +end + +registerotffeature { + name = "features", + description = "features", + default = true, + initializers = { + position = 1, + node = featuresinitializer, + }, + processors = { + node = featuresprocessor, + } +} + +-- This can be used for extra handlers, but should be used with care! + +otf.handlers = handlers diff --git a/src/luaotfload-fonts-tfm.lua b/src/luaotfload-fonts-tfm.lua new file mode 100644 index 0000000..b9bb1bd --- /dev/null +++ b/src/luaotfload-fonts-tfm.lua @@ -0,0 +1,38 @@ +if not modules then modules = { } end modules ['luatex-fonts-tfm'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +local tfm = { } +fonts.handlers.tfm = tfm +fonts.formats.tfm = "type1" -- we need to have at least a value here + +function fonts.readers.tfm(specification) + local fullname = specification.filename or "" + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + fullname = specification.name .. "." .. forced + else + fullname = specification.name + end + end + local foundname = resolvers.findbinfile(fullname, 'tfm') or "" + if foundname == "" then + foundname = resolvers.findbinfile(fullname, 'ofm') or "" + end + if foundname ~= "" then + specification.filename = foundname + specification.format = "ofm" + return font.read_tfm(specification.filename,specification.size) + end +end diff --git a/src/luaotfload-letterspace.lua b/src/luaotfload-letterspace.lua new file mode 100644 index 0000000..20f29f5 --- /dev/null +++ b/src/luaotfload-letterspace.lua @@ -0,0 +1,544 @@ +if not modules then modules = { } end modules ['letterspace'] = { + version = "2.5", + comment = "companion to luaotfload-main.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL; adapted by Philipp Gesang", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local log = luaotfload.log +local report = log.report + +local getmetatable = getmetatable +local require = require +local setmetatable = setmetatable +local tonumber = tonumber + +local next = next +local nodes, node, fonts = nodes, node, fonts + +local find_node_tail = node.tail or node.slide +local free_node = node.free +local copy_node = node.copy +local new_node = node.new +local insert_node_before = node.insert_before + +local nodepool = nodes.pool + +local new_kern = nodepool.kern +local new_glue = nodepool.glue + +local nodecodes = nodes.nodecodes + +local glyph_code = nodecodes.glyph +local kern_code = nodecodes.kern +local disc_code = nodecodes.disc +local math_code = nodecodes.math + +local fonthashes = fonts.hashes +local chardata = fonthashes.characters +local quaddata = fonthashes.quads +local otffeatures = fonts.constructors.newfeatures "otf" + +--[[doc-- + + Since the letterspacing method was derived initially from Context’s + typo-krn.lua we keep the sub-namespace “letterspace” inside the + “luaotfload” table. + +--doc]]-- + +luaotfload.letterspace = luaotfload.letterspace or { } +local letterspace = luaotfload.letterspace + +letterspace.keepligature = false +letterspace.keeptogether = false + +---=================================================================--- +--- preliminary definitions +---=================================================================--- +-- We set up a layer emulating some Context internals that are needed +-- for the letterspacing callback. +----------------------------------------------------------------------- +--- node-ini +----------------------------------------------------------------------- + +local bothways = function (t) return table.swapped (t, t) end +local kerncodes = bothways { [0] = "fontkern" + , [1] = "userkern" + , [2] = "accentkern" + } + +kerncodes.kerning = kerncodes.fontkern --- idiosyncrasy +local kerning_code = kerncodes.kerning +local userkern_code = kerncodes.userkern + + +----------------------------------------------------------------------- +--- node-res +----------------------------------------------------------------------- + +nodes.pool = nodes.pool or { } +local pool = nodes.pool + +local kern = new_node ("kern", kerncodes.userkern) +local glue_spec = new_node "glue_spec" + +pool.kern = function (k) + local n = copy_node (kern) + n.kern = k + return n +end + +pool.glue = function (width, stretch, shrink, + stretch_order, shrink_order) + local n = new_node"glue" + if not width then + -- no spec + elseif width == false or tonumber(width) then + local s = copy_node(glue_spec) + if width then s.width = width end + if stretch then s.stretch = stretch end + if shrink then s.shrink = shrink end + if stretch_order then s.stretch_order = stretch_order end + if shrink_order then s.shrink_order = shrink_order end + n.spec = s + else + -- shared + n.spec = copy_node(width) + end + return n +end + +----------------------------------------------------------------------- +--- font-hsh +----------------------------------------------------------------------- +--- some initialization resembling font-hsh +local fonthashes = fonts.hashes +local identifiers = fonthashes.identifiers --- was: fontdata +local chardata = fonthashes.characters +local quaddata = fonthashes.quads +local parameters = fonthashes.parameters + +--- ('a, 'a) hash -> (('a, 'a) hash -> 'a -> 'a) -> ('a, 'a) hash +local setmetatableindex = function (t, f) + local mt = getmetatable(t) + if mt then + mt.__index = f + else + setmetatable(t, { __index = f }) + end + return t +end + +if not parameters then + parameters = { } + setmetatableindex(parameters, function(t, k) + if k == true then + return parameters[currentfont()] + else + local parameters = identifiers[k].parameters + t[k] = parameters + return parameters + end + end) + --fonthashes.parameters = parameters +end + +if not chardata then + chardata = { } + setmetatableindex(chardata, function(t, k) + if k == true then + return chardata[currentfont()] + else + local tfmdata = identifiers[k] + if not tfmdata then --- unsafe + tfmdata = font.fonts[k] + end + if tfmdata then + local characters = tfmdata.characters + t[k] = characters + return characters + end + end + end) + fonthashes.characters = chardata +end + +if not quaddata then + quaddata = { } + setmetatableindex(quaddata, function(t, k) + if k == true then + return quads[currentfont()] + else + local parameters = parameters[k] + local quad = parameters and parameters.quad or 0 + t[k] = quad + return quad + end + end) + --fonthashes.quads = quaddata +end + +---=================================================================--- +--- character kerning functionality +---=================================================================--- + +local kern_injector = function (fillup, kern) + if fillup then + local g = new_glue(kern) + local s = g.spec + s.stretch = kern + s.stretch_order = 1 + return g + else + return new_kern(kern) + end +end + +--[[doc-- + + Caveat lector. + This is an adaptation of the Context character kerning mechanism + that emulates XeTeX-style fontwise letterspacing. Note that in its + present state it is far inferior to the original, which is + attribute-based and ignores font-boundaries. Nevertheless, due to + popular demand the following callback has been added. + +--doc]]-- + +local kernfactors = { } --- fontid -> factor + +local kerncharacters +kerncharacters = function (head) + local start, done = head, false + local lastfont = nil + local keepligature = letterspace.keepligature --- function + local keeptogether = letterspace.keeptogether --- function + local fillup = false + + local identifiers = fonthashes.identifiers + local kernfactors = kernfactors + + local firstkern = true + + while start do + local id = start.id + if id == glyph_code then + + --- 1) look up kern factor (slow, but cached rudimentarily) + local krn + local fontid = start.font + do + krn = kernfactors[fontid] + if not krn then + local tfmdata = identifiers[fontid] + if not tfmdata then -- unsafe + tfmdata = font.fonts[fontid] + end + if tfmdata then + fontproperties = tfmdata.properties + if fontproperties then + krn = fontproperties.kerncharacters + end + end + kernfactors[fontid] = krn + end + if not krn or krn == 0 then + firstkern = true + goto nextnode + elseif firstkern then + firstkern = false + if (id ~= disc_code) and (not start.components) then + --- not a ligature, skip node + goto nextnode + end + end + end + + if krn == "max" then + krn = .25 + fillup = true + else + fillup = false + end + + lastfont = fontid + + --- 2) resolve ligatures + local c = start.components + + if c then + if keepligature and keepligature(start) then + -- keep 'm + else + --- c = kerncharacters (c) --> taken care of after replacing + local s = start + local p, n = s.prev, s.next + local tail = find_node_tail(c) + if p then + p.next = c + c.prev = p + else + head = c + end + if n then + n.prev = tail + end + tail.next = n + start = c + s.components = nil + -- we now leak nodes ! + -- free_node(s) + done = true + end + end -- kern ligature + + --- 3) apply the extra kerning + local prev = start.prev + if prev then + local pid = prev.id + + if not pid then + -- nothing + + elseif pid == kern_code then + if prev.subtype == kerning_code --- context does this by means of an + or prev.subtype == userkern_code --- attribute; we may need a test + then + if keeptogether and prev.prev.id == glyph_code and keeptogether(prev.prev,start) then + -- keep + else + prev.subtype = userkern_code + prev.kern = prev.kern + quaddata[lastfont]*krn -- here + done = true + end + end + + elseif pid == glyph_code then + if prev.font == lastfont then + local prevchar, lastchar = prev.char, start.char + if keeptogether and keeptogether(prev,start) then + -- keep 'm + elseif identifiers[lastfont] then + local kerns = chardata[lastfont][prevchar].kerns + local kern = kerns and kerns[lastchar] or 0 + krn = kern + quaddata[lastfont]*krn -- here + insert_node_before(head,start,kern_injector(fillup,krn)) + done = true + end + else + krn = quaddata[lastfont]*krn -- here + insert_node_before(head,start,kern_injector(fillup,krn)) + done = true + end + + elseif pid == disc_code then + -- a bit too complicated, we can best not copy and just calculate + -- but we could have multiple glyphs involved so ... + local disc = prev -- disc + local pre, post, replace = disc.pre, disc.post, disc.replace + local prv, nxt = disc.prev, disc.next + + if pre and prv then -- must pair with start.prev + -- this one happens in most cases + local before = copy_node(prv) + pre.prev = before + before.next = pre + before.prev = nil + pre = kerncharacters (before) + pre = pre.next + pre.prev = nil + disc.pre = pre + free_node(before) + end + + if post and nxt then -- must pair with start + local after = copy_node(nxt) + local tail = find_node_tail(post) + tail.next = after + after.prev = tail + after.next = nil + post = kerncharacters (post) + tail.next = nil + disc.post = post + free_node(after) + end + + if replace and prv and nxt then -- must pair with start and start.prev + local before = copy_node(prv) + local after = copy_node(nxt) + local tail = find_node_tail(replace) + replace.prev = before + before.next = replace + before.prev = nil + tail.next = after + after.prev = tail + after.next = nil + replace = kerncharacters (before) + replace = replace.next + replace.prev = nil + after.prev.next = nil + disc.replace = replace + free_node(after) + free_node(before) + elseif identifiers[lastfont] then + if prv and prv.id == glyph_code and prv.font == lastfont then + local prevchar, lastchar = prv.char, start.char + local kerns = chardata[lastfont][prevchar].kerns + local kern = kerns and kerns[lastchar] or 0 + krn = kern + quaddata[lastfont]*krn -- here + else + krn = quaddata[lastfont]*krn -- here + end + disc.replace = kern_injector(false,krn) -- only kerns permitted, no glue + end + + end + end + end + + ::nextnode:: + if start then + start = start.next + end + end + return head, done +end + +---=================================================================--- +--- integration +---=================================================================--- + +--- · callback: kerncharacters +--- · enabler: enablefontkerning +--- · disabler: disablefontkerning + +--- callback wrappers + +--- (node_t -> node_t) -> string -> string list -> bool +local registered_as = { } --- procname -> callbacks +local add_processor = function (processor, name, ...) + local callbacks = { ... } + for i=1, #callbacks do + luatexbase.add_to_callback(callbacks[i], processor, name) + end + registered_as[name] = callbacks --- for removal + return true +end + +--- string -> bool +local remove_processor = function (name) + local callbacks = registered_as[name] + if callbacks then + for i=1, #callbacks do + luatexbase.remove_from_callback(callbacks[i], name) + end + return true + end + return false --> unregistered +end + +--- now for the simplistic variant +--- unit -> bool +local enablefontkerning = function ( ) + return add_processor( kerncharacters + , "luaotfload.letterspace" + , "pre_linebreak_filter" + , "hpack_filter") +end + +--- unit -> bool +local disablefontkerning = function ( ) + return remove_processor "luaotfload.letterspace" +end + +--[[doc-- + + Fontwise kerning is enabled via the “kernfactor” option at font + definition time. Unlike the Context implementation which relies on + Luatex attributes, it uses a font property for passing along the + letterspacing factor of a node. + + The callback is activated the first time a letterspaced font is + requested and stays active until the end of the run. Since the font + is a property of individual glyphs, every glyph in the entire + document must be checked for the kern property. This is quite + inefficient compared to Context’s attribute based approach, but Xetex + compatibility reduces our options significantly. + +--doc]]-- + + +local fontkerning_enabled = false --- callback state + +--- fontobj -> float -> unit +local initializefontkerning = function (tfmdata, factor) + if factor ~= "max" then + factor = tonumber (factor) or 0 + end + if factor == "max" or factor ~= 0 then + local fontproperties = tfmdata.properties + if fontproperties then + --- hopefully this field stays unused otherwise + fontproperties.kerncharacters = factor + end + if not fontkerning_enabled then + fontkerning_enabled = enablefontkerning () + end + end +end + +--- like the font colorization, fontwise kerning is hooked into the +--- feature mechanism + +otffeatures.register { + name = "kernfactor", + description = "kernfactor", + initializers = { + base = initializefontkerning, + node = initializefontkerning, + } +} + +--[[doc-- + + The “letterspace” feature is essentially identical with the above + “kernfactor” method, but scales the factor to percentages to match + Xetex’s behavior. (See the Xetex reference, page 5, section 1.2.2.) + + Since Xetex doesn’t appear to have a (documented) “max” keyword, we + assume all input values are numeric. + +--doc]]-- + +local initializecompatfontkerning = function (tfmdata, percentage) + local factor = tonumber (percentage) + if not factor then + report ("both", 0, "letterspace", + "Invalid argument to letterspace: %s (type %q), " .. + "was expecting percentage as Lua number instead.", + percentage, type (percentage)) + return + end + return initializefontkerning (tfmdata, factor * 0.01) +end + +otffeatures.register { + name = "letterspace", + description = "letterspace", + initializers = { + base = initializecompatfontkerning, + node = initializecompatfontkerning, + } +} + +--[[example-- + +See https://bitbucket.org/phg/lua-la-tex-tests/src/tip/pln-letterspace-8-compare.tex +for an example. + +--example]]-- + +--- vim:sw=2:ts=2:expandtab:tw=71 + diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua new file mode 100644 index 0000000..2aa8c7c --- /dev/null +++ b/src/luaotfload-loaders.lua @@ -0,0 +1,30 @@ +if not modules then modules = { } end modules ["loaders"] = { + version = "2.5", + comment = "companion to luaotfload-main.lua", + author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local fonts = fonts +local readers = fonts.readers +local handlers = fonts.handlers +local formats = fonts.formats + +local pfb_reader = function (specification) + return readers.opentype (specification, "pfb", "type1") +end + +local pfa_reader = function (specification) + return readers.opentype (specification, "pfa", "type1") +end + +formats.pfa = "type1" +readers.pfa = pfa_reader +handlers.pfa = { } + +formats.pfb = "type1" +readers.pfb = pfb_reader +handlers.pfb = { } + +-- vim:tw=71:sw=2:ts=2:expandtab diff --git a/src/luaotfload-log.lua b/src/luaotfload-log.lua new file mode 100644 index 0000000..5698c84 --- /dev/null +++ b/src/luaotfload-log.lua @@ -0,0 +1,404 @@ +if not modules then modules = { } end modules ["luaotfload-log"] = { + version = "2.5", + comment = "companion to Luaotfload", + author = "Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2.0" +} + +--[[doc-- +The logging system is slow in general, as we always have the function +call overhead even if we aren’t going to output anything. On the other +hand, the more efficient approach followed by Context isn’t an option +because we lack a user interface to toggle per-subsystem tracing. +--doc]]-- + +local module_name = "luaotfload" --- prefix for messages + +luaotfload = luaotfload or { } +luaotfload.log = luaotfload.log or { } +local log = luaotfload.log + +local ioopen = io.open +local iowrite = io.write +local lfsisdir = lfs.isdir +local lfsisfile = lfs.isfile +local osdate = os.date +local ostime = os.time +local osuuid = os.uuid +local select = select +local stringformat = string.format +local stringsub = string.sub +local tableconcat = table.concat +local texiowrite_nl = texio.write_nl +local texiowrite = texio.write +local type = type + +local dummyfunction = function () end + +local texjob = false +if tex and (tex.jobname or tex.formatname) then + --- TeX + texjob = true +end + +local loglevel = 0 --- default +local logout = "log" + +--- int -> bool +local set_loglevel = function (n) + if type(n) == "number" then + loglevel = n + end + return true +end +log.set_loglevel = set_loglevel + +--- unit -> int +local get_loglevel = function ( ) + return loglevel +end +log.get_loglevel = get_loglevel + +local writeln --- pointer to terminal/log writer +local statusln --- terminal writer that reuses the current line +local first_status = true --- indicate the begin of a status region + +local log_msg = [[ +logging output redirected to %s +to monitor the progress run "tail -f %s" in another terminal +]] + +local tmppath = os.getenv "TMPDIR" or "/tmp" + +local choose_logfile = function ( ) + if lfsisdir (tmppath) then + local fname + repeat --- ensure that file of that name doesn’t exist + fname = tmppath .. "/luaotfload-log-" .. osuuid() + until not lfsisfile (fname) + iowrite (stringformat (log_msg, fname, fname)) + return ioopen (fname, "w") + end + --- missing /tmp + return false +end + +local set_logout = function (s, finalizers) + if s == "stdout" then + logout = "redirect" + elseif s == "file" then --- inject custom logger + logout = "redirect" + local chan = choose_logfile () + chan:write (stringformat ("logging initiated at %s", + osdate ("%F %T", ostime ()))) + local writefile = function (...) + if select ("#", ...) == 2 then + chan:write (select (2, ...)) + else + chan:write (select (1, ...)) + end + end + local writefile_nl= function (...) + chan:write "\n" + if select ("#", ...) == 2 then + chan:write (select (2, ...)) + else + chan:write (select (1, ...)) + end + end + + local writeln_orig = writeln + + texiowrite = writefile + texiowrite_nl = writefile_nl + writeln = writefile_nl + statusln = dummyfunction + + finalizers[#finalizers+1] = function () + chan:write (stringformat ("\nlogging finished at %s\n", + osdate ("%F %T", ostime ()))) + chan:close () + texiowrite = texio.write + texiowrite_nl = texio.write_nl + writeln = writeln_orig + end + --else --- remains “log” + end + return finalizers +end + +log.set_logout = set_logout + +local basic_logger = function (category, fmt, ...) + local res = { module_name, "|", category, ":" } + if fmt then + res [#res + 1] = stringformat (fmt, ...) + end + texiowrite_nl (logout, tableconcat(res, " ")) +end + +--- with faux db update with maximum verbosity: +--- +--- --------- -------- +--- buffering time (s) +--- --------- -------- +--- full 4.12 +--- line 4.20 +--- none 4.39 +--- --------- -------- +--- + +io.stdout:setvbuf "no" +io.stderr:setvbuf "no" + +local kill_line = "\r\x1b[K" + +if texjob == true then + --- We imitate the texio.* functions so the output is consistent. + writeln = function (str) + iowrite "\n" + iowrite(str) + end + statusln = function (str) + if first_status == false then + iowrite (kill_line) + else + iowrite "\n" + end + iowrite (str) + end +else + writeln = function (str) + iowrite(str) + iowrite "\n" + end + statusln = function (str) + if first_status == false then + iowrite (kill_line) + end + iowrite (str) + end +end + +stdout = function (writer, category, ...) + local res = { module_name, "|", category, ":" } + local nargs = select("#", ...) + if nargs == 0 then + --writeln tableconcat(res, " ") + --return + elseif nargs == 1 then + res[#res+1] = select(1, ...) -- around 30% faster than unpack() + else + res[#res+1] = stringformat(...) + end + writer (tableconcat(res, " ")) +end + +--- at default (zero), we aim to be quiet +local level_ids = { common = 1, loading = 2, search = 3 } + +--[[doc-- + + The report() logger is used more or less all over luaotfload. + Its requirements are twofold: + + 1) Provide two logging channels, the terminal and the log file; + 2) Allow for control over verbosity levels. + + The first part is addressed by specifying the log *mode* as the + first argument that can be either “log”, meaning the log file, or + “both”: log file and stdout. Anything else is taken as referring to + stdout only. + + Verbosity levels, though not as fine-grained as e.g. Context’s + system of tracers, allow keeping the logging spam caused by + different subsystems manageable. By default, luaotfload will not + emit anything if things are running smoothly on level zero. Only + warning messages are relayed, while the other messages are skipped + over. (This is a little sub-optimal performance-wise since the + function calls to the logger are executed regardless.) The log + level during a Luatex run can be adjusted by setting the “loglevel” + field in config.luaotfload, or by calling log.set_loglevel() as + defined above. + +--doc]]-- + +local report = function (mode, lvl, ...) + if type(lvl) == "string" then + lvl = level_ids[lvl] + end + if not lvl then lvl = 0 end + + if loglevel >= lvl then + if mode == "log" then + basic_logger (...) + elseif mode == "both" and logout ~= "redirect" then + basic_logger (...) + stdout (writeln, ...) + else + stdout (writeln, ...) + end + end +end + +log.report = report + +--[[doc-- + + status_logger -- Overwrites the most recently printed line of the + terminal. Its purpose is to provide feedback without spamming + stdout with irrelevant messages, i.e. when building the database. + + Status logging must be initialized by calling status_start() and + properly reset via status_stop(). + + The arguments low and high indicate the loglevel threshold at which + linewise and full logging is triggered, respectively. E.g. + + names_status (1, 4, "term", "Hello, world!") + + will print nothing if the loglevel is less than one, reuse the + current line if the loglevel ranges from one to three inclusively, + and output the message on a separate line otherwise. + +--doc]]-- + +local status_logger = function (mode, ...) + if mode == "log" then + basic_logger (...) + else + if mode == "both" and logout ~= "redirect" then + basic_logger (...) + stdout (statusln, ...) + else + stdout (statusln, ...) + end + first_status = false + end +end + +--[[doc-- + + status_start -- Initialize status logging. This installs the status + logger if the loglevel is in the specified range, and the normal + logger otherwise. It also resets the first line state which + causing the next line printed using the status logger to not kill + the current line. + +--doc]]-- + +local status_writer +local status_low = 99 +local status_high = 99 + +local status_start = function (low, high) + first_status = true + status_low = low + status_high = high + + if os.type == "windows" --- Assume broken terminal. + or os.getenv "TERM" == "dumb" + then + status_writer = function (mode, ...) + report (mode, high, ...) + end + return + end + + if low <= loglevel and loglevel < high then + status_writer = status_logger + else + status_writer = function (mode, ...) + report (mode, high, ...) + end + end +end + +--[[doc-- + + status_stop -- Finalize a status region by outputting a newline and + printing a message. + +--doc]]-- + +local status_stop = function (...) + if first_status == false then + status_writer(...) + if texjob == false then + writeln "" + end + end +end + +log.names_status = function (...) status_writer (...) end +log.names_status_start = status_start +log.names_status_stop = status_stop + +--[[doc-- + + The fontloader comes with the Context logging mechanisms + inaccessible. Instead, it provides dumb fallbacks based + on the functions in texio.write*() that can be overridden + by providing a function texio.reporter(). + + The fontloader output can be quite verbose, so we disable + it entirely by default. + +--doc]]-- + +local texioreporter = function (message) + report ("log", 2, message) +end + +texio.reporter = texioreporter + +--[[doc-- + + Adobe Glyph List. + ------------------------------------------------------------------- + + Context provides a somewhat different font-age.lua from an unclear + origin. Unfortunately, the file name it reads from is hard-coded + in font-enc.lua, so we have to replace the entire table. + + This shouldn’t cause any complications. Due to its implementation + the glyph list will be loaded upon loading a OTF or TTF for the + first time during a TeX run. (If one sticks to TFM/OFM then it is + never read at all.) For this reason we can install a metatable that + looks up the file of our choosing and only falls back to the + Context one in case it cannot be found. + +--doc]]-- + +if fonts then --- need to be running TeX + if next(fonts.encodings.agl) then + --- unnecessary because the file shouldn’t be loaded at this time + --- but we’re just making sure + fonts.encodings.agl = nil + collectgarbage"collect" + end + + + fonts.encodings.agl = { } + + setmetatable(fonts.encodings.agl, { __index = function (t, k) + if k == "unicodes" then + local glyphlist = resolvers.findfile"luaotfload-glyphlist.lua" + if glyphlist then + report ("log", 1, "load", "loading the Adobe glyph list") + else + glyphlist = resolvers.findfile"font-age.lua" + report ("both", 0, "load", + "loading the extended glyph list from ConTeXt") + end + local unicodes = dofile(glyphlist) + fonts.encodings.agl = { unicodes = unicodes } + return unicodes + else + return nil + end + end }) +end + +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua new file mode 100644 index 0000000..f5f012d --- /dev/null +++ b/src/luaotfload-main.lua @@ -0,0 +1,711 @@ +----------------------------------------------------------------------- +-- FILE: luaotfload-main.lua +-- DESCRIPTION: Luatex fontloader initialization +-- REQUIREMENTS: luatex v.0.78 or later, the lualibs package +-- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang +-- VERSION: same as Luaotfload +-- MODIFIED: 2014-02-09 14:42:22+0100 +----------------------------------------------------------------------- +-- +--- Note: +--- This file was part of the original luaotfload.dtx and has been +--- converted to a pure Lua file during the transition from Luaotfload +--- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex) +--- markup. + +if not modules then modules = { } end modules ["luaotfload-main"] = { + version = "2.5", + comment = "fontloader initialization", + author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "GNU General Public License v. 2.0" +} + + +--[[doc-- + + This file initializes the system and loads the font loader. To + minimize potential conflicts between other packages and the code + imported from \CONTEXT, several precautions are in order. Some of + the functionality that the font loader expects to be present, like + raw access to callbacks, are assumed to have been disabled by + \identifier{luatexbase} when this file is processed. In some cases + it is possible to trick it by putting dummies into place and + restoring the behavior from \identifier{luatexbase} after + initilization. Other cases such as attribute allocation require + that we hook the functionality from \identifier{luatexbase} into + locations where they normally wouldn’t be. + + Anyways we can import the code base without modifications, which is + due mostly to the extra effort by Hans Hagen to make \LUATEX-Fonts + self-contained and encapsulate it, and especially due to his + willingness to incorporate our suggestions. + +--doc]]-- + +luaotfload = luaotfload or { } +local luaotfload = luaotfload +luaotfload.log = luaotfload.log or { } + +config = config or { } +config.luaotfload = config.luaotfload or { } +------.luaotfload.resolver = config.luaotfload.resolver or "normal" +config.luaotfload.resolver = config.luaotfload.resolver or "cached" +config.luaotfload.definer = config.luaotfload.definer or "patch" +config.luaotfload.loglevel = config.luaotfload.loglevel or 2 +config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter" +config.luaotfload.prioritize = config.luaotfload.prioritize or "sys" +config.luaotfload.names_dir = config.luaotfload.names_dir or "names" +config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts" +config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua" +config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" + +if not config.luaotfload.strip then + config.luaotfload.strip = true +end + +luaotfload.module = { + name = "luaotfload", + version = 2.50000, + date = "2014/**/**", + description = "OpenType layout system.", + author = "Elie Roux & Hans Hagen", + copyright = "Elie Roux", + license = "GPL v2.0" +} + +local luatexbase = luatexbase + +local setmetatable = setmetatable +local type, next = type, next + +local kpsefind_file = kpse.find_file +local lfsisfile = lfs.isfile + +local add_to_callback, create_callback = + luatexbase.add_to_callback, luatexbase.create_callback +local reset_callback, call_callback = + luatexbase.reset_callback, luatexbase.call_callback + +local dummy_function = function () end + +local error, warning, info, log = + luatexbase.provides_module(luaotfload.module) + +luaotfload.log.tex = { + error = error, + warning = warning, + info = info, + log = log, +} + +--[[doc-- + + We set the minimum version requirement for \LUATEX to v0.76, + because the font loader requires recent features like direct + attribute indexing and \luafunction{node.end_of_math()} that aren’t + available in earlier versions.\footnote{% + See Taco’s announcement of v0.76: + \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042} + and this commit by Hans that introduced those features. + \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}. + } + +--doc]]-- + +local luatex_version = 76 + +if tex.luatexversion < luatex_version then + warning("LuaTeX v%.2f is old, v%.2f is recommended.", + tex.luatexversion/100, + luatex_version /100) + --- we install a fallback for older versions as a safety + if not node.end_of_math then + local math_t = node.id"math" + local traverse_nodes = node.traverse_id + node.end_of_math = function (n) + for n in traverse_nodes(math_t, n.next) do + return n + end + end + end +end + +--[[doc-- + + \subsection{Module loading} + We load the files imported from \CONTEXT with this function. It + automatically prepends the prefix \fileent{luaotfload-} to its + argument, so we can refer to the files with their actual \CONTEXT + name. + +--doc]]-- + +local fl_prefix = "luaotfload" -- “luatex” for luatex-plain +local loadmodule = function (name) + require(fl_prefix .."-"..name) +end + +loadmodule "log.lua" --- messages; used to be part of -override +local log = luaotfload.log +local report = log.report + +log.set_loglevel(config.luaotfload.loglevel) + +--[[doc-- + + Before \TeX Live 2013 version, \LUATEX had a bug that made ofm fonts + fail when called with their extension. There was a side-effect making + ofm totally unloadable when luaotfload was present. The following + lines are a patch for this bug. The utility of these lines is + questionable as they are not necessary since \TeX Live 2013. They + should be removed in the next version. + +--doc]]-- + +local Cs, P, lpegmatch = lpeg.Cs, lpeg.P, lpeg.match + +local p_dot, p_slash = P".", P"/" +local p_suffix = (p_dot * (1 - p_dot - p_slash)^1 * P(-1)) / "" +local p_removesuffix = Cs((p_suffix + 1)^1) + +local find_vf_file = function (name) + local fullname = kpsefind_file(name, "ovf") + if not fullname then + --fullname = kpsefind_file(file.removesuffix(name), "ovf") + fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf") + end + if fullname then + report ("log", 0, "main", + "loading virtual font file %s.", fullname) + end + return fullname +end + +--[[doc-- + + \subsection{Preparing the Font Loader} + We treat the fontloader as a black box so behavior is consistent + between formats. + We load the fontloader code directly in the same fashion as the + Plain format \identifier{luatex-fonts} that is part of Context. + How this is executed depends on the presence on the + \emphasis{merged font loader code}. + In \identifier{luaotfload} this is contained in the file + \fileent{luaotfload-merged.lua}. + If this file cannot be found, the original libraries from \CONTEXT + of which the merged code was composed are loaded instead. + Since these files are not shipped with Luaotfload, an installation + of Context is required. + (Since we pull the fontloader directly from the Context minimals, + the necessary Context version is likely to be more recent than that + of other TeX distributions like Texlive.) + The imported font loader will call \luafunction{callback.register} + once while reading \fileent{font-def.lua}. + This is unavoidable unless we modify the imported files, but + harmless if we make it call a dummy instead. + However, this problem might vanish if we decide to do the merging + ourselves, like the \identifier{lualibs} package does. + With this step we would obtain the freedom to load our own + overrides in the process right where they are needed, at the cost + of losing encapsulation. + The decision on how to progress is currently on indefinite hold. + +--doc]]-- + +local starttime = os.gettimeofday() + +local trapped_register = callback.register +callback.register = dummy_function + +--[[doc-- + + By default, the fontloader requires a number of \emphasis{private + attributes} for internal use. + These must be kept consistent with the attribute handling methods + as provided by \identifier{luatexbase}. + Our strategy is to override the function that allocates new + attributes before we initialize the font loader, making it a + wrapper around \luafunction{luatexbase.new_attribute}.\footnote{% + Many thanks, again, to Hans Hagen for making this part + configurable! + } + The attribute identifiers are prefixed “\fileent{luaotfload@}” to + avoid name clashes. + +--doc]]-- + +do + local new_attribute = luatexbase.new_attribute + local the_attributes = luatexbase.attributes + + attributes = attributes or { } + + attributes.private = function (name) + local attr = "luaotfload@" .. name --- used to be: “otfl@” + local number = the_attributes[attr] + if not number then + number = new_attribute(attr) + end + return number + end +end + +--[[doc-- + + These next lines replicate the behavior of + \fileent{luatex-fonts.lua}. + +--doc]]-- + +local context_environment = { } + +local push_namespaces = function () + report ("log", 1, "main", "push namespace for font loader") + local normalglobal = { } + for k, v in next, _G do + normalglobal[k] = v + end + return normalglobal +end + +local pop_namespaces = function (normalglobal, isolate) + if normalglobal then + local _G = _G + local mode = "non-destructive" + if isolate then mode = "destructive" end + report ("log", 1, "main", "pop namespace from font loader -- " .. mode) + for k, v in next, _G do + if not normalglobal[k] then + context_environment[k] = v + if isolate then + _G[k] = nil + end + end + end + for k, v in next, normalglobal do + _G[k] = v + end + -- just to be sure: + setmetatable(context_environment,_G) + else + report ("both", 0, "main", + "irrecoverable error during pop_namespace: no globals to restore") + os.exit() + end +end + +luaotfload.context_environment = context_environment +luaotfload.push_namespaces = push_namespaces +luaotfload.pop_namespaces = pop_namespaces + +local our_environment = push_namespaces() + +--[[doc-- + + The font loader requires that the attribute with index zero be + zero. We happily oblige. + (Cf. \fileent{luatex-fonts-nod.lua}.) + +--doc]]-- + +tex.attribute[0] = 0 + +--[[doc-- + + Now that things are sorted out we can finally load the fontloader. + +--doc]]-- + +loadmodule "fontloader.lua" +---loadmodule"font-odv.lua" --- <= Devanagari support from Context + +if fonts then + + if not fonts._merge_loaded_message_done_ then + report ("log", 0, "main", [["I am using the merged fontloader here.]]) + report ("log", 0, "main", [[ If you run into problems or experience unexpected]]) + report ("log", 0, "main", [[ behaviour, and if you have ConTeXt installed you can try]]) + report ("log", 0, "main", [[ to delete the file 'luaotfload-fontloader.lua' as I might]]) + report ("log", 0, "main", [[ then use the possibly updated libraries. The merged]]) + report ("log", 0, "main", [[ version is not supported as it is a frozen instance.]]) + report ("log", 0, "main", [[ Problems can be reported to the ConTeXt mailing list."]]) + end + fonts._merge_loaded_message_done_ = true + +else--- the loading sequence is known to change, so this might have to + --- be updated with future updates! + --- do not modify it though unless there is a change to the merged + --- package! + loadmodule("l-lua.lua") + loadmodule("l-lpeg.lua") + loadmodule("l-function.lua") + loadmodule("l-string.lua") + loadmodule("l-table.lua") + loadmodule("l-io.lua") + loadmodule("l-file.lua") + loadmodule("l-boolean.lua") + loadmodule("l-math.lua") + loadmodule("util-str.lua") + loadmodule('luatex-basics-gen.lua') + loadmodule('data-con.lua') + loadmodule('luatex-basics-nod.lua') + loadmodule('font-ini.lua') + loadmodule('font-con.lua') + loadmodule('luatex-fonts-enc.lua') + loadmodule('font-cid.lua') + loadmodule('font-map.lua') + loadmodule('luatex-fonts-syn.lua') + loadmodule('luatex-fonts-tfm.lua') + loadmodule('font-oti.lua') + loadmodule('font-otf.lua') + loadmodule('font-otb.lua') + loadmodule('luatex-fonts-inj.lua') --> since 2014-01-07, replaces node-inj.lua + loadmodule('font-ota.lua') + loadmodule('luatex-fonts-otn.lua') --> since 2014-01-07, replaces font-otn.lua + loadmodule('font-otp.lua') --> since 2013-04-23 + loadmodule('luatex-fonts-lua.lua') + loadmodule('font-def.lua') + loadmodule('luatex-fonts-def.lua') + loadmodule('luatex-fonts-ext.lua') + loadmodule('luatex-fonts-cbk.lua') +end --- non-merge fallback scope + +--[[doc-- + + Here we adjust the globals created during font loader + initialization. If the second argument to + \luafunction{pop_namespaces()} is \verb|true| this will restore the + state of \luafunction{_G}, eliminating every global generated since + the last call to \luafunction{push_namespaces()}. At the moment we + see no reason to do this, and since the font loader is considered + an essential part of \identifier{luatex} as well as a very well + organized piece of code, we happily concede it the right to add to + \luafunction{_G} if needed. + +--doc]]-- + +pop_namespaces(our_environment, false)-- true) + +report ("both", 0, "main", + "fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) + +--[[doc-- + + \subsection{Callbacks} + After the fontloader is ready we can restore the callback trap from + \identifier{luatexbase}. + +--doc]]-- + +callback.register = trapped_register + +--[[doc-- + + We do our own callback handling with the means provided by + luatexbase. + Note: \luafunction{pre_linebreak_filter} and + \luafunction{hpack_filter} are coupled in \CONTEXT in the concept + of \emphasis{node processor}. + +--doc]]-- + +add_to_callback("pre_linebreak_filter", + nodes.simple_font_handler, + "luaotfload.node_processor", + 1) +add_to_callback("hpack_filter", + nodes.simple_font_handler, + "luaotfload.node_processor", + 1) +add_to_callback("find_vf_file", + find_vf_file, "luaotfload.find_vf_file") + +loadmodule "override.lua" --- load glyphlist on demand + +--[[doc-- + + Now we load the modules written for \identifier{luaotfload}. + +--doc]]-- +loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax +loadmodule "loaders.lua" --- “font-pfb” new in 2.0, added 2011 +loadmodule "database.lua" --- “font-nms” +loadmodule "colors.lua" --- “font-clr” + +--[[doc-- + + Relying on the \verb|name:| resolver for everything has been the + source of permanent trouble with the database. + With the introduction of the new syntax parser we now have enough + granularity to distinguish between the \XETEX emulation layer and + the genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts. + Another benefit is that we can now easily plug in or replace new + lookup behaviors if necessary. + The name resolver remains untouched, but it calls + \luafunction{fonts.names.resolve()} internally anyways (see + \fileent{luaotfload-database.lua}). + +--doc]]-- + +local filesuffix = file.suffix +local fileremovesuffix = file.removesuffix +local request_resolvers = fonts.definers.resolvers +local formats = fonts.formats +local names = fonts.names +formats.ofm = "type1" + +fonts.encodings.known = fonts.encodings.known or { } + +--[[doc-- + + \identifier{luaotfload} promises easy access to system fonts. + Without additional precautions, this cannot be achieved by + \identifier{kpathsea} alone, because it searches only the + \fileent{texmf} directories by default. + Although it is possible for \identifier{kpathsea} to include extra + paths by adding them to the \verb|OSFONTDIR| environment variable, + this is still short of the goal »\emphasis{it just works!}«. + When building the font database \identifier{luaotfload} scans + system font directories anyways, so we already have all the + information for looking sytem fonts. + With the release version 2.2 the file names are indexed in the + database as well and we are ready to resolve \verb|file:| lookups + this way. + Thus we no longer need to call the \identifier{kpathsea} library in + most cases when looking up font files, only when generating the + database, and when verifying the existence of a file in the + \fileent{texmf} tree. + +--doc]]-- + +local resolve_file = names.crude_file_lookup +--local resolve_file = names.crude_file_lookup_verbose +local resolve_name = names.resolve_name + +local file_resolver = function (specification) + local name = resolve_file (specification.name) + local suffix = filesuffix(name) + if formats[suffix] then + specification.forced = suffix + specification.forcedname = file.removesuffix(name) + else + specification.name = name + end +end + +request_resolvers.file = file_resolver + +--[[doc-- + + We classify as \verb|anon:| those requests that have neither a + prefix nor brackets. According to Khaled\footnote{% + \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}. + } + they are the \XETEX equivalent of a \verb|name:| request, so we + will be treating them as such. + +--doc]]-- + +--request_resolvers.anon = request_resolvers.name + +--[[doc-- + + There is one drawback, though. + This syntax is also used for requesting fonts in \identifier{Type1} + (\abbrev{tfm}, \abbrev{ofm}) format. + These are essentially \verb|file:| lookups and must be caught + before the \verb|name:| resolver kicks in, lest they cause the + database to update. + Even if we were to require the \verb|file:| prefix for all + \identifier{Type1} requests, tests have shown that certain fonts + still include further fonts (e.~g. \fileent{omlgcb.ofm} will ask + for \fileent{omsecob.tfm}) \emphasis{using the old syntax}. + For this reason, we introduce an extra check with an early return. + +--doc]]-- + +local type1_formats = { "tfm", "ofm", } + +request_resolvers.anon = function (specification) + local name = specification.name + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(name, format) then + specification.forcedname = file.addsuffix(name, format) + specification.forced = format + return + end + end + --- under some weird circumstances absolute paths get + --- passed to the definer; we have to catch them + --- before the name: resolver misinterprets them. + name = specification.specification + local exists, _ = lfsisfile(name) + if exists then --- garbage; we do this because we are nice, + --- not because it is correct + report ("log", 1, "load", "file %q exists", name) + report ("log", 1, "load", + "... overriding borked anon: lookup with path: lookup") + specification.name = name + request_resolvers.path(specification) + return + end + request_resolvers.name(specification) +end + +--[[doc-- + + Prior to version 2.2, \identifier{luaotfload} did not distinguish + \verb|file:| and \verb|path:| lookups, causing complications with + the resolver. + Now we test if the requested name is an absolute path in the file + system, otherwise we fall back to the \verb|file:| lookup. + +--doc]]-- + +request_resolvers.path = function (specification) + local name = specification.name + local exists, _ = lfsisfile(name) + if not exists then -- resort to file: lookup + report ("log", 1, "load", + "path lookup of %q unsuccessful, falling back to file:", + name) + file_resolver (specification) + else + local suffix = filesuffix (name) + if formats[suffix] then + specification.forced = suffix + specification.name = file.removesuffix(name) + specification.forcedname = name + else + specification.name = name + end + end +end + +--[[doc-- + + {\bfseries EXPERIMENTAL}: + \identifier{kpse}-only resolver, for those who can do without + system fonts. + +--doc]]-- + +request_resolvers.kpse = function (specification) + local name = specification.name + local suffix = filesuffix(name) + if suffix and formats[suffix] then + name = file.removesuffix(name) + if resolvers.findfile(name, suffix) then + specification.forced = suffix + specification.forcedname = name + return + end + end + for t, format in next, formats do --- brute force + if kpse.find_file (name, format) then + specification.forced = t + specification.name = name + return + end + end +end + +--[[doc-- + + The \verb|name:| resolver wraps the database function + \luafunction{resolve_name}. + +--doc]]-- + +--- fonts.names.resolvers.name -- Customized version of the +--- generic name resolver. + +request_resolvers.name = function (specification) + local resolved, subfont = resolve_name (specification) + if resolved then + specification.resolved = resolved + specification.sub = subfont + specification.forced = filesuffix (resolved) + specification.forcedname = resolved + specification.name = fileremovesuffix (resolved) + else + file_resolver (specification) + end +end + +--[[doc-- + + Also {\bfseries EXPERIMENTAL}: custom file resolvers via callback. + +--doc]]-- +create_callback("luaotfload.resolve_font", "simple", dummy_function) + +request_resolvers.my = function (specification) + call_callback("luaotfload.resolve_font", specification) +end + +--[[doc-- + + We create a callback for patching fonts on the fly, to be used by + other packages. + It initially contains the empty function that we are going to + override below. + +--doc]]-- + +create_callback("luaotfload.patch_font", "simple", dummy_function) + +--[[doc-- + + \subsection{\CONTEXT override} + \label{define-font} + We provide a simplified version of the original font definition + callback. + +--doc]]-- + +local read_font_file = fonts.definers.read + +--- spec -> size -> id -> tmfdata +local patch_defined_font = function (specification, size, id) + local tfmdata = read_font_file(specification, size, id) + if type(tfmdata) == "table" and tfmdata.shared then + --- We need to test for the “shared” field here + --- or else the fontspec capheight callback will + --- operate on tfm fonts. + call_callback("luaotfload.patch_font", tfmdata, specification) + end + return tfmdata +end + +reset_callback "define_font" + +--[[doc-- + + Finally we register the callbacks. + +--doc]]-- + +local font_definer = config.luaotfload.definer + +if font_definer == "generic" then + add_to_callback("define_font", + fonts.definers.read, + "luaotfload.define_font", + 1) +elseif font_definer == "patch" then + add_to_callback("define_font", + patch_defined_font, + "luaotfload.define_font", + 1) +end + +loadmodule "features.lua" --- contains what was “font-ltx” and “font-otc” +loadmodule "letterspace.lua" --- extra character kerning +loadmodule "auxiliary.lua" --- additionaly high-level functionality (new) + +luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec + +-- vim:tw=79:sw=4:ts=4:et diff --git a/src/luaotfload-override.lua b/src/luaotfload-override.lua new file mode 100644 index 0000000..b75530b --- /dev/null +++ b/src/luaotfload-override.lua @@ -0,0 +1,52 @@ +if not modules then modules = { } end modules ["luaotfload-override"] = { + version = "2.5", + comment = "companion to Luaotfload", + author = "Khaled Hosny, Elie Roux, Philipp Gesang", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2.0" +} + +local findfile = resolvers.findfile +local encodings = fonts.encodings + +local log = luaotfload.log +local report = log.report + +--[[doc-- + + Adobe Glyph List. + ------------------------------------------------------------------- + + Context provides a somewhat different font-age.lua from an unclear + origin. Unfortunately, the file name it reads from is hard-coded + in font-enc.lua, so we have to replace the entire table. + + This shouldn’t cause any complications. Due to its implementation + the glyph list will be loaded upon loading a OTF or TTF for the + first time during a TeX run. (If one sticks to TFM/OFM then it is + never read at all.) For this reason we can install a metatable that + looks up the file of our choosing and only falls back to the + Context one in case it cannot be found. + +--doc]]-- + +encodings.agl = { } + +setmetatable(fonts.encodings.agl, { __index = function (t, k) + if k ~= "unicodes" then + return nil + end + local glyphlist = findfile "luaotfload-glyphlist.lua" + if glyphlist then + report ("log", 1, "load", "loading the Adobe glyph list") + else + glyphlist = findfile "font-age.lua" + report ("both", 0, "load", + "loading the extended glyph list from ConTeXt") + end + local unicodes = dofile(glyphlist) + encodings.agl = { unicodes = unicodes } + return unicodes +end }) + +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua new file mode 100644 index 0000000..1048e1d --- /dev/null +++ b/src/luaotfload-parsers.lua @@ -0,0 +1,578 @@ +#!/usr/bin/env texlua +------------------------------------------------------------------------------- +-- FILE: luaotfload-parsers.lua +-- DESCRIPTION: various lpeg-based parsers used in Luaotfload +-- REQUIREMENTS: Luaotfload > 2.4 +-- AUTHOR: Philipp Gesang (Phg), +-- VERSION: same as Luaotfload +-- CREATED: 2014-01-14 10:15:20+0100 +------------------------------------------------------------------------------- +-- + +if not modules then modules = { } end modules ['luaotfload-parsers'] = { + version = "2.5", + comment = "companion to luaotfload-main.lua", + author = "Philipp Gesang", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2.0" +} + +luaotfload = luaotfload or { } +luaotfload.parsers = luaotfload.parsers or { } +local parsers = luaotfload.parsers + +local lpeg = require "lpeg" +local P, R, S = lpeg.P, lpeg.R, lpeg.S +local lpegmatch = lpeg.match +local C, Cc, Cf = lpeg.C, lpeg.Cc, lpeg.Cf +local Cg, Cmt, Cs, Ct = lpeg.Cg, lpeg.Cmt, lpeg.Cs, lpeg.Ct + +local kpse = kpse +local kpseexpand_path = kpse.expand_path +local kpsereadable_file = kpse.readable_file + +local file = file +local filejoin = file.join +local filedirname = file.dirname + +local io = io +local ioopen = io.open + +local log = luaotfload.log +local report = log.report + +local string = string +local stringsub = string.sub +local stringfind = string.find +local stringlower = string.lower + +local mathceil = math.ceil + +local lfs = lfs +local lfsisfile = lfs.isfile +local lfsisdir = lfs.isdir + +------------------------------------------------------------------------------- +--- COMMON PATTERNS +------------------------------------------------------------------------------- + +local dot = P"." +local colon = P":" +local semicolon = P";" +local comma = P"," +local noncomma = 1 - comma +local slash = P"/" +local equals = P"=" +local lbrk, rbrk = P"[", P"]" + +local spacing = S" \t\v" +local linebreak = S"\n\r" +local whitespace = spacing + linebreak +local ws = spacing^0 +local xmlws = whitespace^1 + +local digit = R"09" +local alpha = R("az", "AZ") +local anum = alpha + digit +local decimal = digit^1 * (dot * digit^0)^-1 + +------------------------------------------------------------------------------- +--- FONTCONFIG +------------------------------------------------------------------------------- + +--[[doc-- + + For fonts installed on the operating system, there are several + options to make Luaotfload index them: + + - 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 function scan_texmf_fonts(). + + - Otherwise + - under Windows and Mac OSX, we take a look at some hardcoded + directories, + - under Unix, it reads /etc/fonts/fonts.conf and processes the + directories specified there. + + This means that if you have fonts in fancy directories, you need to + set them in OSFONTDIR. + + Beware: OSFONTDIR is a kpathsea variable, so fonts found in these + paths, though technically system fonts, are registered in the + category “texmf”, not “system”. This may have consequences for the + lookup order when a font file (or a font with the same name + information) is located in both the system and the texmf tree. + +--doc]]-- + +local tag_name = C(alpha^1) +local comment = P"" + +---> header specifica +local xml_declaration = P"")^0 * P"?>" +local xml_doctype = P"")^0 * P">" +local header = xml_declaration^-1 + * (xml_doctype + comment + xmlws)^0 + +---> enforce root node +local root_start = P"<" * xmlws^-1 * P"fontconfig" * xmlws^-1 * P">" +local root_stop = P"" + +local dquote, squote = P[["]], P"'" +local xml_namestartchar = S":_" + alpha --- ascii only, funk the rest +local xml_namechar = S":._" + alpha + digit +local xml_name = xmlws^-1 + * C(xml_namestartchar * xml_namechar^0) +local xml_attvalue = dquote * C((1 - S[[%&"]])^1) * dquote * xmlws^-1 + + squote * C((1 - S[[%&']])^1) * squote * xmlws^-1 +local xml_attr = Cg(xml_name * P"=" * xml_attvalue) +local xml_attr_list = Cf(Ct"" * xml_attr^1, rawset) + +--[[doc-- + scan_node creates a parser for a given xml tag. +--doc]]-- +--- string -> bool -> lpeg_t +local scan_node = function (tag) + --- Node attributes go into a table with the index “attributes” + --- (relevant for “prefix="xdg"” and the likes). + local p_tag = P(tag) + local with_attributes = P"<" * p_tag + * Cg(xml_attr_list, "attributes")^-1 + * xmlws^-1 + * P">" + local plain = P"<" * p_tag * xmlws^-1 * P">" + local node_start = plain + with_attributes + local node_stop = P"" + --- there is no nesting, the earth is flat ... + local node = node_start + * Cc(tag) * C(comment + (1 - node_stop)^1) + * node_stop + return Ct(node) -- returns {string, string [, attributes = { key = val }] } +end + +--[[doc-- + At the moment, the interesting tags are “dir” for + directory declarations, and “include” for including + further configuration files. + + spec: http://freedesktop.org/software/fontconfig/fontconfig-user.html +--doc]]-- +local include_node = scan_node"include" +local dir_node = scan_node"dir" + +local element = dir_node + + include_node + + comment --> ignore + + P(1-root_stop) --> skip byte + +local root = root_start * Ct(element^0) * root_stop +local p_cheapxml = header * root + +--lpeg.print(p_cheapxml) ---> 757 rules with v0.10 + +--[[doc-- + fonts_conf_scanner() handles configuration files. + It is called on an abolute path to a config file (e.g. + /home/luser/.config/fontconfig/fonts.conf) and returns a list + of the nodes it managed to extract from the file. +--doc]]-- +--- string -> path list +local fonts_conf_scanner = function (path) + local fh = ioopen(path, "r") + if not fh then + report("both", 3, "db", "Cannot open fontconfig file %s.", path) + return + end + local raw = fh:read"*all" + fh:close() + + local confdata = lpegmatch(p_cheapxml, raw) + if not confdata then + report("both", 3, "db", "Cannot scan fontconfig file %s.", path) + return + end + return confdata +end + +local p_conf = P".conf" * P(-1) +local p_filter = (1 - p_conf)^1 * p_conf + +local conf_filter = function (path) + if lpegmatch (p_filter, path) then + return true + end + return false +end + +--[[doc-- + read_fonts_conf_indeed() is called with six arguments; the + latter three are tables that represent the state and are + always returned. + The first three are + · the path to the file + · the expanded $HOME + · the expanded $XDG_CONFIG_DIR +--doc]]-- +--- string -> string -> string -> tab -> tab -> (tab * tab * tab) +local read_fonts_conf_indeed +read_fonts_conf_indeed = function (start, home, xdg_home, + acc, done, dirs_done, + find_files) + + local paths = fonts_conf_scanner(start) + if not paths then --- nothing to do + return acc, done, dirs_done + end + + for i=1, #paths do + local pathobj = paths[i] + local kind, path = pathobj[1], pathobj[2] + local attributes = pathobj.attributes + if attributes and attributes.prefix == "xdg" then + --- this prepends the xdg root (usually ~/.config) + path = filejoin(xdg_home, path) + end + + if kind == "dir" then + if stringsub(path, 1, 1) == "~" then + path = filejoin(home, stringsub(path, 2)) + end + --- We exclude paths with texmf in them, as they should be + --- found anyway; also duplicates are ignored by checking + --- if they are elements of dirs_done. + --- + --- FIXME does this mean we cannot access paths from + --- distributions (e.g. Context minimals) installed + --- separately? + if not (stringfind(path, "texmf") or dirs_done[path]) then + acc[#acc+1] = path + dirs_done[path] = true + end + + elseif kind == "include" then + --- here the path can be four things: a directory or a file, + --- in absolute or relative path. + if stringsub(path, 1, 1) == "~" then + path = filejoin(home, stringsub(path, 2)) + elseif --- if the path is relative, we make it absolute + not ( lfsisfile(path) or lfsisdir(path) ) + then + path = filejoin(filedirname(start), path) + end + if lfsisfile(path) + and kpsereadable_file(path) + and not done[path] + then + --- we exclude path with texmf in them, as they should + --- be found otherwise + acc = read_fonts_conf_indeed( + path, home, xdg_home, + acc, done, dirs_done) + elseif lfsisdir(path) then --- arrow code ahead + local config_files = find_files (path, conf_filter) + for _, filename in next, config_files do + if not done[filename] then + acc = read_fonts_conf_indeed( + filename, home, xdg_home, + acc, done, dirs_done) + end + end + end --- match “kind” + end --- iterate paths + end + + --inspect(acc) + --inspect(done) + return acc, done, dirs_done + end --- read_fonts_conf_indeed() + +--[[doc-- + read_fonts_conf() sets up an accumulator and two sets + for tracking what’s been done. + + Also, the environment variables HOME and XDG_CONFIG_HOME -- + which are constants anyways -- are expanded so don’t have to + repeat that over and over again as with the old parser. + Now they’re just passed on to every call of + read_fonts_conf_indeed(). + + read_fonts_conf() is also the only reference visible outside + the closure. +--doc]]-- + +--- list -> (string -> function option -> string list) -> list + +local read_fonts_conf = function (path_list, find_files) + local home = kpseexpand_path"~" --- could be os.getenv"HOME" + local xdg_home = kpseexpand_path"$XDG_CONFIG_HOME" + if xdg_home == "" then xdg_home = filejoin(home, ".config") end + local acc = { } ---> list: paths collected + local done = { } ---> set: files inspected + local dirs_done = { } ---> set: dirs in list + for i=1, #path_list do --- we keep the state between files + acc, done, dirs_done = read_fonts_conf_indeed( + path_list[i], home, xdg_home, + acc, done, dirs_done, + find_files) + end + return acc +end + +luaotfload.parsers.read_fonts_conf = read_fonts_conf + + + +------------------------------------------------------------------------------- +--- MISC PARSERS +------------------------------------------------------------------------------- + + +local trailingslashes = slash^1 * P(-1) +local stripslashes = C((1 - trailingslashes)^0) +parsers.stripslashes = stripslashes + +local splitcomma = Ct((C(noncomma^1) + comma)^1) +parsers.splitcomma = splitcomma + + + +------------------------------------------------------------------------------- +--- FONT REQUEST +------------------------------------------------------------------------------- + + +--[[doc------------------------------------------------------------------------ + + The luaotfload font request syntax (see manual) + has a canonical form: + + \font=:: + + where + is the control sequence that activates the font + is either “file” or “name”, determining the lookup + is either a file name (no path) or a font + name, depending on the lookup + is a list of switches or options, separated by + semicolons or commas; a switch is of the form “+” foo + or “-” foo, options are of the form lhs “=” rhs + + however, to ensure backward compatibility we also have + support for Xetex-style requests. + + for the Xetex emulation see: + · The XeTeX Reference Guide by Will Robertson, 2011 + · The XeTeX Companion by Michel Goosens, 2010 + · About XeTeX by Jonathan Kew, 2005 + + + caueat emptor. + + the request is parsed into one of **four** different lookup + categories: the regular ones, file and name, as well as the + Xetex compatibility ones, path and anon. (maybe a better choice + of identifier would be “ambig”.) + + according to my reconstruction, the correct chaining of the + lookups for each category is as follows: + + | File -> ( db/filename lookup ) + + | Name -> ( db/name lookup, + db/filename lookup ) + + | Path -> ( db/filename lookup, + fullpath lookup ) + + | Anon -> ( kpse.find_file(), // <- for tfm, ofm + db/name lookup, + db/filename lookup, + fullpath lookup ) + + caching of successful lookups is essential. we now as of v2.2 + have a lookup cache that is stored in a separate file. it + pertains only to name: lookups, and is described in more detail + in luaotfload-database.lua. + +------------------------------------------------------------------------------- + + One further incompatibility between Xetex and Luatex-Fonts consists + in their option list syntax: apparently, Xetex requires key-value + options to be prefixed by a "+" (ascii “plus”) character. We + silently accept this as well, dropping the first byte if it is a + plus or minus character. + + Reference: https://github.com/lualatex/luaotfload/issues/79#issuecomment-18104483 + +--doc]]------------------------------------------------------------------------ + + +local handle_normal_option = function (key, val) + val = stringlower(val) + --- the former “toboolean()” handler + if val == "true" then + val = true + elseif val == "false" then + val = false + end + return key, val +end + +--[[doc-- + + Xetex style indexing begins at zero which we just increment before + passing it along to the font loader. Ymmv. + +--doc]]-- + +local handle_xetex_option = function (key, val) + val = stringlower(val) + local numeric = tonumber(val) --- decimal only; keeps colors intact + if numeric then --- ugh + if mathceil(numeric) == numeric then -- integer, possible index + val = tostring(numeric + 1) + end + elseif val == "true" then + val = true + elseif val == "false" then + val = false + end + return key, val +end + +--[[doc-- + + Instead of silently ignoring invalid options we emit a warning to + the log. + + Note that we have to return a pair to please rawset(). This creates + an entry on the resulting features hash which will later be removed + during set_default_features(). + +--doc]]-- + +local handle_invalid_option = function (opt) + report("log", 0, "load", "font option %q unknown.", opt) + return "", false +end + +--[[doc-- + + Dirty test if a file: request is actually a path: lookup; don’t + ask! Note this fails on Windows-style absolute paths. These will + *really* have to use the correct request. + +--doc]]-- + +local check_garbage = function (_,i, garbage) + if stringfind(garbage, "/") then + report("log", 0, "load", --- ffs use path! + "warning: path in file: lookups is deprecated; ") + report("log", 0, "load", "use bracket syntax instead!") + report("log", 0, "load", + "position: %d; full match: %q", + i, garbage) + return true + end + return false +end + +local featuresep = comma + semicolon + +--- modifiers --------------------------------------------------------- +--[[doc-- + The slash notation: called “modifiers” (Kew) or “font options” + (Robertson, Goosens) + we only support the shorthands for italic / bold / bold italic + shapes, as well as setting optical size, the rest is ignored. +--doc]]-- +local style_modifier = (P"BI" + P"IB" + P"bi" + P"ib" + S"biBI") + / stringlower +local size_modifier = S"Ss" * P"=" --- optical size + * Cc"optsize" * C(decimal) +local other_modifier = P"AAT" + P"aat" --- apple stuff; unsupported + + P"ICU" + P"icu" --- not applicable + + P"GR" + P"gr" --- sil stuff; unsupported +local garbage_modifier = ((1 - colon - slash)^0 * Cc(false)) +local modifier = slash * (other_modifier --> ignore + + Cs(style_modifier) --> collect + + Ct(size_modifier) --> collect + + garbage_modifier) --> warn +local modifier_list = Cg(Ct(modifier^0), "modifiers") + +--- lookups ----------------------------------------------------------- +local fontname = C((1-S":(/")^1) --- like luatex-fonts +local unsupported = Cmt((1-S":(")^1, check_garbage) +local prefixed = P"name:" * ws * Cg(fontname, "name") +--- initially we intended file: to emulate the behavior of +--- luatex-fonts, i.e. no paths allowed. after all, we do have XeTeX +--- emulation with the path lookup and it interferes with db lookups. +--- turns out fontspec and other widely used packages rely on file: +--- with paths already, so we’ll add a less strict rule here. anyways, +--- we’ll emit a warning. + + P"file:" * ws * Cg(unsupported, "path") + + P"file:" * ws * Cg(fontname, "file") +--- EXPERIMENTAL: kpse lookup + + P"kpse:" * ws * Cg(fontname, "kpse") +--- EXPERIMENTAL: custom lookup + + P"my:" * ws * Cg(fontname, "my") +local unprefixed = Cg(fontname, "anon") +local path_lookup = lbrk * Cg(C((1-rbrk)^1), "path") * rbrk + +--- features ---------------------------------------------------------- +local field_char = anum + S"+-." --- sic! +local field = field_char^1 +--- assignments are “lhs=rhs” +--- or “+lhs=rhs” (Xetex-style) +--- switches are “+key” | “-key” +local normal_option = C(field) * ws * equals * ws * C(field) * ws +local xetex_option = P"+" * ws * normal_option +local ignore_option = (1 - equals - featuresep)^1 + * equals + * (1 - featuresep)^1 +local assignment = xetex_option / handle_xetex_option + + normal_option / handle_normal_option + + ignore_option / handle_invalid_option +local switch = P"+" * ws * C(field) * Cc(true) + + P"-" * ws * C(field) * Cc(false) + + C(field) * Cc(true) --- default +local feature_expr = ws * Cg(assignment + switch) * ws +local option = feature_expr +local feature_list = Cf(Ct"" + * option + * (featuresep * option^-1)^0 + , rawset) + * featuresep^-1 + +--- other ------------------------------------------------------------- +--- This rule is present in the original parser. It sets the “sub” +--- field of the specification which allows addressing a specific +--- font inside a TTC container. Neither in Luatex-Fonts nor in +--- Luaotfload is this documented, so we might as well silently drop +--- it. However, as backward compatibility is one of our prime goals we +--- just insert it here and leave it undocumented until someone cares +--- to ask. (Note: afair subfonts are numbered, but this rule matches a +--- string; I won’t mess with it though until someone reports a +--- problem.) +--- local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim +--- Note to self: subfonts apparently start at index 0. Tested with +--- Cambria.ttc that includes “Cambria Math” at 0 and “Cambria” at 1. +--- Other values cause luatex to segfault. +local subfont = P"(" * Cg((1 - S"()")^1, "sub") * P")" +--- top-level rules --------------------------------------------------- +--- \font\foo=: +local features = Cg(feature_list, "features") +local specification = (prefixed + unprefixed) + * subfont^-1 + * modifier_list^-1 +local font_request = Ct(path_lookup * (colon^-1 * features)^-1 + + specification * (colon * features)^-1) + +-- lpeg.print(font_request) +--- v2.5 parser: 1065 rules +--- v1.2 parser: 230 rules + +luaotfload.parsers.font_request = font_request + diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua new file mode 100755 index 0000000..35765b5 --- /dev/null +++ b/src/luaotfload-tool.lua @@ -0,0 +1,1263 @@ +#!/usr/bin/env texlua +----------------------------------------------------------------------- +-- FILE: luaotfload-tool.lua +-- DESCRIPTION: database functionality +-- REQUIREMENTS: luaotfload 2.5 +-- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang +-- VERSION: 2.5 +-- LICENSE: GPL v2.0 +-- MODIFIED: 2014-01-14 13:17:04+0100 +----------------------------------------------------------------------- + +luaotfload = luaotfload or { } +local version = "2.5" --- .- +luaotfload.version = version +luaotfload.self = "luaotfload-tool" + +--[[doc-- + +luaotfload-tool(1) + +This file was originally written (as \fileent{mkluatexfontdb.lua}) by +Elie Roux and Khaled Hosny and, as a derived work of ConTeXt, is +provided under the terms of the GPL v2.0 license as printed in full +text in the manual (luaotfload.pdf). + + \url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. + +This file is a wrapper for the luaotfload font names module +(luaotfload-database.lua). It is part of the luaotfload bundle, please +see the luaotfload documentation for more info. Report bugs to + + \url{https://github.com/lualatex/luaotfload/issues}. + +--doc]]-- + +kpse.set_program_name "luatex" + +--[[doc-- + + We test for Lua 5.1 by means of capability detection to see if + we’re running an outdated Luatex. If so, we bail. + + \url{http://lua-users.org/wiki/LuaVersionCompatibility} + +--doc]]-- + + +local ioopen = io.open +local iowrite = io.write +local kpsefind_file = kpse.find_file +local next = next +local osdate = os.date +local ostype = os.type +local stringexplode = string.explode +local stringformat = string.format +local stringlower = string.lower +local stringrep = string.rep +local tableconcat = table.concat +local texiowrite_nl = texio.write_nl +local texiowrite = texio.write +local tonumber = tonumber +local type = type + +local runtime +if _G.getfenv ~= nil then -- 5.1 or LJ + if _G.jit ~= nil then + runtime = { "jit", jit.version } + else + runtime = { "stock", _VERSION } + print "FATAL ERROR" + print "Luaotfload requires a Luatex version >=0.76." + print "Please update your TeX distribution!" + os.exit (-1) + end +else -- 5.2 + runtime = { "stock", _VERSION } +end + + +local C, Ct, P, S = lpeg.C, lpeg.Ct, lpeg.P, lpeg.S +local lpegmatch = lpeg.match + +local loader_file = "luatexbase.loader.lua" +local loader_path = assert(kpsefind_file(loader_file, "lua"), + "File '"..loader_file.."' not found") + + +string.quoted = string.quoted or function (str) + return string.format("%q",str) +end + +require(loader_path) + +config = config or { } +local config = config +local luaotfloadconfig = config.luaotfload or { } +config.luaotfload = luaotfloadconfig +luaotfloadconfig.version = luaotfloadconfig.version or version +luaotfloadconfig.names_dir = luaotfloadconfig.names_dir or "names" +luaotfloadconfig.cache_dir = luaotfloadconfig.cache_dir or "fonts" +luaotfloadconfig.index_file = luaotfloadconfig.index_file + or "luaotfload-names.lua" +luaotfloadconfig.formats = luaotfloadconfig.formats + or "otf,ttf,ttc,dfont" +luaotfloadconfig.reload = false +if not luaotfloadconfig.strip then + luaotfloadconfig.strip = true +end + +config.lualibs = config.lualibs or { } +config.lualibs.verbose = false +config.lualibs.prefer_merged = true +config.lualibs.load_extended = true + +require "lualibs" +local tabletohash = table.tohash +local stringsplit = string.split + +--[[doc-- +\fileent{luatex-basics-gen.lua} calls functions from the +\luafunction{texio.*} library; too much for our taste. +We intercept them with dummies. + +Also, it sets up dummies in place of the tables created by the Context +libraries. Since we have loaded the lualibs already this would cause +collateral damage for some libraries whose namespace would be +overridden. We employ our usual backup-restore strategy to work around +this. (Postponing the loading of the lualibs code is not an option +because the functionality is needed by basics-gen itself.) +--doc]]-- + +local dummy_function = function ( ) end +local backup = { + write = texio.write, + write_nl = texio.write_nl, + utilities = utilities, +} + +texio.write, texio.write_nl = dummy_function, dummy_function +require"luaotfload-basics-gen.lua" + +texio.write, texio.write_nl = backup.write, backup.write_nl +utilities = backup.utilities + +require"luaotfload-log.lua" --- this populates the luaotfload.log.* namespace +require"luaotfload-parsers" --- fonts.conf and request syntax +require"luaotfload-database" +require"alt_getopt" + +local names = fonts.names +local status_file = "luaotfload-status" +local luaotfloadstatus = require (status_file) +luaotfloadconfig.status = luaotfloadstatus +local sanitize_fontname = names.sanitize_fontname + +local log = luaotfload.log +local report = log.report + +local pathdata = names.path +local names_plain = pathdata.index.lua +local names_gzip = names_plain .. ".gz" +local names_bin = pathdata.index.luc + +local help_messages = { + ["luaotfload-tool"] = [[ + +Usage: %s [OPTIONS...] + + Luaotfload font management and diagnostic utility. + This program is part of the Luaotfload package. + + Valid options are: + +------------------------------------------------------------------------------- + VERBOSITY AND DIAGNOSTICS + + -q --quiet don't output anything + -v --verbose=LEVEL be more verbose (print the searched directories) + -vv print the loaded fonts + -vvv print all steps of directory searching + --log=stdout redirect log output to stdout + + -V --version print version and exit + -h --help print this message + --diagnose=CHECK run a self test procedure; one of "files", + "environment", "index", "permissions", or + "repository" + +------------------------------------------------------------------------------- + DATABASE + + -u --update update the database + -n --no-reload suppress db update + --no-strip keep redundant information in db + -f --force force re-indexing all fonts + -c --no-compress do not gzip index file (text version only) + -l --flush-lookups empty lookup cache of font requests + -D --dry-run skip loading of fonts, just scan + --formats=[+|-]EXTENSIONS set, add, or subtract formats to index + -p --prefer-texmf prefer fonts in the TEXMF over system fonts + --max-fonts=N process at most N font files + + --find="font name" query the database for a font name + -F --fuzzy look for approximate matches if --find fails + --limit=n limit display of fuzzy matches to + (default: n = 1) + + -i --info display basic font metadata + -I --inspect display detailed font metadata + -w --warnings display warnings generated by the + fontloader library + + --list= output list of entries by field + --list=: restrict to entries with = + --fields=,,…, which fields to print with --list + -b --show-blacklist show blacklisted files + +The font database will be saved to + %s + %s + +------------------------------------------------------------------------------- + FONT CACHE + + --cache= operate on font cache, where is + "show", "purge", or "erase" + +The font cache will be written to + %s + +]], + mkluatexfontdb = [[ +FATAL ERROR +As of Luaotfload v2.5, legacy behavior is not supported anymore. Please +update your scripts and/or habits! Kthxbye. +]], + short = [[ +Usage: luaotfload-tool [--help] [--version] [--verbose=] + [--update] [--force] [--prefer-texmf] + [--dry-run] [--formats=] + [--find=] [--fuzzy] [--info] [--inspect] + [--list=] [--fields=] + [--cache=] [--flush-lookups] + [--show-blacklist] [--diagnose=] + +Enter 'luaotfload-tool --help' for a larger list of options. +]] +} + +local help_msg = function (version) + local template = help_messages[version] + iowrite(stringformat(template, + luaotfload.self, +-- names_plain, + names_gzip, + names_bin, + caches.getwritablepath ( + luaotfloadconfig.cache_dir))) +end + +local about = [[ +%s: + Luaotfload font management and diagnostic utility. + License: GNU GPL v2.0. + Report problems to +]] + +local version_msg = function ( ) + local out = function (...) texiowrite_nl (stringformat (...)) end + out (about, luaotfload.self) + out ("%s version %q", luaotfload.self, version) + out ("revision %q", luaotfloadstatus.notes.revision) + out ("database version %q", names.version) + out ("Lua interpreter: %s; version %q", runtime[1], runtime[2]) + out ("Luatex SVN revision %d", status.luatex_svn) + out ("Luatex version %.2f.%d", + status.luatex_version / 100, + status.luatex_revision) + out "" +end + + +--- makeshift formatting + +local head_adornchars = { + [1] = "*", [2] = "=", [3] = "~", [4] = "-", [5] = "·", +} + +local textwidth = 80 +local wd_leftcolumn = math.floor(textwidth * .25) +local key_fmt = stringformat([[%%%ds]], wd_leftcolumn) +local val_fmt = [[%s]] +local fieldseparator = ":" +local info_fmt = key_fmt .. fieldseparator .. " " .. val_fmt + +local currentdepth = 0 +local counterstack = { } -- counters per level +local counterformat = "%d" + +local format_counter = function (stack) + local acc = { } + for lvl=1, #stack do + acc[#acc+1] = stringformat(counterformat, stack[lvl]) + end + return tableconcat(acc, ".") +end + +local print_heading = function (title, level) + local structuredata + if currentdepth == level then -- top is current + counterstack[#counterstack] = counterstack[#counterstack] + 1 + elseif currentdepth < level then -- push new + counterstack[#counterstack+1] = 1 + else -- pop + local diff = currentdepth - level + while diff > 0 do + counterstack[#counterstack] = nil + diff = diff - 1 + end + counterstack[#counterstack] = counterstack[#counterstack] + 1 + end + currentdepth = level + + texiowrite_nl "" + if not level or level > #head_adornchars then + level = #head_adornchars + end + local adornchar = head_adornchars[level] + + local counter = format_counter(counterstack) + + local s = adornchar .. adornchar .. " " + .. counter .. " " + .. title .. " " + texiowrite_nl (s .. stringrep(adornchar, textwidth-utf.len(s))) +end + +local baseindent = " " + +--[[doc-- + + show_info_items -- Together with show_info_table prints the table returned by + fontloader.info(), recursing into nested tables if appropriate (as necessitated + by Luatex versions 0.78+ which include the pfminfo table in the result. + +--doc]]-- + +local show_info_table show_info_table = function (t, depth) + depth = depth or 0 + local indent = stringrep (baseindent, depth) + local keys = table.sortedkeys (t) + for n = 1, #keys do + local key = keys [n] + local val = t [key] + if type (val) == "table" then + texiowrite_nl (indent .. stringformat (info_fmt, key, "
")) + show_info_table (val, depth + 1) + else + texiowrite_nl (indent .. stringformat (info_fmt, key, val)) + end + end +end + +local show_info_items = function (fontinfo) + print_heading (fontinfo.fullname, 1) + texiowrite_nl "" + show_info_table (fontinfo) + texiowrite_nl "" +end + +local p_eol = S"\n\r"^1 +local p_space = S" \t\v"^0 +local p_line = p_space * C((1 - p_eol)^1)^-1 +local p_lines = Ct(p_line * (p_eol^1 * p_line^-1)^0) + +local show_fontloader_warnings = function (ws) + local nws = #ws + print_heading(stringformat( + [[the fontloader emitted %d warnings]], + nws), 2) + texiowrite_nl "" + for i=1, nws do + local w = ws[i] + texiowrite_nl (stringformat("%d:", i)) + local lines = lpegmatch(p_lines, w) + for i=1, #lines do + local line = lines[i] + texiowrite_nl(" · " .. line) + end + texiowrite_nl "" + end +end + +local p_spacechar = S" \n\r\t\v" +local p_wordchar = (1 - p_spacechar) +local p_whitespace = p_spacechar^1 +local p_word = C(p_wordchar^1) +local p_words = Ct(p_word * (p_whitespace * p_word)^0) + +--- string -> int -> string list +local reflow = function (text, width) + local words + if type(text) == "string" then + words = lpegmatch(p_words, text) + if #words < 2 then + return { text } + end + else + words = text + if #words < 2 then + return words + end + end + + local space = " " + local utflen = utf.len + local reflowed = { } + + local first = words[1] + local linelen = #first + local line = { first } + + for i=2, #words do + local word = words[i] + local lword = utflen(word) + linelen = linelen + lword + 1 + if linelen > width then + reflowed[#reflowed+1] = tableconcat(line) + linelen = #word + line = { word } + else + line[#line+1] = space + line[#line+1] = word + end + end + reflowed[#reflowed+1] = tableconcat(line) + return reflowed +end + +--- string -> 'a -> string list +local print_field = function (key, val) + val = tostring(val) + local lhs = stringformat(key_fmt, key) .. fieldseparator .. " " + local wd_lhs = #lhs + local lines = reflow(val, textwidth - wd_lhs) + + texiowrite_nl(lhs) + texiowrite(lines[1]) + if #lines > 1 then + local indent = stringrep(" ", wd_lhs) + for i=2, #lines do + texiowrite_nl(indent) + texiowrite (lines[i]) + end + end +end + +local display_names = function (names) + print_heading("Font Metadata", 2) + for i=1, #names do + local lang, namedata = names[i].lang, names[i].names + print_heading(stringformat("Language: %s ", i, lang), 3) + texiowrite_nl "" + if namedata then + for field, value in next, namedata do + print_field(field, value) + end + end + end +end + +--- see luafflib.c +local general_fields = { + --- second: l -> literal | n -> length | d -> date + { "fullname", "l", "font name" }, + { "version", "l", "font version" }, + { "creationtime", "d", "creation time" }, + { "modificationtime", "d", "modification time" }, + { "subfonts", "n", "number of subfonts" }, + { "glyphcnt", "l", "number of glyphs" }, + { "weight", "l", "weight indicator" }, + { "design_size", "l", "design size" }, + { "design_range_bottom", "l", "design size min" }, + { "design_range_top", "l", "design size max" }, + { "fontstyle_id", "l", "font style id" }, + { "fontstyle_name", "S", "font style name" }, + { "strokewidth", "l", "stroke width" }, + { "units_per_em", "l", "units per em" }, + { "ascent", "l", "ascender height" }, + { "descent", "l", "descender height" }, + { "comments", "l", "comments" }, + { "os2_version", "l", "os2 version" }, + { "sfd_version", "l", "sfd version" }, +} + +local display_general = function (fullinfo) + texiowrite_nl "" + print_heading("General Information", 2) + texiowrite_nl "" + for i=1, #general_fields do + local field = general_fields[i] + local key, mode, desc = unpack(field) + local val + if mode == "l" then + val = fullinfo[key] + elseif mode == "S" then --- style names table + local data = fullinfo[key] + if type (data) == "table" then + if #data > 0 then + for n = 1, #data do + local nth = data[n] + if nth.lang == 1033 then + val = nth.name + goto found + end + end + val = next (data).name + else + val = "" + end + ::found:: + else + val = data + end + elseif mode == "n" then + local v = fullinfo[key] + if v then + val = #fullinfo[key] + end + elseif mode == "d" then + if ostype == "unix" then + val = osdate("%F %T", fullinfo[key]) + else + --- the MS compiler doesn’t support C99, so + --- strftime is missing some functionality; + --- see loslib.c for details. + val = osdate("%Y-%m-d %H:%M:%S", fullinfo[key]) + end + end + if not val then + val = "" + end + print_field(desc, val) + end +end + +local print_features = function (features) + for tag, data in next, features do + print_heading(tag, 4) + for script, languages in next, data do + local field = stringformat(key_fmt, script).. fieldseparator .. " " + local wd_field = #field + local lines = reflow(languages.list, textwidth - wd_field) + local indent = stringrep(" ", wd_field) + texiowrite_nl(field) + texiowrite(lines[1]) + if #lines > 1 then + for i=1, #lines do + texiowrite_nl(indent .. lines[i]) + end + end + end + end +end + +local extract_feature_info = function (set) + local collected = { } + for i=1, #set do + local features = set[i].features + if features then + for j=1, #features do + local feature = features[j] + local scripts = feature.scripts + local tagname = stringlower(feature.tag) + local entry = collected[tagname] or { } + + for k=1, #scripts do + local script = scripts[k] + local scriptname = stringlower(script.script) + local c_script = entry[scriptname] or { + list = { }, + set = { }, + } + local list, set = c_script.list, c_script.set + + for l=1, #script.langs do + local langname = stringlower(script.langs[l]) + if not set[langname] then + list[#list+1] = langname + set[langname] = true + end + end + entry[scriptname] = c_script + end + collected[tagname] = entry + end + end + end + return collected +end + +local display_feature_set = function (set) + local collected = extract_feature_info(set) + print_features(collected) +end + +local display_features = function (gsub, gpos) + texiowrite_nl "" + + if gsub or gpos then + print_heading("Features", 2) + + if gsub then + print_heading("GSUB Features", 3) + display_feature_set(gsub) + end + + if gpos then + print_heading("GPOS Features", 3) + display_feature_set(gpos) + end + end +end + +local show_full_info = function (path, subfont, warnings) + local rawinfo, warn = fontloader.open(path, subfont) + if warnings then + show_fontloader_warnings(warn) + end + if not rawinfo then + texiowrite_nl(stringformat([[cannot open font %s]], path)) + return + end + local fontdata = { } + local fullinfo = fontloader.to_table(rawinfo) + local fields = fontloader.fields(rawinfo) + fontloader.close(rawinfo) + display_names(fullinfo.names) + display_general(fullinfo) + display_features(fullinfo.gsub, fullinfo.gpos) +end + +--- Subfonts returned by fontloader.info() do not correspond +--- to the actual indices required by fontloader.open(), so +--- we try and locate the correct one by matching the request +--- against the full name. + +local subfont_by_name +subfont_by_name = function (lst, askedname, n) + if not n then + return subfont_by_name (lst, askedname, 1) + end + + local font = lst[n] + if font then + if sanitize_fontname (font.fullname) == askedname then + return font + end + return subfont_by_name (lst, askedname, n+1) + end + return false +end + +--[[doc-- +The font info knows two levels of detail: + + a) basic information returned by fontloader.info(); and + b) detailed information that is a subset of the font table + returned by fontloader.open(). +--doc]]-- + +local show_font_info = function (basename, askedname, detail, warnings) + local filenames = names.data().files + local index = filenames.base[basename] + local fullname = filenames.full[index] + askedname = sanitize_fontname (askedname) + if not fullname then -- texmf + fullname = resolvers.findfile(basename) + end + if fullname then + local shortinfo = fontloader.info(fullname) + local nfonts = #shortinfo + if nfonts > 0 then -- true type collection + local subfont + if askedname then + report (true, 1, "resolve", + [[%s is part of the font collection %s]], + askedname, basename) + subfont = subfont_by_name(shortinfo, askedname) + end + if subfont then + show_info_items(subfont) + if detail == true then + show_full_info(fullname, subfont, warnings) + end + else -- list all subfonts + report (true, 1, "resolve", + [[%s is a font collection]], basename) + for subfont = 1, nfonts do + report (true, 1, "resolve", + [[Showing info for font no. %d]], n) + show_info_items(shortinfo[subfont]) + if detail == true then + show_full_info(fullname, subfont, warnings) + end + end + end + else + show_info_items(shortinfo) + if detail == true then + show_full_info(fullname, subfont, warnings) + end + end + else + report (true, 1, "resolve", "Font %s not found", filename) + end +end + +--[[-- +Running the scripts triggers one or more actions that have to be +executed in the correct order. To avoid duplication we track them in a +set. +--]]-- + +local action_sequence = { + "loglevel", "help", "version", "diagnose", + "blacklist", "cache", "flush", "generate", + "list", "query", +} + +local action_pending = tabletohash(action_sequence, false) + +action_pending.loglevel = true --- always set the loglevel +action_pending.generate = false --- this is the default action + +local actions = { } --- (jobspec -> (bool * bool)) list + +actions.loglevel = function (job) + log.set_loglevel(job.log_level) + report ("info", 3, "util", "Setting log level", "%d", job.log_level) + report ("log", 2, "util", "Lua=%q", _VERSION) + return true, true +end + +actions.version = function (job) + version_msg() + return true, false +end + +actions.help = function (job) + help_msg (job.help_version or "luaotfload-tool") + return true, false +end + +actions.blacklist = function (job) + names.read_blacklist() + local n = 0 + for n, entry in next, table.sortedkeys(names.blacklist) do + iowrite (stringformat("(%d %s)\n", n, entry)) + end + return true, false +end + +actions.generate = function (job) + local fontnames, savedname + fontnames = names.update(fontnames, job.force_reload, job.dry_run) + report ("info", 2, "db", "Fonts in the database: %i", #fontnames.mappings) + if names.data() then + return true, true + end + return false, false +end + +actions.flush = function (job) + local success, lookups = names.flush_lookup_cache() + if success then + local success = names.save_lookups() + if success then + report ("info", 2, "cache", "Lookup cache emptied") + return true, true + end + end + return false, false +end + +local cache_directives = { + ["purge"] = names.purge_cache, + ["erase"] = names.erase_cache, + ["show"] = names.show_cache, +} + +actions.cache = function (job) + local directive = cache_directives[job.cache] + if not directive or type(directive) ~= "function" then + report ("info", 2, "cache", + "Invalid font cache directive %s.", job.cache) + return false, false + end + if directive() then + return true, true + end + return false, false +end + +actions.query = function (job) + + require "luaotfload-features" + + local query = job.query + + local tmpspec = { + name = query, + lookup = "name", + specification = query, + optsize = 0, + features = { }, + } + + tmpspec = names.handle_request (tmpspec) + + if not tmpspec.size then + tmpspec.size = 655360 --- assume 10pt + end + + local foundname, subfont, success + + if tmpspec.lookup == "name" + or tmpspec.lookup == "anon" --- not *exactly* as resolvers.anon + then + foundname, subfont = names.resolve_name (tmpspec) + if foundname then + foundname, _, success = names.crude_file_lookup (foundname) + end + elseif tmpspec.lookup == "file" then + foundname, _, success = + names.crude_file_lookup (tmpspec.name) + end + + if success then + report (false, 0, "resolve", "Font %q found!", query) + if subfont then + report (false, 0, "resolve", + "Resolved file name %q, subfont nr. %q", + foundname, subfont) + else + report (false, 0, "resolve", + "Resolved file name %q", foundname) + end + if job.show_info then + show_font_info (foundname, query, job.full_info, job.warnings) + iowrite "\n" + end + else + report (false, 0, "resolve", "Cannot find %q in index.", query) + report (false, 0, "resolve", + "Hint: use the --fuzzy option to display suggestions.", + query) + if job.fuzzy == true then + report (false, 0, "resolve", + "Looking for close matches, this may take a while ...") + local _success = names.find_closest(query, job.fuzzy_limit) + end + end + return true, true +end + +--- --list= +--- --list=: +--- +--- --list= --fields=,,,... + +local get_fields get_fields = function (entry, fields, acc, n) + if not acc then + return get_fields (entry, fields, { }, 1) + end + + local field = fields [n] + if field then + local chain = stringsplit (field, "->") + local tmp = entry + for i = 1, #chain - 1 do + tmp = tmp [chain [i]] + if not tmp then + --- invalid field + break + end + end + if tmp then + local value = tmp [chain [#chain]] + acc[#acc+1] = value or false + else + acc[#acc+1] = false + end + return get_fields (entry, fields, acc, n+1) + end + return acc +end + +local separator = "\t" --- could be “,” for csv + +local format_fields format_fields = function (fields, acc, n) + if not acc then + return format_fields(fields, { }, 1) + end + + local field = fields[n] + if field ~= nil then + if field == false then + acc[#acc+1] = "" + else + acc[#acc+1] = tostring(field) + end + return format_fields(fields, acc, n+1) + end + return tableconcat(acc, separator) +end + +local set_primary_field +set_primary_field = function (fields, addme, acc, n) + if not acc then + return set_primary_field(fields, addme, { addme }, 1) + end + + local field = fields[n] + if field then + if field ~= addme then + acc[#acc+1] = field + end + return set_primary_field(fields, addme, acc, n+1) + end + return acc +end + +local splitcomma = luaotfload.parsers.splitcomma + +actions.list = function (job) + local criterion = job.criterion + local asked_fields = job.asked_fields + local name_index = names.data () + + if asked_fields then + asked_fields = lpegmatch(splitcomma, asked_fields) + end + + if not asked_fields then + --- some defaults + asked_fields = { "plainname", "version", } + end + + if not name_index then + name_index = names.load() + end + + local mappings = name_index.mappings + local nmappings = #mappings + + if criterion == "*" then + report (false, 1, "list", "All %d entries", nmappings) + for i=1, nmappings do + local entry = mappings[i] + local fields = get_fields(entry, asked_fields) + --- we could collect these instead ... + local formatted = format_fields(fields) + texiowrite_nl(formatted) + end + + else + criterion = stringexplode(criterion, ":") --> { field, value } + local asked_value = criterion[2] + criterion = criterion[1] + asked_fields = set_primary_field(asked_fields, criterion) + + report (false, 1, "list", "By %s", criterion) + + --- firstly, build a list of fonts to operate on + local targets = { } + if asked_value then --- only those whose value matches + report (false, 2, "list", "Restricting to value %s", asked_value) + for i=1, nmappings do + local entry = mappings[i] + if entry[criterion] + and tostring(entry[criterion]) == asked_value + then + targets[#targets+1] = entry + end + end + + else --- whichever have the field, sorted + local categories, by_category = { }, { } + for i=1, nmappings do + local entry = mappings[i] + local tmp = entry + local chain = stringsplit (criterion, "->") + for i = 1, #chain - 1 do + tmp = tmp [chain [i]] + if not tmp then + break + end + end + local value = tmp and tmp [chain [#chain]] or "" + if value then + --value = tostring(value) + local entries = by_category[value] + if not entries then + entries = { entry } + categories[#categories+1] = value + else + entries[#entries+1] = entry + end + by_category[value] = entries + end + end + table.sort(categories) + + for i=1, #categories do + local entries = by_category[categories[i]] + for j=1, #entries do + targets[#targets+1] = entries[j] + end + end + end + local ntargets = #targets + report (false, 2, "list", "%d entries", ntargets) + + --- now, output the collection + for i=1, ntargets do + local entry = targets[i] + local fields = get_fields(entry, asked_fields) + local formatted = format_fields(fields) + texiowrite_nl(formatted) + end + end + + texiowrite_nl "" + + return true, true +end + +actions.diagnose = function (job) + --- diagnostics are loaded on demand + local diagnose = require "luaotfload-diagnostics.lua" + return diagnose (job) +end + +--- stuff to be carried out prior to exit + +local finalizers = { } + +--- returns false if at least one of the actions failed, mainly +--- for closing io channels +local finalize = function () + local success = true + for _, fun in next, finalizers do + if type (fun) == "function" then + if fun () == false then success = false end + end + end + return success +end + +--[[-- +Command-line processing. +luaotfload-tool relies on the script alt_getopt to process argv and +analyzes its output. + +TODO with extended lualibs we have the functionality from the +environment.* namespace that could eliminate the dependency on +alt_getopt. +--]]-- + +local process_cmdline = function ( ) -- unit -> jobspec + local result = { -- jobspec + force_reload = nil, + full_info = false, + warnings = false, + criterion = "", + query = "", + log_level = 0, --- 2 is approx. the old behavior + } + + local long_options = { + cache = 1, + ["no-compress"] = "c", + diagnose = 1, + ["dry-run"] = "D", + ["flush-lookups"] = "l", + fields = 1, + find = 1, + force = "f", + formats = 1, + fuzzy = "F", + help = "h", + info = "i", + inspect = "I", + limit = 1, + list = 1, + log = 1, + ["max-fonts"] = 1, + ["no-reload"] = "n", + ["no-strip"] = 0, + ["skip-read"] = "R", + ["prefer-texmf"] = "p", + quiet = "q", + ["show-blacklist"] = "b", + stats = "S", + update = "u", + verbose = 1, + version = "V", + warnings = "w", + } + + local short_options = "bcDfFiIlnpqRSuvVhw" + + local options, _, optarg = + alt_getopt.get_ordered_opts (arg, short_options, long_options) + + local nopts = #options + for n=1, nopts do + local v = options[n] + if v == "q" then + result.log_level = 0 + elseif v == "u" then + action_pending["generate"] = true + elseif v == "v" then + if result.log_level > 0 then + result.log_level = result.log_level + 1 + else + result.log_level = 1 + end + elseif v == "V" then + action_pending["version"] = true + elseif v == "h" then + action_pending["help"] = true + elseif v == "f" then + result.update = true + result.force_reload = 1 + elseif v == "verbose" then + local lvl = optarg[n] + if lvl then + lvl = tonumber(lvl) + result.log_level = lvl + if lvl > 2 then + result.warnings = true + end + end + elseif v == "w" then + result.warnings = true + elseif v == "log" then + local str = optarg[n] + if str then + finalizers = log.set_logout(str, finalizers) + end + elseif v == "find" then + action_pending["query"] = true + result.query = optarg[n] + elseif v == "F" then + result.fuzzy = true + elseif v == "limit" then + local lim = optarg[n] + if lim then + result.fuzzy_limit = tonumber(lim) + end + elseif v == "i" then + result.show_info = true + elseif v == "I" then + result.show_info = true + result.full_info = true + elseif v == "l" then + action_pending["flush"] = true + elseif v == "list" then + action_pending["list"] = true + result.criterion = optarg[n] + elseif v == "fields" then + result.asked_fields = optarg[n] + elseif v == "cache" then + action_pending["cache"] = true + result.cache = optarg[n] + elseif v == "D" then + result.dry_run = true + elseif v == "p" then + names.set_location_precedence { + "local", "texmf", "system" + } + elseif v == "b" then + action_pending["blacklist"] = true + elseif v == "diagnose" then + action_pending["diagnose"] = true + result.asked_diagnostics = optarg[n] + elseif v == "formats" then + names.set_font_filter (optarg[n]) + elseif v == "n" then + luaotfloadconfig.update_live = false + elseif v == "S" then + luaotfloadconfig.statistics = true + elseif v == "R" then + --- dev only, undocumented + luaotfloadconfig.skip_read = true + elseif v == "c" then + luaotfloadconfig.compress = false + elseif v == "no-strip" then + luaotfloadconfig.strip = false + elseif v == "max-fonts" then + local n = optarg[n] + if n then + n = tonumber(n) + if n and n > 0 then + luaotfloadconfig.max_fonts = n + end + end + end + end + + if nopts == 0 then + action_pending["help"] = true + result.help_version = "short" + end + return result +end + +local main = function ( ) -- unit -> int + local retval = 0 + local job = process_cmdline() + +-- inspect(action_pending) +-- inspect(job) + + for i=1, #action_sequence do + local actionname = action_sequence[i] + local exit = false + if action_pending[actionname] then + report ("log", 3, "util", "Preparing for task", "%s", actionname) + + local action = actions[actionname] + local success, continue = action(job) + + if not success then + report (false, 0, "util", + "Could not finish task", "%s", actionname) + retval = -1 + exit = true + elseif not continue then + report (false, 3, "util", + "Task completed, exiting", "%s", actionname) + exit = true + else + report (false, 3, "util", + "Task completed successfully", "%s", actionname) + end + end + if exit then break end + end + + if finalize () == false then + retval = -1 + end + + --texiowrite_nl"" + return retval +end + +return main() + +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/src/luaotfload.sty b/src/luaotfload.sty new file mode 100644 index 0000000..a235d6b --- /dev/null +++ b/src/luaotfload.sty @@ -0,0 +1,45 @@ +%% Copyright (C) 2009-2014 +%% +%% by Elie Roux +%% and Khaled Hosny +%% and Philipp Gesang +%% +%% This file is part of Luaotfload. +%% +%% Home: https://github.com/lualatex/luaotfload +%% Support: . +%% +%% Luaotfload is under the GPL v2.0 (exactly) license. +%% +%% ---------------------------------------------------------------------------- +%% +%% Luaotfload is free software; you can redistribute it and/or +%% modify it under the terms of the GNU General Public License +%% as published by the Free Software Foundation; version 2 +%% of the License. +%% +%% Luaotfload is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU General Public License +%% along with Luaotfload; if not, see . +%% +%% ---------------------------------------------------------------------------- +%% +%% Classical Plain+\LATEX package initialization. +%% +\csname ifluaotfloadloaded\endcsname +\let\ifluaotfloadloaded\endinput +\bgroup\expandafter\expandafter\expandafter\egroup +\expandafter\ifx\csname ProvidesPackage\endcsname\relax + \input luatexbase.sty +\else + \NeedsTeXFormat{LaTeX2e} + \ProvidesPackage{luaotfload}% + [2014/42/42 v2.5 OpenType layout system] + \RequirePackage{luatexbase} +\fi +\RequireLuaModule{luaotfload-main} + -- cgit v1.2.3 From e1c73c29abeb709aff1b2ed6491f84ca2011d9c5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 12 Feb 2014 08:03:09 +0100 Subject: [status] update source locations --- scripts/mkstatus | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/scripts/mkstatus b/scripts/mkstatus index fcf8b24..15cb97c 100755 --- a/scripts/mkstatus +++ b/scripts/mkstatus @@ -36,32 +36,32 @@ local filelist = "./build/luaotfload-status.lua" --- result local names = { --- only the runtime files and scripts - "luaotfload-auxiliary.lua", - "luaotfload-basics-gen.lua", - "luaotfload-basics-nod.lua", - { "build", "luaotfload-characters.lua", }, - "luaotfload-colors.lua", - "luaotfload-database.lua", - "luaotfload-diagnostics.lua", - "luaotfload-features.lua", - "luaotfload-fonts-cbk.lua", - "luaotfload-fonts-def.lua", - "luaotfload-fonts-enc.lua", - "luaotfload-fonts-ext.lua", - "luaotfload-fonts-lua.lua", - "luaotfload-fonts-tfm.lua", - { "build", "luaotfload-glyphlist.lua", }, - "luaotfload-letterspace.lua", - "luaotfload-loaders.lua", - "luaotfload-log.lua", - "luaotfload-main.lua", - "luaotfload-fontloader.lua", - "luaotfload-override.lua", - "luaotfload-parsers.lua", - "luaotfload-tool.lua", - { "scripts", "mkcharacters", }, - { "scripts", "mkglyphlist", }, - { "scripts", "mkstatus", }, + { "src", "luaotfload-auxiliary.lua", }, + { "src", "luaotfload-basics-gen.lua", }, + { "src", "luaotfload-basics-nod.lua", }, + { "build", "luaotfload-characters.lua", }, + { "src", "luaotfload-colors.lua", }, + { "src", "luaotfload-database.lua", }, + { "src", "luaotfload-diagnostics.lua", }, + { "src", "luaotfload-features.lua", }, + { "src", "luaotfload-fonts-cbk.lua", }, + { "src", "luaotfload-fonts-def.lua", }, + { "src", "luaotfload-fonts-enc.lua", }, + { "src", "luaotfload-fonts-ext.lua", }, + { "src", "luaotfload-fonts-lua.lua", }, + { "src", "luaotfload-fonts-tfm.lua", }, + { "build", "luaotfload-glyphlist.lua", }, + { "src", "luaotfload-letterspace.lua", }, + { "src", "luaotfload-loaders.lua", }, + { "src", "luaotfload-log.lua", }, + { "src", "luaotfload-main.lua", }, + { "src", "luaotfload-fontloader.lua", }, + { "src", "luaotfload-override.lua", }, + { "src", "luaotfload-parsers.lua", }, + { "src", "luaotfload-tool.lua", }, + { "scripts", "mkcharacters", }, + { "scripts", "mkglyphlist", }, + { "scripts", "mkstatus", }, } ----------------------------------------------------------------------- -- cgit v1.2.3 From 101bc5daa1e07744841732ba1eb2f54c7e1e8ea7 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 12 Feb 2014 08:03:41 +0100 Subject: [glyphs] print more useful error message --- scripts/mkglyphlist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mkglyphlist b/scripts/mkglyphlist index e660a57..8fde098 100755 --- a/scripts/mkglyphlist +++ b/scripts/mkglyphlist @@ -146,7 +146,7 @@ local get_raw get_raw = function (retry) if glyphdata then local fh = io.open(glyphfile, "wb") if not fh then - print"error: glyph file not writable" + print (string.format ("error: glyph file (%s) not writable", glyphfile)) os.exit(-1) end fh:write(glyphdata) -- cgit v1.2.3 From f507b6b19d8d24b79481b124adbf16206022c531 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 06:57:33 +0100 Subject: [db,*] move default blacklist to ./misc tree --- luaotfload-blacklist.cnf | 4 ---- misc/luaotfload-blacklist.cnf | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 luaotfload-blacklist.cnf create mode 100644 misc/luaotfload-blacklist.cnf diff --git a/luaotfload-blacklist.cnf b/luaotfload-blacklist.cnf deleted file mode 100644 index e82669b..0000000 --- a/luaotfload-blacklist.cnf +++ /dev/null @@ -1,4 +0,0 @@ -spltfgbd.ttf -spltfgbi.ttf -spltfgit.ttf -spltfgrg.ttf diff --git a/misc/luaotfload-blacklist.cnf b/misc/luaotfload-blacklist.cnf new file mode 100644 index 0000000..e82669b --- /dev/null +++ b/misc/luaotfload-blacklist.cnf @@ -0,0 +1,4 @@ +spltfgbd.ttf +spltfgbi.ttf +spltfgit.ttf +spltfgrg.ttf -- cgit v1.2.3 From f1dfd5b05f12b23479796d45dde36180f1dd01f4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 07:02:01 +0100 Subject: [*] adapt Makefile to new dir structure --- Makefile | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 689d9da..aa5e580 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,15 @@ # Makefile for luaotfload NAME = luaotfload -LUAOTFLOAD = $(wildcard luaotfload-*.lua) luaotfload-blacklist.cnf DOCSRCDIR = ./doc SCRIPTSRCDIR = ./scripts +SRCSRCDIR = ./src BUILDDIR = ./build +MISCDIR = ./misc + +SRC = $(wildcard $(SRCSRCDIR)/luaotfload-*.lua) +SRC = $(MISCDIR)/luaotfload-blacklist.cnf GLYPHSCRIPT = $(SCRIPTSRCDIR)/mkglyphlist CHARSCRIPT = $(SCRIPTSRCDIR)/mkcharacters @@ -15,17 +19,17 @@ GLYPHSOURCE = $(BUILDDIR)/glyphlist.txt RESOURCESCRIPTS = $(GLYPHSCRIPT) $(CHARSCRIPT) $(STATUSSCRIPT) -SCRIPTNAME = luaotfload-tool -SCRIPT = $(SCRIPTNAME).lua +TOOLNAME = luaotfload-tool +TOOL = $(SRCSRCDIR)/$(TOOLNAME).lua GRAPH = filegraph DOCSRC = $(DOCSRCDIR)/$(NAME).dtx GRAPHSRC = $(DOCSRCDIR)/$(GRAPH).dot -MANSRC = $(DOCSRCDIR)/$(SCRIPTNAME).rst +MANSRC = $(DOCSRCDIR)/$(TOOLNAME).rst DOCPDF = $(DOCSRCDIR)/$(NAME).pdf DOTPDF = $(DOCSRCDIR)/$(GRAPH).pdf -MANPAGE = $(DOCSRCDIR)/$(SCRIPTNAME).1 +MANPAGE = $(DOCSRCDIR)/$(TOOLNAME).1 DOCS = $(DOCPDF) $(DOTPDF) $(MANPAGE) @@ -34,21 +38,21 @@ GLYPHS = $(BUILDDIR)/$(NAME)-glyphlist.lua CHARS = $(BUILDDIR)/$(NAME)-characters.lua STATUS = $(BUILDDIR)/$(NAME)-status.lua RESOURCES = $(GLYPHS) $(CHARS) $(STATUS) -SOURCE = $(DOCSRC) $(MANSRC) $(LUAOTFLOAD) README Makefile NEWS $(RESOURCESCRIPTS) +SOURCE = $(DOCSRC) $(MANSRC) $(SRC) README Makefile NEWS $(RESOURCESCRIPTS) # Files grouped by installation location -SCRIPTSTATUS = $(SCRIPT) $(OLDSCRIPT) $(RESOURCESCRIPTS) -RUNSTATUS = $(filter-out $(SCRIPTSTATUS),$(LUAOTFLOAD)) +SCRIPTSTATUS = $(TOOL) $(RESOURCESCRIPTS) +RUNSTATUS = $(filter-out $(SCRIPTSTATUS),$(SRC)) DOCSTATUS = $(DOCPDF) $(DOTPDF) README NEWS MANSTATUS = $(MANPAGE) SRCSTATUS = $(DOCSRC) $(MANSRC) $(GRAPHSRC) Makefile # The following definitions should be equivalent # ALL_STATUS = $(RUNSTATUS) $(DOCSTATUS) $(SRCSTATUS) -ALL_STATUS = $(RESOURCES) $(SOURCE) +ALL_STATUS = $(RESOURCES) $(SOURCE) # Installation locations -FORMAT = luatex +FORMAT = luatex SCRIPTDIR = $(TEXMFROOT)/scripts/$(NAME) RUNDIR = $(TEXMFROOT)/tex/$(FORMAT)/$(NAME) DOCDIR = $(TEXMFROOT)/doc/$(FORMAT)/$(NAME) @@ -57,10 +61,10 @@ SRCDIR = $(TEXMFROOT)/source/$(FORMAT)/$(NAME) TEXMFROOT = $(shell kpsewhich --var-value TEXMFHOME) # CTAN-friendly subdirectory for packaging -DISTDIR = ./$(NAME) +DISTDIR = $(BUILDDIR)/$(NAME) -CTAN_ZIP = $(NAME).zip -TDS_ZIP = $(NAME).tds.zip +CTAN_ZIP = $(BUILDDIR)/$(NAME).zip +TDS_ZIP = $(BUILDDIR)/$(NAME).tds.zip ZIPS = $(CTAN_ZIP) $(TDS_ZIP) LUA = texlua @@ -74,9 +78,9 @@ DO_STATUS = $(LUA) $(STATUSSCRIPT) > /dev/null all: $(GENERATED) builddir: $(BUILDDIR) -resources: builddir $(RESOURCES) -chars: builddir $(CHARS) -status: builddir $(STATUS) +resources: $(RESOURCES) +chars: $(CHARS) +status: $(STATUS) ctan: $(CTAN_ZIP) tds: $(TDS_ZIP) world: all ctan @@ -95,13 +99,13 @@ $(DOCPDF): $(MANPAGE): @$(MAKE) -C $(DOCSRCDIR) manual -$(GLYPHS): /dev/null +$(GLYPHS): builddir $(DO_GLYPHS) -$(CHARS): /dev/null +$(CHARS): builddir $(DO_CHARS) -$(STATUS): /dev/null +$(STATUS): builddir $(DO_STATUS) $(BUILDDIR): /dev/null @@ -126,7 +130,7 @@ endef define run-install @mkdir -p $(SCRIPTDIR) && cp -- $(SCRIPTSTATUS) $(SCRIPTDIR) -@mkdir -p $(RUNDIR) && cp -- $(RUNSTATUS) $(RUNDIR) +@mkdir -p $(RUNDIR) && cp -- $(RUNSTATUS) $(RUNDIR) endef $(TDS_ZIP): TEXMFROOT=./tmp-texmf @@ -163,6 +167,6 @@ clean: mrproper: clean $(MAKE) -C $(DOCSRCDIR) $@ @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE) - @$(RM) -r -- $(DISTDIR) $(BUILDDIR) + @$(RM) -r -- $(BUILDDIR) -# vim:set noexpandtab:tabstop=8:shiftwidth=2 +# vim:noexpandtab:tabstop=8:shiftwidth=2 -- cgit v1.2.3 From abffcf7c6f9cb45ac0ceb4380a1b8a05c0327c84 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 07:06:10 +0100 Subject: [*] update news --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 6be91b4..b130f1d 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ Change History instead. * Remove luaotfload.lua from luaotfload.dtx; it is now a separate file luaotfload-main.lua. + * Standard source tree structure: the code is now located in the ./doc, + ./scripts, ./src, ./build, and ./misc directories. * Move the heavier LPEG parsers from luaotfload-features (syntax) and luaotfload-database (fontconfig) into the new file luaotfload-parsers.lua. -- cgit v1.2.3 From 6184dd4e08a8af39f02ab6bc44f3b56835d5c390 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 07:20:30 +0100 Subject: [build] fix Makefile rules for TDS zip --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index aa5e580..1fbc378 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,8 @@ BUILDDIR = ./build MISCDIR = ./misc SRC = $(wildcard $(SRCSRCDIR)/luaotfload-*.lua) -SRC = $(MISCDIR)/luaotfload-blacklist.cnf +SRC += $(SRCSRCDIR)/luaotfload.sty +SRC += $(MISCDIR)/luaotfload-blacklist.cnf GLYPHSCRIPT = $(SCRIPTSRCDIR)/mkglyphlist CHARSCRIPT = $(SCRIPTSRCDIR)/mkcharacters @@ -130,7 +131,7 @@ endef define run-install @mkdir -p $(SCRIPTDIR) && cp -- $(SCRIPTSTATUS) $(SCRIPTDIR) -@mkdir -p $(RUNDIR) && cp -- $(RUNSTATUS) $(RUNDIR) +@mkdir -p $(RUNDIR) && cp -- $(RESOURCES) $(RUNSTATUS) $(RUNDIR) endef $(TDS_ZIP): TEXMFROOT=./tmp-texmf -- cgit v1.2.3 From 06f156fb85fd63147b1ed51fce1bfe497d088fdc Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 08:03:12 +0100 Subject: [fontloader] sync with Context as of 2014-02-14 --- src/luaotfload-fontloader.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 8c31750..d475be6 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 02/07/14 00:57:35 +-- merge date : 02/13/14 11:27:58 do -- begin closure to overcome local limits and interference @@ -82,6 +82,7 @@ function optionalrequire(...) return result end end +lua.mask=load([[τεχ = 1]]) and "utf" or "ascii" end -- closure @@ -172,9 +173,11 @@ patterns.spacer=spacer patterns.whitespace=whitespace patterns.nonspacer=nonspacer patterns.nonwhitespace=nonwhitespace -local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0) local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0)) patterns.stripper=stripper +patterns.fullstripper=fullstripper patterns.collapser=collapser patterns.lowercase=lowercase patterns.uppercase=uppercase @@ -754,11 +757,15 @@ function string.limit(str,n,sentinel) end end local stripper=patterns.stripper +local fullstripper=patterns.fullstripper local collapser=patterns.collapser local longtostring=patterns.longtostring function string.strip(str) return lpegmatch(stripper,str) or "" end +function string.fullstrip(str) + return lpegmatch(fullstripper,str) or "" +end function string.collapsespaces(str) return lpegmatch(collapser,str) or "" end -- cgit v1.2.3 From 87aace07b0302f7124db092221483b5fce5b7755 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 08:15:09 +0100 Subject: [build] describe targets in Makefile (new top target: show) --- Makefile | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Makefile b/Makefile index 1fbc378..26de3bc 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,8 @@ DO_GLYPHS = $(LUA) $(GLYPHSCRIPT) > /dev/null DO_CHARS = $(LUA) $(CHARSCRIPT) > /dev/null DO_STATUS = $(LUA) $(STATUSSCRIPT) > /dev/null +show: showtargets + all: $(GENERATED) builddir: $(BUILDDIR) resources: $(RESOURCES) @@ -170,4 +172,24 @@ mrproper: clean @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE) @$(RM) -r -- $(BUILDDIR) +############################################################################### +showtargets: + @echo "Available targets:" + @echo + @echo " all build everything: documentation, resources," + @echo " world build everything and package zipballs" + @echo " doc compile PDF documentation" + @echo " resources generate resource files (chars, glyphs)" + @echo + @echo " pdf build luaotfload.pdf" + @echo " manual crate manpage for luaotfload-tool (requires Docutils)" + @echo " graph generate file graph (requires GraphViz)" + @echo + @echo " chars import char-def.lua as luaotfload-characters.lua" + @echo " status create repository info (luaotfload-status.lua)" + @echo + @echo " tds package a zipball according to the TDS" + @echo " ctan package a zipball for uploading to CTAN" + @echo + # vim:noexpandtab:tabstop=8:shiftwidth=2 -- cgit v1.2.3 From e50e69e3799204958692e925af79f8f19e017a35 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 18:22:59 +0100 Subject: [fontloader] sync with Context as of 2014-02-14 --- src/luaotfload-fontloader.lua | 127 +++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 40 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index d475be6..3f408b9 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 02/13/14 11:27:58 +-- merge date : 02/14/14 17:07:59 do -- begin closure to overcome local limits and interference @@ -82,7 +82,9 @@ function optionalrequire(...) return result end end -lua.mask=load([[τεχ = 1]]) and "utf" or "ascii" +if lua then + lua.mask=load([[τεχ = 1]]) and "utf" or "ascii" +end end -- closure @@ -2523,11 +2525,18 @@ local unpack,concat=table.unpack,table.concat local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc local patterns,lpegmatch=lpeg.patterns,lpeg.match local utfchar,utfbyte=utf.char,utf.byte -local loadstripped=function(str,shortcuts) - if shortcuts then - return load(dump(load(str),true),nil,nil,shortcuts) - else - return load(dump(load(str),true)) +local loadstripped=nil +if _LUAVERSION<5.2 then + loadstripped=function(str,shortcuts) + return load(str) + end +else + loadstripped=function(str,shortcuts) + if shortcuts then + return load(dump(load(str),true),nil,nil,shortcuts) + else + return load(dump(load(str),true)) + end end end if not number then number={} end @@ -2683,29 +2692,53 @@ local template=[[ %s return function(%s) return %s end ]] -local environment={ - global=global or _G, - lpeg=lpeg, - type=type, - tostring=tostring, - tonumber=tonumber, - format=string.format, - concat=table.concat, - signed=number.signed, - points=number.points, - basepoints=number.basepoints, - utfchar=utf.char, - utfbyte=utf.byte, - lpegmatch=lpeg.match, - nspaces=string.nspaces, - tracedchar=string.tracedchar, - autosingle=string.autosingle, - autodouble=string.autodouble, - sequenced=table.sequenced, - formattednumber=number.formatted, - sparseexponent=number.sparseexponent, -} -local preamble="" +local preamble,environment="",{} +if _LUAVERSION<5.2 then + preamble=[[ +local lpeg=lpeg +local type=type +local tostring=tostring +local tonumber=tonumber +local format=string.format +local concat=table.concat +local signed=number.signed +local points=number.points +local basepoints= number.basepoints +local utfchar=utf.char +local utfbyte=utf.byte +local lpegmatch=lpeg.match +local nspaces=string.nspaces +local tracedchar=string.tracedchar +local autosingle=string.autosingle +local autodouble=string.autodouble +local sequenced=table.sequenced +local formattednumber=number.formatted +local sparseexponent=number.sparseexponent + ]] +else + environment={ + global=global or _G, + lpeg=lpeg, + type=type, + tostring=tostring, + tonumber=tonumber, + format=string.format, + concat=table.concat, + signed=number.signed, + points=number.points, + basepoints=number.basepoints, + utfchar=utf.char, + utfbyte=utf.byte, + lpegmatch=lpeg.match, + nspaces=string.nspaces, + tracedchar=string.tracedchar, + autosingle=string.autosingle, + autodouble=string.autodouble, + sequenced=table.sequenced, + formattednumber=number.formatted, + sparseexponent=number.sparseexponent, + } +end local arguments={ "a1" } setmetatable(arguments,{ __index=function(t,k) local v=t[k-1]..",a"..k @@ -3052,14 +3085,22 @@ local function use(t,fmt,...) return t[fmt](...) end strings.formatters={} -function strings.formatters.new() - local e={} - for k,v in next,environment do - e[k]=v +if _LUAVERSION<5.2 then + function strings.formatters.new() + local t={ _extensions_={},_preamble_=preamble,_environment_={},_type_="formatter" } + setmetatable(t,{ __index=make,__call=use }) + return t + end +else + function strings.formatters.new() + local e={} + for k,v in next,environment do + e[k]=v + end + local t={ _extensions_={},_preamble_="",_environment_=e,_type_="formatter" } + setmetatable(t,{ __index=make,__call=use }) + return t end - local t={ _extensions_={},_preamble_="",_environment_=e,_type_="formatter" } - setmetatable(t,{ __index=make,__call=use }) - return t end local formatters=strings.formatters.new() string.formatters=formatters @@ -3081,9 +3122,15 @@ patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"" patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) -add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) -add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) -add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) +if _LUAVERSION<5.2 then + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") + add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape") +else + add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) + add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) + add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) +end end -- closure -- cgit v1.2.3 From aafd73e8a91f986f011338301adac09f046b745f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 19:17:28 +0100 Subject: [build] declare show and showtargets .PHONY targets --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 26de3bc..d00b237 100644 --- a/Makefile +++ b/Makefile @@ -145,7 +145,7 @@ $(TDS_ZIP): $(DOCS) $(ALL_STATUS) @cd $(TEXMFROOT) && zip -9 ../$@ -r . >/dev/null @$(RM) -r -- $(TEXMFROOT) -.PHONY: install manifest clean mrproper +.PHONY: install manifest clean mrproper show showtargets install: $(ALL_STATUS) @echo "Installing in '$(TEXMFROOT)'." -- cgit v1.2.3 From 0c7e35163064e7d40cddbad129745060d9666ee6 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 21:50:50 +0100 Subject: =?UTF-8?q?[db]=20add=20=E2=80=9Clocal=E2=80=9D=20field=20to=20met?= =?UTF-8?q?a=20table=20in=20db?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/luaotfload-database.lua | 35 ++++++++++++++++++++++------------- src/luaotfload-tool.lua | 2 +- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 4b2d201..54474e2 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -140,7 +140,7 @@ luaotfloadconfig.compress = luaotfloadconfig.compress ~= false local names = fonts.names local name_index = nil --> upvalue for names.data local lookup_cache = nil --> for names.lookups -names.version = 2.5 +names.version = 2.51 names.data = nil --- contains the loaded database names.lookups = nil --- contains the lookup cache @@ -356,6 +356,7 @@ This is a sketch of the luaotfload db: optical : (int, int) list; // design size -> index entry } and metadata = { + local : bool; (* set if local fonts were added to the db *) formats : string list; // { "otf", "ttf", "ttc", "dfont" } statistics : TODO; version : float; @@ -439,7 +440,9 @@ mtx-fonts has in names.tma: --doc]]-- -local initialize_namedata = function (formats) --- returns dbobj +--- string list -> dbobj + +local initialize_namedata = function (formats) return { --families = { }, status = { }, -- was: status; map abspath -> mapping @@ -447,6 +450,7 @@ local initialize_namedata = function (formats) --- returns dbobj names = { }, -- files = { }, -- created later meta = { + ["local"] = false, formats = formats, statistics = { }, version = names.version, @@ -2380,11 +2384,11 @@ local scan_os_fonts = function (currentnames, return n_scanned, n_new end ---- unit -> (bool, lookup_cache) +--- unit -> bool flush_lookup_cache = function () lookup_cache = { } collectgarbage "collect" - return true, lookup_cache + return true end @@ -3164,16 +3168,16 @@ update_names = function (currentnames, force, dry_run) if dry_run ~= true then - save_names () + local success, reason = save_names () + if not success then + report ("both", 0, "db", + "Failed to save database to disk: %s", + reason) + end - local success, _lookups = flush_lookup_cache () - if success then - local success = save_lookups () - if success then - report ("info", 2, "cache", - "Lookup cache emptied.") - return targetnames - end + if flush_lookup_cache () and save_lookups () then + report ("both", 2, "cache", "Lookup cache emptied.") + return targetnames end end return targetnames @@ -3211,6 +3215,11 @@ save_names = function (currentnames) if not currentnames then currentnames = name_index end + if not currentnames or type (currentnames) ~= "table" then + return false, "invalid names table" + elseif currentnames.meta and currentnames.meta["local"] then + return false, "table contains local entries" + end local path = names.path.index local luaname, lucname = path.lua, path.luc if fileiswritable (luaname) and fileiswritable (lucname) then diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 35765b5..01d0361 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -772,7 +772,7 @@ actions.generate = function (job) end actions.flush = function (job) - local success, lookups = names.flush_lookup_cache() + local success = names.flush_lookup_cache() if success then local success = names.save_lookups() if success then -- cgit v1.2.3 From 54269ef7c476dc4952ea2e6c08b6e2bfa9f68ba5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 22:41:20 +0100 Subject: [db,tool] support scanning local fonts into db --- src/luaotfload-database.lua | 67 +++++++++++++++++++++++++++++++++++---------- src/luaotfload-tool.lua | 20 ++++++++++---- 2 files changed, 66 insertions(+), 21 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 54474e2..06a3dea 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -553,6 +553,10 @@ local save_lookups local update_names local get_font_filter local set_font_filter +local generate_filedata +local collect_families +local group_modifiers +local order_design_sizes --- state of the database local fonts_reloaded = false @@ -572,7 +576,7 @@ load_names = function (dry_run) report ("info", 3, "db", "Loading took %0.f ms.", 1000 * (osgettimeofday () - starttime)) - local db_version, nms_version + local db_version, names_version if data.meta then db_version = data.meta.version else @@ -581,11 +585,11 @@ load_names = function (dry_run) --- an earlier index version broke. db_version = data.version or -42 --- invalid end - nms_version = names.version - if db_version ~= nms_version then + names_version = names.version + if db_version ~= names_version then report ("both", 0, "db", [[Version mismatch; expected %4.3f, got %4.3f.]], - nms_version, db_version) + names_version, db_version) if not fonts_reloaded then report ("both", 0, "db", [[Force rebuild.]]) data = update_names ({ }, true, false) @@ -2211,7 +2215,7 @@ local scan_dir = function (dirname, currentnames, targetnames, --- ignore return 0, 0 end - local found = find_font_files (dirname, location ~= "texmf") + local found = find_font_files (dirname, location ~= "texmf" and location ~= "local") if not found then report ("both", 4, "db", "No such directory: %q; skipping.", dirname) @@ -2391,10 +2395,42 @@ flush_lookup_cache = function () return true end +--[[doc-- + + scan_local_fonts() -- Scan font files in $PWD (during a TeX run) + and add them to the database. + + This sets the “local” flag in the subtable “meta” to prevent the + merged table from being saved to disk. + + TODO the local tree could be cached in $PWD. + +--doc]]-- + +local scan_local_fonts = function () + local n_scanned, n_new = 0, 0 + local pwd = lfscurrentdir () + local name_index = name_index + report ("both", 1, "db", "Scanning fonts in $PWD (%q) ...", pwd) + + n_scanned, n_new = scan_dir (pwd, name_index, name_index, false, "local") + if n_new > 0 then + name_index.files = generate_filedata (name_index.mappings) + name_index.families = collect_families (name_index.mappings) + name_index.families = group_modifiers (name_index.mappings, + name_index.families) + name_index.families = order_design_sizes (name_index.families) + name_index.meta["local"] = true --- prevent saving to disk + end + + return n_scanned, n_new +end + +--- dbobj -> dbobj -> int * int --- fontentry list -> filemap -local generate_filedata = function (mappings) +generate_filedata = function (mappings) report ("both", 2, "db", "Creating filename map.") @@ -2672,7 +2708,7 @@ local get_subtable = function (families, entry) return subtable end -local collect_families = function (mappings) +collect_families = function (mappings) report ("info", 2, "db", "Analyzing families.") @@ -2769,7 +2805,7 @@ local bold_weight = 700 local style_categories = { "r", "b", "i", "bi" } local bold_categories = { "b", "bi" } -local group_modifiers = function (mappings, families) +group_modifiers = function (mappings, families) report ("info", 2, "db", "Analyzing shapes, weights, and styles.") for location, location_data in next, families do for format, format_data in next, location_data do @@ -2867,7 +2903,7 @@ local cmp_sizes = function (a, b) return a [1] < b [1] end -local order_design_sizes = function (families) +order_design_sizes = function (families) report ("info", 2, "db", "Ordering design sizes.") @@ -3127,11 +3163,11 @@ update_names = function (currentnames, force, dry_run) read_blacklist () - local n_raw, n_new= retrieve_namedata (currentnames, - targetnames, - dry_run, - n_rawnames, - n_newnames) + local n_raw, n_new = retrieve_namedata (currentnames, + targetnames, + dry_run, + n_rawnames, + n_newnames) report ("info", 3, "db", "Scanned %d font files; %d new entries.", n_rawnames, n_newnames) @@ -3210,7 +3246,7 @@ save_lookups = function ( ) end --- save_names() is usually called without the argument ---- dbobj? -> bool +--- dbobj? -> bool * string option save_names = function (currentnames) if not currentnames then currentnames = name_index @@ -3430,6 +3466,7 @@ names.read_blacklist = read_blacklist names.sanitize_fontname = sanitize_fontname names.getfilename = resolve_fullpath names.set_location_precedence = set_location_precedence +names.scan_local_fonts = scan_local_fonts --- font cache names.purge_cache = purge_cache diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 01d0361..9f00956 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -723,9 +723,9 @@ set. --]]-- local action_sequence = { - "loglevel", "help", "version", "diagnose", - "blacklist", "cache", "flush", "generate", - "list", "query", + "loglevel", "help", "version", "diagnose", + "blacklist", "cache", "flush", "generate", + "scan_local", "list", "query", } local action_pending = tabletohash(action_sequence, false) @@ -762,8 +762,7 @@ actions.blacklist = function (job) end actions.generate = function (job) - local fontnames, savedname - fontnames = names.update(fontnames, job.force_reload, job.dry_run) + local fontnames = names.update(fontnames, job.force_reload, job.dry_run) report ("info", 2, "db", "Fonts in the database: %i", #fontnames.mappings) if names.data() then return true, true @@ -771,6 +770,11 @@ actions.generate = function (job) return false, false end +actions.scan_local = function (job) + names.scan_local_fonts () + return true, true +end + actions.flush = function (job) local success = names.flush_lookup_cache() if success then @@ -1094,6 +1098,7 @@ local process_cmdline = function ( ) -- unit -> jobspec inspect = "I", limit = 1, list = 1, + ["local"] = "L", log = 1, ["max-fonts"] = 1, ["no-reload"] = "n", @@ -1109,7 +1114,7 @@ local process_cmdline = function ( ) -- unit -> jobspec warnings = "w", } - local short_options = "bcDfFiIlnpqRSuvVhw" + local short_options = "bcDfFiIlLnpqRSuvVhw" local options, _, optarg = alt_getopt.get_ordered_opts (arg, short_options, long_options) @@ -1167,6 +1172,9 @@ local process_cmdline = function ( ) -- unit -> jobspec result.full_info = true elseif v == "l" then action_pending["flush"] = true + elseif v == "L" then + action_pending["generate"] = true + action_pending["scan_local"] = true elseif v == "list" then action_pending["list"] = true result.criterion = optarg[n] -- cgit v1.2.3 From fa25005e7ad5ffc537b5aaf39d1b3562daf8f5eb Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 23:01:00 +0100 Subject: [db,tool] make local font scan part of update_names() --- src/luaotfload-database.lua | 41 +++++++++++++++++++++-------------------- src/luaotfload-tool.lua | 2 +- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 06a3dea..54a0398 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -535,28 +535,28 @@ local load_lua_file = function (path) end --- define locals in scope +local collect_families local crude_file_lookup local crude_file_lookup_verbose local find_closest local flush_lookup_cache -local ot_fullinfo -local t1_fullinfo -local load_names +local generate_filedata +local get_font_filter +local group_modifiers local load_lookups +local load_names +local order_design_sizes +local ot_fullinfo local read_blacklist local reload_db -local resolve_name local resolve_cached local resolve_fullpath -local save_names +local resolve_name local save_lookups -local update_names -local get_font_filter +local save_names local set_font_filter -local generate_filedata -local collect_families -local group_modifiers -local order_design_sizes +local t1_fullinfo +local update_names --- state of the database local fonts_reloaded = false @@ -2407,20 +2407,16 @@ end --doc]]-- -local scan_local_fonts = function () +local scan_local_fonts = function (currentnames, + targetnames, + dry_run) local n_scanned, n_new = 0, 0 local pwd = lfscurrentdir () - local name_index = name_index report ("both", 1, "db", "Scanning fonts in $PWD (%q) ...", pwd) - n_scanned, n_new = scan_dir (pwd, name_index, name_index, false, "local") + n_scanned, n_new = scan_dir (pwd, currentnames, targetnames, false, "local") if n_new > 0 then - name_index.files = generate_filedata (name_index.mappings) - name_index.families = collect_families (name_index.mappings) - name_index.families = group_modifiers (name_index.mappings, - name_index.families) - name_index.families = order_design_sizes (name_index.families) - name_index.meta["local"] = true --- prevent saving to disk + targetnames.meta["local"] = true --- prevent saving to disk end return n_scanned, n_new @@ -2938,6 +2934,11 @@ local retrieve_namedata = function (currentnames, n_rawnames = n_rawnames + rawnames n_newnames = n_newnames + new + rawnames, new = scan_local_fonts (currentnames, targetnames, dry_run) + + n_rawnames = n_rawnames + rawnames + n_newnames = n_newnames + new + return n_rawnames, n_newnames end diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 9f00956..d405dc7 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -1174,7 +1174,7 @@ local process_cmdline = function ( ) -- unit -> jobspec action_pending["flush"] = true elseif v == "L" then action_pending["generate"] = true - action_pending["scan_local"] = true + luaotfloadconfig.scan_local = true elseif v == "list" then action_pending["list"] = true result.criterion = optarg[n] -- cgit v1.2.3 From 93d0150fdba1a1e212aafeb4e16faef1cba82d60 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 23:15:37 +0100 Subject: =?UTF-8?q?[db,main,tool]=20scan=20local=20fonts=20upon=20failed?= =?UTF-8?q?=20lookup=20during=20a=20TeX=20run=20(requires=20the=20config?= =?UTF-8?q?=20flag=20=E2=80=9Cscan=5Flocal=E2=80=9D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/luaotfload-database.lua | 42 ++++++++++++++++++++++-------------------- src/luaotfload-main.lua | 5 +++-- src/luaotfload-tool.lua | 11 +++-------- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 54a0398..923f7c5 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -1171,9 +1171,6 @@ resolve_name = function (specification) name, style) end - if not resolved then - resolved = specification.name, false - end if not resolved then if not fonts_reloaded then @@ -2934,10 +2931,12 @@ local retrieve_namedata = function (currentnames, n_rawnames = n_rawnames + rawnames n_newnames = n_newnames + new - rawnames, new = scan_local_fonts (currentnames, targetnames, dry_run) + if luaotfloadconfig.scan_local == true then + rawnames, new = scan_local_fonts (currentnames, targetnames, dry_run) - n_rawnames = n_rawnames + rawnames - n_newnames = n_newnames + new + n_rawnames = n_rawnames + rawnames + n_newnames = n_newnames + new + end return n_rawnames, n_newnames end @@ -3114,8 +3113,8 @@ end --- dbobj? -> bool? -> bool? -> dbobj update_names = function (currentnames, force, dry_run) - local targetnames + local n_raw, n_new = 0, 0 if luaotfloadconfig.update_live == false then report ("info", 2, "db", @@ -3124,8 +3123,7 @@ update_names = function (currentnames, force, dry_run) return currentnames or name_index end - local starttime = osgettimeofday () - local n_rawnames, n_newnames = 0, 0 + local starttime = osgettimeofday () --[[ The main function, scans everything @@ -3164,14 +3162,14 @@ update_names = function (currentnames, force, dry_run) read_blacklist () - local n_raw, n_new = retrieve_namedata (currentnames, - targetnames, - dry_run, - n_rawnames, - n_newnames) + n_raw, n_new = retrieve_namedata (currentnames, + targetnames, + dry_run, + 0, + 0) report ("info", 3, "db", "Scanned %d font files; %d new entries.", - n_rawnames, n_newnames) + n_raw, n_new) end --- pass 2 (optional): collect some stats about the raw font info @@ -3205,11 +3203,15 @@ update_names = function (currentnames, force, dry_run) if dry_run ~= true then - local success, reason = save_names () - if not success then - report ("both", 0, "db", - "Failed to save database to disk: %s", - reason) + if n_new == 0 then + report ("info", 2, "db", "No new fonts found, skip saving to disk.") + else + local success, reason = save_names () + if not success then + report ("both", 0, "db", + "Failed to save database to disk: %s", + reason) + end end if flush_lookup_cache () and save_lookups () then diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index f5f012d..ed7fdd3 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -1,10 +1,10 @@ ----------------------------------------------------------------------- -- FILE: luaotfload-main.lua -- DESCRIPTION: Luatex fontloader initialization --- REQUIREMENTS: luatex v.0.78 or later, the lualibs package +-- REQUIREMENTS: luatex v.0.78 or later; the lualibs package -- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang -- VERSION: same as Luaotfload --- MODIFIED: 2014-02-09 14:42:22+0100 +-- MODIFIED: 2014-02-14 22:51:09+0100 ----------------------------------------------------------------------- -- --- Note: @@ -59,6 +59,7 @@ config.luaotfload.names_dir = config.luaotfload.names_dir or "names config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts" config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua" config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" +config.luaotfload.scan_local = config.luaotfload.scan_local == true if not config.luaotfload.strip then config.luaotfload.strip = true diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index d405dc7..f9b9b94 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -723,9 +723,9 @@ set. --]]-- local action_sequence = { - "loglevel", "help", "version", "diagnose", - "blacklist", "cache", "flush", "generate", - "scan_local", "list", "query", + "loglevel", "help", "version", "diagnose", + "blacklist", "cache", "flush", "generate", + "list", "query", } local action_pending = tabletohash(action_sequence, false) @@ -770,11 +770,6 @@ actions.generate = function (job) return false, false end -actions.scan_local = function (job) - names.scan_local_fonts () - return true, true -end - actions.flush = function (job) local success = names.flush_lookup_cache() if success then -- cgit v1.2.3 From 41a2ac387b7cbc1d724fd0016a156c4b210ec07e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 23:17:02 +0100 Subject: [*] update news --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index b130f1d..1966c03 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,8 @@ Change History luaotfload-database (fontconfig) into the new file luaotfload-parsers.lua. * Move logging routines from luaotfload-override in to luaotfload-log. + * Scan local font files (``--local`` flag to luaotfload-tool, flag + ``scan_local`` during TeX run). 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) -- cgit v1.2.3 From d93945a4fe46d821078fae758e76b0b0770b2d44 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Feb 2014 23:25:42 +0100 Subject: =?UTF-8?q?[tool,doc]=20document=20=E2=80=9C--local=E2=80=9D=20opt?= =?UTF-8?q?ion=20in=20manual=20and=20usage=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/luaotfload-tool.rst | 8 ++++++++ src/luaotfload-tool.lua | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/luaotfload-tool.rst b/doc/luaotfload-tool.rst index 6863918..761b0ec 100644 --- a/doc/luaotfload-tool.rst +++ b/doc/luaotfload-tool.rst @@ -21,6 +21,7 @@ SYNOPSIS [ --prefer-texmf ] [ --dry-run ] [ --formats=[+|-]EXTENSIONS ] [ --no-compress ] [ --no-strip ] + [ --local ] **luaotfload-tool** --find=FONTNAME [ --fuzzy ] [ --info ] [ --inspect ] [ --no-reload ] @@ -57,6 +58,13 @@ update mode --update, -u Update the database; indexes new fonts. --force, -f Force rebuilding of the database; re-indexes all fonts. +--local, -L Include font files in ``$PWD``. This option + will cause large parts of the database to be + rebuilt. Thus it is quite inefficient. + Additionally, if local font files are found, + the database is prevented from being saved + to disk, so the local fonts need to be parsed + with every invocation of ``luaotfload-tool``. --no-reload, -n Suppress auto-updates to the database (e.g. when ``--find`` is passed an unknown name). --no-strip Do not strip redundant information after diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index f9b9b94..9e75944 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -193,6 +193,7 @@ Usage: %s [OPTIONS...] -n --no-reload suppress db update --no-strip keep redundant information in db -f --force force re-indexing all fonts + -L --local scan font files in $PWD -c --no-compress do not gzip index file (text version only) -l --flush-lookups empty lookup cache of font requests -D --dry-run skip loading of fonts, just scan @@ -236,7 +237,7 @@ update your scripts and/or habits! Kthxbye. ]], short = [[ Usage: luaotfload-tool [--help] [--version] [--verbose=] - [--update] [--force] [--prefer-texmf] + [--update] [--force] [--prefer-texmf] [--local] [--dry-run] [--formats=] [--find=] [--fuzzy] [--info] [--inspect] [--list=] [--fields=] -- cgit v1.2.3 From 91ea82ef8644f19a303674611c4ce9bfc47f1274 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 15 Feb 2014 15:37:14 +0100 Subject: [doc] kill dtx The file ``luaotfload.dtx`` which made the documentation a huge PITA to extend, has finally been converted to ordinary Latex format. In the course of this conversion the style directives have been moved to file (``luaotfload-latex.tex``) so as to separate them from the content (``luaotfload-main.tex``). --- doc/luaotfload-latex.tex | 132 +++ doc/luaotfload-main.tex | 1944 ++++++++++++++++++++++++++++++++++++++++++++ doc/luaotfload.dtx | 1994 ---------------------------------------------- 3 files changed, 2076 insertions(+), 1994 deletions(-) create mode 100644 doc/luaotfload-latex.tex create mode 100644 doc/luaotfload-main.tex delete mode 100644 doc/luaotfload.dtx diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex new file mode 100644 index 0000000..615ad42 --- /dev/null +++ b/doc/luaotfload-latex.tex @@ -0,0 +1,132 @@ +%% Copyright (C) 2009-2014 +%% +%% by Elie Roux +%% and Khaled Hosny +%% and Philipp Gesang +%% +%% This file is part of Luaotfload. +%% +%% Home: https://github.com/lualatex/luaotfload +%% Support: . +%% +%% Luaotfload is under the GPL v2.0 (exactly) license. +%% +%% ---------------------------------------------------------------------------- +%% +%% Luaotfload is free software; you can redistribute it and/or +%% modify it under the terms of the GNU General Public License +%% as published by the Free Software Foundation; version 2 +%% of the License. +%% +%% Luaotfload is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU General Public License +%% along with Luaotfload; if not, see . +%% +%% ---------------------------------------------------------------------------- +%% + +\documentclass{ltxdoc} + +\usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace} +\usepackage[x11names]{xcolor} + +\def\primarycolor{DodgerBlue4} %%-> rgb 16 78 139 | #104e8b +\def\secondarycolor{Goldenrod4} %%-> rgb 139 105 200 | #8b6914 + +\usepackage[ + bookmarks=true, + colorlinks=true, + linkcolor=\primarycolor, + urlcolor=\secondarycolor, + citecolor=\primarycolor, + pdftitle={The luaotfload package}, + pdfsubject={OpenType layout system for Plain TeX and LaTeX}, + pdfauthor={Elie Roux & Khaled Hosny & Philipp Gesang}, + pdfkeywords={luatex, lualatex, unicode, opentype} +]{hyperref} + +\usepackage{fontspec} +\usepackage{unicode-math} + +\setmainfont[ +% Numbers = OldStyle, %% buggy with font cache + Ligatures = TeX, + BoldFont = {Linux Libertine O Bold}, + ItalicFont = {Linux Libertine O Italic}, + SlantedFont = {Linux Libertine O Italic}, +]{Linux Libertine O} +\setmonofont[Ligatures=TeX,Scale=MatchLowercase]{Liberation Mono} +%setsansfont[Ligatures=TeX]{Linux Biolinum O} +\setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium} +%setmathfont{XITS Math} + +\usepackage{hologo} + +\newcommand\TEX {\TeX\xspace} +\newcommand\LUA {Lua\xspace} +\newcommand\PDFTEX {pdf\TeX\xspace} +\newcommand\LUATEX {Lua\TeX\xspace} +\newcommand\XETEX {\XeTeX\xspace} +\newcommand\LATEX {\LaTeX\xspace} +\newcommand\LUALATEX {Lua\LaTeX\xspace} +\newcommand\CONTEXT {Con\TeX t\xspace} +\newcommand\OpenType {\identifier{Open\kern-.25ex Type}\xspace} + +\def\definehighlight[#1][#2]% + {\ifcsname #1\endcsname\else + \expandafter\def\csname #1\endcsname% + {\bgroup#2\csname #1_indeed\endcsname} + \expandafter\def\csname #1_indeed\endcsname##1% + {##1\egroup}% + \fi} + +\def\restoreunderscore{\catcode`\_=12\relax} + +\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs +\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs +\definehighlight[luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers +\definehighlight [identifier][\sffamily] %% names +\definehighlight [abbrev][\rmfamily\scshape] %% acronyms +\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph + +\newcommand*\email[1]{\href{mailto:#1}{#1}} + +\renewcommand\partname{Part}%% gets rid of the stupid “file” heading + +\usepackage{syntax}%% bnf for font request syntax + +\usepackage{titlesec} + +\def\movecountertomargin#1{\llap{\rmfamily\upshape#1\hskip2em}} +\def\zeropoint{0pt} +\titleformat \part + {\normalsize\rmfamily\bfseries} + {\movecountertomargin\thepart} \zeropoint {} +\titleformat \section + {\normalsize\rmfamily\scshape} + {\movecountertomargin\thesection} \zeropoint {} +\titleformat \subsection + {\small\rmfamily\itshape} + {\movecountertomargin\thesubsection} \zeropoint {} +\titleformat \subsubsection + {\normalsize\rmfamily\upshape} + {\movecountertomargin\thesubsubsection} \zeropoint {} + +\usepackage{tocloft} +\renewcommand \cftpartfont {\rmfamily\upshape} +\renewcommand \cftsecfont {\rmfamily\upshape} +\renewcommand \cftsubsecfont {\rmfamily\upshape} +\setlength \cftbeforepartskip {1ex} +\setlength \cftbeforesecskip {1ex} + +\VerbatimFootnotes + +\begin {document} + \input {luaotfload-main.tex} +\end {document} + + diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex new file mode 100644 index 0000000..49d1986 --- /dev/null +++ b/doc/luaotfload-main.tex @@ -0,0 +1,1944 @@ +%% Copyright (C) 2009-2014 +%% +%% by Elie Roux +%% and Khaled Hosny +%% and Philipp Gesang +%% +%% This file is part of Luaotfload. +%% +%% Home: https://github.com/lualatex/luaotfload +%% Support: . +%% +%% Luaotfload is under the GPL v2.0 (exactly) license. +%% +%% ---------------------------------------------------------------------------- +%% +%% Luaotfload is free software; you can redistribute it and/or +%% modify it under the terms of the GNU General Public License +%% as published by the Free Software Foundation; version 2 +%% of the License. +%% +%% Luaotfload is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU General Public License +%% along with Luaotfload; if not, see . +%% +%% ---------------------------------------------------------------------------- +%% + +\title{The \identifier{luaotfload} package} +\date{2014/**/** v2.5} +\author{Elie Roux · Khaled Hosny · Philipp Gesang\\ + Home: \url {https://github.com/lualatex/luaotfload}\\ + Support: \email {lualatex-dev@tug.org}} + +\maketitle + +\begin{abstract} + This package is an adaptation of the \CONTEXT font loading system. + It allows for loading \OpenType fonts with an extended syntax and adds + support for a variety of font features. +\end{abstract} + + +\tableofcontents + +\part{Package Description} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Introduction} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Font management and installation has always been painful with \TEX. A +lot of files are needed for one font (\abbrev{tfm}, \abbrev{pfb}, +\abbrev{map}, \abbrev{fd}, \abbrev{vf}), and due to the 8-Bit encoding +each font is limited to 256 characters. + +But the font world has evolved since the original \TEX, and new +typographic systems have appeared, most notably the so called +\emphasis{smart font} technologies like \OpenType fonts (\abbrev{otf}). + +These fonts can contain many more characters than \TEX fonts, as well +as additional functionality like ligatures, old-style numbers, small +capitals, etc., and support more complex writing systems like Arabic +and Indic\footnote{% + Unfortunately, \identifier{luaotfload} doesn‘t support many Indic + scripts right now. + Assistance in implementing the prerequisites is greatly + appreciated. +} +scripts. + +\OpenType fonts are widely deployed and available for all modern +operating systems. + +As of 2013 they have become the de facto standard for advanced text +layout. + +However, until recently the only way to use them directly in the \TEX +world was with the \XETEX engine. + +Unlike \XETEX, \LUATEX has no built-in support for \OpenType or +technologies other than the original \TEX fonts. + +Instead, it provides hooks for executing \LUA code during the \TEX run +that allow implementing extensions for loading fonts and manipulating +how input text is processed without modifying the underlying engine. + +This is where \identifier{luaotfload} comes into play: +Based on code from \CONTEXT, it extends \LUATEX with functionality necessary +for handling \OpenType fonts. + +Additionally, it provides means for accessing fonts known to the operating +system conveniently by indexing the metadata. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Thanks} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\identifier{Luaotfload} is part of \LUALATEX, the community-driven +project to provide a foundation for using the \LATEX format with the +full capabilites of the \LUATEX engine. +% +As such, the distinction between end users, contributors, and project +maintainers is intentionally kept less strict, lest we unduly +personalize the common effort. + +Nevertheless, the current maintainers would like to express their +gratitude to Khaled Hosny, Akira Kakuto, Hironori Kitagawa and Dohyun +Kim. +% +Their contributions -- be it patches, advice, or systematic +testing -- made the switch from version 1.x to 2.2 possible. +% +Also, Hans Hagen, the author of the font loader, made porting the +code to \LATEX a breeze due to the extra effort he invested into +isolating it from the rest of \CONTEXT, not to mention his assistance +in the task and willingness to respond to our suggestions. + + +\section{Loading Fonts} + +\identifier{luaotfload} supports an extended font request syntax: + +\begin{quote} + |\font\foo={|% + \meta{prefix}|:|% + \meta{font name}|:|% + \meta{font features}|}|% + \meta{\TEX font features} +\end{quote} + +\noindent +The curly brackets are optional and escape the spaces in the enclosed +font name. +% +Alternatively, double quotes serve the same purpose. +% +A selection of individual parts of the syntax are discussed below; +for a more formal description see figure \ref{font-syntax}. + +\begin {figure} [b] + \setlength\grammarparsep{12pt plus 2pt minus 2pt} + \setlength\grammarindent{5cm} + \begingroup + \small + \begin{grammar} + ::= `\\font', {\sc csname}, `=', , [ ] ; + + ::= `at', {\sc dimension} ; + + ::= `"', `"' + \alt `{', `}' + \alt ; + + ::= , [`:', ] + \alt `[', `]', [ [`:'], ] ; + + ::= , [ ], \{ \} + \alt , \{ \} ; + + ::= `file:', + \alt `name:', ; + + ::= \{ \} ; + + ::= \{ \} ; + + ::= {\sc tfmname} | ; + + ::= \{ {\sc all_characters} - `]' \} ; + + ::= `/', (`I' | `B' | `BI' | `IB' | `S=', \{ {\sc digit} \} ) ; + + ::= `(', \{ {\sc digit} \}, `)' ; + + ::= , \{ `;', \} ; + + ::= {\sc feature_id}, `=', {\sc feature_value} + \alt , {\sc feature_id} ; + + ::= `+' | `-' ; + + ::= {\sc all_characters} - ( `(' | `/' | `:' ) ; + \end{grammar} + \endgroup + \caption{Font request syntax. + Braces or double quotes around the + \emphasis{specification} rule will + preserve whitespace in file names. + In addition to the font style modifiers + (\emphasis{slash-notation}) given above, there + are others that are recognized but will be silently + ignored: {\ttfamily aat}, + {\ttfamily icu}, and + {\ttfamily gr}. + The special terminals are: + {\sc feature\textunderscore id} for a valid font + feature name and + {\sc feature\textunderscore value} for the corresponding + value. + {\sc tfmname} is the name of a \abbrev{tfm} file. + {\sc digit} again refers to bytes 48--57, and + {\sc all\textunderscore characters} to all byte values. + {\sc csname} and {\sc dimension} are the \TEX concepts.} + \label{font-syntax} +\end {figure} + +\subsection{Prefix -- the \identifier{luaotfload}{ }Way} + +In \identifier{luaotfload}, the canonical syntax for font requests +requires a \emphasis{prefix}: +% +\begin{quote} + |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots +\end{quote} +% +where \meta{prefix} is either \verb|file:| or \verb|name:|.\footnote{% + The development version also knows two further prefixes, + \verb|kpse:| and \verb|my:|. + % + A \verb|kpse| lookup is restricted to files that can be found by + \identifier{kpathsea} and + will not attempt to locate system fonts. + % + This behavior can be of value when an extra degree of encapsulation is + needed, for instance when supplying a customized tex distribution. + + The \verb|my| lookup takes this a step further: it lets you define + a custom resolver function and hook it into the \luafunction{resolve_font} + callback. + % + This ensures full control over how a file is located. + % + For a working example see the + \href{https://bitbucket.org/phg/lua-la-tex-tests/src/5f6a535d/pln-lookup-callback-1.tex} + {test repo}. +} +% +It determines whether the font loader should interpret the request as +a \emphasis{file name} or + \emphasis{font name}, respectively, +which again influences how it will attempt to locate the font. +% +Examples for font names are + “Latin Modern Italic”, + “GFS Bodoni Rg”, and + “PT Serif Caption” +-- they are the human readable identifiers +usually listed in drop-down menus and the like.\footnote{% + Font names may appear like a great choice at first because they + offer seemingly more intuitive identifiers in comparison to arguably + cryptic file names: + % + “PT Sans Bold” is a lot more descriptive than \fileent{PTS75F.ttf}. + On the other hand, font names are quite arbitrary and there is no + universal method to determine their meaning. + % + While \identifier{luaotfload} provides fairly sophisticated heuristic + to figure out a matching font style, weight, and optical size, it + cannot be relied upon to work satisfactorily for all font files. + % + For an in-depth analysis of the situation and how broken font names + are, please refer to + \href{http://www.ntg.nl/pipermail/ntg-context/2013/073889.html} + {this post} + by Hans Hagen, the author of the font loader. + % + If in doubt, use filenames. + % + \fileent{luaotfload-tool} can perform the matching for you with the + option \verb|--find=|, and you can use the file name it returns + in your font definition. +} +% +In order for fonts installed both in system locations and in your +\fileent{texmf} to be accessible by font name, \identifier{luaotfload} must +first collect the metadata included in the files. +% +Please refer to section~\ref{sec:fontdb} below for instructions on how to +create the database. + +File names are whatever your file system allows them to be, except +that that they may not contain the characters + \verb|(|, + \verb|:|, and + \verb|/|. +% +As is obvious from the last exception, the \verb|file:| lookup will +not process paths to the font location -- only those +files found when generating the database are addressable this way. +% +Continue below in the \XETEX section if you need to load your fonts +by path. +% +The file names corresponding to the example font names above are + \fileent{lmroman12-italic.otf}, + \fileent{GFSBodoni.otf}, and + \fileent{PTZ56F.ttf}. + +\subsection{Compatibility Layer} + +In addition to the regular prefixed requests, \identifier{luaotfload} +accepts loading fonts the \XETEX way. +% +There are again two modes: bracketed and unbracketed. +A bracketed request looks as follows. + +\begin{quote} + |\font\fontname=[|\meta{path to file}|]| +\end{quote} + +\noindent +Inside the square brackets, every character except for a closing +bracket is permitted, allowing for specifying paths to a font file. +% +Naturally, path-less file names are equally valid and processed the +same way as an ordinary \verb|file:| lookup. + +\begin{quote} + |\font\fontname=|\meta{font name} \dots +\end{quote} + +Unbracketed (or, for lack of a better word: \emphasis{anonymous}) +font requests resemble the conventional \TEX syntax. +% +However, they have a broader spectrum of possible interpretations: +before anything else, \identifier{luaotfload} attempts to load a +traditional \TEX Font Metric (\abbrev{tfm} or \abbrev{ofm}). +% +If this fails, it performs a \verb|name:| lookup, which itself will +fall back to a \verb|file:| lookup if no database entry matches +\meta{font name}. + +Furthermore, \identifier{luaotfload} supports the slashed (shorthand) +font style notation from \XETEX. + +\begin{quote} + |\font\fontname=|\meta{font name}|/|\meta{modifier}\dots +\end{quote} + +\noindent +Currently, four style modifiers are supported: + \verb|I| for italic shape, + \verb|B| for bold weight, + \verb|BI| or \verb|IB| for the combination of both. +% +Other “slashed” modifiers are too specific to the \XETEX engine and +have no meaning in \LUATEX. + +\subsection{Examples} + +\subsubsection{Loading by File Name} + +For example, conventional \abbrev{type1} font can be loaded with a \verb|file:| +request like so: + +\begin{quote} + \begin{verbatim} + \font\lmromanten={file:ec-lmr10} at 10pt + \end{verbatim} +\end{quote} + +The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa +Półtawskiego}\footnote{% + \url{http://jmn.pl/antykwa-poltawskiego/}, also available in + in \TEX Live. +} +in its condensed variant can be loaded as follows: + +\begin{quote} + \begin{verbatim} + \font\apcregular=file:antpoltltcond-regular.otf at 42pt + \end{verbatim} +\end{quote} + +The next example shows how to load the \emphasis{Porson} font digitized by +the Greek Font Society using \XETEX-style syntax and an absolute path from a +non-standard directory: + +\begin{quote} + \begin{verbatim} + \font\gfsporson="[/tmp/GFSPorson.otf]" at 12pt + \end{verbatim} +\end{quote} + +\subsubsection{Loading by Font Name} + +The \verb|name:| lookup does not depend on cryptic filenames: + +\begin{quote} + \begin{verbatim} + \font\pagellaregular={name:TeX Gyre Pagella} at 9pt + \end{verbatim} +\end{quote} + +A bit more specific but essentially the same lookup would be: + +\begin{quote} + \begin{verbatim} + \font\pagellaregular={name:TeX Gyre Pagella Regular} at 9pt + \end{verbatim} +\end{quote} + +\noindent +Which fits nicely with the whole set: + +\begin{quote} + \begin{verbatim} + \font\pagellaregular ={name:TeX Gyre Pagella Regular} at 9pt + \font\pagellaitalic ={name:TeX Gyre Pagella Italic} at 9pt + \font\pagellabold ={name:TeX Gyre Pagella Bold} at 9pt + \font\pagellabolditalic={name:TeX Gyre Pagella Bolditalic} at 9pt + + {\pagellaregular foo bar baz\endgraf} + {\pagellaitalic foo bar baz\endgraf} + {\pagellabold foo bar baz\endgraf} + {\pagellabolditalic foo bar baz\endgraf} + + ... + \end{verbatim} +\end{quote} + +\subsubsection{Modifiers} + +If the entire \emphasis{Iwona} family\footnote{% + \url{http://jmn.pl/kurier-i-iwona/}, + also in \TEX Live. +} +is installed in some location accessible by \identifier{luaotfload}, +the regular shape can be loaded as follows: + +\begin{quote} + \begin{verbatim} + \font\iwona=Iwona at 20pt + \end{verbatim} +\end{quote} + +\noindent +To load the most common of the other styles, the slash notation can +be employed as shorthand: + +\begin{quote} + \begin{verbatim} + \font\iwonaitalic =Iwona/I at 20pt + \font\iwonabold =Iwona/B at 20pt + \font\iwonabolditalic=Iwona/BI at 20pt + \end{verbatim} +\end{quote} + +\noindent +which is equivalent to these full names: + +\begin{quote} + \begin{verbatim} + \font\iwonaitalic ="Iwona Italic" at 20pt + \font\iwonabold ="Iwona Bold" at 20pt + \font\iwonabolditalic="Iwona BoldItalic" at 20pt + \end{verbatim} +\end{quote} + +\section {Font features} + +\emphasis{Font features} are the second to last component in the +general scheme for font requests: + +\begin{quote} + |\font\foo={|% + \meta{prefix}|:|% + \meta{font name}|:|% + \meta{font features}|}|% + \meta{\TEX font features} +\end{quote} + +\noindent +If style modifiers are present (\XETEX style), they must precede +\meta{font features}. + +The element \meta{font features} is a semicolon-separated list of feature +tags\footnote{% + Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm}. +} +and font options. +% +Prepending a font feature with a |+| (plus sign) enables it, whereas +a |-| (minus) disables it. For instance, the request + +\begin{quote} + \begin{verbatim} + \font\test=LatinModernRoman:+clig;-kern + \end{verbatim} +\end{quote} + +\noindent activates contextual ligatures (|clig|) and disables +kerning (|kern|). +% +Alternatively the options |true| or |false| can be passed to +the feature in a key/value expression. +% +The following request has the same meaning as the last one: + +\begin{quote} + \begin{verbatim} + \font\test=LatinModernRoman:clig=true;kern=false + \end{verbatim} +\end{quote} + +\noindent +Furthermore, this second syntax is required should a font feature +accept other options besides a true/false switch. +% +For example, \emphasis{stylistic alternates} (|salt|) are variants of +given glyphs. +% +They can be selected either explicitly by supplying the variant +index (starting from one), or randomly by setting the value to, +obviously, |random|. + +%% TODO verify that this actually works with a font that supports +%% the salt/random feature!\fi +\begin{quote} + \begin{verbatim} + \font\librmsaltfirst=LatinModernRoman:salt=1 + \end{verbatim} +\end{quote} + +\noindent Other font options include: + +\begin{description} + +\item [mode] \hfill \\ + \identifier{luaotfload} has two \OpenType processing + \emphasis{modes}: + \identifier{base} and \identifier{node}. + + \identifier{base} mode works by mapping \OpenType + features to traditional \TEX ligature and kerning mechanisms. + % + Supporting only non-contextual substitutions and kerning + pairs, it is the slightly faster, albeit somewhat limited, variant. + % + \identifier{node} mode works by processing \TeX’s internal + node list directly at the \LUA end and supports + a wider range of \OpenType features. + % + The downside is that the intricate operations required for + \identifier{node} mode may slow down typesetting especially + with complex fonts and it does not work in math mode. + + By default \identifier{luaotfload} is in \identifier{node} + mode, and \identifier{base} mode has to be requested where needed, + e.~g. for math fonts. + +\item [script] \label{script-tag} \hfill \\ + An \OpenType script tag;\footnote{% + See \url{http://www.microsoft.com/typography/otspec/scripttags.htm} + for a list of valid values. + % + For scripts derived from the Latin alphabet the value + |latn| is good choice. + } + the default value is |dlft|. + % + Some fonts, including very popular ones by foundries like Adobe, + do not assign features to the |dflt| script, in + which case the script needs to be set explicitly. + +\item [language] \hfill \\ + An \OpenType language system identifier,\footnote{% + Cf. \url{http://www.microsoft.com/typography/otspec/languagetags.htm}. + } + defaulting to |dflt|. + +\item [featurefile] \hfill \\ + A comma-separated list of feature files to be applied to the + font. + % + Feature files contain a textual representation of + \OpenType tables and extend the features of a font + on fly. + % + After they are applied to a font, features defined in a + feature file can be enabled or disabled just like any + other font feature. + % + The syntax is documented in \identifier{Adobe}’s + \OpenType Feature File Specification.\footnote{% + Cf. \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. + } + + For a demonstration of how to set a |tkrn| feature consult + the file |tkrn.fea| that is part of \identifier{luaotfload}. + It can be read and applied as follows: + + |\font\test=Latin Modern Roman:featurefile=tkrn.fea;+tkrn| + +\item [color] \hfill \\ + A font color, defined as a triplet of two-digit hexadecimal + \abbrev{rgb} values, with an optional fourth value for + transparency + (where |00| is completely transparent and |FF| is opaque). + + For example, in order to set text in semitransparent red: + + \begin{quote} + \begin{verbatim} + \font\test={Latin Modern Roman}:color=FF0000BB + \end{verbatim} + \end{quote} + +\item [kernfactor \& letterspace] \hfill \\ + Define a font with letterspacing (tracking) enabled. + % + In \identifier{luaotfload}, letterspacing is implemented by + inserting additional kerning between glyphs. + + This approach is derived from and still quite similar to the + \emphasis{character kerning} (\texmacro{setcharacterkerning} / + \texmacro{definecharacterkerning} \& al.) functionality of + Context, see the file \fileent{typo-krn.lua} there. + % + The main difference is that \identifier{luaotfload} does not + use \LUATEX attributes to assign letterspacing to regions, + but defines virtual letterspaced versions of a font. + + The option \identifier{kernfactor} accepts a numeric value that + determines the letterspacing factor to be applied to the font + size. + % + E.~g. a kern factor of $0.42$ applied to a $10$ pt font + results in $4.2$ pt of additional kerning applied to each + pair of glyphs. + % + Ligatures are split into their component glyphs unless + explicitly ignored (see below). + + For compatibility with \XETEX an alternative + \identifier{letterspace} option is supplied that interprets the + supplied value as a \emphasis{percentage} of the font size but + is otherwise identical to \identifier{kernfactor}. + % + Consequently, both definitions in below snippet yield the same + letterspacing width: + + \begin{quote} + \begin{verbatim} + \font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125" + \font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5" + \end{verbatim} + \end{quote} + + Specific pairs of letters and ligatures may be exempt from + letterspacing by defining the \LUA functions + \luafunction{keeptogether} and \luafunction{keepligature}, + respectively, inside the namespace \verb|luaotfload.letterspace|. + % + Both functions are called whenever the letterspacing callback + encounters an appropriate node or set of nodes. + % + If they return a true-ish value, no extra kern is inserted at + the current position. + % + \luafunction{keeptogether} receives a pair of consecutive + glyph nodes in order of their appearance in the node list. + % + \luafunction{keepligature} receives a single node which can be + analyzed into components. + % + (For details refer to the \emphasis{glyph nodes} section in the + \LUATEX reference manual.) + % + The implementation of both functions is left entirely to the + user. + + +\item [protrusion \& expansion] \hfill \\ + These keys control microtypographic features of the font, + namely \emphasis{character protrusion} and \emphasis{font + expansion}. + % + Their arguments are names of \LUA tables that contain + values for the respective features.\footnote{% + For examples of the table layout please refer to the + section of the file \fileent{luaotfload-fonts-ext.lua} where the + default values are defined. + % + Alternatively and with loss of information, you can dump + those tables into your terminal by issuing + \begin{verbatim} + \directlua{inspect(fonts.protrusions.setups.default) + inspect(fonts.expansions.setups.default)} + \end{verbatim} + at some point after loading \fileent{luaotfload.sty}. + } + % + For both, only the set \identifier{default} is predefined. + + For example, to define a font with the default + protrusion vector applied\footnote{% + You also need to set + \verb|pdfprotrudechars=2| and + \verb|pdfadjustspacing=2| + to activate protrusion and expansion, respectively. + See the + \href{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% + {\PDFTEX manual} + for details. + }: + + \begin{quote} + \begin{verbatim} + \font\test=LatinModernRoman:protrusion=default + \end{verbatim} + \end{quote} +\end{description} + +\paragraph{Non-standard font features} +\identifier{luaotfload} adds a number of features that are not defined +in the original \OpenType specification, most of them +aiming at emulating the behavior familiar from other \TEX engines. +% +Currently (2014) there are three of them: + +\begin{description} + + \item [anum] + Substitutes the glyphs in the \abbrev{ascii} number range + with their counterparts from eastern Arabic or Persian, + depending on the value of \identifier{language}. + + \item [tlig] + Applies legacy \TEX ligatures: + + \begin{tabular}{rlrl} + `` & \verb|``| & '' & \verb|''| \\ + ` & \verb|`| & ' & \verb|'| \\ + " & \verb|"| & -- & \verb|--| \\ + --- & \verb|---| & !` & \verb|!`| \\ + ?` & \verb|?`| & & \\ + \end{tabular} + + \footnote{% + These contain the feature set \verb|trep| of earlier + versions of \identifier{luaotfload}. + + Note to \XETEX users: this is the equivalent of the + assignment \verb|mapping=text-tex| using \XETEX's input + remapping feature. + } + + \item [itlc] + Computes italic correction values (active by default). + +\end{description} + + + +\section{Font names database} +\label{sec:fontdb} + +As mentioned above, \identifier{luaotfload} keeps track of which +fonts are available to \LUATEX by means of a \emphasis{database}. +% +This allows referring to fonts not only by explicit filenames but +also by the proper names contained in the metadata which is often +more accessible to humans.\footnote{% + The tool \href{http://www.lcdf.org/type/}{\fileent{otfinfo}} (comes + with \TEX Live), when invoked on a font file with the \verb|-i| + option, lists the variety of name fields defined for it. +} + +When \identifier{luaotfload} is asked to load a font by a font name, +it will check if the database exists and load it, or else generate a +fresh one. +% +Should it then fail to locate the font, an update to the database is +performed in case the font has been added to the system only +recently. +% +As soon as the database is updated, the resolver will try +and look up the font again, all without user intervention. +% +The goal is for \identifier{luaotfload} to act in the background and +behave as unobtrusively as possible, while providing a convenient +interface to the fonts installed on the system. + +Generating the database for the first time may take a while since it +inspects every font file on your computer. +% +This is particularly noticeable if it occurs during a typesetting run. +In any case, subsequent updates to the database will be quite fast. + +\subsection[luaotfload-tool / mkluatexfontdb.lua]% + {\fileent{luaotfload-tool} / + \fileent{mkluatexfontdb.lua}\footnote{% + The script may be named just \fileent{mkluatexfontdb} in your + distribution. +}} + +It can still be desirable at times to do some of these steps +manually, and without having to compile a document. +% +To this end, \identifier{luaotfload} comes with the utility +\fileent{luaotfload-tool} that offers an interface to the database +functionality. +% +Being a \LUA script, there are two ways to run it: +either make it executable (\verb|chmod +x| on unixoid systems) or +pass it as an argument to \fileent{texlua}.\footnote{% + Tests by the maintainer show only marginal performance gain by + running with Luigi Scarso’s + \href{https://foundry.supelec.fr/projects/luajittex/}% + {\identifier{Luajit\kern-.25ex\TEX}}, + which is probably due to the fact that most of the time is spent + on file system operations. + + \emphasis{Note}: + On \abbrev{MS} \identifier{Windows} systems, the script can be run + either by calling the wrapper application + \fileent{luaotfload-tool.exe} or as + \verb|texlua.exe luaotfload-tool.lua|. +} +% +Invoked with the argument \verb|--update| it will perform a database +update, scanning for fonts not indexed. + +\begin{quote} + \begin{verbatim} + luaotfload-tool --update + \end{verbatim} +\end{quote} + +Adding the \verb|--force| switch will initiate a complete +rebuild of the database. + +\begin{quote} + \begin{verbatim} + luaotfload-tool --update --force + \end{verbatim} +\end{quote} + +For sake of backwards compatibility, \fileent{luaotfload-tool} may be +renamed or symlinked to \fileent{mkluatexfontdb}. +% +Whenever it is run under this name, it will update the database +first, mimicking the behavior of earlier versions of +\identifier{luaotfload}. + +\subsection{Search Paths} + +\identifier{luaotfload} scans those directories where fonts are +expected to be located on a given system. +% +On a Linux machine it follows the paths listed in the +\identifier{Fontconfig} configuration files; +consult \verb|man 5 fonts.conf| for further information. +% +On \identifier{Windows} systems, the standard location is +\verb|Windows\Fonts|, +% +while \identifier{Mac OS~X} requires a multitude of paths to +be examined. +% +The complete list is is given in table \ref{table-searchpaths}. +Other paths can be specified by setting the environment variable +\verb+OSFONTDIR+. +% +If it is non-empty, then search will be extended to the included +directories. + +\begin{table}[t] + \hrule + \caption{List of paths searched for each supported operating + system.} + \renewcommand{\arraystretch}{1.2} + \begin{center} + \begin{tabular}{lp{.5\textwidth}} + Windows & \verb|%WINDIR%\Fonts| + \\ + Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break + \fileent{/etc/fonts/fonts.conf} + \\ + Mac & \fileent{\textasciitilde/Library/Fonts},\break + \fileent{/Library/Fonts},\break + \fileent{/System/Library/Fonts}, and\hfill\break + \fileent{/Network/Library/Fonts} + \\ + \end{tabular} + \end{center} + \label{table-searchpaths} + \hrule +\end{table} + +\subsection{Querying from Outside} + +\fileent{luaotfload-tool} also provides rudimentary means of +accessing the information collected in the font database. +% +If the option \verb|--find=|\emphasis{name} is given, the script will +try and search the fonts indexed by \identifier{luaotfload} for a +matching name. +% +For instance, the invocation + +\begin{quote} + \begin{verbatim} + luaotfload-tool --find="Iwona Regular" + \end{verbatim} +\end{quote} + +\noindent +will verify if “Iwona Regular” is found in the database and can be +readily requested in a document. + +If you are unsure about the actual font name, then add the +\verb|-F| (or \verb|--fuzzy|) switch to the command line to enable +approximate matching. +% +Suppose you cannot precisely remember if the variant of +\identifier{Iwona} you are looking for was “Bright” or “Light”. +The query + +\begin{quote} + \begin{verbatim} + luaotfload-tool -F --find="Iwona Bright" + \end{verbatim} +\end{quote} + +\noindent +will tell you that indeed the latter name is correct. + +Basic information about fonts in the database can be displayed +using the \verb|-i| option (\verb|--info|). +% +\begin{quote} + \begin{verbatim} + luaotfload-tool -i --find="Iwona Light Italic" + \end{verbatim} +\end{quote} +% +\noindent +The meaning of the printed values is described in section 4.4 of the +\LUATEX reference manual.\footnote{% + In \TEX Live: \fileent{texmf-dist/doc/luatex/base/luatexref-t.pdf}. +} + +For a much more detailed report about a given font try the \verb|-I| option +instead (\verb|--inspect|). +\begin{quote} + \begin{verbatim} + luaotfload-tool -I --find="Iwona Light Italic" + \end{verbatim} +\end{quote} + +\verb|luaotfload-tool --help| will list the available command line +switches, including some not discussed in detail here. +% +For a full documentation of \identifier{luaotfload-tool} and its +capabilities refer to the manpage +(\verb|man 1 luaotfload-tool|).\footnote{% + Or see \verb|luaotfload-tool.rst| in the source directory. +} + +\subsection{Blacklisting Fonts} +\label{font-blacklist} + +Some fonts are problematic in general, or just in \LUATEX. +% +If you find that compiling your document takes far too long or eats +away all your system’s memory, you can track down the culprit by +running \verb|luaotfload-tool -v| to increase verbosity. +% +Take a note of the \emphasis{filename} of the font that database +creation fails with and append it to the file +\fileent{luaotfload-blacklist.cnf}. + +A blacklist file is a list of font filenames, one per line. +Specifying the full path to where the file is located is optional, the +plain filename should suffice. +% +File extensions (\fileent{.otf}, \fileent{.ttf}, etc.) may be omitted. +% +Anything after a percent (|%|) character until the end of the line +is ignored, so use this to add comments. +% +Place this file to some location where the \identifier{kpse} +library can find it, e.~g. +\fileent{texmf-local/tex/luatex/luaotfload} if you are running +\identifier{\TEX Live},\footnote{% + You may have to run \verb|mktexlsr| if you created a new file in + your \fileent{texmf} tree. +} +or just leave it in the working directory of your document. +% +\identifier{luaotfload} reads all files named +\fileent{luaotfload-blacklist.cnf} it finds, so the fonts in +\fileent{./luaotfload-blacklist.cnf} extend the global blacklist. + +Furthermore, a filename prepended with a dash character (|-|) is +removed from the blacklist, causing it to be temporarily whitelisted +without modifying the global file. +% +An example with explicit paths: + +\begin{verbatim} +% example otf-blacklist.cnf +/Library/Fonts/GillSans.ttc % Luaotfload ignores this font. +-/Library/Fonts/Optima.ttc % This one is usable again, even if + % blacklisted somewhere else. +\end{verbatim} + +\section{Files from \CONTEXT and \LUATEX-Fonts} + +\identifier{luaotfload} relies on code originally written by Hans +Hagen\footnote{% + The creator of the \href{http://wiki.contextgarden.net}{\CONTEXT} + format. +} +for and tested with \CONTEXT. +% +It integrates the font loader as distributed in +the \identifier{\LUATEX-Fonts} package. +% +The original \LUA source files have been combined using the +\fileent{mtx-package} script into a single, self-contained blob. +In this form the font loader has no further dependencies\footnote{% + It covers, however, to some extent the functionality of the + \identifier{lualibs} package. +} +and requires only minor adaptions to integrate into +\identifier{luaotfload}. +% +The guiding principle is to let \CONTEXT/\LUATEX-Fonts take care of +the implementation, and update the imported code from time to time. +% +As maintainers, we aim at importing files from upstream essentially +\emphasis{unmodified}, except for renaming them to prevent name +clashes. +% +This job has been greatly alleviated since the advent of +\LUATEX-Fonts, prior to which the individual dependencies had to be +manually spotted and extracted from the \CONTEXT source code in a +complicated and error-prone fashion. + +Below is a commented list of the files distributed with +\identifier{luaotfload} in one way or the other. +% +See figure \ref{file-graph} on page \pageref{file-graph} for a +graphical representation of the dependencies. +% +From \LUATEX-Fonts, only the file \fileent{luatex-fonts-merged.lua} +has been imported as \fileent{luaotfload-fontloader.lua}. +% +It is generated by \fileent{mtx-package}, a \LUA source code merging +too developed by Hans Hagen.\footnote{% + \fileent{mtx-package} is + \href + {http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua} + {part of \CONTEXT} + and requires \fileent{mtxrun}. + Run + \verb|mtxrun --script package --help| + to display further information. + For the actual merging code see the file + \fileent{util-mrg.lua} that is part of \CONTEXT. +} +It houses several \LUA files that can be classed in three +categories. + + \begin{itemize} + \let\normalitem=\item + \def\incitem#1{% + \normalitem{\fileent{#1}} + } + \normalitem \emphasis{\LUA utility libraries}, a subset + of what is provided by the \identifier{lualibs} + package. + + \begin{multicols}{2} + \begin{itemize} + \incitem{l-lua.lua} \incitem{l-lpeg.lua} + \incitem{l-function.lua} \incitem{l-string.lua} + \incitem{l-table.lua} \incitem{l-io.lua} + \incitem{l-file.lua} \incitem{l-boolean.lua} + \incitem{l-math.lua} \incitem{util-str.lua} + \end{itemize} + \end{multicols} + + \normalitem The \emphasis{font loader} itself. + These files have been written for + \LUATEX-Fonts and they are distributed along + with \identifier{luaotfload}. + \begin{multicols}{2} + \begin{itemize} + \incitem{luatex-basics-gen.lua} + \incitem{luatex-basics-nod.lua} + \incitem{luatex-fonts-enc.lua} + \incitem{luatex-fonts-syn.lua} + \incitem{luatex-fonts-tfm.lua} + \incitem{luatex-fonts-chr.lua} + \incitem{luatex-fonts-lua.lua} + \incitem{luatex-fonts-inj.lua} + \incitem{luatex-fonts-otn.lua} + \incitem{luatex-fonts-def.lua} + \incitem{luatex-fonts-ext.lua} + \incitem{luatex-fonts-cbk.lua} + \end{itemize} + \end{multicols} + + \normalitem Code related to \emphasis{font handling and + node processing}, taken directly from + \CONTEXT. + \begin{multicols}{2} + \begin{itemize} + \incitem{data-con.lua} \incitem{font-ini.lua} + \incitem{font-con.lua} \incitem{font-cid.lua} + \incitem{font-map.lua} \incitem{font-oti.lua} + \incitem{font-otf.lua} \incitem{font-otb.lua} + \incitem{font-ota.lua} \incitem{font-def.lua} + \incitem{font-otp.lua} + \end{itemize} + \end{multicols} + \end{itemize} + +Note that if \identifier{luaotfload} cannot locate the +merged file, it will load the individual \LUA libraries +instead. +% +Their names remain the same as in \CONTEXT (without the +\verb|otfl|-prefix) since we imported the relevant section of +\fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload-main.lua}. +Thus if you prefer running bleeding edge code from the +\CONTEXT beta, all you have to do is remove +\fileent{luaotfload-merged.lua} from the search path. + +Also, the merged file at some point loads the Adobe Glyph List from a +\LUA table that is contained in \fileent{luaotfload-glyphlist.lua}, +which is automatically generated by the script +\fileent{mkglyphlist}.\footnote{% + See \fileent{luaotfload-font-enc.lua}. + The hard-coded file name is why we have to replace the procedure + that loads the file in \fileent{luaotfload-override.lua}. +} +There is a make target \identifier{glyphs} that will create a fresh +glyph list so we don’t need to import it from \CONTEXT any longer. + +In addition to these, \identifier{luaotfload} requires a number of +files not contained in the merge. Some of these have no equivalent in +\LUATEX-Fonts or \CONTEXT, some were taken unmodified from the latter. + +\begin{itemize} + \let\normalitem=\item + \def\ouritem#1{% + \normalitem{\fileent{#1}}% + \space--\hskip1em + } + \ouritem {luaotfload-features.lua} font feature handling; + incorporates some of the code from + \fileent{font-otc} from \CONTEXT; + \ouritem {luaotfload-override.lua} overrides the \CONTEXT logging + functionality. + \ouritem {luaotfload-loaders.lua} registers the \OpenType + font reader as handler for + Postscript fonts + (\abbrev{pfa}, \abbrev{pfb}). + \ouritem {luaotfload-parsers.lua} various \abbrev{lpeg}-based parsers. + \ouritem {luaotfload-database.lua} font names database. + \ouritem {luaotfload-colors.lua} color handling. + \ouritem {luaotfload-auxiliary.lua} access to internal functionality + for package authors + (proposals for additions welcome). + \ouritem {luaotfload-letterspace.lua} font-based letterspacing. +\end{itemize} + +\begin{figure}[b] + \caption{Schematic of the files in \identifier{Luaotfload}} + \includegraphics[width=\textwidth]{filegraph.pdf} + \label{file-graph} +\end{figure} + +\section{Auxiliary Functions} + +With release version 2.2, \identifier{luaotfload} received +additional functions for package authors to call from outside +(see the file \fileent{luaotfload-auxiliary.lua} for details). +% +The purpose of this addition twofold. +% +Firstly, \identifier{luaotfload} failed to provide a stable interface +to internals in the past which resulted in an unmanageable situation +of different packages abusing the raw access to font objects by means +of the \luafunction{patch_font} callback. +% +When the structure of the font object changed due to an update, all +of these imploded and several packages had to be fixed while +simultaneously providing fallbacks for earlier versions. +% +Now the patching is done on the \identifier{luaotfload} side and can +be adapted with future modifications to font objects without touching +the packages that depend on it. +% +Second, some the capabilities of the font loader and the names +database are not immediately relevant in \identifier{luaotfload} +itself but might nevertheless be of great value to package authors or +end users. + +Note that the current interface is not yet set in stone and the +development team is open to suggestions for improvements or +additions. + +\subsection{Callback Functions} + +The \luafunction{patch_font} callback is inserted in the wrapper +\identifier{luaotfload} provides for the font definition callback +(see below, page \pageref{define-font}). +% +At this place it allows manipulating the font object immediately after +the font loader is done creating it. +% +For a short demonstration of its usefulness, here is a snippet that +writes an entire font object to the file \fileent{fontdump.lua}: + +\begin{quote} + \begin{verbatim} + \input luaotfload.sty + \directlua{ + local dumpfile = "fontdump.lua" + local dump_font = function (tfmdata) + local data = table.serialize(tfmdata) + io.savedata(dumpfile, data) + end + + luatexbase.add_to_callback( + "luaotfload.patch_font", + dump_font, + "my_private_callbacks.dump_font" + ) + } + \font\dumpme=name:Iwona + \bye + \end{verbatim} +\end{quote} + +\emphasis{Beware}: this creates a Lua file of around 150,000 lines of +code, taking up 3~\abbrev{mb} of disk space. +% +By inspecting the output you can get a first impression of how a font +is structured in \LUATEX’s memory, what elements it is composed of, +and in what ways it can be rearranged. + +\subsubsection{Compatibility with Earlier Versions} + +As has been touched on in the preface to this section, the structure +of the object as returned by the fontloader underwent rather drastic +changes during different stages of its development, and not all +packages that made use of font patching have kept up with every one +of it. +% +To ensure compatibility with these as well as older versions of +some packages, \identifier{luaotfload} sets up copies of or references +to data in the font table where it used to be located. +% +For instance, important parameters like the requested point size, the +units factor, and the font name have again been made accessible from +the toplevel of the table even though they were migrated to different +subtables in the meantime. + +\subsubsection{Patches} + +These are mostly concerned with establishing compatibility with \XETEX. + +\begin{itemize} + \let\normalitem=\item + \def\ouritem#1{% + \normalitem{\luafunction{#1}}% + \hfill\break + } + + \ouritem {set_sscale_dimens} + Calculate \texmacro{fontdimen}s 10 and 11 to emulate \XETEX. + + \ouritem {set_capheight} + Calculates \texmacro{fontdimen} 8 like \XETEX. + + \ouritem {patch_cambria_domh} + Correct some values of the font \emphasis{Cambria Math}. + +\end{itemize} + +\subsection{Package Author’s Interface} + +As \LUATEX release 1.0 is nearing, the demand for a reliable interface +for package authors increases. + +\subsubsection{Font Properties} + +Below functions mostly concern querying the different components of a +font like for instance the glyphs it contains, or what font features +are defined for which scripts. + +\begin{itemize} + \let\normalitem=\item + \def\ouritem#1{% + \normalitem{\luafunction{#1}}% + \hfill\break + } + + \ouritem {aux.font_has_glyph (id : int, index : int)} + Predicate that returns true if the font \luafunction{id} + has glyph \luafunction{index}. + + \ouritem {aux.slot_of_name(name : string)} + Translates an Adobe Glyph name to the corresponding glyph + slot. + + \ouritem {aux.name_of_slot(slot : int)} + The inverse of \luafunction{slot_of_name}; note that this + might be incomplete as multiple glyph names may map to the + same codepoint, only one of which is returned by + \luafunction{name_of_slot}. + + \ouritem {aux.provides_script(id : int, script : string)} + Test if a font supports \luafunction{script}. + + \ouritem {aux.provides_language(id : int, script : string, language : string)} + Test if a font defines \luafunction{language} for a given + \luafunction{script}. + + \ouritem {aux.provides_feature(id : int, script : string, + language : string, feature : string)} + Test if a font defines \luafunction{feature} for + \luafunction{language} for a given \luafunction{script}. + + \ouritem {aux.get_math_dimension(id : int, dimension : string)} + Get the dimension \luafunction{dimension} of font \luafunction{id}. + + \ouritem {aux.sprint_math_dimension(id : int, dimension : string)} + Same as \luafunction{get_math_dimension()}, but output the value + in scaled points at the \TEX end. + +\end{itemize} + +\subsubsection{Database} + +\begin{itemize} + \let\normalitem=\item + \def\ouritem#1{% + \normalitem{\luafunction{#1}}% + \hfill\break + } + + \ouritem {aux.scan_external_dir(dir : string)} + Include fonts in directory \luafunction{dir} in font lookups without + adding them to the database. + +\end{itemize} + +\section{Troubleshooting} + +\subsection {Database Generation} +If you encounter problems with some fonts, please first update to the +latest version of this package before reporting a bug, as +\identifier{luaotfload} is under active development and still a moving +target. +% +The development takes place on \identifier{github} at +\url{https://github.com/lualatex/luaotfload} where there is an issue +tracker for submitting bug reports, feature requests and the likes +requests and the likes. + +Bug reports are more likely to be addressed if they contain the output +of + +\begin{quote} + \begin{verbatim} + luaotfload-tool --diagnose=environment,files,permissions + \end{verbatim} +\end{quote} + +\noindent Consult the man page for a description of these options. + +Errors during database generation can be traced by increasing the +verbosity level and redirecting log output to \fileent{stdout}: + +\begin{quote} + \begin{verbatim} + luaotfload-tool -fuvvv --log=stdout + \end{verbatim} +\end{quote} + +\noindent or to a file in \fileent{/tmp}: + +\begin{quote} + \begin{verbatim} + luaotfload-tool -fuvvv --log=file + \end{verbatim} +\end{quote} + +\noindent In the latter case, invoke the \verb|tail(1)| utility on the +file for live monitoring of the progress. + +If database generation fails, the font last printed to the terminal or +log file is likely to be the culprit. +% +Please specify it when reporting a bug, and blacklist it for the time +being (see above, page \pageref{font-blacklist}). + +\subsection {Font Features} + +A common problem is the lack of features for some +\OpenType fonts even when specified. +% +This can be related to the fact that some fonts do not provide features +for the \verb|dflt| script (see above on page \pageref{script-tag}), +which is the default one in this package. +% +If this happens, assigning a noth script when the font is defined should +fix it. +% +For example with \verb|latn|: + +\begin{quote} + \begin{verbatim} + \font\test=file:MyFont.otf:script=latn;+liga; + \end{verbatim} +\end{quote} + +You can get a list of features that a font defines for scripts and +languages by querying it in \fileent{luaotfload-tool}: + +\begin{quote} + \begin{verbatim} + luaotfload-tool --find="Iwona" --inspect + \end{verbatim} +\end{quote} + +\subsection {\LUATEX Programming} + +Another strategy that helps avoiding problems is to not access raw +\LUATEX internals directly. +% +Some of them, even though they are dangerous to access, have not been +overridden or disabled. +% +Thus, whenever possible prefer the functions in the \luafunction{aux} +namespace over direct manipulation of font objects. For example, raw +access to the \luafunction{font.fonts} table like: + +\begin{quote} + \begin{verbatim} + local somefont = font.fonts[2] + \end{verbatim} +\end{quote} + +\noindent can render already defined fonts unusable. +% +Instead, the function \luafunction{font.getfont()} should be used +because it has been replaced by a safe variant. + +However, \luafunction{font.getfont()} only covers fonts handled by the +font loader, e.~g. \identifier{OpenType} and \identifier{TrueType} +fonts, but not \abbrev{tfm} or \abbrev{ofm}. +% +Should you absolutely require access to all fonts known to \LUATEX, +including the virtual and autogenerated ones, then you need to query +both \luafunction{font.getfont()} and \luafunction{font.fonts}. +% +In this case, best define you own accessor: + +\begin{quote} + \begin{verbatim} + local unsafe_getfont = function (id) + local tfmdata = font.getfont (id) + if not tfmdata then + tfmdata = font.fonts[id] + end + return tfmdata + end + + --- use like getfont() + local somefont = unsafe_getfont (2) + \end{verbatim} +\end{quote} + +\part{Implementation} + +\section {\fileent{luaotfload.lua}} + +As of version 2.5, the file \fileent{luaotfload.lua} is no longer +generated from the \abbrev{dtx}. +% +Instead, it is maintained separately as a plain \identifier{Lua} file +\fileent{luaotfload-main.lua} in the Luaotfload \identifier{git} tree. +% +The file documentation which used to be found in this section has been +preserved in the comments. + +\section{\fileent{luaotfload.sty}} + +As of version 2.5, the file \fileent{luaotfload.sty} is no longer +generated from the \abbrev{dtx}. +% +Instead, it is maintained separately as a plain \identifier{\TEX} file +in the Luaotfload \identifier{git} tree. +% +The file documentation which used to be found in this section has +been preserved in the comments. + +\clearpage +\section{The GNU GPL License v2} + +The GPL requires the complete license text to be distributed along +with the code. I recommend the canonical source, instead: +\url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. +But if you insist on an included copy, here it is. +You might want to zoom in. + +\newsavebox{\gpl} +\begin{lrbox}{\gpl} +\begin{minipage}{3\textwidth} +\columnsep=3\columnsep +\begin{multicols}{3} +\begin{center} +{\Large GNU GENERAL PUBLIC LICENSE\par} +\bigskip +{Version 2, June 1991} +\end{center} + +\begin{center} +{\parindent 0in + +Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. + +\bigskip + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +\bigskip + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +} +\end{center} + +\begin{center} +{\bf\large Preamble} +\end{center} + + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software---to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and to +any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General Public +License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this service +if you wish), that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free programs; +and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or +for a fee, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And +you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its +recipients to know that what they have is not the original, so that any +problems introduced by others will not reflect on the original authors' +reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +\begin{center} +{\Large \sc Terms and Conditions For Copying, Distribution and + Modification} +\end{center} + +\begin{enumerate} +\item +This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the +terms of this General Public License. The ``Program'', below, refers to +any such program or work, and a ``work based on the Program'' means either +the Program or any derivative work under copyright law: that is to say, a +work containing the Program or a portion of it, either verbatim or with +modifications and/or translated into another language. (Hereinafter, +translation is included without limitation in the term ``modification''.) +Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +\item You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +\item +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +\begin{enumerate} + +\item +You must cause the modified files to carry prominent notices stating that +you changed the files and the date of any change. + +\item +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +\item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) + +\end{enumerate} + + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +\item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +\begin{enumerate} + +\item + +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +\item + +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +\item + +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) + +\end{enumerate} + + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +\item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +\item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +\item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +\item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +\item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +\item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +\item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +\begin{center} +{\Large\sc +No Warranty +} +\end{center} + +\item +{\sc Because the program is licensed free of charge, there is no warranty +for the program, to the extent permitted by applicable law. Except when +otherwise stated in writing the copyright holders and/or other parties +provide the program ``as is'' without warranty of any kind, either expressed +or implied, including, but not limited to, the implied warranties of +merchantability and fitness for a particular purpose. The entire risk as +to the quality and performance of the program is with you. Should the +program prove defective, you assume the cost of all necessary servicing, +repair or correction.} + +\item +{\sc In no event unless required by applicable law or agreed to in writing +will any copyright holder, or any other party who may modify and/or +redistribute the program as permitted above, be liable to you for damages, +including any general, special, incidental or consequential damages arising +out of the use or inability to use the program (including but not limited +to loss of data or data being rendered inaccurate or losses sustained by +you or third parties or a failure of the program to operate with any other +programs), even if such holder or other party has been advised of the +possibility of such damages.} + +\end{enumerate} + + +\begin{center} +{\Large\sc End of Terms and Conditions} +\end{center} + + +\pagebreak[2] + +\section*{Appendix: How to Apply These Terms to Your New Programs} + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to + attach them to the start of each source file to most effectively convey + the exclusion of warranty; and each file should have at least the + ``copyright'' line and a pointer to where the full notice is found. + +\begin{quote} +one line to give the program's name and a brief idea of what it does. \\ +Copyright (C) yyyy name of author \\ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +\end{quote} + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +\begin{quote} +Gnomovision version 69, Copyright (C) yyyy name of author \\ +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +\end{quote} + + +The hypothetical commands {\tt show w} and {\tt show c} should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than {\tt show w} and {\tt show c}; +they could even be mouse-clicks or menu items---whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +\begin{quote} +Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\ +`Gnomovision' (which makes passes at compilers) written by James Hacker. \\ + +signature of Ty Coon, 1 April 1989 \\ +Ty Coon, President of Vice +\end{quote} + + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +\end{multicols} +\end{minipage} +\end{lrbox} + +\begin{center} +\scalebox{0.33}{\usebox{\gpl}} +\end{center} + +\endinput + diff --git a/doc/luaotfload.dtx b/doc/luaotfload.dtx deleted file mode 100644 index e0311a4..0000000 --- a/doc/luaotfload.dtx +++ /dev/null @@ -1,1994 +0,0 @@ -% \iffalse meta-comment -% -% Copyright (C) 2009-2014 -% by Elie Roux -% and Khaled Hosny -% and Philipp Gesang -% -% Home: https://github.com/lualatex/luaotfload -% Support: . -% -% This work is under the GPL v2.0 license. -% -% This work consists of the main source file luaotfload.dtx -% and the derived files -% luaotfload.sty -% -% Unpacking: -% tex luaotfload.dtx -% -% Documentation: -% lualatex luaotfload.dtx -% -% The class ltxdoc loads the configuration file ltxdoc.cfg -% if available. Here you can specify further options, e.g. -% use A4 as paper format: -% \PassOptionsToClass{a4paper}{article} -% -% -% -%<*ignore> -\begingroup - \def\x{LaTeX2e}% -\expandafter\endgroup -\ifcase 0\ifx\install y1\fi\expandafter - \ifx\csname processbatchFile\endcsname\relax\else1\fi - \ifx\fmtname\x\else 1\fi\relax -\else\csname fi\endcsname -% -%<*install> -\input docstrip.tex -\Msg{************************************************************************} -\Msg{* Installation} -\Msg{* Package: luaotfload v2.5 OpenType layout system} -\Msg{************************************************************************} - -\keepsilent -\askforoverwritefalse - -\let\MetaPrefix\relax - -\preamble -This is a generated file. - -Copyright (C) 2009-2014 - by Elie Roux - and Khaled Hosny - and Philipp Gesang - - Home: https://github.com/lualatex/luaotfload - Support: . - -This work is under the GPL v2.0 license. - -This work consists of the main source file luaotfload.dtx -and the derived files - luaotfload.sty - -\endpreamble - -\obeyspaces -\Msg{************************************************************************} -\Msg{*} -\Msg{* To finish the installation you have to move the following} -\Msg{* files into a directory searched by TeX:} -\Msg{*} -\Msg{* luaotfload.sty} -\Msg{*} -\Msg{* Happy TeXing!} -\Msg{*} -\Msg{************************************************************************} - -\endbatchfile -% -%<*ignore> -\fi -% -%<*driver> -\NeedsTeXFormat{LaTeX2e} -\ProvidesFile{luaotfload.drv}% -[2014/**/** v2.5 OpenType layout system]% -\documentclass{ltxdoc} -\usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace} -\usepackage[x11names]{xcolor} -% -\def\primarycolor{DodgerBlue4} %%-> rgb 16 78 139 | #104e8b -\def\secondarycolor{Goldenrod4} %%-> rgb 139 105 200 | #8b6914 -% -\usepackage[ - bookmarks=true, - colorlinks=true, - linkcolor=\primarycolor, - urlcolor=\secondarycolor, - citecolor=\primarycolor, - pdftitle={The luaotfload package}, - pdfsubject={OpenType layout system for Plain TeX and LaTeX}, - pdfauthor={Elie Roux & Khaled Hosny & Philipp Gesang}, - pdfkeywords={luatex, lualatex, unicode, opentype} -]{hyperref} -\usepackage{fontspec} -\usepackage{unicode-math} -\setmainfont[ -% Numbers = OldStyle, %% buggy with font cache - Ligatures = TeX, - BoldFont = {Linux Libertine O Bold}, - ItalicFont = {Linux Libertine O Italic}, - SlantedFont = {Linux Libertine O Italic}, -]{Linux Libertine O} -\setmonofont[Ligatures=TeX,Scale=MatchLowercase]{Liberation Mono} -%setsansfont[Ligatures=TeX]{Linux Biolinum O} -\setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium} -%setmathfont{XITS Math} - -\usepackage{hologo} - -\newcommand\TEX {\TeX\xspace} -\newcommand\LUA {Lua\xspace} -\newcommand\PDFTEX {pdf\TeX\xspace} -\newcommand\LUATEX {Lua\TeX\xspace} -\newcommand\XETEX {\XeTeX\xspace} -\newcommand\LATEX {\LaTeX\xspace} -\newcommand\LUALATEX {Lua\LaTeX\xspace} -\newcommand\CONTEXT {Con\TeX t\xspace} -\newcommand\OpenType {\identifier{Open\kern-.25ex Type}\xspace} - -\def\definehighlight[#1][#2]% - {\ifcsname #1\endcsname\else - \expandafter\def\csname #1\endcsname% - {\bgroup#2\csname #1_indeed\endcsname} - \expandafter\def\csname #1_indeed\endcsname##1% - {##1\egroup}% - \fi} - -\def\restoreunderscore{\catcode`\_=12\relax} - -\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs -\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs -\definehighlight[luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers -\definehighlight [identifier][\sffamily] %% names -\definehighlight [abbrev][\rmfamily\scshape] %% acronyms -\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph - -\newcommand*\email[1]{\href{mailto:#1}{#1}} - -\renewcommand\partname{Part}%% gets rid of the stupid “file” heading - -\usepackage{syntax}%% bnf for font request syntax - -\usepackage{titlesec} - -\def\movecountertomargin#1{\llap{\rmfamily\upshape#1\hskip2em}} -\def\zeropoint{0pt} -\titleformat \part - {\normalsize\rmfamily\bfseries} - {\movecountertomargin\thepart} \zeropoint {} -\titleformat \section - {\normalsize\rmfamily\scshape} - {\movecountertomargin\thesection} \zeropoint {} -\titleformat \subsection - {\small\rmfamily\itshape} - {\movecountertomargin\thesubsection} \zeropoint {} -\titleformat \subsubsection - {\normalsize\rmfamily\upshape} - {\movecountertomargin\thesubsubsection} \zeropoint {} - -\usepackage{tocloft} -\renewcommand \cftpartfont {\rmfamily\upshape} -\renewcommand \cftsecfont {\rmfamily\upshape} -\renewcommand \cftsubsecfont {\rmfamily\upshape} -\setlength \cftbeforepartskip {1ex} -\setlength \cftbeforesecskip {1ex} - -\VerbatimFootnotes -\begin{document} - \DocInput{luaotfload.dtx}% -\end{document} -% -% \fi -% -% \CheckSum{0} -% -% \CharacterTable -% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z -% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z -% Digits \0\1\2\3\4\5\6\7\8\9 -% Exclamation \! Double quote \" Hash (number) \# -% Dollar \$ Percent \% Ampersand \& -% Acute accent \' Left paren \( Right paren \) -% Asterisk \* Plus \+ Comma \, -% Minus \- Point \. Solidus \/ -% Colon \: Semicolon \; Less than \< -% Equals \= Greater than \> Question mark \? -% Commercial at \@ Left bracket \[ Backslash \\ -% Right bracket \] Circumflex \^ Underscore \_ -% Grave accent \` Left brace \{ Vertical bar \| -% Right brace \} Tilde \~} -% -% \GetFileInfo{luaotfload.drv} -% -% \title{The \identifier{luaotfload} package} -% \date{2014/**/** v2.5} -% \author{Elie Roux · Khaled Hosny · Philipp Gesang\\ -% Home: \url{https://github.com/lualatex/luaotfload}\\ -% Support: \email{lualatex-dev@tug.org}} -% -% \maketitle -% -% \begin{abstract} -% This package is an adaptation of the \CONTEXT font loading system. -% It allows for loading \OpenType fonts with an extended syntax and adds -% support for a variety of font features. -% \end{abstract} -% -% \tableofcontents -% -% \part{Package Description} -% -% \section{Introduction} -% -% Font management and installation has always been painful with \TEX. A lot of -% files are needed for one font (\abbrev{tfm}, \abbrev{pfb}, \abbrev{map}, -% \abbrev{fd}, \abbrev{vf}), and due to the 8-Bit encoding each font is limited -% to 256 characters. -% But the font world has evolved since the original -% \TEX, and new typographic systems have appeared, most notably the so -% called \emphasis{smart font} technologies like \OpenType -% fonts (\abbrev{otf}). -% These fonts can contain many more characters than \TEX fonts, as well as additional -% functionality like ligatures, old-style numbers, small capitals, -% etc., and support more complex writing systems like Arabic and -% Indic\footnote{% -% Unfortunately, \identifier{luaotfload} doesn‘t support many Indic -% scripts right now. -% Assistance in implementing the prerequisites is greatly -% appreciated. -% } -% scripts. -% \OpenType fonts are widely deployed and available for all -% modern operating systems. -% As of 2013 they have become the de facto standard for advanced text -% layout. -% However, until recently the only way to use them directly in the \TEX -% world was with the \XETEX engine. -% -% Unlike \XETEX, \LUATEX has no built-in support for -% \OpenType or technologies other than the original \TEX fonts. -% Instead, it provides hooks for executing \LUA code during the \TEX run -% that allow implementing extensions for loading fonts and manipulating -% how input text is processed without modifying the underlying engine. -% This is where \identifier{luaotfload} comes into play: -% Based on code from \CONTEXT, it extends \LUATEX with functionality necessary -% for handling \OpenType fonts. -% Additionally, it provides means for accessing fonts known to the operating -% system conveniently by indexing the metadata. -% -% -% \section{Thanks} -% -% \identifier{Luaotfload} is part of \LUALATEX, the community-driven -% project to provide a foundation for using the \LATEX format with the -% full capabilites of the \LUATEX engine. -% As such, the distinction between end users, contributors, and project -% maintainers is intentionally kept less strict, lest we unduly -% personalize the common effort. -% -% Nevertheless, the current maintainers would like to express their -% gratitude to Khaled Hosny, Akira Kakuto, Hironori Kitagawa and Dohyun -% Kim. -% Their contributions -- be it patches, advice, or systematic -% testing -- made the switch from version 1.x to 2.2 possible. -% Also, Hans Hagen, the author of the font loader, made porting the -% code to \LATEX a breeze due to the extra effort he invested into -% isolating it from the rest of \CONTEXT, not to mention his assistance -% in the task and willingness to respond to our suggestions. -% -% -% \section{Loading Fonts} -% -% \identifier{luaotfload} supports an extended font request syntax: -% -% \begin{quote} -% |\font\foo={|% -% \meta{prefix}|:|% -% \meta{font name}|:|% -% \meta{font features}|}|% -% \meta{\TEX font features} -% \end{quote} -% -% \noindent -% The curly brackets are optional and escape the spaces in the enclosed -% font name. -% Alternatively, double quotes serve the same purpose. -% A selection of individual parts of the syntax are discussed below; -% for a more formal description see figure \ref{font-syntax}. -% -% \begin{figure}[b] -% \setlength\grammarparsep{12pt plus 2pt minus 2pt} -% \setlength\grammarindent{5cm} -% \begingroup -% \small -% \begin{grammar} -% ::= `\\font', {\sc csname}, `=', , [ ] ; -% -% ::= `at', {\sc dimension} ; -% -% ::= `"', `"' -% \alt `{', `}' -% \alt ; -% -% ::= , [`:', ] -% \alt `[', `]', [ [`:'], ] ; -% -% ::= , [ ], \{ \} -% \alt , \{ \} ; -% -% ::= `file:', -% \alt `name:', ; -% -% ::= \{ \} ; -% -% ::= \{ \} ; -% -% ::= {\sc tfmname} | ; -% -% ::= \{ {\sc all_characters} - `]' \} ; -% -% ::= `/', (`I' | `B' | `BI' | `IB' | `S=', \{ {\sc digit} \} ) ; -% -% ::= `(', \{ {\sc digit} \}, `)' ; -% -% ::= , \{ `;', \} ; -% -% ::= {\sc feature_id}, `=', {\sc feature_value} -% \alt , {\sc feature_id} ; -% -% ::= `+' | `-' ; -% -% ::= {\sc all_characters} - ( `(' | `/' | `:' ) ; -% \end{grammar} -% \endgroup -% \caption{Font request syntax. -% Braces or double quotes around the -% \emphasis{specification} rule will -% preserve whitespace in file names. -% In addition to the font style modifiers -% (\emphasis{slash-notation}) given above, there -% are others that are recognized but will be silently -% ignored: {\ttfamily aat}, -% {\ttfamily icu}, and -% {\ttfamily gr}. -% The special terminals are: -% {\sc feature\textunderscore id} for a valid font -% feature name and -% {\sc feature\textunderscore value} for the corresponding -% value. -% {\sc tfmname} is the name of a \abbrev{tfm} file. -% {\sc digit} again refers to bytes 48--57, and -% {\sc all\textunderscore characters} to all byte values. -% {\sc csname} and {\sc dimension} are the \TEX concepts.} -% \label{font-syntax} -% \end{figure} -% -% \subsection{Prefix -- the \identifier{luaotfload}{ }Way} -% -% In \identifier{luaotfload}, the canonical syntax for font requests -% requires a \emphasis{prefix}: -% \begin{quote} -% |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots -% \end{quote} -% where \meta{prefix} is either \verb|file:| or \verb|name:|.\footnote{% -% The development version also knows two further prefixes, -% \verb|kpse:| and \verb|my:|. -% A \verb|kpse| lookup is restricted to files that can be found by -% \identifier{kpathsea} and -% will not attempt to locate system fonts. -% This behavior can be of value when an extra degree of encapsulation is -% needed, for instance when supplying a customized tex distribution. -% -% The \verb|my| lookup takes this a step further: it lets you define -% a custom resolver function and hook it into the \luafunction{resolve_font} -% callback. -% This ensures full control over how a file is located. -% For a working example see the -% \href{https://bitbucket.org/phg/lua-la-tex-tests/src/5f6a535d/pln-lookup-callback-1.tex} -% {test repo}. -% } -% It determines whether the font loader should interpret the request as -% a \emphasis{file name} or -% \emphasis{font name}, respectively, -% which again influences how it will attempt to locate the font. -% Examples for font names are -% “Latin Modern Italic”, -% “GFS Bodoni Rg”, and -% “PT Serif Caption” -% -- they are the human readable identifiers -% usually listed in drop-down menus and the like.\footnote{% -% Font names may appear like a great choice at first because they -% offer seemingly more intuitive identifiers in comparison to arguably -% cryptic file names: -% “PT Sans Bold” is a lot more descriptive than \fileent{PTS75F.ttf}. -% On the other hand, font names are quite arbitrary and there is no -% universal method to determine their meaning. -% While \identifier{luaotfload} provides fairly sophisticated heuristic -% to figure out a matching font style, weight, and optical size, it -% cannot be relied upon to work satisfactorily for all font files. -% For an in-depth analysis of the situation and how broken font names -% are, please refer to -% \href{http://www.ntg.nl/pipermail/ntg-context/2013/073889.html} -% {this post} -% by Hans Hagen, the author of the font loader. -% If in doubt, use filenames. -% \fileent{luaotfload-tool} can perform the matching for you with the -% option \verb|--find=|, and you can use the file name it returns -% in your font definition. -% } -% In order for fonts installed both in system locations and in your -% \fileent{texmf} to be accessible by font name, \identifier{luaotfload} must -% first collect the metadata included in the files. -% Please refer to section~\ref{sec:fontdb} below for instructions on how to -% create the database. -% -% File names are whatever your file system allows them to be, except -% that that they may not contain the characters -% \verb|(|, -% \verb|:|, and -% \verb|/|. -% As is obvious from the last exception, the \verb|file:| lookup will -% not process paths to the font location -- only those -% files found when generating the database are addressable this way. -% Continue below in the \XETEX section if you need to load your fonts -% by path. -% The file names corresponding to the example font names above are -% \fileent{lmroman12-italic.otf}, -% \fileent{GFSBodoni.otf}, and -% \fileent{PTZ56F.ttf}. -% -% \subsection{Compatibility Layer} -% -% In addition to the regular prefixed requests, \identifier{luaotfload} -% accepts loading fonts the \XETEX way. -% There are again two modes: bracketed and unbracketed. -% A bracketed request looks as follows. -% -% \begin{quote} -% |\font\fontname=[|\meta{path to file}|]| -% \end{quote} -% -% \noindent -% Inside the square brackets, every character except for a closing -% bracket is permitted, allowing for specifying paths to a font file. -% Naturally, path-less file names are equally valid and processed the -% same way as an ordinary \verb|file:| lookup. -% -% \begin{quote} -% |\font\fontname=|\meta{font name} \dots -% \end{quote} -% -% Unbracketed (or, for lack of a better word: \emphasis{anonymous}) -% font requests resemble the conventional \TEX syntax. -% However, they have a broader spectrum of possible interpretations: -% before anything else, \identifier{luaotfload} attempts to load a -% traditional \TEX Font Metric (\abbrev{tfm} or \abbrev{ofm}). -% If this fails, it performs a \verb|name:| lookup, which itself will -% fall back to a \verb|file:| lookup if no database entry matches -% \meta{font name}. -% -% Furthermore, \identifier{luaotfload} supports the slashed (shorthand) -% font style notation from \XETEX. -% -% \begin{quote} -% |\font\fontname=|\meta{font name}|/|\meta{modifier}\dots -% \end{quote} -% -% \noindent -% Currently, four style modifiers are supported: -% \verb|I| for italic shape, -% \verb|B| for bold weight, -% \verb|BI| or \verb|IB| for the combination of both. -% Other “slashed” modifiers are too specific to the \XETEX engine and -% have no meaning in \LUATEX. -% -% \subsection{Examples} -% -% \subsubsection{Loading by File Name} -% -% For example, conventional \abbrev{type1} font can be loaded with a \verb|file:| -% request like so: -% -% \begin{quote} -% \begin{verbatim} -% \font\lmromanten={file:ec-lmr10} at 10pt -% \end{verbatim} -% \end{quote} -% -% The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa -% Półtawskiego}\footnote{% -% \url{http://jmn.pl/antykwa-poltawskiego/}, also available in -% in \TEX Live. -% } -% in its condensed variant can be loaded as follows: -% -% \begin{quote} -% \begin{verbatim} -% \font\apcregular=file:antpoltltcond-regular.otf at 42pt -% \end{verbatim} -% \end{quote} -% -% The next example shows how to load the \emphasis{Porson} font digitized by -% the Greek Font Society using \XETEX-style syntax and an absolute path from a -% non-standard directory: -% -% \begin{quote} -% \begin{verbatim} -% \font\gfsporson="[/tmp/GFSPorson.otf]" at 12pt -% \end{verbatim} -% \end{quote} -% -% \subsubsection{Loading by Font Name} -% -% The \verb|name:| lookup does not depend on cryptic filenames: -% -% \begin{quote} -% \begin{verbatim} -% \font\pagellaregular={name:TeX Gyre Pagella} at 9pt -% \end{verbatim} -% \end{quote} -% -% A bit more specific but essentially the same lookup would be: -% -% \begin{quote} -% \begin{verbatim} -% \font\pagellaregular={name:TeX Gyre Pagella Regular} at 9pt -% \end{verbatim} -% \end{quote} -% -% \noindent -% Which fits nicely with the whole set: -% -% \begin{quote} -% \begin{verbatim} -% \font\pagellaregular ={name:TeX Gyre Pagella Regular} at 9pt -% \font\pagellaitalic ={name:TeX Gyre Pagella Italic} at 9pt -% \font\pagellabold ={name:TeX Gyre Pagella Bold} at 9pt -% \font\pagellabolditalic={name:TeX Gyre Pagella Bolditalic} at 9pt -% -% {\pagellaregular foo bar baz\endgraf} -% {\pagellaitalic foo bar baz\endgraf} -% {\pagellabold foo bar baz\endgraf} -% {\pagellabolditalic foo bar baz\endgraf} -% -% ... -% \end{verbatim} -% \end{quote} -% -% \subsubsection{Modifiers} -% -% If the entire \emphasis{Iwona} family\footnote{% -% \url{http://jmn.pl/kurier-i-iwona/}, -% also in \TEX Live. -% } -% is installed in some location accessible by \identifier{luaotfload}, -% the regular shape can be loaded as follows: -% -% \begin{quote} -% \begin{verbatim} -% \font\iwona=Iwona at 20pt -% \end{verbatim} -% \end{quote} -% -% \noindent -% To load the most common of the other styles, the slash notation can -% be employed as shorthand: -% -% \begin{quote} -% \begin{verbatim} -% \font\iwonaitalic =Iwona/I at 20pt -% \font\iwonabold =Iwona/B at 20pt -% \font\iwonabolditalic=Iwona/BI at 20pt -% \end{verbatim} -% \end{quote} -% -% \noindent -% which is equivalent to these full names: -% -% \begin{quote} -% \begin{verbatim} -% \font\iwonaitalic ="Iwona Italic" at 20pt -% \font\iwonabold ="Iwona Bold" at 20pt -% \font\iwonabolditalic="Iwona BoldItalic" at 20pt -% \end{verbatim} -% \end{quote} -% -% \section{Font features} -% -% \emphasis{Font features} are the second to last component in the -% general scheme for font requests: -% -% \begin{quote} -% |\font\foo={|% -% \meta{prefix}|:|% -% \meta{font name}|:|% -% \meta{font features}|}|% -% \meta{\TEX font features} -% \end{quote} -% -% \noindent -% If style modifiers are present (\XETEX style), they must precede -% \meta{font features}. -% -% The element \meta{font features} is a semicolon-separated list of feature -% tags\footnote{% -% Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm}. -% } -% and font options. -% Prepending a font feature with a |+| (plus sign) enables it, whereas -% a |-| (minus) disables it. For instance, the request -% -% \begin{quote} -% \begin{verbatim} -% \font\test=LatinModernRoman:+clig;-kern -% \end{verbatim} -% \end{quote} -% -% \noindent activates contextual ligatures (|clig|) and disables -% kerning (|kern|). -% Alternatively the options |true| or |false| can be passed to -% the feature in a key/value expression. -% The following request has the same meaning as the last one: -% -% \begin{quote} -% \begin{verbatim} -% \font\test=LatinModernRoman:clig=true;kern=false -% \end{verbatim} -% \end{quote} -% -% \noindent -% Furthermore, this second syntax is required should a font feature -% accept other options besides a true/false switch. -% For example, \emphasis{stylistic alternates} (|salt|) are variants of given -% glyphs. -% They can be selected either explicitly by supplying the variant -% index (starting from one), or randomly by setting the value to, -% obviously, |random|. -% -% \iffalse TODO verify that this actually works with a font that supports -% the salt/random feature!\fi -% \begin{quote} -% \begin{verbatim} -% \font\librmsaltfirst=LatinModernRoman:salt=1 -% \end{verbatim} -% \end{quote} -% -% \noindent Other font options include: -% -% \begin{description} -% -% \item [mode] \hfill \\ -% \identifier{luaotfload} has two \OpenType processing -% \emphasis{modes}: -% \identifier{base} and \identifier{node}. -% -% \identifier{base} mode works by mapping \OpenType -% features to traditional \TEX ligature and kerning mechanisms. -% Supporting only non-contextual substitutions and kerning -% pairs, it is the slightly faster, albeit somewhat limited, variant. -% \identifier{node} mode works by processing \TeX’s internal -% node list directly at the \LUA end and supports -% a wider range of \OpenType features. -% The downside is that the intricate operations required for -% \identifier{node} mode may slow down typesetting especially -% with complex fonts and it does not work in math mode. -% -% By default \identifier{luaotfload} is in \identifier{node} -% mode, and \identifier{base} mode has to be requested where needed, -% e.~g. for math fonts. -% -% \item [script] \label{script-tag} \hfill \\ -% An \OpenType script tag;\footnote{% -% See \url{http://www.microsoft.com/typography/otspec/scripttags.htm} -% for a list of valid values. -% For scripts derived from the Latin alphabet the value -% |latn| is good choice. -% } -% the default value is |dlft|. -% Some fonts, including very popular ones by foundries like Adobe, -% do not assign features to the |dflt| script, in -% which case the script needs to be set explicitly. -% -% \item [language] \hfill \\ -% An \OpenType language system identifier,\footnote{% -% Cf. \url{http://www.microsoft.com/typography/otspec/languagetags.htm}. -% } -% defaulting to |dflt|. -% -% \item [featurefile] \hfill \\ -% A comma-separated list of feature files to be applied to the -% font. -% Feature files contain a textual representation of -% \OpenType tables and extend the features of a font -% on fly. -% After they are applied to a font, features defined in a -% feature file can be enabled or disabled just like any -% other font feature. -% The syntax is documented in \identifier{Adobe}’s -% \OpenType Feature File Specification.\footnote{% -% Cf. \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. -% } -% -% For a demonstration of how to set a |tkrn| feature consult -% the file |tkrn.fea| that is part of \identifier{luaotfload}. -% It can be read and applied as follows: -% -% |\font\test=Latin Modern Roman:featurefile=tkrn.fea;+tkrn| -% -% \item [color] \hfill \\ -% A font color, defined as a triplet of two-digit hexadecimal -% \abbrev{rgb} values, with an optional fourth value for -% transparency -% (where |00| is completely transparent and |FF| is opaque). -% -% For example, in order to set text in semitransparent red: -% -% \begin{quote} -% \begin{verbatim} -% \font\test={Latin Modern Roman}:color=FF0000BB -% \end{verbatim} -% \end{quote} -% -% \item [kernfactor \& letterspace] \hfill \\ -% Define a font with letterspacing (tracking) enabled. -% In \identifier{luaotfload}, letterspacing is implemented by -% inserting additional kerning between glyphs. -% -% This approach is derived from and still quite similar to the -% \emphasis{character kerning} (\texmacro{setcharacterkerning} / -% \texmacro{definecharacterkerning} \& al.) functionality of -% Context, see the file \fileent{typo-krn.lua} there. -% The main difference is that \identifier{luaotfload} does not -% use \LUATEX attributes to assign letterspacing to regions, -% but defines virtual letterspaced versions of a font. -% -% The option \identifier{kernfactor} accepts a numeric value that -% determines the letterspacing factor to be applied to the font -% size. -% E.~g. a kern factor of $0.42$ applied to a $10$ pt font -% results in $4.2$ pt of additional kerning applied to each -% pair of glyphs. -% Ligatures are split into their component glyphs unless -% explicitly ignored (see below). -% -% For compatibility with \XETEX an alternative -% \identifier{letterspace} option is supplied that interprets the -% supplied value as a \emphasis{percentage} of the font size but -% is otherwise identical to \identifier{kernfactor}. -% Consequently, both definitions in below snippet yield the same -% letterspacing width: -% -% \begin{quote} -% \begin{verbatim} -% \font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125" -% \font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5" -% \end{verbatim} -% \end{quote} -% -% Specific pairs of letters and ligatures may be exempt from -% letterspacing by defining the \LUA functions -% \luafunction{keeptogether} and \luafunction{keepligature}, -% respectively, inside the namespace \verb|luaotfload.letterspace|. -% Both functions are called whenever the letterspacing callback -% encounters an appropriate node or set of nodes. -% If they return a true-ish value, no extra kern is inserted at -% the current position. -% \luafunction{keeptogether} receives a pair of consecutive -% glyph nodes in order of their appearance in the node list. -% \luafunction{keepligature} receives a single node which can be -% analyzed into components. -% (For details refer to the \emphasis{glyph nodes} section in the -% \LUATEX reference manual.) -% The implementation of both functions is left entirely to the -% user. -% -% -% \item [protrusion \& expansion] \hfill \\ -% These keys control microtypographic features of the font, -% namely \emphasis{character protrusion} and \emphasis{font -% expansion}. -% Their arguments are names of \LUA tables that contain -% values for the respective features.\footnote{% -% For examples of the table layout please refer to the -% section of the file \fileent{luaotfload-fonts-ext.lua} where the -% default values are defined. -% Alternatively and with loss of information, you can dump -% those tables into your terminal by issuing -% \begin{verbatim} -% \directlua{inspect(fonts.protrusions.setups.default) -% inspect(fonts.expansions.setups.default)} -% \end{verbatim} -% at some point after loading \fileent{luaotfload.sty}. -% } -% For both, only the set \identifier{default} is predefined. -% -% For example, to define a font with the default -% protrusion vector applied\footnote{% -% You also need to set -% \verb|pdfprotrudechars=2| and -% \verb|pdfadjustspacing=2| -% to activate protrusion and expansion, respectively. -% See the -% \href{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% -% {\PDFTEX manual} -% for details. -% }: -% -% \begin{quote} -% \begin{verbatim} -% \font\test=LatinModernRoman:protrusion=default -% \end{verbatim} -% \end{quote} -% \end{description} -% -% \paragraph{Non-standard font features} -% \identifier{luaotfload} adds a number of features that are not defined -% in the original \OpenType specification, most of them -% aiming at emulating the behavior familiar from other \TEX engines. -% Currently (2014) there are three of them: -% -% \begin{description} -% -% \item [anum] -% Substitutes the glyphs in the \abbrev{ascii} number range -% with their counterparts from eastern Arabic or Persian, -% depending on the value of \identifier{language}. -% -% \item [tlig] -% Applies legacy \TEX ligatures: -% -% \begin{tabular}{rlrl} -% `` & \verb|``| & '' & \verb|''| \\ -% ` & \verb|`| & ' & \verb|'| \\ -% " & \verb|"| & -- & \verb|--| \\ -% --- & \verb|---| & !` & \verb|!`| \\ -% ?` & \verb|?`| & & \\ -% \end{tabular} -% -% \footnote{% -% These contain the feature set \verb|trep| of earlier -% versions of \identifier{luaotfload}. -% -% Note to \XETEX users: this is the equivalent of the -% assignment \verb|mapping=text-tex| using \XETEX's input -% remapping feature. -% } -% -% \item [itlc] -% Computes italic correction values (active by default). -% -% \end{description} -% -% -% -% \section{Font names database} -% \label{sec:fontdb} -% -% As mentioned above, \identifier{luaotfload} keeps track of which -% fonts are available to \LUATEX by means of a \emphasis{database}. -% This allows referring to fonts not only by explicit filenames but -% also by the proper names contained in the metadata which is often -% more accessible to humans.\footnote{% -% The tool \href{http://www.lcdf.org/type/}{\fileent{otfinfo}} (comes -% with \TEX Live), when invoked on a font file with the \verb|-i| -% option, lists the variety of name fields defined for it. -% } -% -% When \identifier{luaotfload} is asked to load a font by a font name, -% it will check if the database exists and load it, or else generate a -% fresh one. -% Should it then fail to locate the font, an update to the database is -% performed in case the font has been added to the system only -% recently. As soon as the database is updated, the resolver will try -% and look up the font again, all without user intervention. -% The goal is for \identifier{luaotfload} to act in the background and -% behave as unobtrusively as possible, while providing a convenient -% interface to the fonts installed on the system. -% -% Generating the database for the first time may take a while since it -% inspects every font file on your computer. -% This is particularly noticeable if it occurs during a typesetting run. -% In any case, subsequent updates to the database will be quite fast. -% -% \subsection[luaotfload-tool / mkluatexfontdb.lua]% -% {\fileent{luaotfload-tool} / -% \fileent{mkluatexfontdb.lua}\footnote{% -% The script may be named just \fileent{mkluatexfontdb} in your -% distribution. -% }} -% -% It can still be desirable at times to do some of these steps -% manually, and without having to compile a document. -% To this end, \identifier{luaotfload} comes with the utility -% \fileent{luaotfload-tool} that offers an interface to the database -% functionality. -% Being a \LUA script, there are two ways to run it: -% either make it executable (\verb|chmod +x| on unixoid systems) or -% pass it as an argument to \fileent{texlua}.\footnote{% -% Tests by the maintainer show only marginal performance gain by -% running with Luigi Scarso’s -% \href{https://foundry.supelec.fr/projects/luajittex/}% -% {\identifier{Luajit\kern-.25ex\TEX}}, -% which is probably due to the fact that most of the time is spent -% on file system operations. -% -% \emphasis{Note}: -% On \abbrev{MS} \identifier{Windows} systems, the script can be run -% either by calling the wrapper application -% \fileent{luaotfload-tool.exe} or as -% \verb|texlua.exe luaotfload-tool.lua|. -% } -% Invoked with the argument \verb|--update| it will perform a database -% update, scanning for fonts not indexed. -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --update -% \end{verbatim} -% \end{quote} -% -% Adding the \verb|--force| switch will initiate a complete -% rebuild of the database. -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --update --force -% \end{verbatim} -% \end{quote} -% -% For sake of backwards compatibility, \fileent{luaotfload-tool} may be -% renamed or symlinked to \fileent{mkluatexfontdb}. -% Whenever it is run under this name, it will update the database -% first, mimicking the behavior of earlier versions of -% \identifier{luaotfload}. -% -% \subsection{Search Paths} -% -% \identifier{luaotfload} scans those directories where fonts are -% expected to be located on a given system. -% On a Linux machine it follows the paths listed in the -% \identifier{Fontconfig} configuration files; -% consult \verb|man 5 fonts.conf| for further information. -% On \identifier{Windows} systems, the standard location is -% \verb|Windows\Fonts|, -% while \identifier{Mac OS~X} requires a multitude of paths to -% be examined. -% The complete list is is given in table \ref{table-searchpaths}. -% Other paths can be specified by setting the environment variable -% \verb+OSFONTDIR+. -% If it is non-empty, then search will be extended to the included -% directories. -% -% \begin{table}[t] -% \hrule -% \caption{List of paths searched for each supported operating -% system.} -% \renewcommand{\arraystretch}{1.2} -% \begin{center} -% \begin{tabular}{lp{.5\textwidth}} -% Windows & \verb|%WINDIR%\Fonts| -% \\ -% Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break -% \fileent{/etc/fonts/fonts.conf} -% \\ -% Mac & \fileent{\textasciitilde/Library/Fonts},\break -% \fileent{/Library/Fonts},\break -% \fileent{/System/Library/Fonts}, and\hfill\break -% \fileent{/Network/Library/Fonts} -% \\ -% \end{tabular} -% \end{center} -% \label{table-searchpaths} -% \hrule -% \end{table} -% -% \subsection{Querying from Outside} -% -% \fileent{luaotfload-tool} also provides rudimentary means of -% accessing the information collected in the font database. -% If the option \verb|--find=|\emphasis{name} is given, the script will -% try and search the fonts indexed by \identifier{luaotfload} for a -% matching name. -% For instance, the invocation -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --find="Iwona Regular" -% \end{verbatim} -% \end{quote} -% -% \noindent -% will verify if “Iwona Regular” is found in the database and can be -% readily requested in a document. -% -% If you are unsure about the actual font name, then add the -% \verb|-F| (or \verb|--fuzzy|) switch to the command line to enable -% approximate matching. -% Suppose you cannot precisely remember if the variant of -% \identifier{Iwona} you are looking for was “Bright” or “Light”. -% The query -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -F --find="Iwona Bright" -% \end{verbatim} -% \end{quote} -% -% \noindent -% will tell you that indeed the latter name is correct. -% -% Basic information about fonts in the database can be displayed -% using the \verb|-i| option (\verb|--info|). -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -i --find="Iwona Light Italic" -% \end{verbatim} -% \end{quote} -% \noindent -% The meaning of the printed values is described in section 4.4 of the -% \LUATEX reference manual.\footnote{% -% In \TEX Live: \fileent{texmf-dist/doc/luatex/base/luatexref-t.pdf}. -% } -% -% For a much more detailed report about a given font try the \verb|-I| option -% instead (\verb|--inspect|). -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -I --find="Iwona Light Italic" -% \end{verbatim} -% \end{quote} -% -% \verb|luaotfload-tool --help| will list the available command line -% switches, including some not discussed in detail here. -% For a full documentation of \identifier{luaotfload-tool} and its -% capabilities refer to the manpage -% (\verb|man 1 luaotfload-tool|).\footnote{% -% Or see \verb|luaotfload-tool.rst| in the source directory. -% } -% -% \subsection{Blacklisting Fonts} -% \label{font-blacklist} -% -% Some fonts are problematic in general, or just in \LUATEX. -% If you find that compiling your document takes far too long or eats -% away all your system’s memory, you can track down the culprit by -% running \verb|luaotfload-tool -v| to increase verbosity. -% Take a note of the \emphasis{filename} of the font that database -% creation fails with and append it to the file -% \fileent{luaotfload-blacklist.cnf}. -% -% A blacklist file is a list of font filenames, one per line. -% Specifying the full path to where the file is located is optional, the -% plain filename should suffice. -% File extensions (\fileent{.otf}, \fileent{.ttf}, etc.) may be omitted. -% Anything after a percent (|%|) character until the end of the line -% is ignored, so use this to add comments. -% Place this file to some location where the \identifier{kpse} -% library can find it, e.~g. -% \fileent{texmf-local/tex/luatex/luaotfload} if you are running -% \identifier{\TEX Live},\footnote{% -% You may have to run \verb|mktexlsr| if you created a new file in -% your \fileent{texmf} tree. -% } -% or just leave it in the working directory of your document. -% \identifier{luaotfload} reads all files named -% \fileent{luaotfload-blacklist.cnf} it finds, so the fonts in -% \fileent{./luaotfload-blacklist.cnf} extend the global blacklist. -% -% Furthermore, a filename prepended with a dash character (|-|) is -% removed from the blacklist, causing it to be temporarily whitelisted -% without modifying the global file. -% An example with explicit paths: -% -% \begin{verbatim} -% % example otf-blacklist.cnf -% /Library/Fonts/GillSans.ttc % Luaotfload ignores this font. -% -/Library/Fonts/Optima.ttc % This one is usable again, even if -% % blacklisted somewhere else. -% \end{verbatim} -% -% \section{Files from \CONTEXT and \LUATEX-Fonts} -% -% \identifier{luaotfload} relies on code originally written by Hans -% Hagen\footnote{% -% The creator of the \href{http://wiki.contextgarden.net}{\CONTEXT} -% format. -% } -% for and tested with \CONTEXT. -% It integrates the font loader as distributed in -% the \identifier{\LUATEX-Fonts} package. -% The original \LUA source files have been combined using the -% \fileent{mtx-package} script into a single, self-contained blob. -% In this form the font loader has no further dependencies\footnote{% -% It covers, however, to some extent the functionality of the -% \identifier{lualibs} package. -% } -% and requires only minor adaptions to integrate into -% \identifier{luaotfload}. -% The guiding principle is to let \CONTEXT/\LUATEX-Fonts take care of -% the implementation, and update the imported code from time to time. -% As maintainers, we aim at importing files from upstream essentially -% \emphasis{unmodified}, except for renaming them to prevent name -% clashes. -% This job has been greatly alleviated since the advent of -% \LUATEX-Fonts, prior to which the individual dependencies had to be -% manually spotted and extracted from the \CONTEXT source code in a -% complicated and error-prone fashion. -% -% Below is a commented list of the files distributed with -% \identifier{luaotfload} in one way or the other. -% See figure \ref{file-graph} on page \pageref{file-graph} for a -% graphical representation of the dependencies. -% From \LUATEX-Fonts, only the file \fileent{luatex-fonts-merged.lua} -% has been imported as \fileent{luaotfload-fontloader.lua}. -% It is generated by \fileent{mtx-package}, a \LUA source code merging -% too developed by Hans Hagen.\footnote{% -% \fileent{mtx-package} is -% \href -% {http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua} -% {part of \CONTEXT} -% and requires \fileent{mtxrun}. -% Run -% \verb|mtxrun --script package --help| -% to display further information. -% For the actual merging code see the file -% \fileent{util-mrg.lua} that is part of \CONTEXT. -% } -% It houses several \LUA files that can be classed in three -% categories. -% -% \begin{itemize} -% \let\normalitem=\item -% \def\incitem#1{% -% \normalitem{\fileent{#1}} -% } -% \normalitem \emphasis{\LUA utility libraries}, a subset -% of what is provided by the \identifier{lualibs} -% package. -% -% \begin{multicols}{2} -% \begin{itemize} -% \incitem{l-lua.lua} \incitem{l-lpeg.lua} -% \incitem{l-function.lua} \incitem{l-string.lua} -% \incitem{l-table.lua} \incitem{l-io.lua} -% \incitem{l-file.lua} \incitem{l-boolean.lua} -% \incitem{l-math.lua} \incitem{util-str.lua} -% \end{itemize} -% \end{multicols} -% -% \normalitem The \emphasis{font loader} itself. -% These files have been written for -% \LUATEX-Fonts and they are distributed along -% with \identifier{luaotfload}. -% \begin{multicols}{2} -% \begin{itemize} -% \incitem{luatex-basics-gen.lua} -% \incitem{luatex-basics-nod.lua} -% \incitem{luatex-fonts-enc.lua} -% \incitem{luatex-fonts-syn.lua} -% \incitem{luatex-fonts-tfm.lua} -% \incitem{luatex-fonts-chr.lua} -% \incitem{luatex-fonts-lua.lua} -% \incitem{luatex-fonts-inj.lua} -% \incitem{luatex-fonts-otn.lua} -% \incitem{luatex-fonts-def.lua} -% \incitem{luatex-fonts-ext.lua} -% \incitem{luatex-fonts-cbk.lua} -% \end{itemize} -% \end{multicols} -% -% \normalitem Code related to \emphasis{font handling and -% node processing}, taken directly from -% \CONTEXT. -% \begin{multicols}{2} -% \begin{itemize} -% \incitem{data-con.lua} \incitem{font-ini.lua} -% \incitem{font-con.lua} \incitem{font-cid.lua} -% \incitem{font-map.lua} \incitem{font-oti.lua} -% \incitem{font-otf.lua} \incitem{font-otb.lua} -% \incitem{font-ota.lua} \incitem{font-def.lua} -% \incitem{font-otp.lua} -% \end{itemize} -% \end{multicols} -% \end{itemize} -% -% Note that if \identifier{luaotfload} cannot locate the -% merged file, it will load the individual \LUA libraries -% instead. -% Their names remain the same as in \CONTEXT (without the -% \verb|otfl|-prefix) since we imported the relevant section of -% \fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload-main.lua}. -% Thus if you prefer running bleeding edge code from the -% \CONTEXT beta, all you have to do is remove -% \fileent{luaotfload-merged.lua} from the search path. -% -% Also, the merged file at some point -% loads the Adobe Glyph List from a \LUA table that is contained in -% \fileent{luaotfload-glyphlist.lua}, which is automatically generated by the -% script \fileent{mkglyphlist}.\footnote{% -% See \fileent{luaotfload-font-enc.lua}. -% The hard-coded file name is why we have to replace the procedure -% that loads the file in \fileent{luaotfload-override.lua}. -% } -% There is a make target \identifier{glyphs} that will create a fresh -% glyph list so we don’t need to import it from \CONTEXT -% any longer. -% -% In addition to these, \identifier{luaotfload} requires a number of -% files not contained in the merge. Some of these have no equivalent in -% \LUATEX-Fonts or \CONTEXT, some were taken unmodified from the -% latter. -% -% \begin{itemize} -% \let\normalitem=\item -% \def\ouritem#1{% -% \normalitem{\fileent{#1}}% -% \space--\hskip1em -% } -% \ouritem {luaotfload-features.lua} font feature handling; -% incorporates some of the code from -% \fileent{font-otc} from \CONTEXT; -% \ouritem {luaotfload-override.lua} overrides the \CONTEXT logging -% functionality. -% \ouritem {luaotfload-loaders.lua} registers the \OpenType -% font reader as handler for -% Postscript fonts -% (\abbrev{pfa}, \abbrev{pfb}). -% \ouritem {luaotfload-parsers.lua} various \abbrev{lpeg}-based parsers. -% \ouritem {luaotfload-database.lua} font names database. -% \ouritem {luaotfload-colors.lua} color handling. -% \ouritem {luaotfload-auxiliary.lua} access to internal functionality -% for package authors -% (proposals for additions welcome). -% \ouritem {luaotfload-letterspace.lua} font-based letterspacing. -% \end{itemize} -% -% \begin{figure}[b] -% \caption{Schematic of the files in \identifier{Luaotfload}} -% \includegraphics[width=\textwidth]{filegraph.pdf} -% \label{file-graph} -% \end{figure} -% -% \section{Auxiliary Functions} -% -% With release version 2.2, \identifier{luaotfload} received -% additional functions for package authors to call from outside -% (see the file \fileent{luaotfload-auxiliary.lua} for details). -% The purpose of this addition twofold. -% Firstly, \identifier{luaotfload} failed to provide a stable interface -% to internals in the past which resulted in an unmanageable situation -% of different packages abusing the raw access to font objects by means -% of the \luafunction{patch_font} callback. -% When the structure of the font object changed due to an update, all -% of these imploded and several packages had to be fixed while -% simultaneously providing fallbacks for earlier versions. -% Now the patching is done on the \identifier{luaotfload} side and can -% be adapted with future modifications to font objects without touching -% the packages that depend on it. -% Second, some the capabilities of the font loader and the names -% database are not immediately relevant in \identifier{luaotfload} -% itself but might nevertheless be of great value to package authors or -% end users. -% -% Note that the current interface is not yet set in stone and the -% development team is open to suggestions for improvements or -% additions. -% -% \subsection{Callback Functions} -% -% The \luafunction{patch_font} callback is inserted in the wrapper -% \identifier{luaotfload} provides for the font definition callback -% (see below, page \pageref{define-font}). -% At this place it allows manipulating the font object immediately after -% the font loader is done creating it. -% For a short demonstration of its usefulness, here is a snippet that -% writes an entire font object to the file \fileent{fontdump.lua}: -% -% \begin{quote} -% \begin{verbatim} -% \input luaotfload.sty -% \directlua{ -% local dumpfile = "fontdump.lua" -% local dump_font = function (tfmdata) -% local data = table.serialize(tfmdata) -% io.savedata(dumpfile, data) -% end -% -% luatexbase.add_to_callback( -% "luaotfload.patch_font", -% dump_font, -% "my_private_callbacks.dump_font" -% ) -% } -% \font\dumpme=name:Iwona -% \bye -% \end{verbatim} -% \end{quote} -% -% \emphasis{Beware}: this creates a Lua file of around 150,000 lines of -% code, taking up 3~\abbrev{mb} of disk space. -% By inspecting the output you can get a first impression of how a font -% is structured in \LUATEX’s memory, what elements it is composed of, -% and in what ways it can be rearranged. -% -% \subsubsection{Compatibility with Earlier Versions} -% -% As has been touched on in the preface to this section, the structure -% of the object as returned by the fontloader underwent rather drastic -% changes during different stages of its development, and not all -% packages that made use of font patching have kept up with every one -% of it. -% To ensure compatibility with these as well as older versions of -% some packages, \identifier{luaotfload} sets up copies of or references -% to data in the font table where it used to be located. -% For instance, important parameters like the requested point size, the -% units factor, and the font name have again been made accessible from -% the toplevel of the table even though they were migrated to different -% subtables in the meantime. -% -% \subsubsection{Patches} -% -% These are mostly concerned with establishing compatibility with -% \XETEX. -% -% \begin{itemize} -% \let\normalitem=\item -% \def\ouritem#1{% -% \normalitem{\luafunction{#1}}% -% \hfill\break -% } -% -% \ouritem {set_sscale_dimens} -% Calculate \texmacro{fontdimen}s 10 and 11 to emulate \XETEX. -% -% \ouritem {set_capheight} -% Calculates \texmacro{fontdimen} 8 like \XETEX. -% -% \ouritem {patch_cambria_domh} -% Correct some values of the font \emphasis{Cambria Math}. -% -% \end{itemize} -% -% \subsection{Package Author’s Interface} -% -% As \LUATEX release 1.0 is nearing, the demand for a reliable interface -% for package authors increases. -% -% \subsubsection{Font Properties} -% -% Below functions mostly concern querying the different components of a -% font like for instance the glyphs it contains, or what font features -% are defined for which scripts. -% -% \begin{itemize} -% \let\normalitem=\item -% \def\ouritem#1{% -% \normalitem{\luafunction{#1}}% -% \hfill\break -% } -% -% \ouritem {aux.font_has_glyph (id : int, index : int)} -% Predicate that returns true if the font \luafunction{id} -% has glyph \luafunction{index}. -% -% \ouritem {aux.slot_of_name(name : string)} -% Translates an Adobe Glyph name to the corresponding glyph -% slot. -% -% \ouritem {aux.name_of_slot(slot : int)} -% The inverse of \luafunction{slot_of_name}; note that this -% might be incomplete as multiple glyph names may map to the -% same codepoint, only one of which is returned by -% \luafunction{name_of_slot}. -% -% \ouritem {aux.provides_script(id : int, script : string)} -% Test if a font supports \luafunction{script}. -% -% \ouritem {aux.provides_language(id : int, script : string, language : string)} -% Test if a font defines \luafunction{language} for a given -% \luafunction{script}. -% -% \ouritem {aux.provides_feature(id : int, script : string, -% language : string, feature : string)} -% Test if a font defines \luafunction{feature} for -% \luafunction{language} for a given \luafunction{script}. -% -% \ouritem {aux.get_math_dimension(id : int, dimension : string)} -% Get the dimension \luafunction{dimension} of font \luafunction{id}. -% -% \ouritem {aux.sprint_math_dimension(id : int, dimension : string)} -% Same as \luafunction{get_math_dimension()}, but output the value -% in scaled points at the \TEX end. -% -% \end{itemize} -% -% \subsubsection{Database} -% -% \begin{itemize} -% \let\normalitem=\item -% \def\ouritem#1{% -% \normalitem{\luafunction{#1}}% -% \hfill\break -% } -% -% \ouritem {aux.scan_external_dir(dir : string)} -% Include fonts in directory \luafunction{dir} in font lookups without -% adding them to the database. -% -% \end{itemize} -% -% \section{Troubleshooting} -% -% \subsection {Database Generation} -% If you encounter problems with some fonts, please first update to the latest -% version of this package before reporting a bug, as -% \identifier{luaotfload} is under active development and still a -% moving target. -% The development takes place on \identifier{github} at -% \url{https://github.com/lualatex/luaotfload} where there is an issue -% tracker for submitting bug reports, feature requests and the likes -% requests and the likes. -% -% Bug reports are more likely to be addressed if they contain the output of -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --diagnose=environment,files,permissions -% \end{verbatim} -% \end{quote} -% -% \noindent Consult the man page for a description of these options. -% -% Errors during database generation can be traced by increasing the -% verbosity level and redirecting log output to \fileent{stdout}: -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -fuvvv --log=stdout -% \end{verbatim} -% \end{quote} -% -% \noindent or to a file in \fileent{/tmp}: -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool -fuvvv --log=file -% \end{verbatim} -% \end{quote} -% -% \noindent In the latter case, invoke the \verb|tail(1)| utility on the file -% for live monitoring of the progress. -% -% If database generation fails, the font last printed to the terminal or log -% file is likely to be the culprit. -% Please specify it when reporting a bug, and blacklist it for the time -% being (see above, page \pageref{font-blacklist}). -% -% \subsection {Font Features} -% A common problem is the lack of features for some -% \OpenType fonts even when specified. -% This can be related to the fact that some fonts do not provide -% features for the \verb|dflt| script (see above on page -% \pageref{script-tag}), -% which is the default one in this package. -% If this happens, assigning a noth script when the font is defined should -% fix it. -% For example with \verb|latn|: -% -% \begin{quote} -% \begin{verbatim} -% \font\test=file:MyFont.otf:script=latn;+liga; -% \end{verbatim} -% \end{quote} -% -% You can get a list of features that a font defines for scripts and languages -% by querying it in \fileent{luaotfload-tool}: -% -% \begin{quote} -% \begin{verbatim} -% luaotfload-tool --find="Iwona" --inspect -% \end{verbatim} -% \end{quote} -% -% \subsection {\LUATEX Programming} -% Another strategy that helps avoiding problems is to not access raw \LUATEX -% internals directly. -% Some of them, even though they are dangerous to access, have not been -% overridden or disabled. -% Thus, whenever possible prefer the functions in the -% \luafunction{aux} namespace over direct manipulation of font objects. -% For example, raw access to the \luafunction{font.fonts} table like: -% -% \begin{quote} -% \begin{verbatim} -% local somefont = font.fonts[2] -% \end{verbatim} -% \end{quote} -% -% \noindent can render already defined fonts unusable. -% Instead, the function \luafunction{font.getfont()} should be used because -% it has been replaced by a safe variant. -% -% However, \luafunction{font.getfont()} only covers fonts handled by the font -% loader, e.~g. \identifier{OpenType} and \identifier{TrueType} fonts, but -% not \abbrev{tfm} or \abbrev{ofm}. -% Should you absolutely require access to all fonts known to \LUATEX, including -% the virtual and autogenerated ones, then you need to query both -% \luafunction{font.getfont()} and \luafunction{font.fonts}. -% In this case, best define you own accessor: -% -% \begin{quote} -% \begin{verbatim} -% local unsafe_getfont = function (id) -% local tfmdata = font.getfont (id) -% if not tfmdata then -% tfmdata = font.fonts[id] -% end -% return tfmdata -% end -% -% --- use like getfont() -% local somefont = unsafe_getfont (2) -% \end{verbatim} -% \end{quote} -% -% \part{Implementation} -% -% \section{\fileent{luaotfload.lua}} -% -% As of version 2.5, the file \fileent{luaotfload.lua} is no longer -% generated from the \abbrev{dtx}. -% Instead, it is maintained separately as a plain \identifier{Lua} file -% \fileent{luaotfload-main.lua} in the Luaotfload \identifier{git} tree. -% The file documentation which used to be found in this section has -% been preserved in the comments. -% -% \section{\fileent{luaotfload.sty}} -% -% As of version 2.5, the file \fileent{luaotfload.sty} is no longer -% generated from the \abbrev{dtx}. -% Instead, it is maintained separately as a plain \identifier{\TEX} file -% in the Luaotfload \identifier{git} tree. -% The file documentation which used to be found in this section has -% been preserved in the comments. -% -% \clearpage -% \section{The GNU GPL License v2} -% -% The GPL requires the complete license text to be distributed along -% with the code. I recommend the canonical source, instead: -% \url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. -% But if you insist on an included copy, here it is. -% You might want to zoom in. -% -% \newsavebox{\gpl} -% \begin{lrbox}{\gpl} -% \begin{minipage}{3\textwidth} -% \columnsep=3\columnsep -% \begin{multicols}{3} -% \begin{center} -% {\Large GNU GENERAL PUBLIC LICENSE\par} -% \bigskip -% {Version 2, June 1991} -% \end{center} -% -% \begin{center} -% {\parindent 0in -% -% Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. -% -% \bigskip -% -% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -% -% \bigskip -% -% Everyone is permitted to copy and distribute verbatim copies -% of this license document, but changing it is not allowed. -% } -% \end{center} -% -% \begin{center} -% {\bf\large Preamble} -% \end{center} -% -% -% The licenses for most software are designed to take away your freedom to -% share and change it. By contrast, the GNU General Public License is -% intended to guarantee your freedom to share and change free software---to -% make sure the software is free for all its users. This General Public -% License applies to most of the Free Software Foundation's software and to -% any other program whose authors commit to using it. (Some other Free -% Software Foundation software is covered by the GNU Library General Public -% License instead.) You can apply it to your programs, too. -% -% When we speak of free software, we are referring to freedom, not price. -% Our General Public Licenses are designed to make sure that you have the -% freedom to distribute copies of free software (and charge for this service -% if you wish), that you receive source code or can get it if you want it, -% that you can change the software or use pieces of it in new free programs; -% and that you know you can do these things. -% -% To protect your rights, we need to make restrictions that forbid anyone to -% deny you these rights or to ask you to surrender the rights. These -% restrictions translate to certain responsibilities for you if you -% distribute copies of the software, or if you modify it. -% -% For example, if you distribute copies of such a program, whether gratis or -% for a fee, you must give the recipients all the rights that you have. You -% must make sure that they, too, receive or can get the source code. And -% you must show them these terms so they know their rights. -% -% We protect your rights with two steps: (1) copyright the software, and (2) -% offer you this license which gives you legal permission to copy, -% distribute and/or modify the software. -% -% Also, for each author's protection and ours, we want to make certain that -% everyone understands that there is no warranty for this free software. If -% the software is modified by someone else and passed on, we want its -% recipients to know that what they have is not the original, so that any -% problems introduced by others will not reflect on the original authors' -% reputations. -% -% Finally, any free program is threatened constantly by software patents. -% We wish to avoid the danger that redistributors of a free program will -% individually obtain patent licenses, in effect making the program -% proprietary. To prevent this, we have made it clear that any patent must -% be licensed for everyone's free use or not licensed at all. -% -% The precise terms and conditions for copying, distribution and -% modification follow. -% -% \begin{center} -% {\Large \sc Terms and Conditions For Copying, Distribution and -% Modification} -% \end{center} -% -% \begin{enumerate} -% \item -% This License applies to any program or other work which contains a notice -% placed by the copyright holder saying it may be distributed under the -% terms of this General Public License. The ``Program'', below, refers to -% any such program or work, and a ``work based on the Program'' means either -% the Program or any derivative work under copyright law: that is to say, a -% work containing the Program or a portion of it, either verbatim or with -% modifications and/or translated into another language. (Hereinafter, -% translation is included without limitation in the term ``modification''.) -% Each licensee is addressed as ``you''. -% -% Activities other than copying, distribution and modification are not -% covered by this License; they are outside its scope. The act of -% running the Program is not restricted, and the output from the Program -% is covered only if its contents constitute a work based on the -% Program (independent of having been made by running the Program). -% Whether that is true depends on what the Program does. -% -% \item You may copy and distribute verbatim copies of the Program's source -% code as you receive it, in any medium, provided that you conspicuously -% and appropriately publish on each copy an appropriate copyright notice -% and disclaimer of warranty; keep intact all the notices that refer to -% this License and to the absence of any warranty; and give any other -% recipients of the Program a copy of this License along with the Program. -% -% You may charge a fee for the physical act of transferring a copy, and you -% may at your option offer warranty protection in exchange for a fee. -% -% \item -% You may modify your copy or copies of the Program or any portion -% of it, thus forming a work based on the Program, and copy and -% distribute such modifications or work under the terms of Section 1 -% above, provided that you also meet all of these conditions: -% -% \begin{enumerate} -% -% \item -% You must cause the modified files to carry prominent notices stating that -% you changed the files and the date of any change. -% -% \item -% You must cause any work that you distribute or publish, that in -% whole or in part contains or is derived from the Program or any -% part thereof, to be licensed as a whole at no charge to all third -% parties under the terms of this License. -% -% \item -% If the modified program normally reads commands interactively -% when run, you must cause it, when started running for such -% interactive use in the most ordinary way, to print or display an -% announcement including an appropriate copyright notice and a -% notice that there is no warranty (or else, saying that you provide -% a warranty) and that users may redistribute the program under -% these conditions, and telling the user how to view a copy of this -% License. (Exception: if the Program itself is interactive but -% does not normally print such an announcement, your work based on -% the Program is not required to print an announcement.) -% -% \end{enumerate} -% -% -% These requirements apply to the modified work as a whole. If -% identifiable sections of that work are not derived from the Program, -% and can be reasonably considered independent and separate works in -% themselves, then this License, and its terms, do not apply to those -% sections when you distribute them as separate works. But when you -% distribute the same sections as part of a whole which is a work based -% on the Program, the distribution of the whole must be on the terms of -% this License, whose permissions for other licensees extend to the -% entire whole, and thus to each and every part regardless of who wrote it. -% -% Thus, it is not the intent of this section to claim rights or contest -% your rights to work written entirely by you; rather, the intent is to -% exercise the right to control the distribution of derivative or -% collective works based on the Program. -% -% In addition, mere aggregation of another work not based on the Program -% with the Program (or with a work based on the Program) on a volume of -% a storage or distribution medium does not bring the other work under -% the scope of this License. -% -% \item -% You may copy and distribute the Program (or a work based on it, -% under Section 2) in object code or executable form under the terms of -% Sections 1 and 2 above provided that you also do one of the following: -% -% \begin{enumerate} -% -% \item -% -% Accompany it with the complete corresponding machine-readable -% source code, which must be distributed under the terms of Sections -% 1 and 2 above on a medium customarily used for software interchange; or, -% -% \item -% -% Accompany it with a written offer, valid for at least three -% years, to give any third party, for a charge no more than your -% cost of physically performing source distribution, a complete -% machine-readable copy of the corresponding source code, to be -% distributed under the terms of Sections 1 and 2 above on a medium -% customarily used for software interchange; or, -% -% \item -% -% Accompany it with the information you received as to the offer -% to distribute corresponding source code. (This alternative is -% allowed only for noncommercial distribution and only if you -% received the program in object code or executable form with such -% an offer, in accord with Subsection b above.) -% -% \end{enumerate} -% -% -% The source code for a work means the preferred form of the work for -% making modifications to it. For an executable work, complete source -% code means all the source code for all modules it contains, plus any -% associated interface definition files, plus the scripts used to -% control compilation and installation of the executable. However, as a -% special exception, the source code distributed need not include -% anything that is normally distributed (in either source or binary -% form) with the major components (compiler, kernel, and so on) of the -% operating system on which the executable runs, unless that component -% itself accompanies the executable. -% -% If distribution of executable or object code is made by offering -% access to copy from a designated place, then offering equivalent -% access to copy the source code from the same place counts as -% distribution of the source code, even though third parties are not -% compelled to copy the source along with the object code. -% -% \item -% You may not copy, modify, sublicense, or distribute the Program -% except as expressly provided under this License. Any attempt -% otherwise to copy, modify, sublicense or distribute the Program is -% void, and will automatically terminate your rights under this License. -% However, parties who have received copies, or rights, from you under -% this License will not have their licenses terminated so long as such -% parties remain in full compliance. -% -% \item -% You are not required to accept this License, since you have not -% signed it. However, nothing else grants you permission to modify or -% distribute the Program or its derivative works. These actions are -% prohibited by law if you do not accept this License. Therefore, by -% modifying or distributing the Program (or any work based on the -% Program), you indicate your acceptance of this License to do so, and -% all its terms and conditions for copying, distributing or modifying -% the Program or works based on it. -% -% \item -% Each time you redistribute the Program (or any work based on the -% Program), the recipient automatically receives a license from the -% original licensor to copy, distribute or modify the Program subject to -% these terms and conditions. You may not impose any further -% restrictions on the recipients' exercise of the rights granted herein. -% You are not responsible for enforcing compliance by third parties to -% this License. -% -% \item -% If, as a consequence of a court judgment or allegation of patent -% infringement or for any other reason (not limited to patent issues), -% conditions are imposed on you (whether by court order, agreement or -% otherwise) that contradict the conditions of this License, they do not -% excuse you from the conditions of this License. If you cannot -% distribute so as to satisfy simultaneously your obligations under this -% License and any other pertinent obligations, then as a consequence you -% may not distribute the Program at all. For example, if a patent -% license would not permit royalty-free redistribution of the Program by -% all those who receive copies directly or indirectly through you, then -% the only way you could satisfy both it and this License would be to -% refrain entirely from distribution of the Program. -% -% If any portion of this section is held invalid or unenforceable under -% any particular circumstance, the balance of the section is intended to -% apply and the section as a whole is intended to apply in other -% circumstances. -% -% It is not the purpose of this section to induce you to infringe any -% patents or other property right claims or to contest validity of any -% such claims; this section has the sole purpose of protecting the -% integrity of the free software distribution system, which is -% implemented by public license practices. Many people have made -% generous contributions to the wide range of software distributed -% through that system in reliance on consistent application of that -% system; it is up to the author/donor to decide if he or she is willing -% to distribute software through any other system and a licensee cannot -% impose that choice. -% -% This section is intended to make thoroughly clear what is believed to -% be a consequence of the rest of this License. -% -% \item -% If the distribution and/or use of the Program is restricted in -% certain countries either by patents or by copyrighted interfaces, the -% original copyright holder who places the Program under this License -% may add an explicit geographical distribution limitation excluding -% those countries, so that distribution is permitted only in or among -% countries not thus excluded. In such case, this License incorporates -% the limitation as if written in the body of this License. -% -% \item -% The Free Software Foundation may publish revised and/or new versions -% of the General Public License from time to time. Such new versions will -% be similar in spirit to the present version, but may differ in detail to -% address new problems or concerns. -% -% Each version is given a distinguishing version number. If the Program -% specifies a version number of this License which applies to it and ``any -% later version'', you have the option of following the terms and conditions -% either of that version or of any later version published by the Free -% Software Foundation. If the Program does not specify a version number of -% this License, you may choose any version ever published by the Free Software -% Foundation. -% -% \item -% If you wish to incorporate parts of the Program into other free -% programs whose distribution conditions are different, write to the author -% to ask for permission. For software which is copyrighted by the Free -% Software Foundation, write to the Free Software Foundation; we sometimes -% make exceptions for this. Our decision will be guided by the two goals -% of preserving the free status of all derivatives of our free software and -% of promoting the sharing and reuse of software generally. -% -% \begin{center} -% {\Large\sc -% No Warranty -% } -% \end{center} -% -% \item -% {\sc Because the program is licensed free of charge, there is no warranty -% for the program, to the extent permitted by applicable law. Except when -% otherwise stated in writing the copyright holders and/or other parties -% provide the program ``as is'' without warranty of any kind, either expressed -% or implied, including, but not limited to, the implied warranties of -% merchantability and fitness for a particular purpose. The entire risk as -% to the quality and performance of the program is with you. Should the -% program prove defective, you assume the cost of all necessary servicing, -% repair or correction.} -% -% \item -% {\sc In no event unless required by applicable law or agreed to in writing -% will any copyright holder, or any other party who may modify and/or -% redistribute the program as permitted above, be liable to you for damages, -% including any general, special, incidental or consequential damages arising -% out of the use or inability to use the program (including but not limited -% to loss of data or data being rendered inaccurate or losses sustained by -% you or third parties or a failure of the program to operate with any other -% programs), even if such holder or other party has been advised of the -% possibility of such damages.} -% -% \end{enumerate} -% -% -% \begin{center} -% {\Large\sc End of Terms and Conditions} -% \end{center} -% -% -% \pagebreak[2] -% -% \section*{Appendix: How to Apply These Terms to Your New Programs} -% -% If you develop a new program, and you want it to be of the greatest -% possible use to the public, the best way to achieve this is to make it -% free software which everyone can redistribute and change under these -% terms. -% -% To do so, attach the following notices to the program. It is safest to -% attach them to the start of each source file to most effectively convey -% the exclusion of warranty; and each file should have at least the -% ``copyright'' line and a pointer to where the full notice is found. -% -% \begin{quote} -% one line to give the program's name and a brief idea of what it does. \\ -% Copyright (C) yyyy name of author \\ -% -% This program is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% This program is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with this program; if not, write to the Free Software -% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -% \end{quote} -% -% Also add information on how to contact you by electronic and paper mail. -% -% If the program is interactive, make it output a short notice like this -% when it starts in an interactive mode: -% -% \begin{quote} -% Gnomovision version 69, Copyright (C) yyyy name of author \\ -% Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ -% This is free software, and you are welcome to redistribute it -% under certain conditions; type `show c' for details. -% \end{quote} -% -% -% The hypothetical commands {\tt show w} and {\tt show c} should show the -% appropriate parts of the General Public License. Of course, the commands -% you use may be called something other than {\tt show w} and {\tt show c}; -% they could even be mouse-clicks or menu items---whatever suits your -% program. -% -% You should also get your employer (if you work as a programmer) or your -% school, if any, to sign a ``copyright disclaimer'' for the program, if -% necessary. Here is a sample; alter the names: -% -% \begin{quote} -% Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\ -% `Gnomovision' (which makes passes at compilers) written by James Hacker. \\ -% -% signature of Ty Coon, 1 April 1989 \\ -% Ty Coon, President of Vice -% \end{quote} -% -% -% This General Public License does not permit incorporating your program -% into proprietary programs. If your program is a subroutine library, you -% may consider it more useful to permit linking proprietary applications -% with the library. If this is what you want to do, use the GNU Library -% General Public License instead of this License. -% -% \end{multicols} -% \end{minipage} -% \end{lrbox} -% -% \begin{center} -% \scalebox{0.33}{\usebox{\gpl}} -% \end{center} -% -% \Finale -\endinput -- cgit v1.2.3 From bf21af737122e0c904f305b3213b91710bb93216 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 15 Feb 2014 15:51:10 +0100 Subject: [doc,*] adapt makefiles to new doc source --- Makefile | 2 +- doc/Makefile | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index d00b237..a4b3fe7 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ TOOLNAME = luaotfload-tool TOOL = $(SRCSRCDIR)/$(TOOLNAME).lua GRAPH = filegraph -DOCSRC = $(DOCSRCDIR)/$(NAME).dtx +DOCSRC = $(addprefix $(DOCSRCDIR)/$(NAME), -main.tex -latex.tex) GRAPHSRC = $(DOCSRCDIR)/$(GRAPH).dot MANSRC = $(DOCSRCDIR)/$(TOOLNAME).rst diff --git a/doc/Makefile b/doc/Makefile index 2040f5a..9e2d591 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,6 +1,6 @@ NAME = luaotfload DOCPDF = $(NAME).pdf -DOCDTX = $(NAME).dtx +DOCSRC = $(NAME)-latex.tex SCRIPTNAME = luaotfload-tool MANSOURCE = $(SCRIPTNAME).rst @@ -23,10 +23,11 @@ all: manual doc graph: $(DOTPDF) manual: $(MANPAGE) -$(DOCPDF): $(DOCDTX) - @echo "creating PDF documentation ($(DOCPDF))" +$(DOCPDF): $(DOCSRC) + @echo "creating PDF documentation ($@)" $(DO_LATEX) $(DO_LATEX) + mv -f -- $(<:tex=pdf) $@ $(MANPAGE): $(MANSOURCE) @echo "creating man page ($(MANPAGE))" @@ -44,4 +45,4 @@ clean: mrproper: clean @$(RM) -- $(DOCS) -# vim:set noexpandtab:tabstop=8:shiftwidth=2 +# vim:noexpandtab:tabstop=8:shiftwidth=2 -- cgit v1.2.3 From 8a24f30e6a055de51461304d40ece070511530bb Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 16 Feb 2014 13:43:19 +0100 Subject: [doc] use abstract macros --- doc/luaotfload-latex.tex | 147 +++++++++- doc/luaotfload-main.tex | 684 ++++++++++++++++++++++------------------------- 2 files changed, 461 insertions(+), 370 deletions(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index 615ad42..d00a617 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -1,3 +1,4 @@ +\luatexsuppresslongerror1%% sigh ... %% Copyright (C) 2009-2014 %% %% by Elie Roux @@ -31,6 +32,8 @@ \documentclass{ltxdoc} +\makeatletter + \usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace} \usepackage[x11names]{xcolor} @@ -125,8 +128,150 @@ \VerbatimFootnotes +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% structurals +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def \definestructural #1{% + \expandafter \let \csname end#1\endcsname \relax + + \expandafter \def \csname begin#1\endcsname {% + \@ifnextchar[{\csname begin#1indeed\endcsname} + {\csname begin#1indeed\endcsname[]}% + } + + \expandafter \def \csname begin#1indeed\endcsname [##1]##2{% + \edef \first {##1}% + \ifx \first \empty + \csname #1\endcsname [##2]{##2}% + \else + \csname #1\endcsname [\first]{##2}% + \fi + } +} + +\definestructural {section} +\definestructural {subsection} +\definestructural {subsubsection} + +\def \fakesection #1{\section*{#1}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% codelistings; this sucks hard since we lack access to buffers +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcount \othercatcode \othercatcode 12 +\newcount \activecatcode \othercatcode 13 + +\newluatexcatcodetable \vrbcatcodes +\setluatexcatcodetable \vrbcatcodes {% + \luatexcatcodetable \CatcodeTableIniTeX + \catcode 9 \othercatcode %% \tabasciicode + \catcode 13 \othercatcode %% \endoflineasciicode + \catcode 12 \othercatcode %% \formfeedasciicode + \catcode 26 \othercatcode %% \endoffileasciicode + \catcode 32 \othercatcode %% \spaceasciicode +} + +\newluatexcatcodetable \literalcatcodes +\setluatexcatcodetable \literalcatcodes {% + \luatexcatcodetable \CatcodeTableString + \catcode 32 \activecatcode %% \spaceasciicode +} + +\def \beginlisting {% + \begingroup + \luatexcatcodetable \vrbcatcodes + \beginlistingindeed% +} + +\directlua { + local texprint = tex.print + document = document or { } + document.printlines = function (buffer) + for _, line in next, string.explode (buffer, "\noexpand\n") do + texprint (-1, line) + texprint (-1, "") + end + end +} + +\def \beginlistingindeed#1\endlisting{% + \endgroup + \begingroup + \ttfamily + \small + \begin {quote} + \bgroup + \luatexcatcodetable \literalcatcodes + \obeyspaces + \obeylines + \directlua{document.printlines ([==[\detokenize {#1}]==])} + \egroup + \end {quote} + \endgroup +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% enumerations and lists +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def \definelist [#1]#2{% name, itemcode + \expandafter \def \csname begin#1\endcsname {% + \begin {itemize} + \let \normalitem = \item + \def \altitem ####1{% + \def \first {####1}% + #2 + } + } + + \expandafter \def \csname end#1\endcsname {% + \end {itemize} + } +} + +\definelist [definitions]{\normalitem {\fileent {\first}}} +\definelist [filelist]{\normalitem {\fileent {\first}}\space--\hskip 1em} +\definelist [functionlist]{\normalitem {\luafunction {\first}}\hfill\break} + +\def \beginenumeration {\begin {enumerate}} +\def \endenumeration {\end {enumerate}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% columns +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def \begindoublecolumns {\begin {multicols} {2}} +\def \enddoublecolumns {\end {multicols}} + +\def \begintriplecolumns {\begin {multicols} {3}} +\def \endtriplecolumns {\end {multicols}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% alignment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def \begincentered {\begin {center}} +\def \endcentered {\end {center}} +\makeatother + +\def \beginnarrower {\begin {quote}} +\def \endnarrower {\end {quote}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% special elements +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def \beginabstractcontent {\begin {abstract}} +\def \endabstractcontent {\end {abstract}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% main +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \begin {document} - \input {luaotfload-main.tex} + \input {luaotfload-main.tex} \end {document} diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 49d1986..37127b7 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -37,19 +37,17 @@ \maketitle -\begin{abstract} +\beginabstractcontent This package is an adaptation of the \CONTEXT font loading system. It allows for loading \OpenType fonts with an extended syntax and adds support for a variety of font features. -\end{abstract} +\endabstractcontent \tableofcontents -\part{Package Description} - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\section{Introduction} +\beginsection {Introduction} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Font management and installation has always been painful with \TEX. A @@ -95,8 +93,10 @@ for handling \OpenType fonts. Additionally, it provides means for accessing fonts known to the operating system conveniently by indexing the metadata. +\endsection + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\section{Thanks} +\beginsection {Thanks} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \identifier{Luaotfload} is part of \LUALATEX, the community-driven @@ -119,18 +119,21 @@ code to \LATEX a breeze due to the extra effort he invested into isolating it from the rest of \CONTEXT, not to mention his assistance in the task and willingness to respond to our suggestions. +\endsection -\section{Loading Fonts} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\beginsection {Loading Fonts} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \identifier{luaotfload} supports an extended font request syntax: -\begin{quote} +\beginnarrower |\font\foo={|% \meta{prefix}|:|% \meta{font name}|:|% \meta{font features}|}|% \meta{\TEX font features} -\end{quote} +\endnarrower \noindent The curly brackets are optional and escape the spaces in the enclosed @@ -208,14 +211,14 @@ for a more formal description see figure \ref{font-syntax}. \label{font-syntax} \end {figure} -\subsection{Prefix -- the \identifier{luaotfload}{ }Way} +\beginsubsection{Prefix -- the \identifier{luaotfload}{ }Way} In \identifier{luaotfload}, the canonical syntax for font requests requires a \emphasis{prefix}: % -\begin{quote} +\beginnarrower |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots -\end{quote} +\endnarrower % where \meta{prefix} is either \verb|file:| or \verb|name:|.\footnote{% The development version also knows two further prefixes, @@ -300,7 +303,9 @@ The file names corresponding to the example font names above are \fileent{GFSBodoni.otf}, and \fileent{PTZ56F.ttf}. -\subsection{Compatibility Layer} +\endsubsection + +\beginsubsection {Compatibility Layer} In addition to the regular prefixed requests, \identifier{luaotfload} accepts loading fonts the \XETEX way. @@ -308,9 +313,9 @@ accepts loading fonts the \XETEX way. There are again two modes: bracketed and unbracketed. A bracketed request looks as follows. -\begin{quote} +\beginnarrower |\font\fontname=[|\meta{path to file}|]| -\end{quote} +\endnarrower \noindent Inside the square brackets, every character except for a closing @@ -319,9 +324,9 @@ bracket is permitted, allowing for specifying paths to a font file. Naturally, path-less file names are equally valid and processed the same way as an ordinary \verb|file:| lookup. -\begin{quote} +\beginnarrower |\font\fontname=|\meta{font name} \dots -\end{quote} +\endnarrower Unbracketed (or, for lack of a better word: \emphasis{anonymous}) font requests resemble the conventional \TEX syntax. @@ -337,9 +342,9 @@ fall back to a \verb|file:| lookup if no database entry matches Furthermore, \identifier{luaotfload} supports the slashed (shorthand) font style notation from \XETEX. -\begin{quote} +\beginnarrower |\font\fontname=|\meta{font name}|/|\meta{modifier}\dots -\end{quote} +\endnarrower \noindent Currently, four style modifiers are supported: @@ -350,18 +355,18 @@ Currently, four style modifiers are supported: Other “slashed” modifiers are too specific to the \XETEX engine and have no meaning in \LUATEX. -\subsection{Examples} +\endsubsection + +\beginsubsection{Examples} -\subsubsection{Loading by File Name} +\beginsubsubsection{Loading by File Name} For example, conventional \abbrev{type1} font can be loaded with a \verb|file:| request like so: -\begin{quote} - \begin{verbatim} - \font\lmromanten={file:ec-lmr10} at 10pt - \end{verbatim} -\end{quote} +\beginlisting + \font \lmromanten = {file:ec-lmr10} at 10pt +\endlisting The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa Półtawskiego}\footnote{% @@ -370,49 +375,42 @@ Półtawskiego}\footnote{% } in its condensed variant can be loaded as follows: -\begin{quote} - \begin{verbatim} - \font\apcregular=file:antpoltltcond-regular.otf at 42pt - \end{verbatim} -\end{quote} +\beginlisting + \font \apcregular = file:antpoltltcond-regular.otf at 42pt +\endlisting The next example shows how to load the \emphasis{Porson} font digitized by the Greek Font Society using \XETEX-style syntax and an absolute path from a non-standard directory: -\begin{quote} - \begin{verbatim} - \font\gfsporson="[/tmp/GFSPorson.otf]" at 12pt - \end{verbatim} -\end{quote} +\beginlisting + \font \gfsporson = "[/tmp/GFSPorson.otf]" at 12pt +\endlisting + +\endsubsubsection -\subsubsection{Loading by Font Name} +\beginsubsubsection{Loading by Font Name} The \verb|name:| lookup does not depend on cryptic filenames: -\begin{quote} - \begin{verbatim} - \font\pagellaregular={name:TeX Gyre Pagella} at 9pt - \end{verbatim} -\end{quote} +\beginlisting + \font \pagellaregular = {name:TeX Gyre Pagella} at 9pt +\endlisting A bit more specific but essentially the same lookup would be: -\begin{quote} - \begin{verbatim} - \font\pagellaregular={name:TeX Gyre Pagella Regular} at 9pt - \end{verbatim} -\end{quote} +\beginlisting + \font \pagellaregular = {name:TeX Gyre Pagella Regular} at 9pt +\endlisting \noindent Which fits nicely with the whole set: -\begin{quote} - \begin{verbatim} - \font\pagellaregular ={name:TeX Gyre Pagella Regular} at 9pt - \font\pagellaitalic ={name:TeX Gyre Pagella Italic} at 9pt - \font\pagellabold ={name:TeX Gyre Pagella Bold} at 9pt - \font\pagellabolditalic={name:TeX Gyre Pagella Bolditalic} at 9pt +\beginlisting + \font\pagellaregular = {name:TeX Gyre Pagella Regular} at 9pt + \font\pagellaitalic = {name:TeX Gyre Pagella Italic} at 9pt + \font\pagellabold = {name:TeX Gyre Pagella Bold} at 9pt + \font\pagellabolditalic = {name:TeX Gyre Pagella Bolditalic} at 9pt {\pagellaregular foo bar baz\endgraf} {\pagellaitalic foo bar baz\endgraf} @@ -420,10 +418,11 @@ Which fits nicely with the whole set: {\pagellabolditalic foo bar baz\endgraf} ... - \end{verbatim} -\end{quote} +\endlisting -\subsubsection{Modifiers} +\endsubsubsection + +\beginsubsubsection{Modifiers} If the entire \emphasis{Iwona} family\footnote{% \url{http://jmn.pl/kurier-i-iwona/}, @@ -432,47 +431,47 @@ If the entire \emphasis{Iwona} family\footnote{% is installed in some location accessible by \identifier{luaotfload}, the regular shape can be loaded as follows: -\begin{quote} - \begin{verbatim} +\beginlisting \font\iwona=Iwona at 20pt - \end{verbatim} -\end{quote} +\endlisting \noindent To load the most common of the other styles, the slash notation can be employed as shorthand: -\begin{quote} - \begin{verbatim} +\beginlisting \font\iwonaitalic =Iwona/I at 20pt \font\iwonabold =Iwona/B at 20pt \font\iwonabolditalic=Iwona/BI at 20pt - \end{verbatim} -\end{quote} +\endlisting \noindent which is equivalent to these full names: -\begin{quote} - \begin{verbatim} +\beginlisting \font\iwonaitalic ="Iwona Italic" at 20pt \font\iwonabold ="Iwona Bold" at 20pt \font\iwonabolditalic="Iwona BoldItalic" at 20pt - \end{verbatim} -\end{quote} +\endlisting + +\endsubsubsection +\endsubsection +\endsection -\section {Font features} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\beginsection {Font features} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \emphasis{Font features} are the second to last component in the general scheme for font requests: -\begin{quote} +\beginnarrower |\font\foo={|% \meta{prefix}|:|% \meta{font name}|:|% \meta{font features}|}|% \meta{\TEX font features} -\end{quote} +\endnarrower \noindent If style modifiers are present (\XETEX style), they must precede @@ -487,11 +486,9 @@ and font options. Prepending a font feature with a |+| (plus sign) enables it, whereas a |-| (minus) disables it. For instance, the request -\begin{quote} - \begin{verbatim} +\beginlisting \font\test=LatinModernRoman:+clig;-kern - \end{verbatim} -\end{quote} +\endlisting \noindent activates contextual ligatures (|clig|) and disables kerning (|kern|). @@ -501,11 +498,9 @@ the feature in a key/value expression. % The following request has the same meaning as the last one: -\begin{quote} - \begin{verbatim} +\beginlisting \font\test=LatinModernRoman:clig=true;kern=false - \end{verbatim} -\end{quote} +\endlisting \noindent Furthermore, this second syntax is required should a font feature @@ -520,11 +515,9 @@ obviously, |random|. %% TODO verify that this actually works with a font that supports %% the salt/random feature!\fi -\begin{quote} - \begin{verbatim} +\beginlisting \font\librmsaltfirst=LatinModernRoman:salt=1 - \end{verbatim} -\end{quote} +\endlisting \noindent Other font options include: @@ -604,11 +597,9 @@ obviously, |random|. For example, in order to set text in semitransparent red: - \begin{quote} - \begin{verbatim} + \beginlisting \font\test={Latin Modern Roman}:color=FF0000BB - \end{verbatim} - \end{quote} + \endlisting \item [kernfactor \& letterspace] \hfill \\ Define a font with letterspacing (tracking) enabled. @@ -644,12 +635,10 @@ obviously, |random|. Consequently, both definitions in below snippet yield the same letterspacing width: - \begin{quote} - \begin{verbatim} + \beginlisting \font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125" \font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5" - \end{verbatim} - \end{quote} + \endlisting Specific pairs of letters and ligatures may be exempt from letterspacing by defining the \LUA functions @@ -688,10 +677,10 @@ obviously, |random|. % Alternatively and with loss of information, you can dump those tables into your terminal by issuing - \begin{verbatim} + \beginlisting \directlua{inspect(fonts.protrusions.setups.default) inspect(fonts.expansions.setups.default)} - \end{verbatim} + \endlisting at some point after loading \fileent{luaotfload.sty}. } % @@ -709,11 +698,9 @@ obviously, |random|. for details. }: - \begin{quote} - \begin{verbatim} + \beginlisting \font\test=LatinModernRoman:protrusion=default - \end{verbatim} - \end{quote} + \endlisting \end{description} \paragraph{Non-standard font features} @@ -756,8 +743,12 @@ Currently (2014) there are three of them: \end{description} +\endsection + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\beginsection {Font names database} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\section{Font names database} \label{sec:fontdb} As mentioned above, \identifier{luaotfload} keeps track of which @@ -792,12 +783,8 @@ inspects every font file on your computer. This is particularly noticeable if it occurs during a typesetting run. In any case, subsequent updates to the database will be quite fast. -\subsection[luaotfload-tool / mkluatexfontdb.lua]% - {\fileent{luaotfload-tool} / - \fileent{mkluatexfontdb.lua}\footnote{% - The script may be named just \fileent{mkluatexfontdb} in your - distribution. -}} +\beginsubsection[luaotfload-tool] + {\fileent{luaotfload-tool}} It can still be desirable at times to do some of these steps manually, and without having to compile a document. @@ -826,29 +813,23 @@ pass it as an argument to \fileent{texlua}.\footnote{% Invoked with the argument \verb|--update| it will perform a database update, scanning for fonts not indexed. -\begin{quote} - \begin{verbatim} - luaotfload-tool --update - \end{verbatim} -\end{quote} +\beginlisting + luaotfload-tool --update +\endlisting Adding the \verb|--force| switch will initiate a complete rebuild of the database. -\begin{quote} - \begin{verbatim} - luaotfload-tool --update --force - \end{verbatim} -\end{quote} - -For sake of backwards compatibility, \fileent{luaotfload-tool} may be -renamed or symlinked to \fileent{mkluatexfontdb}. -% +\beginlisting + luaotfload-tool --update --force +\endlisting Whenever it is run under this name, it will update the database first, mimicking the behavior of earlier versions of \identifier{luaotfload}. -\subsection{Search Paths} +\endsubsection + +\beginsubsection{Search Paths} \identifier{luaotfload} scans those directories where fonts are expected to be located on a given system. @@ -875,7 +856,7 @@ directories. \caption{List of paths searched for each supported operating system.} \renewcommand{\arraystretch}{1.2} - \begin{center} + \begincentered \begin{tabular}{lp{.5\textwidth}} Windows & \verb|%WINDIR%\Fonts| \\ @@ -888,12 +869,14 @@ directories. \fileent{/Network/Library/Fonts} \\ \end{tabular} - \end{center} + \endcentered \label{table-searchpaths} \hrule \end{table} -\subsection{Querying from Outside} +\endsubsection + +\beginsubsection{Querying from Outside} \fileent{luaotfload-tool} also provides rudimentary means of accessing the information collected in the font database. @@ -904,11 +887,9 @@ matching name. % For instance, the invocation -\begin{quote} - \begin{verbatim} - luaotfload-tool --find="Iwona Regular" - \end{verbatim} -\end{quote} +\beginlisting + luaotfload-tool --find="Iwona Regular" +\endlisting \noindent will verify if “Iwona Regular” is found in the database and can be @@ -922,11 +903,9 @@ Suppose you cannot precisely remember if the variant of \identifier{Iwona} you are looking for was “Bright” or “Light”. The query -\begin{quote} - \begin{verbatim} - luaotfload-tool -F --find="Iwona Bright" - \end{verbatim} -\end{quote} +\beginlisting + luaotfload-tool -F --find="Iwona Bright" +\endlisting \noindent will tell you that indeed the latter name is correct. @@ -934,11 +913,9 @@ will tell you that indeed the latter name is correct. Basic information about fonts in the database can be displayed using the \verb|-i| option (\verb|--info|). % -\begin{quote} - \begin{verbatim} - luaotfload-tool -i --find="Iwona Light Italic" - \end{verbatim} -\end{quote} +\beginlisting + luaotfload-tool -i --find="Iwona Light Italic" +\endlisting % \noindent The meaning of the printed values is described in section 4.4 of the @@ -948,11 +925,9 @@ The meaning of the printed values is described in section 4.4 of the For a much more detailed report about a given font try the \verb|-I| option instead (\verb|--inspect|). -\begin{quote} - \begin{verbatim} - luaotfload-tool -I --find="Iwona Light Italic" - \end{verbatim} -\end{quote} +\beginlisting + luaotfload-tool -I --find="Iwona Light Italic" +\endlisting \verb|luaotfload-tool --help| will list the available command line switches, including some not discussed in detail here. @@ -963,7 +938,9 @@ capabilities refer to the manpage Or see \verb|luaotfload-tool.rst| in the source directory. } -\subsection{Blacklisting Fonts} +\endsubsection + +\beginsubsection {Blacklisting Fonts} \label{font-blacklist} Some fonts are problematic in general, or just in \LUATEX. @@ -1004,14 +981,19 @@ without modifying the global file. % An example with explicit paths: -\begin{verbatim} +\beginlisting % example otf-blacklist.cnf /Library/Fonts/GillSans.ttc % Luaotfload ignores this font. -/Library/Fonts/Optima.ttc % This one is usable again, even if % blacklisted somewhere else. -\end{verbatim} +\endlisting -\section{Files from \CONTEXT and \LUATEX-Fonts} +\endsubsection +\endsection + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\beginsection {Files from \CONTEXT and \LUATEX-Fonts} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \identifier{luaotfload} relies on code originally written by Hans Hagen\footnote{% @@ -1069,60 +1051,56 @@ too developed by Hans Hagen.\footnote{% It houses several \LUA files that can be classed in three categories. - \begin{itemize} - \let\normalitem=\item - \def\incitem#1{% - \normalitem{\fileent{#1}} - } +\begindefinitions \normalitem \emphasis{\LUA utility libraries}, a subset of what is provided by the \identifier{lualibs} package. - \begin{multicols}{2} - \begin{itemize} - \incitem{l-lua.lua} \incitem{l-lpeg.lua} - \incitem{l-function.lua} \incitem{l-string.lua} - \incitem{l-table.lua} \incitem{l-io.lua} - \incitem{l-file.lua} \incitem{l-boolean.lua} - \incitem{l-math.lua} \incitem{util-str.lua} - \end{itemize} - \end{multicols} + \begindoublecolumns + \begindefinitions + \altitem{l-lua.lua} \altitem{l-lpeg.lua} + \altitem{l-function.lua} \altitem{l-string.lua} + \altitem{l-table.lua} \altitem{l-io.lua} + \altitem{l-file.lua} \altitem{l-boolean.lua} + \altitem{l-math.lua} \altitem{util-str.lua} + \enddefinitions + \enddoublecolumns \normalitem The \emphasis{font loader} itself. These files have been written for \LUATEX-Fonts and they are distributed along with \identifier{luaotfload}. - \begin{multicols}{2} - \begin{itemize} - \incitem{luatex-basics-gen.lua} - \incitem{luatex-basics-nod.lua} - \incitem{luatex-fonts-enc.lua} - \incitem{luatex-fonts-syn.lua} - \incitem{luatex-fonts-tfm.lua} - \incitem{luatex-fonts-chr.lua} - \incitem{luatex-fonts-lua.lua} - \incitem{luatex-fonts-inj.lua} - \incitem{luatex-fonts-otn.lua} - \incitem{luatex-fonts-def.lua} - \incitem{luatex-fonts-ext.lua} - \incitem{luatex-fonts-cbk.lua} - \end{itemize} - \end{multicols} + \begindoublecolumns + \begindefinitions + \altitem{luatex-basics-gen.lua} + \altitem{luatex-basics-nod.lua} + \altitem{luatex-fonts-enc.lua} + \altitem{luatex-fonts-syn.lua} + \altitem{luatex-fonts-tfm.lua} + \altitem{luatex-fonts-chr.lua} + \altitem{luatex-fonts-lua.lua} + \altitem{luatex-fonts-inj.lua} + \altitem{luatex-fonts-otn.lua} + \altitem{luatex-fonts-def.lua} + \altitem{luatex-fonts-ext.lua} + \altitem{luatex-fonts-cbk.lua} + \enddefinitions + \enddoublecolumns \normalitem Code related to \emphasis{font handling and node processing}, taken directly from \CONTEXT. - \begin{multicols}{2} - \begin{itemize} - \incitem{data-con.lua} \incitem{font-ini.lua} - \incitem{font-con.lua} \incitem{font-cid.lua} - \incitem{font-map.lua} \incitem{font-oti.lua} - \incitem{font-otf.lua} \incitem{font-otb.lua} - \incitem{font-ota.lua} \incitem{font-def.lua} - \incitem{font-otp.lua} - \end{itemize} - \end{multicols} - \end{itemize} + \begindoublecolumns + \begindefinitions + \altitem{data-con.lua} \altitem{font-ini.lua} + \altitem{font-con.lua} \altitem{font-cid.lua} + \altitem{font-map.lua} \altitem{font-oti.lua} + \altitem{font-otf.lua} \altitem{font-otb.lua} + \altitem{font-ota.lua} \altitem{font-def.lua} + \altitem{font-otp.lua} + \enddefinitions + \enddoublecolumns +\enddefinitions Note that if \identifier{luaotfload} cannot locate the merged file, it will load the individual \LUA libraries @@ -1150,29 +1128,25 @@ In addition to these, \identifier{luaotfload} requires a number of files not contained in the merge. Some of these have no equivalent in \LUATEX-Fonts or \CONTEXT, some were taken unmodified from the latter. -\begin{itemize} - \let\normalitem=\item - \def\ouritem#1{% - \normalitem{\fileent{#1}}% - \space--\hskip1em - } - \ouritem {luaotfload-features.lua} font feature handling; + +\beginfilelist + \altitem {luaotfload-features.lua} font feature handling; incorporates some of the code from \fileent{font-otc} from \CONTEXT; - \ouritem {luaotfload-override.lua} overrides the \CONTEXT logging + \altitem {luaotfload-override.lua} overrides the \CONTEXT logging functionality. - \ouritem {luaotfload-loaders.lua} registers the \OpenType + \altitem {luaotfload-loaders.lua} registers the \OpenType font reader as handler for Postscript fonts (\abbrev{pfa}, \abbrev{pfb}). - \ouritem {luaotfload-parsers.lua} various \abbrev{lpeg}-based parsers. - \ouritem {luaotfload-database.lua} font names database. - \ouritem {luaotfload-colors.lua} color handling. - \ouritem {luaotfload-auxiliary.lua} access to internal functionality + \altitem {luaotfload-parsers.lua} various \abbrev{lpeg}-based parsers. + \altitem {luaotfload-database.lua} font names database. + \altitem {luaotfload-colors.lua} color handling. + \altitem {luaotfload-auxiliary.lua} access to internal functionality for package authors (proposals for additions welcome). - \ouritem {luaotfload-letterspace.lua} font-based letterspacing. -\end{itemize} + \altitem {luaotfload-letterspace.lua} font-based letterspacing. +\endfilelist \begin{figure}[b] \caption{Schematic of the files in \identifier{Luaotfload}} @@ -1180,7 +1154,11 @@ files not contained in the merge. Some of these have no equivalent in \label{file-graph} \end{figure} -\section{Auxiliary Functions} +\endsection + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\beginsection {Auxiliary Functions} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% With release version 2.2, \identifier{luaotfload} received additional functions for package authors to call from outside @@ -1210,7 +1188,7 @@ Note that the current interface is not yet set in stone and the development team is open to suggestions for improvements or additions. -\subsection{Callback Functions} +\beginsubsection {Callback Functions} The \luafunction{patch_font} callback is inserted in the wrapper \identifier{luaotfload} provides for the font definition callback @@ -1222,8 +1200,7 @@ the font loader is done creating it. For a short demonstration of its usefulness, here is a snippet that writes an entire font object to the file \fileent{fontdump.lua}: -\begin{quote} - \begin{verbatim} +\beginlisting \input luaotfload.sty \directlua{ local dumpfile = "fontdump.lua" @@ -1240,8 +1217,7 @@ writes an entire font object to the file \fileent{fontdump.lua}: } \font\dumpme=name:Iwona \bye - \end{verbatim} -\end{quote} +\endlisting \emphasis{Beware}: this creates a Lua file of around 150,000 lines of code, taking up 3~\abbrev{mb} of disk space. @@ -1250,7 +1226,7 @@ By inspecting the output you can get a first impression of how a font is structured in \LUATEX’s memory, what elements it is composed of, and in what ways it can be rearranged. -\subsubsection{Compatibility with Earlier Versions} +\beginsubsubsection {Compatibility with Earlier Versions} As has been touched on in the preface to this section, the structure of the object as returned by the fontloader underwent rather drastic @@ -1267,99 +1243,100 @@ units factor, and the font name have again been made accessible from the toplevel of the table even though they were migrated to different subtables in the meantime. -\subsubsection{Patches} +\endsubsubsection + +\beginsubsubsection{Patches} These are mostly concerned with establishing compatibility with \XETEX. -\begin{itemize} - \let\normalitem=\item - \def\ouritem#1{% - \normalitem{\luafunction{#1}}% - \hfill\break - } +\beginfunctionlist - \ouritem {set_sscale_dimens} + \altitem {set_sscale_dimens} Calculate \texmacro{fontdimen}s 10 and 11 to emulate \XETEX. - \ouritem {set_capheight} + \altitem {set_capheight} Calculates \texmacro{fontdimen} 8 like \XETEX. - \ouritem {patch_cambria_domh} + \altitem {patch_cambria_domh} Correct some values of the font \emphasis{Cambria Math}. -\end{itemize} +\endfunctionlist -\subsection{Package Author’s Interface} +\endsubsection + +\beginsubsection {Package Author’s Interface} As \LUATEX release 1.0 is nearing, the demand for a reliable interface for package authors increases. -\subsubsection{Font Properties} +\endsubsubsection + +\beginsubsubsection{Font Properties} Below functions mostly concern querying the different components of a font like for instance the glyphs it contains, or what font features are defined for which scripts. -\begin{itemize} - \let\normalitem=\item - \def\ouritem#1{% - \normalitem{\luafunction{#1}}% - \hfill\break - } +\beginfunctionlist - \ouritem {aux.font_has_glyph (id : int, index : int)} + \altitem {aux.font_has_glyph (id : int, index : int)} Predicate that returns true if the font \luafunction{id} has glyph \luafunction{index}. - \ouritem {aux.slot_of_name(name : string)} + \altitem {aux.slot_of_name(name : string)} Translates an Adobe Glyph name to the corresponding glyph slot. - \ouritem {aux.name_of_slot(slot : int)} + \altitem {aux.name_of_slot(slot : int)} The inverse of \luafunction{slot_of_name}; note that this might be incomplete as multiple glyph names may map to the same codepoint, only one of which is returned by \luafunction{name_of_slot}. - \ouritem {aux.provides_script(id : int, script : string)} + \altitem {aux.provides_script(id : int, script : string)} Test if a font supports \luafunction{script}. - \ouritem {aux.provides_language(id : int, script : string, language : string)} + \altitem {aux.provides_language(id : int, script : string, language : string)} Test if a font defines \luafunction{language} for a given \luafunction{script}. - \ouritem {aux.provides_feature(id : int, script : string, + \altitem {aux.provides_feature(id : int, script : string, language : string, feature : string)} Test if a font defines \luafunction{feature} for \luafunction{language} for a given \luafunction{script}. - \ouritem {aux.get_math_dimension(id : int, dimension : string)} + \altitem {aux.get_math_dimension(id : int, dimension : string)} Get the dimension \luafunction{dimension} of font \luafunction{id}. - \ouritem {aux.sprint_math_dimension(id : int, dimension : string)} + \altitem {aux.sprint_math_dimension(id : int, dimension : string)} Same as \luafunction{get_math_dimension()}, but output the value in scaled points at the \TEX end. -\end{itemize} +\endfunctionlist -\subsubsection{Database} +\endsubsubsection -\begin{itemize} - \let\normalitem=\item - \def\ouritem#1{% - \normalitem{\luafunction{#1}}% - \hfill\break - } +%% not implemented, may come back later +% \beginsubsubsection{Database} +% +% \beginfunctionlist +% \altitem {aux.scan_external_dir(dir : string)} +% Include fonts in directory \luafunction{dir} in font lookups without +% adding them to the database. +% +% \endfunctionlist +% +% \endsubsubsection - \ouritem {aux.scan_external_dir(dir : string)} - Include fonts in directory \luafunction{dir} in font lookups without - adding them to the database. +\endsubsection +\endsection -\end{itemize} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\beginsection {Troubleshooting} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\section{Troubleshooting} +\beginsubsection {Database Generation} -\subsection {Database Generation} If you encounter problems with some fonts, please first update to the latest version of this package before reporting a bug, as \identifier{luaotfload} is under active development and still a moving @@ -1373,30 +1350,24 @@ requests and the likes. Bug reports are more likely to be addressed if they contain the output of -\begin{quote} - \begin{verbatim} +\beginlisting luaotfload-tool --diagnose=environment,files,permissions - \end{verbatim} -\end{quote} +\endlisting \noindent Consult the man page for a description of these options. Errors during database generation can be traced by increasing the verbosity level and redirecting log output to \fileent{stdout}: -\begin{quote} - \begin{verbatim} +\beginlisting luaotfload-tool -fuvvv --log=stdout - \end{verbatim} -\end{quote} +\endlisting \noindent or to a file in \fileent{/tmp}: -\begin{quote} - \begin{verbatim} +\beginlisting luaotfload-tool -fuvvv --log=file - \end{verbatim} -\end{quote} +\endlisting \noindent In the latter case, invoke the \verb|tail(1)| utility on the file for live monitoring of the progress. @@ -1407,7 +1378,9 @@ log file is likely to be the culprit. Please specify it when reporting a bug, and blacklist it for the time being (see above, page \pageref{font-blacklist}). -\subsection {Font Features} +\endsubsection + +\beginsubsection {Font Features} A common problem is the lack of features for some \OpenType fonts even when specified. @@ -1421,22 +1394,20 @@ fix it. % For example with \verb|latn|: -\begin{quote} - \begin{verbatim} +\beginlisting \font\test=file:MyFont.otf:script=latn;+liga; - \end{verbatim} -\end{quote} +\endlisting You can get a list of features that a font defines for scripts and languages by querying it in \fileent{luaotfload-tool}: -\begin{quote} - \begin{verbatim} +\beginlisting luaotfload-tool --find="Iwona" --inspect - \end{verbatim} -\end{quote} +\endlisting + +\endsubsection -\subsection {\LUATEX Programming} +\beginsubsection {\LUATEX Programming} Another strategy that helps avoiding problems is to not access raw \LUATEX internals directly. @@ -1448,11 +1419,9 @@ Thus, whenever possible prefer the functions in the \luafunction{aux} namespace over direct manipulation of font objects. For example, raw access to the \luafunction{font.fonts} table like: -\begin{quote} - \begin{verbatim} +\beginlisting local somefont = font.fonts[2] - \end{verbatim} -\end{quote} +\endlisting \noindent can render already defined fonts unusable. % @@ -1469,8 +1438,7 @@ both \luafunction{font.getfont()} and \luafunction{font.fonts}. % In this case, best define you own accessor: -\begin{quote} - \begin{verbatim} +\beginlisting local unsafe_getfont = function (id) local tfmdata = font.getfont (id) if not tfmdata then @@ -1481,35 +1449,15 @@ In this case, best define you own accessor: --- use like getfont() local somefont = unsafe_getfont (2) - \end{verbatim} -\end{quote} +\endlisting -\part{Implementation} - -\section {\fileent{luaotfload.lua}} - -As of version 2.5, the file \fileent{luaotfload.lua} is no longer -generated from the \abbrev{dtx}. -% -Instead, it is maintained separately as a plain \identifier{Lua} file -\fileent{luaotfload-main.lua} in the Luaotfload \identifier{git} tree. -% -The file documentation which used to be found in this section has been -preserved in the comments. - -\section{\fileent{luaotfload.sty}} - -As of version 2.5, the file \fileent{luaotfload.sty} is no longer -generated from the \abbrev{dtx}. -% -Instead, it is maintained separately as a plain \identifier{\TEX} file -in the Luaotfload \identifier{git} tree. -% -The file documentation which used to be found in this section has -been preserved in the comments. +\endsubsection +\endsection \clearpage -\section{The GNU GPL License v2} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\beginsection {The GNU GPL License v2} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% The GPL requires the complete license text to be distributed along with the code. I recommend the canonical source, instead: @@ -1521,32 +1469,28 @@ You might want to zoom in. \begin{lrbox}{\gpl} \begin{minipage}{3\textwidth} \columnsep=3\columnsep -\begin{multicols}{3} -\begin{center} -{\Large GNU GENERAL PUBLIC LICENSE\par} -\bigskip -{Version 2, June 1991} -\end{center} +\begintriplecolumns +\begincentered + {\Large GNU GENERAL PUBLIC LICENSE\par} + \bigskip + {Version 2, June 1991} -\begin{center} -{\parindent 0in + {\parindent 0in -Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. + Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. -\bigskip + \bigskip -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -\bigskip + \bigskip -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. -} -\end{center} + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + } -\begin{center} -{\bf\large Preamble} -\end{center} + {\bf\large Preamble} +\endcentered The licenses for most software are designed to take away your freedom to @@ -1595,12 +1539,12 @@ be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. -\begin{center} -{\Large \sc Terms and Conditions For Copying, Distribution and - Modification} -\end{center} +\begincentered + {\Large \sc Terms and Conditions For Copying, Distribution and + Modification} +\endcentered -\begin{enumerate} +\beginenumeration \item This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the @@ -1635,7 +1579,7 @@ of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: -\begin{enumerate} +\beginenumeration \item You must cause the modified files to carry prominent notices stating that @@ -1659,7 +1603,7 @@ License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) -\end{enumerate} +\endenumeration These requirements apply to the modified work as a whole. If @@ -1687,7 +1631,7 @@ You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: -\begin{enumerate} +\beginenumeration \item @@ -1712,7 +1656,7 @@ allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) -\end{enumerate} +\endenumeration The source code for a work means the preferred form of the work for @@ -1825,11 +1769,9 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. -\begin{center} -{\Large\sc -No Warranty -} -\end{center} +\begincentered + {\Large\sc No Warranty} +\endcentered \item {\sc Because the program is licensed free of charge, there is no warranty @@ -1853,17 +1795,19 @@ you or third parties or a failure of the program to operate with any other programs), even if such holder or other party has been advised of the possibility of such damages.} -\end{enumerate} +\endenumeration -\begin{center} -{\Large\sc End of Terms and Conditions} -\end{center} +\begincentered + {\Large\sc End of Terms and Conditions} +\endcentered \pagebreak[2] -\section*{Appendix: How to Apply These Terms to Your New Programs} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\fakesection {Appendix: How to Apply These Terms to Your New Programs} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it @@ -1875,36 +1819,36 @@ terms. the exclusion of warranty; and each file should have at least the ``copyright'' line and a pointer to where the full notice is found. -\begin{quote} -one line to give the program's name and a brief idea of what it does. \\ -Copyright (C) yyyy name of author \\ +\beginnarrower + one line to give the program's name and a brief idea of what it does. \\ + Copyright (C) yyyy name of author \\ -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -\end{quote} + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +\endnarrower Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: -\begin{quote} -Gnomovision version 69, Copyright (C) yyyy name of author \\ -Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ -This is free software, and you are welcome to redistribute it -under certain conditions; type `show c' for details. -\end{quote} +\beginnarrower + Gnomovision version 69, Copyright (C) yyyy name of author \\ + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. +\endnarrower The hypothetical commands {\tt show w} and {\tt show c} should show the @@ -1917,13 +1861,13 @@ You should also get your employer (if you work as a programmer) or your school, if any, to sign a ``copyright disclaimer'' for the program, if necessary. Here is a sample; alter the names: -\begin{quote} +\beginnarrower Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\ `Gnomovision' (which makes passes at compilers) written by James Hacker. \\ signature of Ty Coon, 1 April 1989 \\ Ty Coon, President of Vice -\end{quote} +\endnarrower This General Public License does not permit incorporating your program @@ -1932,13 +1876,15 @@ may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. -\end{multicols} +\endtriplecolumns \end{minipage} \end{lrbox} -\begin{center} -\scalebox{0.33}{\usebox{\gpl}} -\end{center} +\begincentered + \scalebox{0.33}{\usebox{\gpl}} +\endcentered + +\endsection \endinput -- cgit v1.2.3 From 5eedcacffc80114cd64523d1fca6fb8eb1d189e1 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 16 Feb 2014 13:48:17 +0100 Subject: [doc] add note concerning state of feature file support As suggested by @Crissov, https://github.com/lualatex/luaotfload/issues/200#issuecomment-35163810 --- doc/luaotfload-main.tex | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 37127b7..f5a068d 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -581,6 +581,13 @@ obviously, |random|. The syntax is documented in \identifier{Adobe}’s \OpenType Feature File Specification.\footnote{% Cf. \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. + Feature file support is part of the engine which at the + time of this writing (2014) implements the spec only + partially. + See the + \href{http://tracker.luatex.org/view.php?id=231} + {\LUATEX tracker} + for details. } For a demonstration of how to set a |tkrn| feature consult -- cgit v1.2.3 From 22caa4363c76153ba2ac1ef896ca5d08f5c34236 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 17 Feb 2014 07:35:44 +0100 Subject: [doc] wrap further code in more abstract macros --- doc/luaotfload-latex.tex | 64 ++++++++- doc/luaotfload-main.tex | 364 ++++++++++++++++++++++------------------------- 2 files changed, 236 insertions(+), 192 deletions(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index d00a617..dcdcab2 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -156,6 +156,18 @@ \def \fakesection #1{\section*{#1}} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% inline verbatim +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Context offers both \type{…} and \type<<…>>, but not an unbalanced +%% one that we could map directly onto Latex’s \verb|…|. + +%% TODO +%\def \inlinecode·#1·{% + %\verb·#1·% +%} + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% codelistings; this sucks hard since we lack access to buffers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -203,6 +215,7 @@ \small \begin {quote} \bgroup + \addfontfeature {RawFeature=-tlig;-liga}%% So one can’t just turn them all off at once using the ``Ligatures`` key? \luatexcatcodetable \literalcatcodes \obeyspaces \obeylines @@ -254,7 +267,6 @@ \def \begincentered {\begin {center}} \def \endcentered {\end {center}} -\makeatother \def \beginnarrower {\begin {quote}} \def \endnarrower {\end {quote}} @@ -266,10 +278,60 @@ \def \beginabstractcontent {\begin {abstract}} \def \endabstractcontent {\end {abstract}} +\let \setdocumenttitle \title +\let \setdocumentdate \date +\let \setdocumentauthor \author +\let \typesetdocumenttitle \maketitle + +\let \typesetcontent \tableofcontent + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% figure floats +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def \beginsyntaxfloat #1{%% the request syntax diagram + \begin {figure} [b] + \def \syntaxcaption {#1}% + \setlength\grammarparsep{12pt plus 2pt minus 2pt}% + \setlength\grammarindent{5cm}% + \begingroup + \small + \begin {grammar} +} + +\def \endsyntaxfloat {% + \end {grammar} + \endgroup + \caption \syntaxcaption + \label{font-syntax} + \end {figure} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% hyperlinks +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\protected \def \hyperlink{% + \@ifnextchar[{\hyperlinkindeed}% + {\hyperlinkindeed[]}% +} + +\def \hyperlinkindeed [#1]#2{% + \def \first {#1}% + \ifx \first \empty + \url {#2}% + \else + \href {#2}{#1}% + \fi% +} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% main %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\makeatother + \begin {document} \input {luaotfload-main.tex} \end {document} diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index f5a068d..6c0d0b6 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -29,21 +29,20 @@ %% ---------------------------------------------------------------------------- %% -\title{The \identifier{luaotfload} package} -\date{2014/**/** v2.5} -\author{Elie Roux · Khaled Hosny · Philipp Gesang\\ - Home: \url {https://github.com/lualatex/luaotfload}\\ - Support: \email {lualatex-dev@tug.org}} +\setdocumenttitle {The \identifier{luaotfload} package} +\setdocumentdate {2014/**/** v2.5} +\setdocumentauthor {Elie Roux · Khaled Hosny · Philipp Gesang\\ + Home: \hyperlink {https://github.com/lualatex/luaotfload}\\ + Support: \email {lualatex-dev@tug.org}} -\maketitle +\typesetdocumenttitle \beginabstractcontent - This package is an adaptation of the \CONTEXT font loading system. - It allows for loading \OpenType fonts with an extended syntax and adds - support for a variety of font features. + This package is an adaptation of the \CONTEXT font loading system. + It allows for loading \OpenType fonts with an extended syntax and adds + support for a variety of font features. \endabstractcontent - \tableofcontents %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -63,10 +62,10 @@ These fonts can contain many more characters than \TEX fonts, as well as additional functionality like ligatures, old-style numbers, small capitals, etc., and support more complex writing systems like Arabic and Indic\footnote{% - Unfortunately, \identifier{luaotfload} doesn‘t support many Indic - scripts right now. - Assistance in implementing the prerequisites is greatly - appreciated. + Unfortunately, \identifier{luaotfload} doesn‘t support many Indic + scripts right now. + Assistance in implementing the prerequisites is greatly + appreciated. } scripts. @@ -128,11 +127,11 @@ in the task and willingness to respond to our suggestions. \identifier{luaotfload} supports an extended font request syntax: \beginnarrower - |\font\foo={|% - \meta{prefix}|:|% - \meta{font name}|:|% - \meta{font features}|}|% - \meta{\TEX font features} + |\font\foo={|% + \meta{prefix}|:|% + \meta{font name}|:|% + \meta{font features}|}|% + \meta{\TEX font features} \endnarrower \noindent @@ -144,12 +143,27 @@ Alternatively, double quotes serve the same purpose. A selection of individual parts of the syntax are discussed below; for a more formal description see figure \ref{font-syntax}. -\begin {figure} [b] - \setlength\grammarparsep{12pt plus 2pt minus 2pt} - \setlength\grammarindent{5cm} - \begingroup - \small - \begin{grammar} +\beginsyntaxfloat + {Font request syntax. + Braces or double quotes around the + \emphasis{specification} rule will + preserve whitespace in file names. + In addition to the font style modifiers + (\emphasis{slash-notation}) given above, there + are others that are recognized but will be silently + ignored: {\ttfamily aat}, + {\ttfamily icu}, and + {\ttfamily gr}. + The special terminals are: + {\sc feature\textunderscore id} for a valid font + feature name and + {\sc feature\textunderscore value} for the corresponding + value. + {\sc tfmname} is the name of a \abbrev{tfm} file. + {\sc digit} again refers to bytes 48--57, and + {\sc all\textunderscore characters} to all byte values. + {\sc csname} and {\sc dimension} are the \TEX concepts.} +% ::= `\\font', {\sc csname}, `=', , [ ] ; ::= `at', {\sc dimension} ; @@ -187,29 +201,7 @@ for a more formal description see figure \ref{font-syntax}. ::= `+' | `-' ; ::= {\sc all_characters} - ( `(' | `/' | `:' ) ; - \end{grammar} - \endgroup - \caption{Font request syntax. - Braces or double quotes around the - \emphasis{specification} rule will - preserve whitespace in file names. - In addition to the font style modifiers - (\emphasis{slash-notation}) given above, there - are others that are recognized but will be silently - ignored: {\ttfamily aat}, - {\ttfamily icu}, and - {\ttfamily gr}. - The special terminals are: - {\sc feature\textunderscore id} for a valid font - feature name and - {\sc feature\textunderscore value} for the corresponding - value. - {\sc tfmname} is the name of a \abbrev{tfm} file. - {\sc digit} again refers to bytes 48--57, and - {\sc all\textunderscore characters} to all byte values. - {\sc csname} and {\sc dimension} are the \TEX concepts.} - \label{font-syntax} -\end {figure} +\endsyntaxfloat \beginsubsection{Prefix -- the \identifier{luaotfload}{ }Way} @@ -220,26 +212,25 @@ requires a \emphasis{prefix}: |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots \endnarrower % -where \meta{prefix} is either \verb|file:| or \verb|name:|.\footnote{% +where \meta{prefix} is either \inlinecode·file:· or \verb|name:|.\footnote{% The development version also knows two further prefixes, \verb|kpse:| and \verb|my:|. - % + % A \verb|kpse| lookup is restricted to files that can be found by \identifier{kpathsea} and will not attempt to locate system fonts. - % + % This behavior can be of value when an extra degree of encapsulation is needed, for instance when supplying a customized tex distribution. The \verb|my| lookup takes this a step further: it lets you define a custom resolver function and hook it into the \luafunction{resolve_font} callback. - % + % This ensures full control over how a file is located. - % + % For a working example see the - \href{https://bitbucket.org/phg/lua-la-tex-tests/src/5f6a535d/pln-lookup-callback-1.tex} - {test repo}. + \hyperlink [test repo]{https://bitbucket.org/phg/lua-la-tex-tests/src/5f6a535d/pln-lookup-callback-1.tex}. } % It determines whether the font loader should interpret the request as @@ -256,23 +247,22 @@ usually listed in drop-down menus and the like.\footnote{% Font names may appear like a great choice at first because they offer seemingly more intuitive identifiers in comparison to arguably cryptic file names: - % + % “PT Sans Bold” is a lot more descriptive than \fileent{PTS75F.ttf}. On the other hand, font names are quite arbitrary and there is no universal method to determine their meaning. - % + % While \identifier{luaotfload} provides fairly sophisticated heuristic to figure out a matching font style, weight, and optical size, it cannot be relied upon to work satisfactorily for all font files. - % + % For an in-depth analysis of the situation and how broken font names are, please refer to - \href{http://www.ntg.nl/pipermail/ntg-context/2013/073889.html} - {this post} + \hyperlink [this post]{http://www.ntg.nl/pipermail/ntg-context/2013/073889.html} by Hans Hagen, the author of the font loader. - % + % If in doubt, use filenames. - % + % \fileent{luaotfload-tool} can perform the matching for you with the option \verb|--find=|, and you can use the file name it returns in your font definition. @@ -287,9 +277,9 @@ create the database. File names are whatever your file system allows them to be, except that that they may not contain the characters - \verb|(|, - \verb|:|, and - \verb|/|. + \verb|(|, + \verb|:|, and + \verb|/|. % As is obvious from the last exception, the \verb|file:| lookup will not process paths to the font location -- only those @@ -299,9 +289,9 @@ Continue below in the \XETEX section if you need to load your fonts by path. % The file names corresponding to the example font names above are - \fileent{lmroman12-italic.otf}, - \fileent{GFSBodoni.otf}, and - \fileent{PTZ56F.ttf}. + \fileent{lmroman12-italic.otf}, + \fileent{GFSBodoni.otf}, and + \fileent{PTZ56F.ttf}. \endsubsection @@ -370,7 +360,7 @@ request like so: The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa Półtawskiego}\footnote{% - \url{http://jmn.pl/antykwa-poltawskiego/}, also available in + \hyperlink {http://jmn.pl/antykwa-poltawskiego/}, also available in in \TEX Live. } in its condensed variant can be loaded as follows: @@ -425,7 +415,7 @@ Which fits nicely with the whole set: \beginsubsubsection{Modifiers} If the entire \emphasis{Iwona} family\footnote{% - \url{http://jmn.pl/kurier-i-iwona/}, + \hyperlink {http://jmn.pl/kurier-i-iwona/}, also in \TEX Live. } is installed in some location accessible by \identifier{luaotfload}, @@ -479,7 +469,7 @@ If style modifiers are present (\XETEX style), they must precede The element \meta{font features} is a semicolon-separated list of feature tags\footnote{% - Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm}. + Cf. \hyperlink {http://www.microsoft.com/typography/otspec/featurelist.htm}. } and font options. % @@ -513,8 +503,8 @@ They can be selected either explicitly by supplying the variant index (starting from one), or randomly by setting the value to, obviously, |random|. -%% TODO verify that this actually works with a font that supports -%% the salt/random feature!\fi +%% TODO verify that this actually works with a font that supports +%% the salt/random feature!\fi \beginlisting \font\librmsaltfirst=LatinModernRoman:salt=1 \endlisting @@ -530,14 +520,14 @@ obviously, |random|. \identifier{base} mode works by mapping \OpenType features to traditional \TEX ligature and kerning mechanisms. - % + % Supporting only non-contextual substitutions and kerning pairs, it is the slightly faster, albeit somewhat limited, variant. - % + % \identifier{node} mode works by processing \TeX’s internal node list directly at the \LUA end and supports a wider range of \OpenType features. - % + % The downside is that the intricate operations required for \identifier{node} mode may slow down typesetting especially with complex fonts and it does not work in math mode. @@ -548,46 +538,45 @@ obviously, |random|. \item [script] \label{script-tag} \hfill \\ An \OpenType script tag;\footnote{% - See \url{http://www.microsoft.com/typography/otspec/scripttags.htm} + See \hyperlink {http://www.microsoft.com/typography/otspec/scripttags.htm} for a list of valid values. - % + % For scripts derived from the Latin alphabet the value |latn| is good choice. } the default value is |dlft|. - % + % Some fonts, including very popular ones by foundries like Adobe, do not assign features to the |dflt| script, in which case the script needs to be set explicitly. \item [language] \hfill \\ An \OpenType language system identifier,\footnote{% - Cf. \url{http://www.microsoft.com/typography/otspec/languagetags.htm}. + Cf. \hyperlink {http://www.microsoft.com/typography/otspec/languagetags.htm}. } defaulting to |dflt|. \item [featurefile] \hfill \\ A comma-separated list of feature files to be applied to the font. - % + % Feature files contain a textual representation of \OpenType tables and extend the features of a font on fly. - % + % After they are applied to a font, features defined in a feature file can be enabled or disabled just like any other font feature. - % + % The syntax is documented in \identifier{Adobe}’s \OpenType Feature File Specification.\footnote{% - Cf. \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. - Feature file support is part of the engine which at the - time of this writing (2014) implements the spec only - partially. - See the - \href{http://tracker.luatex.org/view.php?id=231} - {\LUATEX tracker} - for details. + Cf. \hyperlink {http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. + Feature file support is part of the engine which at the + time of this writing (2014) implements the spec only + partially. + See the + \hyperlink [\LUATEX tracker]{http://tracker.luatex.org/view.php?id=231} + for details. } For a demonstration of how to set a |tkrn| feature consult @@ -604,13 +593,13 @@ obviously, |random|. For example, in order to set text in semitransparent red: - \beginlisting + \beginlisting \font\test={Latin Modern Roman}:color=FF0000BB - \endlisting + \endlisting \item [kernfactor \& letterspace] \hfill \\ Define a font with letterspacing (tracking) enabled. - % + % In \identifier{luaotfload}, letterspacing is implemented by inserting additional kerning between glyphs. @@ -618,7 +607,7 @@ obviously, |random|. \emphasis{character kerning} (\texmacro{setcharacterkerning} / \texmacro{definecharacterkerning} \& al.) functionality of Context, see the file \fileent{typo-krn.lua} there. - % + % The main difference is that \identifier{luaotfload} does not use \LUATEX attributes to assign letterspacing to regions, but defines virtual letterspaced versions of a font. @@ -626,11 +615,11 @@ obviously, |random|. The option \identifier{kernfactor} accepts a numeric value that determines the letterspacing factor to be applied to the font size. - % + % E.~g. a kern factor of $0.42$ applied to a $10$ pt font results in $4.2$ pt of additional kerning applied to each pair of glyphs. - % + % Ligatures are split into their component glyphs unless explicitly ignored (see below). @@ -638,35 +627,35 @@ obviously, |random|. \identifier{letterspace} option is supplied that interprets the supplied value as a \emphasis{percentage} of the font size but is otherwise identical to \identifier{kernfactor}. - % + % Consequently, both definitions in below snippet yield the same letterspacing width: - \beginlisting + \beginlisting \font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125" \font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5" - \endlisting + \endlisting Specific pairs of letters and ligatures may be exempt from letterspacing by defining the \LUA functions \luafunction{keeptogether} and \luafunction{keepligature}, respectively, inside the namespace \verb|luaotfload.letterspace|. - % + % Both functions are called whenever the letterspacing callback encounters an appropriate node or set of nodes. - % + % If they return a true-ish value, no extra kern is inserted at the current position. - % + % \luafunction{keeptogether} receives a pair of consecutive glyph nodes in order of their appearance in the node list. - % + % \luafunction{keepligature} receives a single node which can be analyzed into components. - % + % (For details refer to the \emphasis{glyph nodes} section in the \LUATEX reference manual.) - % + % The implementation of both functions is left entirely to the user. @@ -675,22 +664,22 @@ obviously, |random|. These keys control microtypographic features of the font, namely \emphasis{character protrusion} and \emphasis{font expansion}. - % + % Their arguments are names of \LUA tables that contain values for the respective features.\footnote{% For examples of the table layout please refer to the section of the file \fileent{luaotfload-fonts-ext.lua} where the default values are defined. - % + % Alternatively and with loss of information, you can dump those tables into your terminal by issuing - \beginlisting + \beginlisting \directlua{inspect(fonts.protrusions.setups.default) inspect(fonts.expansions.setups.default)} - \endlisting + \endlisting at some point after loading \fileent{luaotfload.sty}. } - % + % For both, only the set \identifier{default} is predefined. For example, to define a font with the default @@ -700,14 +689,13 @@ obviously, |random|. \verb|pdfadjustspacing=2| to activate protrusion and expansion, respectively. See the - \href{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% - {\PDFTEX manual} + \hyperlink [\PDFTEX manual]{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% for details. }: - \beginlisting + \beginlisting \font\test=LatinModernRoman:protrusion=default - \endlisting + \endlisting \end{description} \paragraph{Non-standard font features} @@ -764,7 +752,7 @@ fonts are available to \LUATEX by means of a \emphasis{database}. This allows referring to fonts not only by explicit filenames but also by the proper names contained in the metadata which is often more accessible to humans.\footnote{% - The tool \href{http://www.lcdf.org/type/}{\fileent{otfinfo}} (comes + The tool \hyperlink[\fileent{otfinfo}]{http://www.lcdf.org/type/} (comes with \TEX Live), when invoked on a font file with the \verb|-i| option, lists the variety of name fields defined for it. } @@ -791,7 +779,7 @@ This is particularly noticeable if it occurs during a typesetting run. In any case, subsequent updates to the database will be quite fast. \beginsubsection[luaotfload-tool] - {\fileent{luaotfload-tool}} + {\fileent{luaotfload-tool}} It can still be desirable at times to do some of these steps manually, and without having to compile a document. @@ -805,8 +793,7 @@ either make it executable (\verb|chmod +x| on unixoid systems) or pass it as an argument to \fileent{texlua}.\footnote{% Tests by the maintainer show only marginal performance gain by running with Luigi Scarso’s - \href{https://foundry.supelec.fr/projects/luajittex/}% - {\identifier{Luajit\kern-.25ex\TEX}}, + \hyperlink [\identifier{Luajit\kern-.25ex\TEX}]{https://foundry.supelec.fr/projects/luajittex/}, which is probably due to the fact that most of the time is spent on file system operations. @@ -821,14 +808,14 @@ Invoked with the argument \verb|--update| it will perform a database update, scanning for fonts not indexed. \beginlisting - luaotfload-tool --update + luaotfload-tool --update \endlisting Adding the \verb|--force| switch will initiate a complete rebuild of the database. \beginlisting - luaotfload-tool --update --force + luaotfload-tool --update --force \endlisting Whenever it is run under this name, it will update the database first, mimicking the behavior of earlier versions of @@ -863,7 +850,7 @@ directories. \caption{List of paths searched for each supported operating system.} \renewcommand{\arraystretch}{1.2} - \begincentered + \begincentered \begin{tabular}{lp{.5\textwidth}} Windows & \verb|%WINDIR%\Fonts| \\ @@ -876,7 +863,7 @@ directories. \fileent{/Network/Library/Fonts} \\ \end{tabular} - \endcentered + \endcentered \label{table-searchpaths} \hrule \end{table} @@ -895,7 +882,7 @@ matching name. For instance, the invocation \beginlisting - luaotfload-tool --find="Iwona Regular" + luaotfload-tool --find="Iwona Regular" \endlisting \noindent @@ -911,7 +898,7 @@ Suppose you cannot precisely remember if the variant of The query \beginlisting - luaotfload-tool -F --find="Iwona Bright" + luaotfload-tool -F --find="Iwona Bright" \endlisting \noindent @@ -921,7 +908,7 @@ Basic information about fonts in the database can be displayed using the \verb|-i| option (\verb|--info|). % \beginlisting - luaotfload-tool -i --find="Iwona Light Italic" + luaotfload-tool -i --find="Iwona Light Italic" \endlisting % \noindent @@ -933,7 +920,7 @@ The meaning of the printed values is described in section 4.4 of the For a much more detailed report about a given font try the \verb|-I| option instead (\verb|--inspect|). \beginlisting - luaotfload-tool -I --find="Iwona Light Italic" + luaotfload-tool -I --find="Iwona Light Italic" \endlisting \verb|luaotfload-tool --help| will list the available command line @@ -1003,11 +990,8 @@ An example with explicit paths: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \identifier{luaotfload} relies on code originally written by Hans -Hagen\footnote{% - The creator of the \href{http://wiki.contextgarden.net}{\CONTEXT} - format. -} -for and tested with \CONTEXT. +Hagen for the \hyperlink[\identifier\CONTEXT]{http://wiki.contextgarden.net} +format. % It integrates the font loader as distributed in the \identifier{\LUATEX-Fonts} package. @@ -1045,9 +1029,7 @@ has been imported as \fileent{luaotfload-fontloader.lua}. It is generated by \fileent{mtx-package}, a \LUA source code merging too developed by Hans Hagen.\footnote{% \fileent{mtx-package} is - \href - {http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua} - {part of \CONTEXT} + \hyperlink [part of \CONTEXT]{http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua} and requires \fileent{mtxrun}. Run \verb|mtxrun --script package --help| @@ -1063,22 +1045,22 @@ categories. of what is provided by the \identifier{lualibs} package. - \begindoublecolumns - \begindefinitions - \altitem{l-lua.lua} \altitem{l-lpeg.lua} - \altitem{l-function.lua} \altitem{l-string.lua} - \altitem{l-table.lua} \altitem{l-io.lua} - \altitem{l-file.lua} \altitem{l-boolean.lua} - \altitem{l-math.lua} \altitem{util-str.lua} - \enddefinitions - \enddoublecolumns + \begindoublecolumns + \begindefinitions + \altitem{l-lua.lua} \altitem{l-lpeg.lua} + \altitem{l-function.lua} \altitem{l-string.lua} + \altitem{l-table.lua} \altitem{l-io.lua} + \altitem{l-file.lua} \altitem{l-boolean.lua} + \altitem{l-math.lua} \altitem{util-str.lua} + \enddefinitions + \enddoublecolumns \normalitem The \emphasis{font loader} itself. These files have been written for \LUATEX-Fonts and they are distributed along with \identifier{luaotfload}. - \begindoublecolumns - \begindefinitions + \begindoublecolumns + \begindefinitions \altitem{luatex-basics-gen.lua} \altitem{luatex-basics-nod.lua} \altitem{luatex-fonts-enc.lua} @@ -1091,22 +1073,22 @@ categories. \altitem{luatex-fonts-def.lua} \altitem{luatex-fonts-ext.lua} \altitem{luatex-fonts-cbk.lua} - \enddefinitions - \enddoublecolumns + \enddefinitions + \enddoublecolumns \normalitem Code related to \emphasis{font handling and node processing}, taken directly from \CONTEXT. - \begindoublecolumns - \begindefinitions + \begindoublecolumns + \begindefinitions \altitem{data-con.lua} \altitem{font-ini.lua} \altitem{font-con.lua} \altitem{font-cid.lua} \altitem{font-map.lua} \altitem{font-oti.lua} \altitem{font-otf.lua} \altitem{font-otb.lua} \altitem{font-ota.lua} \altitem{font-def.lua} \altitem{font-otp.lua} - \enddefinitions - \enddoublecolumns + \enddefinitions + \enddoublecolumns \enddefinitions Note that if \identifier{luaotfload} cannot locate the @@ -1350,7 +1332,7 @@ latest version of this package before reporting a bug, as target. % The development takes place on \identifier{github} at -\url{https://github.com/lualatex/luaotfload} where there is an issue +\hyperlink {https://github.com/lualatex/luaotfload} where there is an issue tracker for submitting bug reports, feature requests and the likes requests and the likes. @@ -1468,7 +1450,7 @@ In this case, best define you own accessor: The GPL requires the complete license text to be distributed along with the code. I recommend the canonical source, instead: -\url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. +\hyperlink {http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. But if you insist on an included copy, here it is. You might want to zoom in. @@ -1478,25 +1460,25 @@ You might want to zoom in. \columnsep=3\columnsep \begintriplecolumns \begincentered - {\Large GNU GENERAL PUBLIC LICENSE\par} - \bigskip - {Version 2, June 1991} + {\Large GNU GENERAL PUBLIC LICENSE\par} + \bigskip + {Version 2, June 1991} - {\parindent 0in + {\parindent 0in - Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. + Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. - \bigskip + \bigskip - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - \bigskip + \bigskip - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - } + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + } - {\bf\large Preamble} + {\bf\large Preamble} \endcentered @@ -1547,8 +1529,8 @@ The precise terms and conditions for copying, distribution and modification follow. \begincentered - {\Large \sc Terms and Conditions For Copying, Distribution and - Modification} + {\Large \sc Terms and Conditions For Copying, Distribution and + Modification} \endcentered \beginenumeration @@ -1777,7 +1759,7 @@ of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. \begincentered - {\Large\sc No Warranty} + {\Large\sc No Warranty} \endcentered \item @@ -1806,7 +1788,7 @@ possibility of such damages.} \begincentered - {\Large\sc End of Terms and Conditions} + {\Large\sc End of Terms and Conditions} \endcentered @@ -1827,22 +1809,22 @@ terms. ``copyright'' line and a pointer to where the full notice is found. \beginnarrower - one line to give the program's name and a brief idea of what it does. \\ - Copyright (C) yyyy name of author \\ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + one line to give the program's name and a brief idea of what it does. \\ + Copyright (C) yyyy name of author \\ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. \endnarrower Also add information on how to contact you by electronic and paper mail. @@ -1851,10 +1833,10 @@ If the program is interactive, make it output a short notice like this when it starts in an interactive mode: \beginnarrower - Gnomovision version 69, Copyright (C) yyyy name of author \\ - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. + Gnomovision version 69, Copyright (C) yyyy name of author \\ + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. \endnarrower @@ -1888,7 +1870,7 @@ General Public License instead of this License. \end{lrbox} \begincentered - \scalebox{0.33}{\usebox{\gpl}} + \scalebox{0.33}{\usebox{\gpl}} \endcentered \endsection -- cgit v1.2.3 From e9a3f733bd81599244a10bdd8f729e01da11764f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 17 Feb 2014 22:50:14 +0100 Subject: [misc] add valgrind suppression file for Luatex debugging --- misc/valgrind-kpse-suppression.sup | 111 +++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 misc/valgrind-kpse-suppression.sup diff --git a/misc/valgrind-kpse-suppression.sup b/misc/valgrind-kpse-suppression.sup new file mode 100644 index 0000000..bda96b4 --- /dev/null +++ b/misc/valgrind-kpse-suppression.sup @@ -0,0 +1,111 @@ +{ + kpathsea-garbage-1 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:kpathsea_cnf_get +} + +{ + kpathsea-garbage-2 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:kpse_program_basename +} + + +{ + kpathsea-garbage-3 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:kpse_find_file +} + + +{ + kpathsea-garbage-4 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:find_file +} + +{ + kpathsea-garbage-5 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:lua_kpse_lookup +} + +{ + kpathsea-garbage-6 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:find_file +} + + +{ + kpathsea-garbage-7 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:expand_path +} + +{ + kpathsea-garbage-8 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:do_lua_kpathsea_lookup +} + + +{ + kpathsea-garbage-9 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:kpathsea_find_file +} + + +{ + kpathsea-garbage-10 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:kpathsea_init_db +} + + +{ + kpathsea-garbage-11 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:kpathsea_find_file_generic +} + + +{ + kpathsea-garbage-12 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:expand_var +} + + +{ + kpathsea-garbage-13 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:init_path +} -- cgit v1.2.3 From f3b498a2b1d5dcb4dec2c01d757616fd968e1ee0 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 17 Feb 2014 23:02:01 +0100 Subject: [misc,build] package suppressionfile in doc/ tree --- Makefile | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index a4b3fe7..8b6bff2 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,8 @@ SRC = $(wildcard $(SRCSRCDIR)/luaotfload-*.lua) SRC += $(SRCSRCDIR)/luaotfload.sty SRC += $(MISCDIR)/luaotfload-blacklist.cnf +VGND = $(MISCDIR)/valgrind-kpse-suppression.sup + GLYPHSCRIPT = $(SCRIPTSRCDIR)/mkglyphlist CHARSCRIPT = $(SCRIPTSRCDIR)/mkcharacters STATUSSCRIPT = $(SCRIPTSRCDIR)/mkstatus @@ -64,8 +66,10 @@ TEXMFROOT = $(shell kpsewhich --var-value TEXMFHOME) # CTAN-friendly subdirectory for packaging DISTDIR = $(BUILDDIR)/$(NAME) -CTAN_ZIP = $(BUILDDIR)/$(NAME).zip -TDS_ZIP = $(BUILDDIR)/$(NAME).tds.zip +CTAN_ZIPFILE = $(NAME).zip +TDS_ZIPFILE = $(NAME).tds.zip +CTAN_ZIP = $(BUILDDIR)/$(CTAN_ZIPFILE) +TDS_ZIP = $(BUILDDIR)/$(TDS_ZIPFILE) ZIPS = $(CTAN_ZIP) $(TDS_ZIP) LUA = texlua @@ -116,17 +120,17 @@ $(BUILDDIR): /dev/null define make-ctandir @$(RM) -rf $(DISTDIR) -@mkdir -p $(DISTDIR) && cp $(SOURCE) $(COMPILED) $(DISTDIR) +@mkdir -p $(DISTDIR) && cp $(VGND) $(SOURCE) $(COMPILED) $(DISTDIR) endef $(CTAN_ZIP): $(DOCS) $(SOURCE) $(COMPILED) $(TDS_ZIP) @echo "Making $@ for CTAN upload." @$(RM) -- $@ $(make-ctandir) - @zip -r -9 $@ $(TDS_ZIP) $(DISTDIR) >/dev/null + cd $(BUILDDIR) && zip -r -9 $(CTAN_ZIPFILE) $(TDS_ZIPFILE) $(NAME) >/dev/null define run-install-doc -@mkdir -p $(DOCDIR) && cp -- $(DOCSTATUS) $(DOCDIR) +@mkdir -p $(DOCDIR) && cp -- $(DOCSTATUS) $(VGND) $(DOCDIR) @mkdir -p $(SRCDIR) && cp -- $(SRCSTATUS) $(SRCDIR) @mkdir -p $(MANDIR) && cp -- $(MANSTATUS) $(MANDIR) endef -- cgit v1.2.3 From a7d9c930f9f0c688a2530bd23e59f0cd830fc9d6 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 25 Feb 2014 07:02:39 +0100 Subject: [doc] move Latex inline verbatim commands to listings --- doc/luaotfload-latex.tex | 26 +++++---- doc/luaotfload-main.tex | 109 +++++++++++++++++++------------------ misc/valgrind-kpse-suppression.sup | 20 +++++++ src/luaotfload-main.lua | 2 +- 4 files changed, 91 insertions(+), 66 deletions(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index dcdcab2..58c8793 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -34,11 +34,11 @@ \makeatletter -\usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace} -\usepackage[x11names]{xcolor} +\usepackage {metalogo,multicol,mdwlist,fancyvrb,xspace} +\usepackage [x11names] {xcolor} -\def\primarycolor{DodgerBlue4} %%-> rgb 16 78 139 | #104e8b -\def\secondarycolor{Goldenrod4} %%-> rgb 139 105 200 | #8b6914 +\def \primarycolor {DodgerBlue4} %%-> rgb 16 78 139 | #104e8b +\def \secondarycolor {Goldenrod4} %%-> rgb 139 105 200 | #8b6914 \usepackage[ bookmarks=true, @@ -46,14 +46,14 @@ linkcolor=\primarycolor, urlcolor=\secondarycolor, citecolor=\primarycolor, - pdftitle={The luaotfload package}, + pdftitle={The Luaotfload package}, pdfsubject={OpenType layout system for Plain TeX and LaTeX}, pdfauthor={Elie Roux & Khaled Hosny & Philipp Gesang}, pdfkeywords={luatex, lualatex, unicode, opentype} ]{hyperref} -\usepackage{fontspec} -\usepackage{unicode-math} +\usepackage {fontspec} +\usepackage {unicode-math} \setmainfont[ % Numbers = OldStyle, %% buggy with font cache @@ -163,10 +163,14 @@ %% Context offers both \type{…} and \type<<…>>, but not an unbalanced %% one that we could map directly onto Latex’s \verb|…|. -%% TODO -%\def \inlinecode·#1·{% - %\verb·#1·% -%} +\usepackage {listings} +\lstset { + basicstyle=\ttfamily, +} + +\def \inlinecode #1{% + \lstinline {#1}% +} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% codelistings; this sucks hard since we lack access to buffers diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 6c0d0b6..c7a7297 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -212,18 +212,18 @@ requires a \emphasis{prefix}: |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots \endnarrower % -where \meta{prefix} is either \inlinecode·file:· or \verb|name:|.\footnote{% +where \meta{prefix} is either \inlinecode{file:} or \inlinecode {name:}.\footnote{% The development version also knows two further prefixes, - \verb|kpse:| and \verb|my:|. + \inlinecode {kpse:} and \inlinecode {my:}. % - A \verb|kpse| lookup is restricted to files that can be found by + A \inlinecode {kpse} lookup is restricted to files that can be found by \identifier{kpathsea} and will not attempt to locate system fonts. % This behavior can be of value when an extra degree of encapsulation is needed, for instance when supplying a customized tex distribution. - The \verb|my| lookup takes this a step further: it lets you define + The \inlinecode {my} lookup takes this a step further: it lets you define a custom resolver function and hook it into the \luafunction{resolve_font} callback. % @@ -264,7 +264,7 @@ usually listed in drop-down menus and the like.\footnote{% If in doubt, use filenames. % \fileent{luaotfload-tool} can perform the matching for you with the - option \verb|--find=|, and you can use the file name it returns + option \inlinecode {--find=}, and you can use the file name it returns in your font definition. } % @@ -277,11 +277,11 @@ create the database. File names are whatever your file system allows them to be, except that that they may not contain the characters - \verb|(|, - \verb|:|, and - \verb|/|. + \inlinecode {(}, + \inlinecode {:}, and + \inlinecode {/}. % -As is obvious from the last exception, the \verb|file:| lookup will +As is obvious from the last exception, the \inlinecode {file:} lookup will not process paths to the font location -- only those files found when generating the database are addressable this way. % @@ -312,7 +312,7 @@ Inside the square brackets, every character except for a closing bracket is permitted, allowing for specifying paths to a font file. % Naturally, path-less file names are equally valid and processed the -same way as an ordinary \verb|file:| lookup. +same way as an ordinary \inlinecode {file:} lookup. \beginnarrower |\font\fontname=|\meta{font name} \dots @@ -325,8 +325,8 @@ However, they have a broader spectrum of possible interpretations: before anything else, \identifier{luaotfload} attempts to load a traditional \TEX Font Metric (\abbrev{tfm} or \abbrev{ofm}). % -If this fails, it performs a \verb|name:| lookup, which itself will -fall back to a \verb|file:| lookup if no database entry matches +If this fails, it performs a \inlinecode {name:} lookup, which itself will +fall back to a \inlinecode {file:} lookup if no database entry matches \meta{font name}. Furthermore, \identifier{luaotfload} supports the slashed (shorthand) @@ -338,9 +338,9 @@ font style notation from \XETEX. \noindent Currently, four style modifiers are supported: - \verb|I| for italic shape, - \verb|B| for bold weight, - \verb|BI| or \verb|IB| for the combination of both. + \inlinecode {I} for italic shape, + \inlinecode {B} for bold weight, + \inlinecode {BI} or \inlinecode {IB} for the combination of both. % Other “slashed” modifiers are too specific to the \XETEX engine and have no meaning in \LUATEX. @@ -351,8 +351,8 @@ have no meaning in \LUATEX. \beginsubsubsection{Loading by File Name} -For example, conventional \abbrev{type1} font can be loaded with a \verb|file:| -request like so: +For example, conventional \abbrev{type1} font can be loaded with a +\inlinecode {file:} request like so: \beginlisting \font \lmromanten = {file:ec-lmr10} at 10pt @@ -381,7 +381,7 @@ non-standard directory: \beginsubsubsection{Loading by Font Name} -The \verb|name:| lookup does not depend on cryptic filenames: +The \inlinecode {name:} lookup does not depend on cryptic filenames: \beginlisting \font \pagellaregular = {name:TeX Gyre Pagella} at 9pt @@ -639,7 +639,7 @@ obviously, |random|. Specific pairs of letters and ligatures may be exempt from letterspacing by defining the \LUA functions \luafunction{keeptogether} and \luafunction{keepligature}, - respectively, inside the namespace \verb|luaotfload.letterspace|. + respectively, inside the namespace \inlinecode {luaotfload.letterspace}. % Both functions are called whenever the letterspacing callback encounters an appropriate node or set of nodes. @@ -685,8 +685,8 @@ obviously, |random|. For example, to define a font with the default protrusion vector applied\footnote{% You also need to set - \verb|pdfprotrudechars=2| and - \verb|pdfadjustspacing=2| + \inlinecode {pdfprotrudechars=2} and + \inlinecode {pdfadjustspacing=2} to activate protrusion and expansion, respectively. See the \hyperlink [\PDFTEX manual]{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% @@ -716,19 +716,19 @@ Currently (2014) there are three of them: Applies legacy \TEX ligatures: \begin{tabular}{rlrl} - `` & \verb|``| & '' & \verb|''| \\ - ` & \verb|`| & ' & \verb|'| \\ - " & \verb|"| & -- & \verb|--| \\ - --- & \verb|---| & !` & \verb|!`| \\ - ?` & \verb|?`| & & \\ + `` & \inlinecode {``} & '' & \inlinecode {''} \\ + ` & \inlinecode {`} & ' & \inlinecode {'} \\ + " & \inlinecode {"} & -- & \inlinecode {--} \\ + --- & \inlinecode {---} & !` & \inlinecode {!`} \\ + ?` & \inlinecode {?`} & & \\ \end{tabular} \footnote{% - These contain the feature set \verb|trep| of earlier + These contain the feature set \inlinecode {trep} of earlier versions of \identifier{luaotfload}. Note to \XETEX users: this is the equivalent of the - assignment \verb|mapping=text-tex| using \XETEX's input + assignment \inlinecode {mapping=text-tex} using \XETEX's input remapping feature. } @@ -752,9 +752,10 @@ fonts are available to \LUATEX by means of a \emphasis{database}. This allows referring to fonts not only by explicit filenames but also by the proper names contained in the metadata which is often more accessible to humans.\footnote{% - The tool \hyperlink[\fileent{otfinfo}]{http://www.lcdf.org/type/} (comes - with \TEX Live), when invoked on a font file with the \verb|-i| - option, lists the variety of name fields defined for it. + The tool \hyperlink[\fileent{otfinfo}]{http://www.lcdf.org/type/} + (comes with \TEX Live), when invoked on a font file with the + \inlinecode {-i} option, lists the variety of name fields defined for + it. } When \identifier{luaotfload} is asked to load a font by a font name, @@ -789,7 +790,7 @@ To this end, \identifier{luaotfload} comes with the utility functionality. % Being a \LUA script, there are two ways to run it: -either make it executable (\verb|chmod +x| on unixoid systems) or +either make it executable (\inlinecode {chmod +x} on unixoid systems) or pass it as an argument to \fileent{texlua}.\footnote{% Tests by the maintainer show only marginal performance gain by running with Luigi Scarso’s @@ -801,17 +802,17 @@ pass it as an argument to \fileent{texlua}.\footnote{% On \abbrev{MS} \identifier{Windows} systems, the script can be run either by calling the wrapper application \fileent{luaotfload-tool.exe} or as - \verb|texlua.exe luaotfload-tool.lua|. + \inlinecode {texlua.exe luaotfload-tool.lua}. } % -Invoked with the argument \verb|--update| it will perform a database +Invoked with the argument \inlinecode {--update} it will perform a database update, scanning for fonts not indexed. \beginlisting luaotfload-tool --update \endlisting -Adding the \verb|--force| switch will initiate a complete +Adding the \inlinecode {--force} switch will initiate a complete rebuild of the database. \beginlisting @@ -830,17 +831,17 @@ expected to be located on a given system. % On a Linux machine it follows the paths listed in the \identifier{Fontconfig} configuration files; -consult \verb|man 5 fonts.conf| for further information. +consult \inlinecode {man 5 fonts.conf} for further information. % On \identifier{Windows} systems, the standard location is -\verb|Windows\Fonts|, +\inlinecode {Windows\\Fonts}, % while \identifier{Mac OS~X} requires a multitude of paths to be examined. % The complete list is is given in table \ref{table-searchpaths}. Other paths can be specified by setting the environment variable -\verb+OSFONTDIR+. +\inlinecode {OSFONTDIR}. % If it is non-empty, then search will be extended to the included directories. @@ -852,7 +853,7 @@ directories. \renewcommand{\arraystretch}{1.2} \begincentered \begin{tabular}{lp{.5\textwidth}} - Windows & \verb|%WINDIR%\Fonts| + Windows & \inlinecode {\%WINDIR\%\\Fonts} \\ Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break \fileent{/etc/fonts/fonts.conf} @@ -875,7 +876,7 @@ directories. \fileent{luaotfload-tool} also provides rudimentary means of accessing the information collected in the font database. % -If the option \verb|--find=|\emphasis{name} is given, the script will +If the option \inlinecode {--find=}\emphasis{name} is given, the script will try and search the fonts indexed by \identifier{luaotfload} for a matching name. % @@ -890,7 +891,7 @@ will verify if “Iwona Regular” is found in the database and can be readily requested in a document. If you are unsure about the actual font name, then add the -\verb|-F| (or \verb|--fuzzy|) switch to the command line to enable +\inlinecode {-F} (or \inlinecode {--fuzzy}) switch to the command line to enable approximate matching. % Suppose you cannot precisely remember if the variant of @@ -905,7 +906,7 @@ The query will tell you that indeed the latter name is correct. Basic information about fonts in the database can be displayed -using the \verb|-i| option (\verb|--info|). +using the \inlinecode {-i} option (\inlinecode {--info}). % \beginlisting luaotfload-tool -i --find="Iwona Light Italic" @@ -917,19 +918,19 @@ The meaning of the printed values is described in section 4.4 of the In \TEX Live: \fileent{texmf-dist/doc/luatex/base/luatexref-t.pdf}. } -For a much more detailed report about a given font try the \verb|-I| option -instead (\verb|--inspect|). +For a much more detailed report about a given font try the +\inlinecode {-I} option instead (\inlinecode {--inspect}). \beginlisting luaotfload-tool -I --find="Iwona Light Italic" \endlisting -\verb|luaotfload-tool --help| will list the available command line +\inlinecode {luaotfload-tool --help} will list the available command line switches, including some not discussed in detail here. % For a full documentation of \identifier{luaotfload-tool} and its capabilities refer to the manpage -(\verb|man 1 luaotfload-tool|).\footnote{% - Or see \verb|luaotfload-tool.rst| in the source directory. +(\inlinecode {man 1 luaotfload-tool}).\footnote{% + Or see \inlinecode {luaotfload-tool.rst} in the source directory. } \endsubsection @@ -941,7 +942,7 @@ Some fonts are problematic in general, or just in \LUATEX. % If you find that compiling your document takes far too long or eats away all your system’s memory, you can track down the culprit by -running \verb|luaotfload-tool -v| to increase verbosity. +running \inlinecode {luaotfload-tool -v} to increase verbosity. % Take a note of the \emphasis{filename} of the font that database creation fails with and append it to the file @@ -960,7 +961,7 @@ Place this file to some location where the \identifier{kpse} library can find it, e.~g. \fileent{texmf-local/tex/luatex/luaotfload} if you are running \identifier{\TEX Live},\footnote{% - You may have to run \verb|mktexlsr| if you created a new file in + You may have to run \inlinecode {mktexlsr} if you created a new file in your \fileent{texmf} tree. } or just leave it in the working directory of your document. @@ -1032,7 +1033,7 @@ too developed by Hans Hagen.\footnote{% \hyperlink [part of \CONTEXT]{http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua} and requires \fileent{mtxrun}. Run - \verb|mtxrun --script package --help| + \inlinecode {mtxrun --script package --help} to display further information. For the actual merging code see the file \fileent{util-mrg.lua} that is part of \CONTEXT. @@ -1096,7 +1097,7 @@ merged file, it will load the individual \LUA libraries instead. % Their names remain the same as in \CONTEXT (without the -\verb|otfl|-prefix) since we imported the relevant section of +\inlinecode {otfl}-prefix) since we imported the relevant section of \fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload-main.lua}. Thus if you prefer running bleeding edge code from the \CONTEXT beta, all you have to do is remove @@ -1358,7 +1359,7 @@ verbosity level and redirecting log output to \fileent{stdout}: luaotfload-tool -fuvvv --log=file \endlisting -\noindent In the latter case, invoke the \verb|tail(1)| utility on the +\noindent In the latter case, invoke the \inlinecode {tail(1)} utility on the file for live monitoring of the progress. If database generation fails, the font last printed to the terminal or @@ -1375,13 +1376,13 @@ A common problem is the lack of features for some \OpenType fonts even when specified. % This can be related to the fact that some fonts do not provide features -for the \verb|dflt| script (see above on page \pageref{script-tag}), +for the \inlinecode {dflt} script (see above on page \pageref{script-tag}), which is the default one in this package. % If this happens, assigning a noth script when the font is defined should fix it. % -For example with \verb|latn|: +For example with \inlinecode {latn}: \beginlisting \font\test=file:MyFont.otf:script=latn;+liga; diff --git a/misc/valgrind-kpse-suppression.sup b/misc/valgrind-kpse-suppression.sup index bda96b4..e1cc5f5 100644 --- a/misc/valgrind-kpse-suppression.sup +++ b/misc/valgrind-kpse-suppression.sup @@ -109,3 +109,23 @@ ... fun:init_path } + + +{ + kpathsea-garbage-14 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:kpse_in_name_ok +} + + +{ + kpathsea-garbage-15 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:kpathsea_var_value +} + + diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index ed7fdd3..0055982 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -61,7 +61,7 @@ config.luaotfload.index_file = config.luaotfload.index_file or "luaot config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" config.luaotfload.scan_local = config.luaotfload.scan_local == true -if not config.luaotfload.strip then +if config.luaotfload.strip = nil then config.luaotfload.strip = true end -- cgit v1.2.3 From 07d7f65c89e51d356145560dc6acfaded011398e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 26 Feb 2014 07:08:04 +0100 Subject: [doc] move description and figure environment to more abstract macros --- doc/luaotfload-latex.tex | 29 +++- doc/luaotfload-main.tex | 407 ++++++++++++++++++++++++----------------------- 2 files changed, 228 insertions(+), 208 deletions(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index 58c8793..0e86b83 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -248,9 +248,10 @@ } } -\definelist [definitions]{\normalitem {\fileent {\first}}} -\definelist [filelist]{\normalitem {\fileent {\first}}\space--\hskip 1em} -\definelist [functionlist]{\normalitem {\luafunction {\first}}\hfill\break} +\definelist [descriptions]{\normalitem {\textbf \first}\hfill\break} +\definelist [definitions]{\normalitem {\fileent {\first}}} +\definelist [filelist]{\normalitem {\fileent {\first}}\space--\hskip 1em} +\definelist [functionlist]{\normalitem {\luafunction {\first}}\hfill\break} \def \beginenumeration {\begin {enumerate}} \def \endenumeration {\end {enumerate}} @@ -293,9 +294,11 @@ %% figure floats %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\def \beginsyntaxfloat #1{%% the request syntax diagram +%% syntax definition +\def \beginsyntaxfloat #1#2{%% #1:label #2:caption \begin {figure} [b] - \def \syntaxcaption {#1}% + \edef \syntaxlabel {#1}% + \def \syntaxcaption {#2}% \setlength\grammarparsep{12pt plus 2pt minus 2pt}% \setlength\grammarindent{5cm}% \begingroup @@ -307,10 +310,24 @@ \end {grammar} \endgroup \caption \syntaxcaption - \label{font-syntax} + \label \syntaxlabel \end {figure} } +%% figures, e.g. the file graph +\def \beginfigurefloat #1#2{%% #1:label #2:caption + \begingroup + \begin {figure} [b] + \edef \figurelabel {#1}% + \caption {#2}% +} + +\def \endfigurefloat {% + \label \figurelabel + \end {figure} + \endgroup +} + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% hyperlinks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index c7a7297..7a5526a 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -144,6 +144,7 @@ A selection of individual parts of the syntax are discussed below; for a more formal description see figure \ref{font-syntax}. \beginsyntaxfloat + {font-syntax} {Font request syntax. Braces or double quotes around the \emphasis{specification} rule will @@ -509,210 +510,212 @@ obviously, |random|. \font\librmsaltfirst=LatinModernRoman:salt=1 \endlisting -\noindent Other font options include: - -\begin{description} - -\item [mode] \hfill \\ - \identifier{luaotfload} has two \OpenType processing - \emphasis{modes}: - \identifier{base} and \identifier{node}. - - \identifier{base} mode works by mapping \OpenType - features to traditional \TEX ligature and kerning mechanisms. - % - Supporting only non-contextual substitutions and kerning - pairs, it is the slightly faster, albeit somewhat limited, variant. - % - \identifier{node} mode works by processing \TeX’s internal - node list directly at the \LUA end and supports - a wider range of \OpenType features. - % - The downside is that the intricate operations required for - \identifier{node} mode may slow down typesetting especially - with complex fonts and it does not work in math mode. - - By default \identifier{luaotfload} is in \identifier{node} - mode, and \identifier{base} mode has to be requested where needed, - e.~g. for math fonts. - -\item [script] \label{script-tag} \hfill \\ - An \OpenType script tag;\footnote{% - See \hyperlink {http://www.microsoft.com/typography/otspec/scripttags.htm} - for a list of valid values. +\beginsubsection {Basic font features} + +\begindescriptions + + \altitem {mode} + \identifier{luaotfload} has two \OpenType processing + \emphasis{modes}: + \identifier{base} and \identifier{node}. + + \identifier{base} mode works by mapping \OpenType + features to traditional \TEX ligature and kerning mechanisms. + % + Supporting only non-contextual substitutions and kerning + pairs, it is the slightly faster, albeit somewhat limited, variant. + % + \identifier{node} mode works by processing \TeX’s internal + node list directly at the \LUA end and supports + a wider range of \OpenType features. + % + The downside is that the intricate operations required for + \identifier{node} mode may slow down typesetting especially + with complex fonts and it does not work in math mode. + + By default \identifier{luaotfload} is in \identifier{node} + mode, and \identifier{base} mode has to be requested where needed, + e.~g. for math fonts. + + \altitem {script} \label{script-tag} + An \OpenType script tag;\footnote{% + See \hyperlink {http://www.microsoft.com/typography/otspec/scripttags.htm} + for a list of valid values. + % + For scripts derived from the Latin alphabet the value + |latn| is good choice. + } + the default value is |dlft|. + % + Some fonts, including very popular ones by foundries like Adobe, + do not assign features to the |dflt| script, in + which case the script needs to be set explicitly. + + \altitem {language} + An \OpenType language system identifier,\footnote{% + Cf. \hyperlink {http://www.microsoft.com/typography/otspec/languagetags.htm}. + } + defaulting to |dflt|. + + \altitem {featurefile} + A comma-separated list of feature files to be applied to the + font. + % + Feature files contain a textual representation of + \OpenType tables and extend the features of a font + on fly. + % + After they are applied to a font, features defined in a + feature file can be enabled or disabled just like any + other font feature. + % + The syntax is documented in \identifier{Adobe}’s + \OpenType Feature File Specification.\footnote{% + Cf. \hyperlink {http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. + Feature file support is part of the engine which at the + time of this writing (2014) implements the spec only + partially. + See the + \hyperlink [\LUATEX tracker]{http://tracker.luatex.org/view.php?id=231} + for details. + } + + For a demonstration of how to set a |tkrn| feature consult + the file |tkrn.fea| that is part of \identifier{luaotfload}. + It can be read and applied as follows: + + |\font\test=Latin Modern Roman:featurefile=tkrn.fea;+tkrn| + + \altitem {color} + A font color, defined as a triplet of two-digit hexadecimal + \abbrev{rgb} values, with an optional fourth value for + transparency + (where |00| is completely transparent and |FF| is opaque). + + For example, in order to set text in semitransparent red: + + \beginlisting +\font\test={Latin Modern Roman}:color=FF0000BB + \endlisting + + \altitem {kernfactor \& letterspace} + Define a font with letterspacing (tracking) enabled. + % + In \identifier{luaotfload}, letterspacing is implemented by + inserting additional kerning between glyphs. + + This approach is derived from and still quite similar to the + \emphasis{character kerning} (\texmacro{setcharacterkerning} / + \texmacro{definecharacterkerning} \& al.) functionality of + Context, see the file \fileent{typo-krn.lua} there. + % + The main difference is that \identifier{luaotfload} does not + use \LUATEX attributes to assign letterspacing to regions, + but defines virtual letterspaced versions of a font. + + The option \identifier{kernfactor} accepts a numeric value that + determines the letterspacing factor to be applied to the font + size. + % + E.~g. a kern factor of $0.42$ applied to a $10$ pt font + results in $4.2$ pt of additional kerning applied to each + pair of glyphs. + % + Ligatures are split into their component glyphs unless + explicitly ignored (see below). + + For compatibility with \XETEX an alternative + \identifier{letterspace} option is supplied that interprets the + supplied value as a \emphasis{percentage} of the font size but + is otherwise identical to \identifier{kernfactor}. + % + Consequently, both definitions in below snippet yield the same + letterspacing width: + + \beginlisting +\font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125" +\font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5" + \endlisting + + Specific pairs of letters and ligatures may be exempt from + letterspacing by defining the \LUA functions + \luafunction{keeptogether} and \luafunction{keepligature}, + respectively, inside the namespace \inlinecode {luaotfload.letterspace}. % - For scripts derived from the Latin alphabet the value - |latn| is good choice. - } - the default value is |dlft|. - % - Some fonts, including very popular ones by foundries like Adobe, - do not assign features to the |dflt| script, in - which case the script needs to be set explicitly. - -\item [language] \hfill \\ - An \OpenType language system identifier,\footnote{% - Cf. \hyperlink {http://www.microsoft.com/typography/otspec/languagetags.htm}. - } - defaulting to |dflt|. - -\item [featurefile] \hfill \\ - A comma-separated list of feature files to be applied to the - font. - % - Feature files contain a textual representation of - \OpenType tables and extend the features of a font - on fly. - % - After they are applied to a font, features defined in a - feature file can be enabled or disabled just like any - other font feature. - % - The syntax is documented in \identifier{Adobe}’s - \OpenType Feature File Specification.\footnote{% - Cf. \hyperlink {http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. - Feature file support is part of the engine which at the - time of this writing (2014) implements the spec only - partially. - See the - \hyperlink [\LUATEX tracker]{http://tracker.luatex.org/view.php?id=231} - for details. - } - - For a demonstration of how to set a |tkrn| feature consult - the file |tkrn.fea| that is part of \identifier{luaotfload}. - It can be read and applied as follows: - - |\font\test=Latin Modern Roman:featurefile=tkrn.fea;+tkrn| - -\item [color] \hfill \\ - A font color, defined as a triplet of two-digit hexadecimal - \abbrev{rgb} values, with an optional fourth value for - transparency - (where |00| is completely transparent and |FF| is opaque). - - For example, in order to set text in semitransparent red: - - \beginlisting - \font\test={Latin Modern Roman}:color=FF0000BB - \endlisting - -\item [kernfactor \& letterspace] \hfill \\ - Define a font with letterspacing (tracking) enabled. - % - In \identifier{luaotfload}, letterspacing is implemented by - inserting additional kerning between glyphs. - - This approach is derived from and still quite similar to the - \emphasis{character kerning} (\texmacro{setcharacterkerning} / - \texmacro{definecharacterkerning} \& al.) functionality of - Context, see the file \fileent{typo-krn.lua} there. - % - The main difference is that \identifier{luaotfload} does not - use \LUATEX attributes to assign letterspacing to regions, - but defines virtual letterspaced versions of a font. - - The option \identifier{kernfactor} accepts a numeric value that - determines the letterspacing factor to be applied to the font - size. - % - E.~g. a kern factor of $0.42$ applied to a $10$ pt font - results in $4.2$ pt of additional kerning applied to each - pair of glyphs. - % - Ligatures are split into their component glyphs unless - explicitly ignored (see below). - - For compatibility with \XETEX an alternative - \identifier{letterspace} option is supplied that interprets the - supplied value as a \emphasis{percentage} of the font size but - is otherwise identical to \identifier{kernfactor}. - % - Consequently, both definitions in below snippet yield the same - letterspacing width: - - \beginlisting - \font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125" - \font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5" - \endlisting - - Specific pairs of letters and ligatures may be exempt from - letterspacing by defining the \LUA functions - \luafunction{keeptogether} and \luafunction{keepligature}, - respectively, inside the namespace \inlinecode {luaotfload.letterspace}. - % - Both functions are called whenever the letterspacing callback - encounters an appropriate node or set of nodes. - % - If they return a true-ish value, no extra kern is inserted at - the current position. - % - \luafunction{keeptogether} receives a pair of consecutive - glyph nodes in order of their appearance in the node list. - % - \luafunction{keepligature} receives a single node which can be - analyzed into components. - % - (For details refer to the \emphasis{glyph nodes} section in the - \LUATEX reference manual.) - % - The implementation of both functions is left entirely to the - user. - - -\item [protrusion \& expansion] \hfill \\ - These keys control microtypographic features of the font, - namely \emphasis{character protrusion} and \emphasis{font - expansion}. - % - Their arguments are names of \LUA tables that contain - values for the respective features.\footnote{% - For examples of the table layout please refer to the - section of the file \fileent{luaotfload-fonts-ext.lua} where the - default values are defined. - % - Alternatively and with loss of information, you can dump - those tables into your terminal by issuing - \beginlisting - \directlua{inspect(fonts.protrusions.setups.default) - inspect(fonts.expansions.setups.default)} - \endlisting - at some point after loading \fileent{luaotfload.sty}. - } - % - For both, only the set \identifier{default} is predefined. - - For example, to define a font with the default - protrusion vector applied\footnote{% - You also need to set - \inlinecode {pdfprotrudechars=2} and - \inlinecode {pdfadjustspacing=2} - to activate protrusion and expansion, respectively. - See the - \hyperlink [\PDFTEX manual]{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% - for details. - }: - - \beginlisting - \font\test=LatinModernRoman:protrusion=default - \endlisting -\end{description} - -\paragraph{Non-standard font features} + Both functions are called whenever the letterspacing callback + encounters an appropriate node or set of nodes. + % + If they return a true-ish value, no extra kern is inserted at + the current position. + % + \luafunction{keeptogether} receives a pair of consecutive + glyph nodes in order of their appearance in the node list. + % + \luafunction{keepligature} receives a single node which can be + analyzed into components. + % + (For details refer to the \emphasis{glyph nodes} section in the + \LUATEX reference manual.) + % + The implementation of both functions is left entirely to the + user. + + + \altitem {protrusion \& expansion} + These keys control microtypographic features of the font, + namely \emphasis{character protrusion} and \emphasis{font + expansion}. + % + Their arguments are names of \LUA tables that contain + values for the respective features.\footnote{% + For examples of the table layout please refer to the + section of the file \fileent{luaotfload-fonts-ext.lua} where the + default values are defined. + % + Alternatively and with loss of information, you can dump + those tables into your terminal by issuing + \beginlisting +\directlua{inspect(fonts.protrusions.setups.default) + inspect(fonts.expansions.setups.default)} + \endlisting + at some point after loading \fileent{luaotfload.sty}. + } + % + For both, only the set \identifier{default} is predefined. + + For example, to define a font with the default + protrusion vector applied\footnote{% + You also need to set + \inlinecode {pdfprotrudechars=2} and + \inlinecode {pdfadjustspacing=2} + to activate protrusion and expansion, respectively. + See the + \hyperlink [\PDFTEX manual]{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% + for details. + }: + + \beginlisting +\font\test=LatinModernRoman:protrusion=default + \endlisting +\enddescriptions + +\endsubsection + +\beginsubsection {Non-standard font features} \identifier{luaotfload} adds a number of features that are not defined in the original \OpenType specification, most of them aiming at emulating the behavior familiar from other \TEX engines. % Currently (2014) there are three of them: -\begin{description} +\begindescriptions - \item [anum] + \altitem {anum} Substitutes the glyphs in the \abbrev{ascii} number range with their counterparts from eastern Arabic or Persian, depending on the value of \identifier{language}. - \item [tlig] + \altitem {tlig} Applies legacy \TEX ligatures: \begin{tabular}{rlrl} @@ -732,12 +735,12 @@ Currently (2014) there are three of them: remapping feature. } - \item [itlc] + \altitem {itlc} Computes italic correction values (active by default). -\end{description} - +\enddescriptions +\endsubsection \endsection %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1138,11 +1141,11 @@ files not contained in the merge. Some of these have no equivalent in \altitem {luaotfload-letterspace.lua} font-based letterspacing. \endfilelist -\begin{figure}[b] - \caption{Schematic of the files in \identifier{Luaotfload}} +\beginfigurefloat + {file-graph} + {Schematic of the files in \identifier{Luaotfload}} \includegraphics[width=\textwidth]{filegraph.pdf} - \label{file-graph} -\end{figure} +\endfigurefloat \endsection @@ -1308,14 +1311,14 @@ are defined for which scripts. %% not implemented, may come back later % \beginsubsubsection{Database} -% +% % \beginfunctionlist % \altitem {aux.scan_external_dir(dir : string)} % Include fonts in directory \luafunction{dir} in font lookups without % adding them to the database. -% +% % \endfunctionlist -% +% % \endsubsubsection \endsubsection -- cgit v1.2.3 From 8b97d87e20f9ef4a97e81fc9afb852f1620ee790 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 3 Mar 2014 07:20:56 +0100 Subject: [main] fix syntax --- src/luaotfload-main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 0055982..3c4c770 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -61,7 +61,7 @@ config.luaotfload.index_file = config.luaotfload.index_file or "luaot config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" config.luaotfload.scan_local = config.luaotfload.scan_local == true -if config.luaotfload.strip = nil then +if config.luaotfload.strip == nil then config.luaotfload.strip = true end -- cgit v1.2.3 From 1c0ee1f4368f57f820392b8b5398c0503215656d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 3 Mar 2014 07:57:00 +0100 Subject: [doc] conver GPL in manual to abstract macros --- doc/luaotfload-main.tex | 534 ++++++++++++++++++++++++------------------------ 1 file changed, 265 insertions(+), 269 deletions(-) diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 7a5526a..4c848bf 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -1464,28 +1464,28 @@ You might want to zoom in. \columnsep=3\columnsep \begintriplecolumns \begincentered - {\Large GNU GENERAL PUBLIC LICENSE\par} + {\Largefont{GNU GENERAL PUBLIC LICENSE}\par} \bigskip {Version 2, June 1991} - {\parindent 0in + \begingroup + \parindent 0in - Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. + Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. - \bigskip + \bigskip - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - \bigskip + \bigskip - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - } + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + \endgroup {\bf\large Preamble} \endcentered - The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software---to @@ -1533,266 +1533,262 @@ The precise terms and conditions for copying, distribution and modification follow. \begincentered - {\Large \sc Terms and Conditions For Copying, Distribution and - Modification} + %% so … these aren’t actual headings‽ + \Largefont{\smallcaps{% + Terms and Conditions For Copying, Distribution and Modification + }} \endcentered \beginenumeration -\item -This License applies to any program or other work which contains a notice -placed by the copyright holder saying it may be distributed under the -terms of this General Public License. The ``Program'', below, refers to -any such program or work, and a ``work based on the Program'' means either -the Program or any derivative work under copyright law: that is to say, a -work containing the Program or a portion of it, either verbatim or with -modifications and/or translated into another language. (Hereinafter, -translation is included without limitation in the term ``modification''.) -Each licensee is addressed as ``you''. - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. +\item This License applies to any program or other work which contains + a notice placed by the copyright holder saying it may be + distributed under the terms of this General Public License. The + ``Program'', below, refers to any such program or work, and a + ``work based on the Program'' means either the Program or any + derivative work under copyright law: that is to say, a work + containing the Program or a portion of it, either verbatim or + with modifications and/or translated into another language. + (Hereinafter, translation is included without limitation in the + term ``modification''.) Each licensee is addressed as ``you''. + + Activities other than copying, distribution and modification are + not covered by this License; they are outside its scope. The act + of running the Program is not restricted, and the output from the + Program is covered only if its contents constitute a work based + on the Program (independent of having been made by running the + Program). Whether that is true depends on what the Program does. \item You may copy and distribute verbatim copies of the Program's source - code as you receive it, in any medium, provided that you conspicuously - and appropriately publish on each copy an appropriate copyright notice - and disclaimer of warranty; keep intact all the notices that refer to - this License and to the absence of any warranty; and give any other - recipients of the Program a copy of this License along with the Program. - -You may charge a fee for the physical act of transferring a copy, and you -may at your option offer warranty protection in exchange for a fee. - -\item -You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - -\beginenumeration - -\item -You must cause the modified files to carry prominent notices stating that -you changed the files and the date of any change. - -\item -You must cause any work that you distribute or publish, that in -whole or in part contains or is derived from the Program or any -part thereof, to be licensed as a whole at no charge to all third -parties under the terms of this License. - -\item -If the modified program normally reads commands interactively -when run, you must cause it, when started running for such -interactive use in the most ordinary way, to print or display an -announcement including an appropriate copyright notice and a -notice that there is no warranty (or else, saying that you provide -a warranty) and that users may redistribute the program under -these conditions, and telling the user how to view a copy of this -License. (Exception: if the Program itself is interactive but -does not normally print such an announcement, your work based on -the Program is not required to print an announcement.) - -\endenumeration - - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - -\item -You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - -\beginenumeration - -\item - -Accompany it with the complete corresponding machine-readable -source code, which must be distributed under the terms of Sections -1 and 2 above on a medium customarily used for software interchange; or, - -\item - -Accompany it with a written offer, valid for at least three -years, to give any third party, for a charge no more than your -cost of physically performing source distribution, a complete -machine-readable copy of the corresponding source code, to be -distributed under the terms of Sections 1 and 2 above on a medium -customarily used for software interchange; or, - -\item - -Accompany it with the information you received as to the offer -to distribute corresponding source code. (This alternative is -allowed only for noncommercial distribution and only if you -received the program in object code or executable form with such -an offer, in accord with Subsection b above.) - -\endenumeration - - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - -\item -You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - -\item -You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - -\item -Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - -\item -If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - -\item -If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - -\item -The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and ``any -later version'', you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - -\item -If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. + code as you receive it, in any medium, provided that you + conspicuously and appropriately publish on each copy an + appropriate copyright notice and disclaimer of warranty; keep + intact all the notices that refer to this License and to the + absence of any warranty; and give any other recipients of the + Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, + and you may at your option offer warranty protection in exchange + for a fee. + +\item You may modify your copy or copies of the Program or any portion + of it, thus forming a work based on the Program, and copy and + distribute such modifications or work under the terms of Section + 1 above, provided that you also meet all of these conditions: + + \beginenumeration + + \item You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + \item You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + \item If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you + provide a warranty) and that users may redistribute the program + under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is + interactive but does not normally print such an announcement, + your work based on the Program is not required to print an + announcement.) + + \endenumeration + + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the + Program, and can be reasonably considered independent and + separate works in themselves, then this License, and its terms, + do not apply to those sections when you distribute them as + separate works. But when you distribute the same sections as + part of a whole which is a work based on the Program, the + distribution of the whole must be on the terms of this License, + whose permissions for other licensees extend to the entire whole, + and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or + contest your rights to work written entirely by you; rather, the + intent is to exercise the right to control the distribution of + derivative or collective works based on the Program. + + In addition, mere aggregation of another work not based on the + Program with the Program (or with a work based on the Program) on + a volume of a storage or distribution medium does not bring the + other work under the scope of this License. + +\item You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the + terms of Sections 1 and 2 above provided that you also do one of + the following: + + \beginenumeration + + \item Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Sections 1 and 2 above on a medium customarily used for software + interchange; or, + + \item Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + \item Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + + \endenumeration + + + The source code for a work means the preferred form of the work + for making modifications to it. For an executable work, complete + source code means all the source code for all modules it + contains, plus any associated interface definition files, plus + the scripts used to control compilation and installation of the + executable. However, as a special exception, the source code + distributed need not include anything that is normally + distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system + on which the executable runs, unless that component itself + accompanies the executable. + + If distribution of executable or object code is made by offering + access to copy from a designated place, then offering equivalent + access to copy the source code from the same place counts as + distribution of the source code, even though third parties are + not compelled to copy the source along with the object code. + +\item You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense or distribute the Program + is void, and will automatically terminate your rights under this + License. However, parties who have received copies, or rights, + from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + +\item You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify + or distribute the Program or its derivative works. These actions + are prohibited by law if you do not accept this License. + Therefore, by modifying or distributing the Program (or any work + based on the Program), you indicate your acceptance of this + License to do so, and all its terms and conditions for copying, + distributing or modifying the Program or works based on it. + +\item Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program + subject to these terms and conditions. You may not impose any + further restrictions on the recipients' exercise of the rights + granted herein. You are not responsible for enforcing compliance + by third parties to this License. + +\item If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent + issues), conditions are imposed on you (whether by court order, + agreement or otherwise) that contradict the conditions of this + License, they do not excuse you from the conditions of this + License. If you cannot distribute so as to satisfy + simultaneously your obligations under this License and any other + pertinent obligations, then as a consequence you may not + distribute the Program at all. For example, if a patent license + would not permit royalty-free redistribution of the Program by + all those who receive copies directly or indirectly through you, + then the only way you could satisfy both it and this License + would be to refrain entirely from distribution of the Program. + + If any portion of this section is held invalid or unenforceable + under any particular circumstance, the balance of the section is + intended to apply and the section as a whole is intended to apply + in other circumstances. + + It is not the purpose of this section to induce you to infringe + any patents or other property right claims or to contest validity + of any such claims; this section has the sole purpose of + protecting the integrity of the free software distribution + system, which is implemented by public license practices. Many + people have made generous contributions to the wide range of + software distributed through that system in reliance on + consistent application of that system; it is up to the + author/donor to decide if he or she is willing to distribute + software through any other system and a licensee cannot impose + that choice. + + This section is intended to make thoroughly clear what is + believed to be a consequence of the rest of this License. + +\item If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, + the original copyright holder who places the Program under this + License may add an explicit geographical distribution limitation + excluding those countries, so that distribution is permitted only + in or among countries not thus excluded. In such case, this + License incorporates the limitation as if written in the body of + this License. + +\item The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such + new versions will be similar in spirit to the present version, + but may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies a version number of this License which applies + to it and ``any later version'', you have the option of following + the terms and conditions either of that version or of any later + version published by the Free Software Foundation. If the + Program does not specify a version number of this License, you + may choose any version ever published by the Free Software + Foundation. + +\item If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to + the author to ask for permission. For software which is + copyrighted by the Free Software Foundation, write to the Free + Software Foundation; we sometimes make exceptions for this. Our + decision will be guided by the two goals of preserving the free + status of all derivatives of our free software and of promoting + the sharing and reuse of software generally. \begincentered - {\Large\sc No Warranty} + \Largefont{\smallcaps{No Warranty}} \endcentered -\item -{\sc Because the program is licensed free of charge, there is no warranty -for the program, to the extent permitted by applicable law. Except when -otherwise stated in writing the copyright holders and/or other parties -provide the program ``as is'' without warranty of any kind, either expressed -or implied, including, but not limited to, the implied warranties of -merchantability and fitness for a particular purpose. The entire risk as -to the quality and performance of the program is with you. Should the -program prove defective, you assume the cost of all necessary servicing, -repair or correction.} - -\item -{\sc In no event unless required by applicable law or agreed to in writing -will any copyright holder, or any other party who may modify and/or -redistribute the program as permitted above, be liable to you for damages, -including any general, special, incidental or consequential damages arising -out of the use or inability to use the program (including but not limited -to loss of data or data being rendered inaccurate or losses sustained by -you or third parties or a failure of the program to operate with any other -programs), even if such holder or other party has been advised of the -possibility of such damages.} +\item \smallcaps{Because the program is licensed free of charge, there + is no warranty for the program, to the extent permitted by + applicable law. Except when otherwise stated in writing the + copyright holders and/or other parties provide the program ``as + is'' without warranty of any kind, either expressed or implied, + including, but not limited to, the implied warranties of + merchantability and fitness for a particular purpose. The entire + risk as to the quality and performance of the program is with + you. Should the program prove defective, you assume the cost of + all necessary servicing, repair or correction.} + +\item \smallcaps{In no event unless required by applicable law or + agreed to in writing will any copyright holder, or any other + party who may modify and/or redistribute the program as permitted + above, be liable to you for damages, including any general, + special, incidental or consequential damages arising out of the + use or inability to use the program (including but not limited to + loss of data or data being rendered inaccurate or losses + sustained by you or third parties or a failure of the program to + operate with any other programs), even if such holder or other + party has been advised of the possibility of such damages.} \endenumeration \begincentered - {\Large\sc End of Terms and Conditions} + \Largefont{\smallcaps{End of Terms and Conditions}} \endcentered @@ -1807,10 +1803,10 @@ possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. - To do so, attach the following notices to the program. It is safest to - attach them to the start of each source file to most effectively convey - the exclusion of warranty; and each file should have at least the - ``copyright'' line and a pointer to where the full notice is found. +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +``copyright'' line and a pointer to where the full notice is found. \beginnarrower one line to give the program's name and a brief idea of what it does. \\ @@ -1844,22 +1840,22 @@ when it starts in an interactive mode: \endnarrower -The hypothetical commands {\tt show w} and {\tt show c} should show the -appropriate parts of the General Public License. Of course, the commands -you use may be called something other than {\tt show w} and {\tt show c}; -they could even be mouse-clicks or menu items---whatever suits your -program. +The hypothetical commands \proportional{show w} and \proportional{show +c} should show the appropriate parts of the General Public License. Of +course, the commands you use may be called something other than +\proportional{show w} and \proportional{show c}; they could even be +mouse-clicks or menu items---whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a ``copyright disclaimer'' for the program, if necessary. Here is a sample; alter the names: \beginnarrower -Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\ -`Gnomovision' (which makes passes at compilers) written by James Hacker. \\ + Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\ + `Gnomovision' (which makes passes at compilers) written by James Hacker. \\ -signature of Ty Coon, 1 April 1989 \\ -Ty Coon, President of Vice + signature of Ty Coon, 1 April 1989 \\ + Ty Coon, President of Vice \endnarrower -- cgit v1.2.3 From 959794804e53aaea76298cba35ccb6c3f347fb1c Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 4 Mar 2014 06:45:57 +0100 Subject: [doc] move table floats and inline code snippets to new macros --- doc/luaotfload-latex.tex | 43 ++++++++++++++++--- doc/luaotfload-main.tex | 108 +++++++++++++++++++++++------------------------ 2 files changed, 88 insertions(+), 63 deletions(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index 0e86b83..e0afe45 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -89,12 +89,16 @@ \def\restoreunderscore{\catcode`\_=12\relax} -\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs -\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs -\definehighlight[luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers -\definehighlight [identifier][\sffamily] %% names -\definehighlight [abbrev][\rmfamily\scshape] %% acronyms -\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph +\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs +\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs +\definehighlight [luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers +\definehighlight [identifier][\sffamily] %% names +\definehighlight [abbrev][\rmfamily\scshape] %% acronyms +\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph + +\definehighlight [Largefont][\Large] %% font size +\definehighlight [smallcaps][\sc] %% font feature +\definehighlight [proportional][\tt] %% font switch \newcommand*\email[1]{\href{mailto:#1}{#1}} @@ -291,7 +295,7 @@ \let \typesetcontent \tableofcontent %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% figure floats +%% floats %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% syntax definition @@ -328,6 +332,24 @@ \endgroup } +%% tables + +\def \begintablefloat #1#2{%% #1:label #2:caption + \begingroup + \begin {table} [t] + \hrule + \edef \floatlabel {#1}% + \caption {#2}% +} + +\def \endtablefloat {% + \label \floatlabel + \hrule + \end {table} + \endgroup +} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% hyperlinks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -347,6 +369,13 @@ } +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% escaped characters +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\let \charpercent \textpercent +\let \charbackslash \textbackslash + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% main %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 4c848bf..002b1bb 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -127,10 +127,10 @@ in the task and willingness to respond to our suggestions. \identifier{luaotfload} supports an extended font request syntax: \beginnarrower - |\font\foo={|% - \meta{prefix}|:|% - \meta{font name}|:|% - \meta{font features}|}|% + \inlinecode{\\font\\foo=\{}% + \meta{prefix}\inlinecode{:}% + \meta{font name}\inlinecode{:}% + \meta{font features}\inlinecode{\}}% \meta{\TEX font features} \endnarrower @@ -210,7 +210,7 @@ In \identifier{luaotfload}, the canonical syntax for font requests requires a \emphasis{prefix}: % \beginnarrower - |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots + \inlinecode{\\font\\fontname=}\meta{prefix}\inlinecode{:}\meta{fontname}\dots \endnarrower % where \meta{prefix} is either \inlinecode{file:} or \inlinecode {name:}.\footnote{% @@ -305,7 +305,7 @@ There are again two modes: bracketed and unbracketed. A bracketed request looks as follows. \beginnarrower - |\font\fontname=[|\meta{path to file}|]| + \inlinecode{\\font\\fontname=[}\meta{path to file}\inlinecode{]} \endnarrower \noindent @@ -316,7 +316,7 @@ Naturally, path-less file names are equally valid and processed the same way as an ordinary \inlinecode {file:} lookup. \beginnarrower - |\font\fontname=|\meta{font name} \dots + \inlinecode{\\font\\fontname=}\meta{font name} \dots \endnarrower Unbracketed (or, for lack of a better word: \emphasis{anonymous}) @@ -334,7 +334,7 @@ Furthermore, \identifier{luaotfload} supports the slashed (shorthand) font style notation from \XETEX. \beginnarrower - |\font\fontname=|\meta{font name}|/|\meta{modifier}\dots + \inlinecode{\\font\\fontname=}\meta{font name}\inlinecode{/}\meta{modifier}\dots \endnarrower \noindent @@ -423,7 +423,7 @@ is installed in some location accessible by \identifier{luaotfload}, the regular shape can be loaded as follows: \beginlisting - \font\iwona=Iwona at 20pt + \font \iwona = Iwona at 20pt \endlisting \noindent @@ -431,18 +431,18 @@ To load the most common of the other styles, the slash notation can be employed as shorthand: \beginlisting - \font\iwonaitalic =Iwona/I at 20pt - \font\iwonabold =Iwona/B at 20pt - \font\iwonabolditalic=Iwona/BI at 20pt + \font \iwonaitalic = Iwona/I at 20pt + \font \iwonabold = Iwona/B at 20pt + \font \iwonabolditalic = Iwona/BI at 20pt \endlisting \noindent which is equivalent to these full names: \beginlisting - \font\iwonaitalic ="Iwona Italic" at 20pt - \font\iwonabold ="Iwona Bold" at 20pt - \font\iwonabolditalic="Iwona BoldItalic" at 20pt + \font \iwonaitalic = "Iwona Italic" at 20pt + \font \iwonabold = "Iwona Bold" at 20pt + \font \iwonabolditalic = "Iwona BoldItalic" at 20pt \endlisting \endsubsubsection @@ -457,10 +457,10 @@ which is equivalent to these full names: general scheme for font requests: \beginnarrower - |\font\foo={|% - \meta{prefix}|:|% - \meta{font name}|:|% - \meta{font features}|}|% + \inlinecode{\\font\\foo=\{}% + \meta{prefix}\inlinecode{:}% + \meta{font name}\inlinecode{:}% + \meta{font features}\inlinecode{\}}% \meta{\TEX font features} \endnarrower @@ -474,40 +474,40 @@ tags\footnote{% } and font options. % -Prepending a font feature with a |+| (plus sign) enables it, whereas -a |-| (minus) disables it. For instance, the request +Prepending a font feature with a \inlinecode{+} (plus sign) enables it, +whereas a \inlinecode{-} (minus) disables it. For instance, the request \beginlisting - \font\test=LatinModernRoman:+clig;-kern + \font \test = LatinModernRoman:+clig;-kern \endlisting -\noindent activates contextual ligatures (|clig|) and disables -kerning (|kern|). +\noindent activates contextual ligatures (\inlinecode{clig}) and +disables kerning (\inlinecode{kern}). % -Alternatively the options |true| or |false| can be passed to -the feature in a key/value expression. +Alternatively the options \inlinecode{true} or \inlinecode{false} can +be passed to the feature in a key/value expression. % The following request has the same meaning as the last one: \beginlisting - \font\test=LatinModernRoman:clig=true;kern=false + \font \test = LatinModernRoman:clig=true;kern=false \endlisting \noindent Furthermore, this second syntax is required should a font feature accept other options besides a true/false switch. % -For example, \emphasis{stylistic alternates} (|salt|) are variants of -given glyphs. +For example, \emphasis{stylistic alternates} (\inlinecode{salt}) are +variants of given glyphs. % They can be selected either explicitly by supplying the variant index (starting from one), or randomly by setting the value to, -obviously, |random|. +obviously, \inlinecode{random}. %% TODO verify that this actually works with a font that supports %% the salt/random feature!\fi \beginlisting - \font\librmsaltfirst=LatinModernRoman:salt=1 + \font \librmsaltfirst = LatinModernRoman:salt=1 \endlisting \beginsubsection {Basic font features} @@ -543,19 +543,19 @@ obviously, |random|. for a list of valid values. % For scripts derived from the Latin alphabet the value - |latn| is good choice. + \inlinecode{latn} is good choice. } - the default value is |dlft|. + the default value is \inlinecode{dlft}. % Some fonts, including very popular ones by foundries like Adobe, - do not assign features to the |dflt| script, in + do not assign features to the \inlinecode{dflt} script, in which case the script needs to be set explicitly. \altitem {language} An \OpenType language system identifier,\footnote{% Cf. \hyperlink {http://www.microsoft.com/typography/otspec/languagetags.htm}. } - defaulting to |dflt|. + defaulting to \inlinecode{dflt}. \altitem {featurefile} A comma-separated list of feature files to be applied to the @@ -580,22 +580,23 @@ obviously, |random|. for details. } - For a demonstration of how to set a |tkrn| feature consult - the file |tkrn.fea| that is part of \identifier{luaotfload}. + For a demonstration of how to set a \inlinecode{tkrn} feature consult + the file \inlinecode{tkrn.fea} that is part of \identifier{luaotfload}. It can be read and applied as follows: - |\font\test=Latin Modern Roman:featurefile=tkrn.fea;+tkrn| + \inlinecode{\\font \\test = Latin Modern Roman:featurefile=tkrn.fea;+tkrn} \altitem {color} A font color, defined as a triplet of two-digit hexadecimal \abbrev{rgb} values, with an optional fourth value for transparency - (where |00| is completely transparent and |FF| is opaque). + (where \inlinecode{00} is completely transparent and + \inlinecode{FF} is opaque). For example, in order to set text in semitransparent red: \beginlisting -\font\test={Latin Modern Roman}:color=FF0000BB +\font \test = "Latin Modern Roman:color=FF0000BB" \endlisting \altitem {kernfactor \& letterspace} @@ -633,8 +634,8 @@ obviously, |random|. letterspacing width: \beginlisting -\font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125" -\font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5" +\font \iwonakernedA = "file:Iwona-Regular.otf:kernfactor=0.125" +\font \iwonakernedB = "file:Iwona-Regular.otf:letterspace=12.5" \endlisting Specific pairs of letters and ligatures may be exempt from @@ -695,7 +696,7 @@ obviously, |random|. }: \beginlisting -\font\test=LatinModernRoman:protrusion=default +\font \test = LatinModernRoman:protrusion=default \endlisting \enddescriptions @@ -849,14 +850,11 @@ Other paths can be specified by setting the environment variable If it is non-empty, then search will be extended to the included directories. -\begin{table}[t] - \hrule - \caption{List of paths searched for each supported operating - system.} - \renewcommand{\arraystretch}{1.2} +\begintablefloat {table-searchpaths} + {List of paths searched for each supported operating system.} \begincentered \begin{tabular}{lp{.5\textwidth}} - Windows & \inlinecode {\%WINDIR\%\\Fonts} + Windows & \inlinecode {\% WINDIR\%\\ Fonts} \\ Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break \fileent{/etc/fonts/fonts.conf} @@ -868,9 +866,7 @@ directories. \\ \end{tabular} \endcentered - \label{table-searchpaths} - \hrule -\end{table} +\endtablefloat \endsubsection @@ -957,7 +953,7 @@ plain filename should suffice. % File extensions (\fileent{.otf}, \fileent{.ttf}, etc.) may be omitted. % -Anything after a percent (|%|) character until the end of the line +Anything after a percent (\inlinecode {\%}) character until the end of the line is ignored, so use this to add comments. % Place this file to some location where the \identifier{kpse} @@ -973,7 +969,7 @@ or just leave it in the working directory of your document. \fileent{luaotfload-blacklist.cnf} it finds, so the fonts in \fileent{./luaotfload-blacklist.cnf} extend the global blacklist. -Furthermore, a filename prepended with a dash character (|-|) is +Furthermore, a filename prepended with a dash character (\inlinecode{-}) is removed from the blacklist, causing it to be temporarily whitelisted without modifying the global file. % @@ -1208,7 +1204,7 @@ writes an entire font object to the file \fileent{fontdump.lua}: "my_private_callbacks.dump_font" ) } - \font\dumpme=name:Iwona + \font \dumpme = name:Iwona \bye \endlisting @@ -1388,7 +1384,7 @@ fix it. For example with \inlinecode {latn}: \beginlisting - \font\test=file:MyFont.otf:script=latn;+liga; + \font \test = file:MyFont.otf:script=latn;+liga; \endlisting You can get a list of features that a font defines for scripts and -- cgit v1.2.3 From dea1714fbd825b67b8116ca20fd8e5464af79f29 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 4 Mar 2014 06:50:56 +0100 Subject: [db] treat missing fontname as invalid font --- src/luaotfload-database.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 923f7c5..54738df 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -1346,6 +1346,7 @@ end --- find_closest() local load_font_file = function (filename, subfont) local rawfont, _msg = fontloaderopen (filename, subfont) + --local rawfont, _msg = fontloaderinfo (filename, subfont) if not rawfont then report ("log", 1, "db", "ERROR: failed to open %s.", filename) return @@ -1406,12 +1407,13 @@ end --]]-- local get_raw_info = function (metadata, basename) local fullname - local fontname + local fontname = metadata.fontname + local fullname = metadata.fullname local psname local validation_state = metadata.validation_state - if validation_state - and tablecontains (validation_state, "bad_ps_fontname") + if (validation_state and tablecontains (validation_state, "bad_ps_fontname")) + or not fontname then --- Broken names table, e.g. avkv.ttf with UTF-16 strings; --- we put some dummies in place like the fontloader @@ -1421,9 +1423,6 @@ local get_raw_info = function (metadata, basename) basename) fontname = "bad-fontname-" .. basename fullname = "bad-fullname-" .. basename - else - fontname = metadata.fontname - fullname = metadata.fullname end return { @@ -1541,7 +1540,7 @@ local organize_styledata = function (fontname, metadata, english_names, info) - local pfminfo = metadata.pfminfo + local pfminfo = metadata.pfminfo or { } local names = metadata.names return { -- cgit v1.2.3 From fba29372991e64a7ee36366988d2da59be03f728 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 5 Mar 2014 22:42:53 +0100 Subject: [doc] move tables to more abstract syntax --- doc/luaotfload-latex.tex | 24 ++++++++++++++++++++++++ doc/luaotfload-main.tex | 41 ++++++++++++++++++++++------------------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index e0afe45..80e741c 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -369,12 +369,36 @@ } +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% tables +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Our tables aren’t anything special so we stick with “tabular” on the +%% Latex end. +%% +%% This is going to be largely incompatible with Context since format +%% specifications work quite differently (even between different +%% Context table variants). + +\def \begintabulate [#1]#2\endtabulate{% + \begingroup + \let \beginrow = \relax %% -> \NC in Context + \let \newcell = & %% -> \NC + \let \endrow = \cr %% -> \NC \NR + \begin {tabular}{#1}% + #2 + \end {tabular} + \endgroup +} + +\let \endtabulate \relax + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% escaped characters %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \let \charpercent \textpercent \let \charbackslash \textbackslash +\let \chartilde \textasciitilde %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% main diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 002b1bb..32a97fe 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -719,13 +719,13 @@ Currently (2014) there are three of them: \altitem {tlig} Applies legacy \TEX ligatures: - \begin{tabular}{rlrl} - `` & \inlinecode {``} & '' & \inlinecode {''} \\ - ` & \inlinecode {`} & ' & \inlinecode {'} \\ - " & \inlinecode {"} & -- & \inlinecode {--} \\ - --- & \inlinecode {---} & !` & \inlinecode {!`} \\ - ?` & \inlinecode {?`} & & \\ - \end{tabular} + \begintabulate [rlrl] + \beginrow `` \newcell \inlinecode {``} \newcell '' \newcell \inlinecode {''} \endrow + \beginrow ` \newcell \inlinecode {`} \newcell ' \newcell \inlinecode {'} \endrow + \beginrow " \newcell \inlinecode {"} \newcell -- \newcell \inlinecode {--} \endrow + \beginrow --- \newcell \inlinecode {---} \newcell !` \newcell \inlinecode {!`} \endrow + \beginrow ?` \newcell \inlinecode {?`} \newcell \newcell \endrow + \endtabulate \footnote{% These contain the feature set \inlinecode {trep} of earlier @@ -853,18 +853,21 @@ directories. \begintablefloat {table-searchpaths} {List of paths searched for each supported operating system.} \begincentered - \begin{tabular}{lp{.5\textwidth}} - Windows & \inlinecode {\% WINDIR\%\\ Fonts} - \\ - Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break - \fileent{/etc/fonts/fonts.conf} - \\ - Mac & \fileent{\textasciitilde/Library/Fonts},\break - \fileent{/Library/Fonts},\break - \fileent{/System/Library/Fonts}, and\hfill\break - \fileent{/Network/Library/Fonts} - \\ - \end{tabular} + \begintabulate [lp{.5\textwidth}] + \beginrow + Windows \newcell \inlinecode {\% WINDIR\%\\ Fonts} + \endrow + \beginrow + Linux \newcell \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break + \fileent{/etc/fonts/fonts.conf} + \endrow + \beginrow + Mac \newcell \fileent{\textasciitilde/Library/Fonts},\break + \fileent{/Library/Fonts},\break + \fileent{/System/Library/Fonts}, and\hfill\break + \fileent{/Network/Library/Fonts} + \endrow + \endtabulate \endcentered \endtablefloat -- cgit v1.2.3 From 7ccc3a604d4856e00c2274512bbca85d015ace2f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 7 Mar 2014 06:54:25 +0100 Subject: [doc] erase gpl from pdf --- doc/luaotfload-latex.tex | 6 - doc/luaotfload-main.tex | 429 ----------------------------------------------- 2 files changed, 435 deletions(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index 80e741c..42799bf 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -257,9 +257,6 @@ \definelist [filelist]{\normalitem {\fileent {\first}}\space--\hskip 1em} \definelist [functionlist]{\normalitem {\luafunction {\first}}\hfill\break} -\def \beginenumeration {\begin {enumerate}} -\def \endenumeration {\end {enumerate}} - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% columns %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -267,9 +264,6 @@ \def \begindoublecolumns {\begin {multicols} {2}} \def \enddoublecolumns {\end {multicols}} -\def \begintriplecolumns {\begin {multicols} {3}} -\def \endtriplecolumns {\end {multicols}} - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% alignment %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 32a97fe..1613eaa 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -1445,434 +1445,5 @@ In this case, best define you own accessor: \endsubsection \endsection - -\clearpage -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\beginsection {The GNU GPL License v2} -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -The GPL requires the complete license text to be distributed along -with the code. I recommend the canonical source, instead: -\hyperlink {http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. -But if you insist on an included copy, here it is. -You might want to zoom in. - -\newsavebox{\gpl} -\begin{lrbox}{\gpl} -\begin{minipage}{3\textwidth} -\columnsep=3\columnsep -\begintriplecolumns -\begincentered - {\Largefont{GNU GENERAL PUBLIC LICENSE}\par} - \bigskip - {Version 2, June 1991} - - \begingroup - \parindent 0in - - Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc. - - \bigskip - - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - - \bigskip - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - \endgroup - - {\bf\large Preamble} -\endcentered - -The licenses for most software are designed to take away your freedom to -share and change it. By contrast, the GNU General Public License is -intended to guarantee your freedom to share and change free software---to -make sure the software is free for all its users. This General Public -License applies to most of the Free Software Foundation's software and to -any other program whose authors commit to using it. (Some other Free -Software Foundation software is covered by the GNU Library General Public -License instead.) You can apply it to your programs, too. - -When we speak of free software, we are referring to freedom, not price. -Our General Public Licenses are designed to make sure that you have the -freedom to distribute copies of free software (and charge for this service -if you wish), that you receive source code or can get it if you want it, -that you can change the software or use pieces of it in new free programs; -and that you know you can do these things. - -To protect your rights, we need to make restrictions that forbid anyone to -deny you these rights or to ask you to surrender the rights. These -restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - -For example, if you distribute copies of such a program, whether gratis or -for a fee, you must give the recipients all the rights that you have. You -must make sure that they, too, receive or can get the source code. And -you must show them these terms so they know their rights. - -We protect your rights with two steps: (1) copyright the software, and (2) -offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - -Also, for each author's protection and ours, we want to make certain that -everyone understands that there is no warranty for this free software. If -the software is modified by someone else and passed on, we want its -recipients to know that what they have is not the original, so that any -problems introduced by others will not reflect on the original authors' -reputations. - -Finally, any free program is threatened constantly by software patents. -We wish to avoid the danger that redistributors of a free program will -individually obtain patent licenses, in effect making the program -proprietary. To prevent this, we have made it clear that any patent must -be licensed for everyone's free use or not licensed at all. - -The precise terms and conditions for copying, distribution and -modification follow. - -\begincentered - %% so … these aren’t actual headings‽ - \Largefont{\smallcaps{% - Terms and Conditions For Copying, Distribution and Modification - }} -\endcentered - -\beginenumeration -\item This License applies to any program or other work which contains - a notice placed by the copyright holder saying it may be - distributed under the terms of this General Public License. The - ``Program'', below, refers to any such program or work, and a - ``work based on the Program'' means either the Program or any - derivative work under copyright law: that is to say, a work - containing the Program or a portion of it, either verbatim or - with modifications and/or translated into another language. - (Hereinafter, translation is included without limitation in the - term ``modification''.) Each licensee is addressed as ``you''. - - Activities other than copying, distribution and modification are - not covered by this License; they are outside its scope. The act - of running the Program is not restricted, and the output from the - Program is covered only if its contents constitute a work based - on the Program (independent of having been made by running the - Program). Whether that is true depends on what the Program does. - -\item You may copy and distribute verbatim copies of the Program's source - code as you receive it, in any medium, provided that you - conspicuously and appropriately publish on each copy an - appropriate copyright notice and disclaimer of warranty; keep - intact all the notices that refer to this License and to the - absence of any warranty; and give any other recipients of the - Program a copy of this License along with the Program. - - You may charge a fee for the physical act of transferring a copy, - and you may at your option offer warranty protection in exchange - for a fee. - -\item You may modify your copy or copies of the Program or any portion - of it, thus forming a work based on the Program, and copy and - distribute such modifications or work under the terms of Section - 1 above, provided that you also meet all of these conditions: - - \beginenumeration - - \item You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - \item You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - \item If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you - provide a warranty) and that users may redistribute the program - under these conditions, and telling the user how to view a copy - of this License. (Exception: if the Program itself is - interactive but does not normally print such an announcement, - your work based on the Program is not required to print an - announcement.) - - \endenumeration - - - These requirements apply to the modified work as a whole. If - identifiable sections of that work are not derived from the - Program, and can be reasonably considered independent and - separate works in themselves, then this License, and its terms, - do not apply to those sections when you distribute them as - separate works. But when you distribute the same sections as - part of a whole which is a work based on the Program, the - distribution of the whole must be on the terms of this License, - whose permissions for other licensees extend to the entire whole, - and thus to each and every part regardless of who wrote it. - - Thus, it is not the intent of this section to claim rights or - contest your rights to work written entirely by you; rather, the - intent is to exercise the right to control the distribution of - derivative or collective works based on the Program. - - In addition, mere aggregation of another work not based on the - Program with the Program (or with a work based on the Program) on - a volume of a storage or distribution medium does not bring the - other work under the scope of this License. - -\item You may copy and distribute the Program (or a work based on it, - under Section 2) in object code or executable form under the - terms of Sections 1 and 2 above provided that you also do one of - the following: - - \beginenumeration - - \item Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Sections 1 and 2 above on a medium customarily used for software - interchange; or, - - \item Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - \item Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - - \endenumeration - - - The source code for a work means the preferred form of the work - for making modifications to it. For an executable work, complete - source code means all the source code for all modules it - contains, plus any associated interface definition files, plus - the scripts used to control compilation and installation of the - executable. However, as a special exception, the source code - distributed need not include anything that is normally - distributed (in either source or binary form) with the major - components (compiler, kernel, and so on) of the operating system - on which the executable runs, unless that component itself - accompanies the executable. - - If distribution of executable or object code is made by offering - access to copy from a designated place, then offering equivalent - access to copy the source code from the same place counts as - distribution of the source code, even though third parties are - not compelled to copy the source along with the object code. - -\item You may not copy, modify, sublicense, or distribute the Program - except as expressly provided under this License. Any attempt - otherwise to copy, modify, sublicense or distribute the Program - is void, and will automatically terminate your rights under this - License. However, parties who have received copies, or rights, - from you under this License will not have their licenses - terminated so long as such parties remain in full compliance. - -\item You are not required to accept this License, since you have not - signed it. However, nothing else grants you permission to modify - or distribute the Program or its derivative works. These actions - are prohibited by law if you do not accept this License. - Therefore, by modifying or distributing the Program (or any work - based on the Program), you indicate your acceptance of this - License to do so, and all its terms and conditions for copying, - distributing or modifying the Program or works based on it. - -\item Each time you redistribute the Program (or any work based on the - Program), the recipient automatically receives a license from the - original licensor to copy, distribute or modify the Program - subject to these terms and conditions. You may not impose any - further restrictions on the recipients' exercise of the rights - granted herein. You are not responsible for enforcing compliance - by third parties to this License. - -\item If, as a consequence of a court judgment or allegation of patent - infringement or for any other reason (not limited to patent - issues), conditions are imposed on you (whether by court order, - agreement or otherwise) that contradict the conditions of this - License, they do not excuse you from the conditions of this - License. If you cannot distribute so as to satisfy - simultaneously your obligations under this License and any other - pertinent obligations, then as a consequence you may not - distribute the Program at all. For example, if a patent license - would not permit royalty-free redistribution of the Program by - all those who receive copies directly or indirectly through you, - then the only way you could satisfy both it and this License - would be to refrain entirely from distribution of the Program. - - If any portion of this section is held invalid or unenforceable - under any particular circumstance, the balance of the section is - intended to apply and the section as a whole is intended to apply - in other circumstances. - - It is not the purpose of this section to induce you to infringe - any patents or other property right claims or to contest validity - of any such claims; this section has the sole purpose of - protecting the integrity of the free software distribution - system, which is implemented by public license practices. Many - people have made generous contributions to the wide range of - software distributed through that system in reliance on - consistent application of that system; it is up to the - author/donor to decide if he or she is willing to distribute - software through any other system and a licensee cannot impose - that choice. - - This section is intended to make thoroughly clear what is - believed to be a consequence of the rest of this License. - -\item If the distribution and/or use of the Program is restricted in - certain countries either by patents or by copyrighted interfaces, - the original copyright holder who places the Program under this - License may add an explicit geographical distribution limitation - excluding those countries, so that distribution is permitted only - in or among countries not thus excluded. In such case, this - License incorporates the limitation as if written in the body of - this License. - -\item The Free Software Foundation may publish revised and/or new - versions of the General Public License from time to time. Such - new versions will be similar in spirit to the present version, - but may differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the - Program specifies a version number of this License which applies - to it and ``any later version'', you have the option of following - the terms and conditions either of that version or of any later - version published by the Free Software Foundation. If the - Program does not specify a version number of this License, you - may choose any version ever published by the Free Software - Foundation. - -\item If you wish to incorporate parts of the Program into other free - programs whose distribution conditions are different, write to - the author to ask for permission. For software which is - copyrighted by the Free Software Foundation, write to the Free - Software Foundation; we sometimes make exceptions for this. Our - decision will be guided by the two goals of preserving the free - status of all derivatives of our free software and of promoting - the sharing and reuse of software generally. - -\begincentered - \Largefont{\smallcaps{No Warranty}} -\endcentered - -\item \smallcaps{Because the program is licensed free of charge, there - is no warranty for the program, to the extent permitted by - applicable law. Except when otherwise stated in writing the - copyright holders and/or other parties provide the program ``as - is'' without warranty of any kind, either expressed or implied, - including, but not limited to, the implied warranties of - merchantability and fitness for a particular purpose. The entire - risk as to the quality and performance of the program is with - you. Should the program prove defective, you assume the cost of - all necessary servicing, repair or correction.} - -\item \smallcaps{In no event unless required by applicable law or - agreed to in writing will any copyright holder, or any other - party who may modify and/or redistribute the program as permitted - above, be liable to you for damages, including any general, - special, incidental or consequential damages arising out of the - use or inability to use the program (including but not limited to - loss of data or data being rendered inaccurate or losses - sustained by you or third parties or a failure of the program to - operate with any other programs), even if such holder or other - party has been advised of the possibility of such damages.} - -\endenumeration - - -\begincentered - \Largefont{\smallcaps{End of Terms and Conditions}} -\endcentered - - -\pagebreak[2] - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\fakesection {Appendix: How to Apply These Terms to Your New Programs} -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - -To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively convey -the exclusion of warranty; and each file should have at least the -``copyright'' line and a pointer to where the full notice is found. - -\beginnarrower - one line to give the program's name and a brief idea of what it does. \\ - Copyright (C) yyyy name of author \\ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -\endnarrower - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - -\beginnarrower - Gnomovision version 69, Copyright (C) yyyy name of author \\ - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. -\endnarrower - - -The hypothetical commands \proportional{show w} and \proportional{show -c} should show the appropriate parts of the General Public License. Of -course, the commands you use may be called something other than -\proportional{show w} and \proportional{show c}; they could even be -mouse-clicks or menu items---whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a ``copyright disclaimer'' for the program, if -necessary. Here is a sample; alter the names: - -\beginnarrower - Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\ - `Gnomovision' (which makes passes at compilers) written by James Hacker. \\ - - signature of Ty Coon, 1 April 1989 \\ - Ty Coon, President of Vice -\endnarrower - - -This General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications -with the library. If this is what you want to do, use the GNU Library -General Public License instead of this License. - -\endtriplecolumns -\end{minipage} -\end{lrbox} - -\begincentered - \scalebox{0.33}{\usebox{\gpl}} -\endcentered - -\endsection - \endinput -- cgit v1.2.3 From 857388de9eb867e23981eb6a6cf926b77834c6d5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 7 Mar 2014 07:23:32 +0100 Subject: [*] add license text as toplevel file COPYING --- COPYING | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d769b3f --- /dev/null +++ b/COPYING @@ -0,0 +1,350 @@ +Context, where the fontloader Luaotfload is built around originates, is +licensed under the GPL version 2.0 (exactly). As a derived work, anything +Luaotflaod adds to that is also subject to the same license at the same +version. The “any later version” clause as used by the FSF in the license text +*does not apply* to either Context or Luaotfload, despite being kept around in +the license text given below. + +------------------------------------------------------------------------------- + LICENSE TEXT BELOW +------------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. -- cgit v1.2.3 From 31652228ee93553b05bfbaa48b9bec6cf247a401 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 7 Mar 2014 07:55:42 +0100 Subject: [*] handle license file in makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8b6bff2..2dc68bd 100644 --- a/Makefile +++ b/Makefile @@ -41,12 +41,12 @@ GLYPHS = $(BUILDDIR)/$(NAME)-glyphlist.lua CHARS = $(BUILDDIR)/$(NAME)-characters.lua STATUS = $(BUILDDIR)/$(NAME)-status.lua RESOURCES = $(GLYPHS) $(CHARS) $(STATUS) -SOURCE = $(DOCSRC) $(MANSRC) $(SRC) README Makefile NEWS $(RESOURCESCRIPTS) +SOURCE = $(DOCSRC) $(MANSRC) $(SRC) README COPYING Makefile NEWS $(RESOURCESCRIPTS) # Files grouped by installation location SCRIPTSTATUS = $(TOOL) $(RESOURCESCRIPTS) RUNSTATUS = $(filter-out $(SCRIPTSTATUS),$(SRC)) -DOCSTATUS = $(DOCPDF) $(DOTPDF) README NEWS +DOCSTATUS = $(DOCPDF) $(DOTPDF) README NEWS COPYING MANSTATUS = $(MANPAGE) SRCSTATUS = $(DOCSRC) $(MANSRC) $(GRAPHSRC) Makefile -- cgit v1.2.3 From 3ac343c10c8a02f7758eb75034e513d5509b584c Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 7 Mar 2014 07:56:29 +0100 Subject: [doc] add license note to pdf manual --- doc/luaotfload-main.tex | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 1613eaa..0098405 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -1445,5 +1445,26 @@ In this case, best define you own accessor: \endsubsection \endsection + +\beginsection {License} + +\identifier {luaotfload} is licensed under the terms of the +\hyperlink [GNU General Public License version 2.0]% + {https://www.gnu.org/licenses/old-licenses/gpl-2.0.html}. +Following the underlying fontloader code \identifier {luaotfload} +recognizes only that exact version as its license. +The „any later version” clause of the original license text as +copyrighted by the \hyperlink [Free Software Foundation]{http://www.fsf.org/} +\emphasis {does not apply} to either \identifier {luaotfload} or the +code imported from \CONTEXT. + +The complete text of the license is given as a separate file \fileent +{COPYING} in the toplevel directory of the +\hyperlink [\fileent {Luaotfload} Git repository]{https://github.com/lualatex/luaotfload/blob/master/COPYING}. +Distributions probably package it as \fileent +{doc/luatex/luaotfload/COPYING} in the relevant \fileent {texmf} tree. + +\endsection + \endinput -- cgit v1.2.3 From b220b4598c6c09e3148fa42b987f9615b1e8f135 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 9 Mar 2014 14:17:30 +0100 Subject: [doc] introduce fake environment for items --- doc/luaotfload-latex.tex | 2 + doc/luaotfload-main.tex | 183 ++++++++++++++++++++++++++++++----------------- 2 files changed, 121 insertions(+), 64 deletions(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index 42799bf..3045c26 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -245,6 +245,8 @@ \def \first {####1}% #2 } + \let \beginaltitem \altitem + \let \endaltitem \relax } \expandafter \def \csname end#1\endcsname {% diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 0098405..0e74aa9 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -1,3 +1,16 @@ +%\beginsection {foo} + %bar baz +%\endsection + +%\begindescriptions + + %\beginaltitem {mode} foo + %%\identifier{luaotfload} has two \OpenType processing + %%\emphasis{modes}: +%\enddescriptions + +%\endinput + %% Copyright (C) 2009-2014 %% %% by Elie Roux @@ -514,7 +527,7 @@ obviously, \inlinecode{random}. \begindescriptions - \altitem {mode} + \beginaltitem {mode} \identifier{luaotfload} has two \OpenType processing \emphasis{modes}: \identifier{base} and \identifier{node}. @@ -536,8 +549,9 @@ obviously, \inlinecode{random}. By default \identifier{luaotfload} is in \identifier{node} mode, and \identifier{base} mode has to be requested where needed, e.~g. for math fonts. + \endaltitem - \altitem {script} \label{script-tag} + \beginaltitem {script} \label{script-tag} An \OpenType script tag;\footnote{% See \hyperlink {http://www.microsoft.com/typography/otspec/scripttags.htm} for a list of valid values. @@ -550,14 +564,16 @@ obviously, \inlinecode{random}. Some fonts, including very popular ones by foundries like Adobe, do not assign features to the \inlinecode{dflt} script, in which case the script needs to be set explicitly. + \endaltitem - \altitem {language} + \beginaltitem {language} An \OpenType language system identifier,\footnote{% Cf. \hyperlink {http://www.microsoft.com/typography/otspec/languagetags.htm}. } defaulting to \inlinecode{dflt}. + \endaltitem - \altitem {featurefile} + \beginaltitem {featurefile} A comma-separated list of feature files to be applied to the font. % @@ -585,8 +601,9 @@ obviously, \inlinecode{random}. It can be read and applied as follows: \inlinecode{\\font \\test = Latin Modern Roman:featurefile=tkrn.fea;+tkrn} + \endaltitem - \altitem {color} + \beginaltitem {color} A font color, defined as a triplet of two-digit hexadecimal \abbrev{rgb} values, with an optional fourth value for transparency @@ -598,8 +615,9 @@ obviously, \inlinecode{random}. \beginlisting \font \test = "Latin Modern Roman:color=FF0000BB" \endlisting + \endaltitem - \altitem {kernfactor \& letterspace} + \beginaltitem {kernfactor \& letterspace} Define a font with letterspacing (tracking) enabled. % In \identifier{luaotfload}, letterspacing is implemented by @@ -660,9 +678,10 @@ obviously, \inlinecode{random}. % The implementation of both functions is left entirely to the user. + \endaltitem - \altitem {protrusion \& expansion} + \beginaltitem {protrusion \& expansion} These keys control microtypographic features of the font, namely \emphasis{character protrusion} and \emphasis{font expansion}. @@ -698,6 +717,7 @@ obviously, \inlinecode{random}. \beginlisting \font \test = LatinModernRoman:protrusion=default \endlisting + \endaltitem \enddescriptions \endsubsection @@ -711,12 +731,13 @@ Currently (2014) there are three of them: \begindescriptions - \altitem {anum} + \beginaltitem {anum} Substitutes the glyphs in the \abbrev{ascii} number range with their counterparts from eastern Arabic or Persian, depending on the value of \identifier{language}. + \endaltitem - \altitem {tlig} + \beginaltitem {tlig} Applies legacy \TEX ligatures: \begintabulate [rlrl] @@ -735,9 +756,11 @@ Currently (2014) there are three of them: assignment \inlinecode {mapping=text-tex} using \XETEX's input remapping feature. } + \endaltitem - \altitem {itlc} + \beginaltitem {itlc} Computes italic correction values (active by default). + \endaltitem \enddescriptions @@ -1050,11 +1073,16 @@ categories. \begindoublecolumns \begindefinitions - \altitem{l-lua.lua} \altitem{l-lpeg.lua} - \altitem{l-function.lua} \altitem{l-string.lua} - \altitem{l-table.lua} \altitem{l-io.lua} - \altitem{l-file.lua} \altitem{l-boolean.lua} - \altitem{l-math.lua} \altitem{util-str.lua} + \beginaltitem {l-lua.lua} \endaltitem + \beginaltitem {l-lpeg.lua} \endaltitem + \beginaltitem {l-function.lua} \endaltitem + \beginaltitem {l-string.lua} \endaltitem + \beginaltitem {l-table.lua} \endaltitem + \beginaltitem {l-io.lua} \endaltitem + \beginaltitem {l-file.lua} \endaltitem + \beginaltitem {l-boolean.lua} \endaltitem + \beginaltitem {l-math.lua} \endaltitem + \beginaltitem {util-str.lua} \endaltitem \enddefinitions \enddoublecolumns @@ -1064,18 +1092,18 @@ categories. with \identifier{luaotfload}. \begindoublecolumns \begindefinitions - \altitem{luatex-basics-gen.lua} - \altitem{luatex-basics-nod.lua} - \altitem{luatex-fonts-enc.lua} - \altitem{luatex-fonts-syn.lua} - \altitem{luatex-fonts-tfm.lua} - \altitem{luatex-fonts-chr.lua} - \altitem{luatex-fonts-lua.lua} - \altitem{luatex-fonts-inj.lua} - \altitem{luatex-fonts-otn.lua} - \altitem{luatex-fonts-def.lua} - \altitem{luatex-fonts-ext.lua} - \altitem{luatex-fonts-cbk.lua} + \beginaltitem{luatex-basics-gen.lua} \endaltitem + \beginaltitem{luatex-basics-nod.lua} \endaltitem + \beginaltitem{luatex-fonts-enc.lua} \endaltitem + \beginaltitem{luatex-fonts-syn.lua} \endaltitem + \beginaltitem{luatex-fonts-tfm.lua} \endaltitem + \beginaltitem{luatex-fonts-chr.lua} \endaltitem + \beginaltitem{luatex-fonts-lua.lua} \endaltitem + \beginaltitem{luatex-fonts-inj.lua} \endaltitem + \beginaltitem{luatex-fonts-otn.lua} \endaltitem + \beginaltitem{luatex-fonts-def.lua} \endaltitem + \beginaltitem{luatex-fonts-ext.lua} \endaltitem + \beginaltitem{luatex-fonts-cbk.lua} \endaltitem \enddefinitions \enddoublecolumns @@ -1084,12 +1112,17 @@ categories. \CONTEXT. \begindoublecolumns \begindefinitions - \altitem{data-con.lua} \altitem{font-ini.lua} - \altitem{font-con.lua} \altitem{font-cid.lua} - \altitem{font-map.lua} \altitem{font-oti.lua} - \altitem{font-otf.lua} \altitem{font-otb.lua} - \altitem{font-ota.lua} \altitem{font-def.lua} - \altitem{font-otp.lua} + \beginaltitem{data-con.lua} \endaltitem + \beginaltitem{font-ini.lua} \endaltitem + \beginaltitem{font-con.lua} \endaltitem + \beginaltitem{font-cid.lua} \endaltitem + \beginaltitem{font-map.lua} \endaltitem + \beginaltitem{font-oti.lua} \endaltitem + \beginaltitem{font-otf.lua} \endaltitem + \beginaltitem{font-otb.lua} \endaltitem + \beginaltitem{font-ota.lua} \endaltitem + \beginaltitem{font-def.lua} \endaltitem + \beginaltitem{font-otp.lua} \endaltitem \enddefinitions \enddoublecolumns \enddefinitions @@ -1122,22 +1155,33 @@ files not contained in the merge. Some of these have no equivalent in \beginfilelist - \altitem {luaotfload-features.lua} font feature handling; - incorporates some of the code from - \fileent{font-otc} from \CONTEXT; - \altitem {luaotfload-override.lua} overrides the \CONTEXT logging - functionality. - \altitem {luaotfload-loaders.lua} registers the \OpenType - font reader as handler for - Postscript fonts - (\abbrev{pfa}, \abbrev{pfb}). - \altitem {luaotfload-parsers.lua} various \abbrev{lpeg}-based parsers. - \altitem {luaotfload-database.lua} font names database. - \altitem {luaotfload-colors.lua} color handling. - \altitem {luaotfload-auxiliary.lua} access to internal functionality - for package authors - (proposals for additions welcome). - \altitem {luaotfload-letterspace.lua} font-based letterspacing. + \beginaltitem {luaotfload-features.lua} + font feature handling; incorporates some of the code from + \fileent{font-otc} from \CONTEXT; + \endaltitem + \beginaltitem {luaotfload-override.lua} + overrides the \CONTEXT logging functionality. + \endaltitem + \beginaltitem {luaotfload-loaders.lua} + registers the \OpenType font reader as handler for Postscript + fonts (\abbrev{pfa}, \abbrev{pfb}). + \endaltitem + \beginaltitem {luaotfload-parsers.lua} + various \abbrev{lpeg}-based parsers. + \endaltitem + \beginaltitem {luaotfload-database.lua} + font names database. + \endaltitem + \beginaltitem {luaotfload-colors.lua} + color handling. + \endaltitem + \beginaltitem {luaotfload-auxiliary.lua} + access to internal functionality for package authors (proposals + for additions welcome). + \endaltitem + \beginaltitem {luaotfload-letterspace.lua} + font-based letterspacing. + \endaltitem \endfilelist \beginfigurefloat @@ -1243,14 +1287,17 @@ These are mostly concerned with establishing compatibility with \XETEX. \beginfunctionlist - \altitem {set_sscale_dimens} - Calculate \texmacro{fontdimen}s 10 and 11 to emulate \XETEX. + \beginaltitem {set_sscale_dimens} + Calculate \texmacro{fontdimen}s 10 and 11 to emulate \XETEX. + \endaltitem - \altitem {set_capheight} - Calculates \texmacro{fontdimen} 8 like \XETEX. + \beginaltitem {set_capheight} + Calculates \texmacro{fontdimen} 8 like \XETEX. + \endaltitem - \altitem {patch_cambria_domh} - Correct some values of the font \emphasis{Cambria Math}. + \beginaltitem {patch_cambria_domh} + Correct some values of the font \emphasis{Cambria Math}. + \endaltitem \endfunctionlist @@ -1271,38 +1318,46 @@ are defined for which scripts. \beginfunctionlist - \altitem {aux.font_has_glyph (id : int, index : int)} + \beginaltitem {aux.font_has_glyph (id : int, index : int)} Predicate that returns true if the font \luafunction{id} has glyph \luafunction{index}. + \endaltitem - \altitem {aux.slot_of_name(name : string)} + \beginaltitem {aux.slot_of_name(name : string)} Translates an Adobe Glyph name to the corresponding glyph slot. + \endaltitem - \altitem {aux.name_of_slot(slot : int)} + \beginaltitem {aux.name_of_slot(slot : int)} The inverse of \luafunction{slot_of_name}; note that this might be incomplete as multiple glyph names may map to the same codepoint, only one of which is returned by \luafunction{name_of_slot}. + \endaltitem - \altitem {aux.provides_script(id : int, script : string)} + \beginaltitem {aux.provides_script(id : int, script : string)} Test if a font supports \luafunction{script}. + \endaltitem - \altitem {aux.provides_language(id : int, script : string, language : string)} + \beginaltitem {aux.provides_language(id : int, script : string, language : string)} Test if a font defines \luafunction{language} for a given \luafunction{script}. + \endaltitem - \altitem {aux.provides_feature(id : int, script : string, + \beginaltitem {aux.provides_feature(id : int, script : string, language : string, feature : string)} Test if a font defines \luafunction{feature} for \luafunction{language} for a given \luafunction{script}. + \endaltitem - \altitem {aux.get_math_dimension(id : int, dimension : string)} + \beginaltitem {aux.get_math_dimension(id : int, dimension : string)} Get the dimension \luafunction{dimension} of font \luafunction{id}. + \endaltitem - \altitem {aux.sprint_math_dimension(id : int, dimension : string)} + \beginaltitem {aux.sprint_math_dimension(id : int, dimension : string)} Same as \luafunction{get_math_dimension()}, but output the value in scaled points at the \TEX end. + \endaltitem \endfunctionlist @@ -1312,7 +1367,7 @@ are defined for which scripts. % \beginsubsubsection{Database} % % \beginfunctionlist -% \altitem {aux.scan_external_dir(dir : string)} +% \beginaltitem {aux.scan_external_dir(dir : string)} % Include fonts in directory \luafunction{dir} in font lookups without % adding them to the database. % -- cgit v1.2.3 From 0cc9acaf9a99206c53eb046517953a9ca1d9db51 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 13 Mar 2014 22:28:40 +0100 Subject: [doc] add first draft Context layer --- doc/luaotfload-context.tex | 457 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 457 insertions(+) create mode 100644 doc/luaotfload-context.tex diff --git a/doc/luaotfload-context.tex b/doc/luaotfload-context.tex new file mode 100644 index 0000000..aeca7cb --- /dev/null +++ b/doc/luaotfload-context.tex @@ -0,0 +1,457 @@ +% macros=mkvi +%% Copyright (C) 2009-2014 +%% +%% by Elie Roux +%% and Khaled Hosny +%% and Philipp Gesang +%% +%% This file is part of Luaotfload. +%% +%% Home: https://github.com/lualatex/luaotfload +%% Support: . +%% +%% Luaotfload is under the GPL v2.0 (exactly) license. +%% +%% ---------------------------------------------------------------------------- +%% +%% Luaotfload is free software; you can redistribute it and/or +%% modify it under the terms of the GNU General Public License +%% as published by the Free Software Foundation; version 2 +%% of the License. +%% +%% Luaotfload is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU General Public License +%% along with Luaotfload; if not, see . +%% +%% ---------------------------------------------------------------------------- +%% + +\unprotect + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% layout and paper +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\setuppapersize [A5] [A5] %% 148×210 + +\definelayout [mainlayout] [ + backspace=15mm, %% 133 + textwidth=103mm, + topspace=15mm, +] + +\setuplayout [mainlayout] + +\setuppagenumbering [location=,alternative=doublesided] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% colors +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\usecolors [x11] +\definecolor [primarycolor] [dodgerblue4] +\definecolor [secondarycolor] [goldenrod4] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% interaction +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\setupinteraction [ + state=start, + page=no, + click=yes, + style=italic, + color=primarycolor, + contrastcolor=secondarycolor, + title={The Luaotfload package}, + subtitle={OpenType layout system for Plain TeX and LaTeX}, + author={Elie Roux & Khaled Hosny & Philipp Gesang}, + keywords={luatex, lualatex, unicode, opentype}, +] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% fonts +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\usemodule [simplefonts] + +\definefontfeature [default] [default] [mode=base,liga=yes,dlig=yes,tlig=yes,onum=yes] +\definefontfeature [monospace] [liga=no,tlig=no,onum=no] + +\definefontfamily [mainface] [serif] [Linux Libertine O] [features=default] +%\definefontfamily [mainface] [serif] [Liberation Serif] [feature=default] +%\definefontfamily [mainface] [sans] [Iwona] [feature=default] +\definefontfamily [mainface] [sans] [Iwona Medium] [ + feature=default, + it=file:IwonaMedium-Italic.otf, + tf=file:IwonaMedium-Regular.otf, + bf=file:Iwona-Bold.otf, + bi=file:Iwona-BoldItalic.otf, +] +%definefontfamily [mainface] [sans] [DejaVu Sans] [feature=default] +\definefontfamily [mainface] [mono] [Liberation Mono] [scale=0.85,features=monospace] + +\setupbodyfont [mainface,10pt] + +\def \LUA {Lua} +\def \LUALATEX {Lua\LATEX} +\def \OpenType {\identifier{Open\kern-.25ex Type}} + +\definealternativestyle [emphasis:texmacro] [\ss \it \letterbackslash] [\ss \it \letterbackslash] +\definealternativestyle [emphasis:identifier] [\ss] [\ss] +\definealternativestyle [emphasis:normal] [\sl] [\sl] +\definealternativestyle [emphasis:abbrev] [{\feature [+][smallcaps]}] [{\feature [+][smallcaps]}] +\definealternativestyle [emphasis:Largefont] [{\switchtobodyfont[14pt]}] [{\switchtobodyfont[14pt]}] +\definealternativestyle [emphasis:smallcaps] [{\feature [+][smallcaps]}] [{\feature [+][smallcaps]}] +%definealternativestyle [emphasis:nonproportional] [\mono] [\mono] +\definealternativestyle [emphasis:nonproportional] [\tt] [\tt] +\definealternativestyle [head:section] [{\roman\feature[+][smallcaps]}] [{\roman\feature[+][smallcaps]}] +\definealternativestyle [head:subsection] [{\roman\feature[+][smallcaps]}] [{\roman\feature[+][smallcaps]}] +\definealternativestyle [head:subsubsection] [{\roman\feature[+][smallcaps]}] [{\roman\feature[+][smallcaps]}] +\definealternativestyle [typing:luafunction] [\italic] [\italic] +\definealternativestyle [typing:fileent] [\tt] [\tt] + +\definehighlight [texmacro] [style=emphasis:texmacro] %% cs +\definehighlight [identifier] [style=emphasis:identifier] %% names +\definehighlight [abbrev] [style=emphasis:abbrev] %% acronyms +\definehighlight [emphasis] [style=emphasis:normal] %% level 1 emph + +\definehighlight [Largefont] [style=emphasis:Largefont] %% font size +\definehighlight [smallcaps] [style=emphasis:smallcaps] %% font feature +\definehighlight [nonproportional] [style=emphasis:nonproportional] %% font switch + +\definetype [fileent] [style=typing:fileent] +\definetype [luafunction] [style=typing:luafunction] +\setuptyping [style=ttx] + +\definebodyfontenvironment [8pt] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% headings +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\setuphead [section] [style=head:section, alternative=inmargin] +\setuphead [subsection] [style=head:subsection, alternative=inmargin] +\setuphead [subsubsection] [style=head:subsubsection,alternative=inmargin] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% running headers +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\setupheadertexts + [{\tfx \getmarking[section]}] [pagenumber] + [pagenumber] [{\tfx \fileent{Luaotfload} Manual}] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% structurals +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% section +\def \beginsection {\dosingleempty \section_begin_indeed} + +\def \section_begin_indeed [#ref]#title{% + \iffirstargument + \startsection [reference=#ref,title=#title]% + \else + \startsection [title=#title]% + \fi +} + +\let \endsection \stopsection + +%% subsection +\def \beginsubsection {\dosingleempty \section_begin_indeed} + +\def \subsection_begin_indeed [#ref]#title{% + \iffirstargument + \startsubsection [reference=#ref,title=#title]% + \else + \startsubsection [title=#title]% + \fi +} + +\let \endsubsection \stopsection + +%% subsubsection +\def \beginsubsubsection {\dosingleempty \section_begin_indeed} + +\def \subsubsection_begin_indeed [#ref]#title{% + \iffirstargument + \startsubsubsection [reference=#ref,title=#title]% + \else + \startsubsubsection [title=#title]% + \fi +} + +\let \endsubsubsection \stopsection + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% inline verbatim +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Context offers both \type{…} and \type<<…>>, but not an unbalanced +%% one that we could map directly onto Latex’s \verb|…|. + +\definetype [inlinecode] [style=emphasis:nonproportional] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% codelistings +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Now *that’s* what I call easy. + +\unexpanded \def \beginlisting {% + \grabbufferdatadirect{listing}{beginlisting}{endlisting}% +} + +\unexpanded \def \endlisting {\typebuffer [listing]} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% enumerations and lists +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\definedescription [descriptionitem] [ + align=right, + alternative=hanging, + width=2em, +] + +\def \begindescriptions {% + \begingroup + \def \beginnormalitem ##1\endnormalitem{% + \startitem##1\stopitem + } + \let \endnormalitem \relax + \let \beginaltitem \startdescriptionitem + \let \endaltitem \stopdescriptionitem +} + +\let \enddescriptions \endgroup + + +\definedescription [definitionitem] [ + align=right, + alternative=hanging, +] + +\def \begindefinitions {% + \begingroup + \def \beginnormalitem ##1\endnormalitem{% + \startitem##1\stopitem + } + \let \endnormalitem \relax + \let \beginaltitem \startdefinitionitem + \let \endaltitem \stopdefinitionitem +} + +\let \enddefinitions \endgroup + + +\definedescription [filelistitem] [ + align=normal, + alternative=hanging, + headstyle=typing:fileent, + width=4cm, +] + +\def \beginfilelist {% + \begingroup + \def \beginnormalitem ##1\endnormalitem{% + \startitem##1\stopitem + } + \let \endnormalitem \relax + \let \beginaltitem \startfilelistitem + \let \endaltitem \stopfilelistitem +} + +\let \endfilelist \endgroup + +\definedescription [functionlistitem] [ + align=normal, + alternative=hanging, + headstyle=typing:luafunction, + width=4cm, +] + +\def \beginfunctionlist {% + \begingroup + \def \beginnormalitem ##1\endnormalitem{% + \startitem##1\stopitem + } + \let \endnormalitem \relax + \let \beginaltitem \startfunctionlistitem + \let \endaltitem \stopfunctionlistitem +} + +\let \endfunctionlist \endgroup + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% columns +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def \begindoublecolumns {\startcolumns [2]} +\let \enddoublecolumns \stopcolumns + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% alignment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\setupnarrower [before={\blank[line]},after={\blank[line]}] +\let \beginnarrower \startnarrower +\let \endnarrower \stopnarrower + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% special elements +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def \meta #1{<{\italic #1}>} + +\def \beginabstractcontent {% + \grabbufferdatadirect{abstractcontent}{beginabstractcontent}{endabstractcontent}% +} + +\let \endabstractcontent \relax + +\def \setdocumenttitle #1{\setvalue {document_title}{#1}} +\def \setdocumentdate #1{\setvalue {document_date}{#1}} +\def \setdocumentauthor #1{\setvalue {document_author}{#1}} + +\let \typesetdocumenttitle \relax +\let \beginfrontmatter \relax + +\def \endfrontmatter { + \startstandardmakeup + \vfill + \strut \hfill + \startframed [frame=off,align=middle,width=.5\textwidth] + \Largefont{\getvalue {document_title}} + \stopframed + \hfill \strut \par + + \blank [2*big] + + \strut \hfill + \startframed [frame=off,align=middle,width=.65\textwidth] + \setuplocalinterlinespace [18pt] + \getvalue {document_author} + \stopframed + \hfill \strut \par + + \vfill + \strut \hfill \getvalue {document_date} \hfill \strut + \blank [2*big] + + \strut \hfill + \startframed [width=.7\textwidth,align=normal,style=tfx,frame=off]% + \getbuffer [abstractcontent] + \stopframed + \hfill \strut + \stopstandardmakeup +} + +\let \typesetcontent \completecontent + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% floats +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% XXX we can improve on this part later + +\usemodule [vim] +\definevimtyping [bnf] [syntax=bnf] +\definefloat [syntax] [figure] + +\def \beginsyntaxfloat #reference#caption{% + \begingroup + \edef \currentreference {#reference}% + \edef \currentcaption {#caption}% + \grabbufferdatadirect{rawsyntaxdata}{beginsyntaxfloat}{endsyntaxfloat}% +} + +\def \endsyntaxfloat {% + \savebuffer [rawsyntaxdata] [rawsyntaxdata] + \startplacesyntax [ + reference=\currentreference, + title={\currentcaption}, + ] + %% there’s no \typebnfbuffer in t-vim :( + \typebnffile {\jobname-rawsyntaxdata.tmp} + \stopplacesyntax + \endgroup% +} + +\def \figurefloat #reference#caption#file{% + \startplacefigure [ + reference=#reference, + title={#caption}, + ] + \externalfigure [#file] [width=\textwidth] + \stopplacefigure +} + + +\def \tablefloat #reference#caption#content{% + \startplacetable [ + reference=#reference, + title={#caption}, + ] + #content + \stopplacetable +} + + +%% tables + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% tables +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\setupxtable [frame=off,option=stretch,textwidth=\dimexpr(\textwidth/2)] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% hyperlinks and references +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\unexpanded \def \hyperlink{% + \dosingleempty \hyperlink_indeed% +} + +\def \hyperlink_indeed [#text]#url{% + \iffirstargument + \useURL [temporary_url] [#url] [] [#text]% + \else + \useURL [temporary_url] [#url]% + \fi% + \from [temporary_url]% +} + + +\def \email #1{\goto{#1}[url(mailto:#1)]} + +\def \label #tag{\reference [#tag]\empty} +\def \pageref #tag{\at{page}{#tag}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% escaped characters +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\let \charpercent \letterpercent +\let \charbackslash \letterbackslash +\let \chartilde \lettertilde + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% main +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\protect + +\newif \ifcontextmkiv \contextmkivtrue + +\starttext + \input luaotfload-main.tex +\stoptext + -- cgit v1.2.3 From 2f69d30cbc1b21c6d7171bf644ac406c64f58295 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Mar 2014 07:40:22 +0100 Subject: [fontloader] sync with Context as of 2014-03-14 --- src/luaotfload-fontloader.lua | 200 +++++++++++++++++++++++++++++++----------- 1 file changed, 147 insertions(+), 53 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 3f408b9..90d46df 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 02/14/14 17:07:59 +-- merge date : 03/07/14 11:42:21 do -- begin closure to overcome local limits and interference @@ -901,6 +901,36 @@ local function sortedkeys(tab) return {} end end +local function sortedhashonly(tab) + if tab then + local srt,s={},0 + for key,_ in next,tab do + if type(key)=="string" then + s=s+1 + srt[s]=key + end + end + sort(srt) + return srt + else + return {} + end +end +local function sortedindexonly(tab) + if tab then + local srt,s={},0 + for key,_ in next,tab do + if type(key)=="number" then + s=s+1 + srt[s]=key + end + end + sort(srt) + return srt + else + return {} + end +end local function sortedhashkeys(tab,cmp) if tab then local srt,s={},0 @@ -926,6 +956,8 @@ function table.allkeys(t) return sortedkeys(keys) end table.sortedkeys=sortedkeys +table.sortedhashonly=sortedhashonly +table.sortedindexonly=sortedindexonly table.sortedhashkeys=sortedhashkeys local function nothing() end local function sortedhash(t,cmp) @@ -1723,7 +1755,7 @@ local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format local concat=table.concat local floor=math.floor local type=type -if string.find(os.getenv("PATH"),";") then +if string.find(os.getenv("PATH"),";",1,true) then io.fileseparator,io.pathseparator="\\",";" else io.fileseparator,io.pathseparator="/",":" @@ -2607,11 +2639,39 @@ local pattern=Carg(1)/function(t) function strings.tabtospace(str,tab) return lpegmatch(pattern,str,1,tab or 7) end -function strings.striplong(str) - str=gsub(str,"^%s*","") - str=gsub(str,"[\n\r]+ *","\n") - return str +local newline=patterns.newline +local endofstring=patterns.endofstring +local whitespace=patterns.whitespace +local spacer=patterns.spacer +local space=spacer^0 +local nospace=space/"" +local endofline=nospace*newline +local stripend=(whitespace^1*endofstring)/"" +local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace) +local stripempty=endofline^1/"" +local normalempty=endofline^1 +local singleempty=endofline*(endofline^0/"") +local doubleempty=endofline*endofline^-1*(endofline^0/"") +local stripstart=stripempty +local p_retain_normal=Cs ((normalline+normalempty )^0 ) +local p_retain_collapse=Cs ((normalline+doubleempty )^0 ) +local p_retain_noempty=Cs ((normalline+singleempty )^0 ) +local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 ) +local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 ) +local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 ) +local striplinepatterns={ + ["prune"]=p_prune_normal, + ["prune and collapse"]=p_prune_collapse, + ["prune and no empty"]=p_prune_noempty, + ["retain"]=p_retain_normal, + ["retain and collapse"]=p_retain_collapse, + ["retain and no empty"]=p_retain_noempty, +} +strings.striplinepatterns=striplinepatterns +function strings.striplines(str,how) + return str and lpegmatch(how and striplinepatterns[how] or p_prune_collapse,str) or str end +strings.striplong=strings.striplines function strings.nice(str) str=gsub(str,"[:%-+_]+"," ") return str @@ -6504,7 +6564,7 @@ local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf otf.glists={ "gsub","gpos" } -otf.version=2.751 +otf.version=2.754 otf.cache=containers.define("fonts","otf",otf.version,true) local fontdata=fonts.hashes.identifiers local chardata=characters and characters.data @@ -7047,15 +7107,22 @@ actions["prepare glyphs"]=function(data,filename,raw) local glyph=cidglyphs[index] if glyph then local unicode=glyph.unicode +if unicode>=0x00E000 and unicode<=0x00F8FF then + unicode=-1 +elseif unicode>=0x0F0000 and unicode<=0x0FFFFD then + unicode=-1 +elseif unicode>=0x100000 and unicode<=0x10FFFD then + unicode=-1 +end local name=glyph.name or cidnames[index] - if not unicode or unicode==-1 or unicode>=criterium then + if not unicode or unicode==-1 then unicode=cidunicodes[index] end if unicode and descriptions[unicode] then report_otf("preventing glyph %a at index %H to overload unicode %U",name or "noname",index,unicode) unicode=-1 end - if not unicode or unicode==-1 or unicode>=criterium then + if not unicode or unicode==-1 then if not name then name=format("u%06X",private) end @@ -7101,7 +7168,7 @@ actions["prepare glyphs"]=function(data,filename,raw) if glyph then local unicode=glyph.unicode local name=glyph.name - if not unicode or unicode==-1 or unicode>=criterium then + if not unicode or unicode==-1 then unicode=private unicodes[name]=private if trace_private then @@ -7156,47 +7223,43 @@ actions["check encoding"]=function(data,filename,raw) local unicodetoindex=mapdata and mapdata.map or {} local indextounicode=mapdata and mapdata.backmap or {} local encname=lower(data.enc_name or mapdata.enc_name or "") - local criterium=0xFFFF + local criterium=0xFFFF + local privateoffset=constructors.privateoffset if find(encname,"unicode") then if trace_loading then report_otf("checking embedded unicode map %a",encname) end - local hash={} - for index,unicode in next,indices do - hash[index]=descriptions[unicode] - end - local reported={} - for unicode,index in next,unicodetoindex do - if not descriptions[unicode] then - local d=hash[index] + local reported={} + for maybeunicode,index in next,unicodetoindex do + if descriptions[maybeunicode] then + else + local unicode=indices[index] + if not unicode then + elseif maybeunicode==unicode then + elseif unicode>privateoffset then + else + local d=descriptions[unicode] if d then - if d.unicode~=unicode then - local c=d.copies - if c then - c[unicode]=true - else - d.copies={ [unicode]=true } - end + local c=d.copies + if c then + c[maybeunicode]=true + else + d.copies={ [maybeunicode]=true } end - elseif not reported[i] then + elseif index and not reported[index] then report_otf("missing index %i",index) - reported[i]=true + reported[index]=true end end end - for index,data in next,hash do - data.copies=sortedkeys(data.copies) - end - for index,unicode in next,indices do - local description=hash[index] - local copies=description.copies - if copies then - duplicates[unicode]=copies - description.copies=nil - else - report_otf("copies but no unicode parent %U",unicode) - end + end + for unicode,data in next,descriptions do + local d=data.copies + if d then + duplicates[unicode]=sortedkeys(d) + data.copies=nil end + end elseif properties.cidinfo then report_otf("warning: no unicode map, used cidmap %a",properties.cidinfo.usedname) else @@ -7238,7 +7301,7 @@ actions["add duplicates"]=function(data,filename,raw) end end end - if u>0 then + if u>0 then local duplicate=table.copy(description) duplicate.comment=format("copy of U+%05X",unicode) descriptions[u]=duplicate @@ -7440,10 +7503,16 @@ actions["reorganize subtables"]=function(data,filename,raw) report_otf("skipping weird lookup number %s",k) elseif features then local f={} + local o={} for i=1,#features do local df=features[i] local tag=strip(lower(df.tag)) - local ft=f[tag] if not ft then ft={} f[tag]=ft end + local ft=f[tag] + if not ft then + ft={} + f[tag]=ft + o[#o+1]=tag + end local dscripts=df.scripts for i=1,#dscripts do local d=dscripts[i] @@ -7463,6 +7532,7 @@ actions["reorganize subtables"]=function(data,filename,raw) subtables=subtables, markclass=markclass, features=f, + order=o, } else lookups[name]={ @@ -9042,9 +9112,9 @@ function injections.setkern(current,factor,rlmode,x,tfmchr) return 0,0 end end -function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) - local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) - local bound=base[a_markbase] +function injections.setmark(start,base,factor,rlmode,ba,ma) + local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) + local bound=base[a_markbase] local index=1 if bound then local mb=marks[bound] @@ -9063,7 +9133,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) base[a_markbase]=bound start[a_markmark]=bound start[a_markdone]=index - marks[bound]={ [index]={ dx,dy,rlmode,baseismark } } + marks[bound]={ [index]={ dx,dy,rlmode } } return dx,dy,bound end local function dir(n) @@ -11413,9 +11483,11 @@ local autofeatures=fonts.analyzers.features local function initialize(sequence,script,language,enabled) local features=sequence.features if features then - for kind,scripts in next,features do + for i=1,#order do + local kind=order[i] local valid=enabled[kind] if valid then + local scripts=features[kind] local languages=scripts[script] or scripts[wildcard] if languages and (languages[language] or languages[wildcard]) then return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence } @@ -11447,12 +11519,12 @@ function otf.dataset(tfmdata,font) } rs[language]=rl local sequences=tfmdata.resources.sequences -for s=1,#sequences do - local v=enabled and initialize(sequences[s],script,language,enabled) - if v then - rl[#rl+1]=v - end -end + for s=1,#sequences do + local v=enabled and initialize(sequences[s],script,language,enabled) + if v then + rl[#rl+1]=v + end + end end return rl end @@ -12479,6 +12551,14 @@ local function packdata(data) features[script]=pack_normal(feature) end end + local order=sequence.order + if order then + sequence.order=pack_indexed(order) + end + local markclass=sequence.markclass + if markclass then + sequence.markclass=pack_boolean(markclass) + end end end local lookups=resources.lookups @@ -12891,6 +12971,20 @@ local function unpackdata(data) end end end + local order=feature.order + if order then + local tv=tables[order] + if tv then + feature.order=tv + end + end + local markclass=feature.markclass + if markclass then + local tv=tables[markclass] + if tv then + feature.markclass=tv + end + end end end local lookups=resources.lookups -- cgit v1.2.3 From a757b962eed6042eb9dc867e141c0ad704e406b2 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Mar 2014 08:26:48 +0100 Subject: [fontloader] fix node mode See http://www.ntg.nl/pipermail/ntg-context/2014/077372.html --- src/luaotfload-fontloader.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 90d46df..2b87b3f 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -11483,8 +11483,8 @@ local autofeatures=fonts.analyzers.features local function initialize(sequence,script,language,enabled) local features=sequence.features if features then - for i=1,#order do - local kind=order[i] + for i=1,#features do + local kind=features[i] local valid=enabled[kind] if valid then local scripts=features[kind] -- cgit v1.2.3 From 478c9cd68681cd182b1278f784c6768d48d207b4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 14 Mar 2014 18:25:27 +0100 Subject: [doc] actually patch node mode correctly (Thanks, Hans\!) --- src/luaotfload-fontloader.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 2b87b3f..d1b12bb 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -11482,9 +11482,10 @@ end) local autofeatures=fonts.analyzers.features local function initialize(sequence,script,language,enabled) local features=sequence.features - if features then - for i=1,#features do - local kind=features[i] + local order=features.order + if order then + for i=1,#order do + local kind=order[i] local valid=enabled[kind] if valid then local scripts=features[kind] -- cgit v1.2.3 From 07ec8b32c52a6effac900ff8bc564b7a11f5268a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 16 Mar 2014 16:01:58 +0100 Subject: [fontloader] sync with Context as of 2014-03-16 --- src/luaotfload-fontloader.lua | 88 +++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index d1b12bb..196fc61 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 03/07/14 11:42:21 +-- merge date : 03/16/14 11:40:51 do -- begin closure to overcome local limits and interference @@ -2652,13 +2652,13 @@ local stripempty=endofline^1/"" local normalempty=endofline^1 local singleempty=endofline*(endofline^0/"") local doubleempty=endofline*endofline^-1*(endofline^0/"") -local stripstart=stripempty -local p_retain_normal=Cs ((normalline+normalempty )^0 ) -local p_retain_collapse=Cs ((normalline+doubleempty )^0 ) -local p_retain_noempty=Cs ((normalline+singleempty )^0 ) +local stripstart=stripempty^0 local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 ) local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 ) local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 ) +local p_retain_normal=Cs ((normalline+normalempty )^0 ) +local p_retain_collapse=Cs ((normalline+doubleempty )^0 ) +local p_retain_noempty=Cs ((normalline+singleempty )^0 ) local striplinepatterns={ ["prune"]=p_prune_normal, ["prune and collapse"]=p_prune_collapse, @@ -2837,7 +2837,7 @@ local format_i=function(f) if f and f~="" then return format("format('%%%si',a%s)",f,n) else - return format("format('%%i',a%s)",n) + return format("format('%%i',a%s)",n) end end local format_d=format_i @@ -2849,6 +2849,10 @@ local format_f=function(f) n=n+1 return format("format('%%%sf',a%s)",f,n) end +local format_F=function(f) + n=n+1 + return format("((a%s == 0 and '0') or (a%s == 1 and '1') or format('%%%sf',a%s))",n,n,f,n) +end local format_g=function(f) n=n+1 return format("format('%%%sg',a%s)",f,n) @@ -3063,7 +3067,7 @@ local builder=Cs { "start", ( P("%")/""*( V("!") -+V("s")+V("q")+V("i")+V("d")+V("f")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") ++V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") +V("c")+V("C")+V("S") +V("Q") +V("N") @@ -3083,6 +3087,7 @@ local builder=Cs { "start", ["i"]=(prefix_any*P("i"))/format_i, ["d"]=(prefix_any*P("d"))/format_d, ["f"]=(prefix_any*P("f"))/format_f, + ["F"]=(prefix_any*P("F"))/format_F, ["g"]=(prefix_any*P("g"))/format_g, ["G"]=(prefix_any*P("G"))/format_G, ["e"]=(prefix_any*P("e"))/format_e, @@ -3130,7 +3135,7 @@ local function make(t,str) f=loadstripped(p)() else n=0 - p=lpegmatch(builder,str,1,"..",t._extensions_) + p=lpegmatch(builder,str,1,t._connector_,t._extensions_) if n>0 then p=format(template,preamble,t._preamble_,arguments[n],p) f=loadstripped(p,t._environment_)() @@ -3146,18 +3151,18 @@ local function use(t,fmt,...) end strings.formatters={} if _LUAVERSION<5.2 then - function strings.formatters.new() - local t={ _extensions_={},_preamble_=preamble,_environment_={},_type_="formatter" } + function strings.formatters.new(noconcat) + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} } setmetatable(t,{ __index=make,__call=use }) return t end else - function strings.formatters.new() + function strings.formatters.new(noconcat) local e={} for k,v in next,environment do e[k]=v end - local t={ _extensions_={},_preamble_="",_environment_=e,_type_="formatter" } + local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_="",_environment_=e } setmetatable(t,{ __index=make,__call=use }) return t end @@ -8978,8 +8983,9 @@ basemethods.shared={ basemethod="independent" local function featuresinitializer(tfmdata,value) if true then - local t=trace_preparing and os.clock() + local starttime=trace_preparing and os.clock() local features=tfmdata.shared.features + local fullname=trace_preparing and tfmdata.properties.fullname if features then applybasemethod("initializehashes",tfmdata) local collectlookups=otf.collectlookups @@ -8989,26 +8995,34 @@ local function featuresinitializer(tfmdata,value) local language=properties.language local basesubstitutions=rawdata.resources.features.gsub local basepositionings=rawdata.resources.features.gpos - if basesubstitutions then - for feature,data in next,basesubstitutions do - local value=features[feature] - if value then - local validlookups,lookuplist=collectlookups(rawdata,feature,script,language) - if validlookups then - applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist) - registerbasefeature(feature,value) - end - end - end - end - if basepositionings then - for feature,data in next,basepositionings do - local value=features[feature] - if value then - local validlookups,lookuplist=collectlookups(rawdata,feature,script,language) - if validlookups then - applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist) - registerbasefeature(feature,value) + if basesubstitutions or basepositionings then + local sequences=tfmdata.resources.sequences + for s=1,#sequences do + local sequence=sequences[s] + local sfeatures=sequence.features + if sfeatures then + local order=sequence.order + if order then + for i=1,#order do + local feature=order[i] + if features[feature] then + local validlookups,lookuplist=collectlookups(rawdata,feature,script,language) + if not validlookups then + elseif basesubstitutions and basesubstitutions[feature] then + if trace_preparing then + report_prepare("filtering base feature %a for %a",feature,fullname) + end + applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist) + registerbasefeature(feature,value) + elseif basepositionings and basepositionings[feature] then + if trace_preparing then + report_prepare("filtering base feature %a for %a",feature,fullname) + end + applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist) + registerbasefeature(feature,value) + end + end + end end end end @@ -9016,7 +9030,7 @@ local function featuresinitializer(tfmdata,value) registerbasehash(tfmdata) end if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %a",os.clock()-t,tfmdata.properties.fullname) + report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,fullname) end end end @@ -11482,9 +11496,9 @@ end) local autofeatures=fonts.analyzers.features local function initialize(sequence,script,language,enabled) local features=sequence.features - local order=features.order - if order then - for i=1,#order do + if features then + local order=features.order + for i=1,#order do local kind=order[i] local valid=enabled[kind] if valid then -- cgit v1.2.3 From 4e1f4ca586bd51ccf70343411162103dfc96e1e0 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 20 Mar 2014 07:17:33 +0100 Subject: [doc] make inline code work consistently with both formats --- doc/luaotfload-context.tex | 38 +++++- doc/luaotfload-latex.tex | 124 +++++++++++------ doc/luaotfload-main.tex | 329 ++++++++++++++++++++++++++------------------- 3 files changed, 303 insertions(+), 188 deletions(-) diff --git a/doc/luaotfload-context.tex b/doc/luaotfload-context.tex index aeca7cb..6c8d4b2 100644 --- a/doc/luaotfload-context.tex +++ b/doc/luaotfload-context.tex @@ -83,8 +83,8 @@ \definefontfeature [monospace] [liga=no,tlig=no,onum=no] \definefontfamily [mainface] [serif] [Linux Libertine O] [features=default] -%\definefontfamily [mainface] [serif] [Liberation Serif] [feature=default] -%\definefontfamily [mainface] [sans] [Iwona] [feature=default] +%definefontfamily [mainface] [serif] [Liberation Serif] [feature=default] +%definefontfamily [mainface] [sans] [Iwona] [feature=default] \definefontfamily [mainface] [sans] [Iwona Medium] [ feature=default, it=file:IwonaMedium-Italic.otf, @@ -128,7 +128,9 @@ \definetype [luafunction] [style=typing:luafunction] \setuptyping [style=ttx] -\definebodyfontenvironment [8pt] +\definebodyfontenvironment [8pt] +\definebodyfontenvironment [10pt] +\definebodyfontenvironment [12pt] %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% headings @@ -196,7 +198,26 @@ %% Context offers both \type{…} and \type<<…>>, but not an unbalanced %% one that we could map directly onto Latex’s \verb|…|. -\definetype [inlinecode] [style=emphasis:nonproportional] +\definetype [inlinecode_indeed] [style=emphasis:nonproportional] + +%% The listings macros don’t seem to handle backslashes and braces +%% well. We emulate this behavior by handling the escaping in Lua. + +\startluacode + local lpeg = require "lpeg" + local Cs, P, S = lpeg.Cs, lpeg.P, lpeg.S + local lpegmatch = lpeg.match + local unescape_char = S[[\letterbackslash\letterleftbrace\letterrightbrace]] + local backslash = P[[\letterbackslash]] + local unescape = Cs (((backslash / "" * unescape_char) + 1)^0) + commands.unescape_things = function (str) + context.type (lpegmatch (unescape, str)) + end +\stopluacode + +\unexpanded \def \inlinecode #content{% + \ctxcommand {unescape_things \!!bs \detokenize {#content}\!!es}% +} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% codelistings @@ -209,6 +230,7 @@ \unexpanded \def \endlisting {\typebuffer [listing]} + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% enumerations and lists %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -308,7 +330,13 @@ %% special elements %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\def \meta #1{<{\italic #1}>} +\definefont [lmromantenregular] [file:lmroman10-regular.otf*default] + +\def \meta #1{% + {\lmromantenregular<}% + {\italic #1}% + {\lmromantenregular>}% +} \def \beginabstractcontent {% \grabbufferdatadirect{abstractcontent}{beginabstractcontent}{endabstractcontent}% diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index 3045c26..409adcf 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -79,26 +79,64 @@ \newcommand\CONTEXT {Con\TeX t\xspace} \newcommand\OpenType {\identifier{Open\kern-.25ex Type}\xspace} -\def\definehighlight[#1][#2]% - {\ifcsname #1\endcsname\else - \expandafter\def\csname #1\endcsname% - {\bgroup#2\csname #1_indeed\endcsname} - \expandafter\def\csname #1_indeed\endcsname##1% - {##1\egroup}% - \fi} +%% \groupedcommand, with some omissions taken from syst-aux.mkiv +\let \handlegroupnormalbefore \relax +\let \handlegroupnormalafter \relax + +\protected \def \handlegroupnormal #1#2{% + \bgroup % 1 + \def \handlegroupbefore {#1}% + \def \handlegroupafter {#2}% + \afterassignment \handlegroupnormalbefore + \let \next = +} + +\def \handlegroupnormalbefore {% + \bgroup % 2 + \handlegroupbefore + \bgroup % 3 + \aftergroup \handlegroupnormalafter% +} + +\def \handlegroupnormalafter {% + \handlegroupafter + \egroup % 3 + \egroup % 2 +} + +\let \groupedcommand \handlegroupnormal %% only the two arg version + +\def \definehighlight [#1][#2]{% + \ifcsname #1\endcsname\else + \expandafter\def\csname #1\endcsname{% + \leavevmode + \groupedcommand {#2}\empty% + } + \fi% +} + +%% old, simplistic definition: obsolete now that we have +%% \groupedcommand +%\def\definehighlight[#1][#2]% + %{\ifcsname #1\endcsname\else + %\expandafter\def\csname #1\endcsname% + %{\bgroup#2\csname #1_indeed\endcsname} + %\expandafter\def\csname #1_indeed\endcsname##1% + %{##1\egroup}% + %\fi} \def\restoreunderscore{\catcode`\_=12\relax} -\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs -\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs -\definehighlight [luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers -\definehighlight [identifier][\sffamily] %% names -\definehighlight [abbrev][\rmfamily\scshape] %% acronyms -\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph +\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs +\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs +\definehighlight [luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers +\definehighlight [identifier][\sffamily] %% names +\definehighlight [abbrev][\rmfamily\scshape] %% acronyms +\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph -\definehighlight [Largefont][\Large] %% font size -\definehighlight [smallcaps][\sc] %% font feature -\definehighlight [proportional][\tt] %% font switch +\definehighlight [Largefont][\Large] %% font size +\definehighlight [smallcaps][\sc] %% font feature +\definehighlight [nonproportional][\tt] %% font switch \newcommand*\email[1]{\href{mailto:#1}{#1}} @@ -170,6 +208,7 @@ \usepackage {listings} \lstset { basicstyle=\ttfamily, + escapechar=Ö, } \def \inlinecode #1{% @@ -245,8 +284,10 @@ \def \first {####1}% #2 } - \let \beginaltitem \altitem - \let \endaltitem \relax + \let \beginnormalitem \item + \let \endnormalitem \relax + \let \beginaltitem \altitem + \let \endaltitem \relax } \expandafter \def \csname end#1\endcsname {% @@ -280,6 +321,9 @@ %% special elements %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\let \beginfrontmatter \relax +\let \endfrontmatter \relax + \def \beginabstractcontent {\begin {abstract}} \def \endabstractcontent {\end {abstract}} @@ -288,7 +332,9 @@ \let \setdocumentauthor \author \let \typesetdocumenttitle \maketitle -\let \typesetcontent \tableofcontent +\AtBeginDocument {%% seriously? + \let \typesetcontent \tableofcontents% +} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% floats @@ -315,34 +361,22 @@ } %% figures, e.g. the file graph -\def \beginfigurefloat #1#2{%% #1:label #2:caption - \begingroup - \begin {figure} [b] - \edef \figurelabel {#1}% - \caption {#2}% -} - -\def \endfigurefloat {% - \label \figurelabel - \end {figure} - \endgroup +\def \figurefloat #1#2#3{%% #1:label #2:caption #3:file + \begin {figure} [b] + \caption {#2}% + \includegraphics[width=\textwidth]{#3}% + \label {#1} + \end {figure} } %% tables - -\def \begintablefloat #1#2{%% #1:label #2:caption - \begingroup - \begin {table} [t] - \hrule - \edef \floatlabel {#1}% - \caption {#2}% -} - -\def \endtablefloat {% - \label \floatlabel - \hrule - \end {table} - \endgroup +\def \tablefloat #1#2{%% #1:label #2:caption + \begin {table} [t] + \hrule + \caption {#2}% + \label {#1} + \hrule + \end {table} } @@ -402,6 +436,8 @@ \makeatother +\newif \ifcontextmkiv \contextmkivfalse + \begin {document} \input {luaotfload-main.tex} \end {document} diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 0e74aa9..5b033c0 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -1,16 +1,3 @@ -%\beginsection {foo} - %bar baz -%\endsection - -%\begindescriptions - - %\beginaltitem {mode} foo - %%\identifier{luaotfload} has two \OpenType processing - %%\emphasis{modes}: -%\enddescriptions - -%\endinput - %% Copyright (C) 2009-2014 %% %% by Elie Roux @@ -42,21 +29,25 @@ %% ---------------------------------------------------------------------------- %% -\setdocumenttitle {The \identifier{luaotfload} package} -\setdocumentdate {2014/**/** v2.5} -\setdocumentauthor {Elie Roux · Khaled Hosny · Philipp Gesang\\ - Home: \hyperlink {https://github.com/lualatex/luaotfload}\\ - Support: \email {lualatex-dev@tug.org}} +\beginfrontmatter -\typesetdocumenttitle + \setdocumenttitle {The \identifier{luaotfload} package} + \setdocumentdate {2014/**/** v2.5} + \setdocumentauthor {Elie Roux · Khaled Hosny · Philipp Gesang\\ + Home: \hyperlink {https://github.com/lualatex/luaotfload}\\ + Support: \email {lualatex-dev@tug.org}} -\beginabstractcontent - This package is an adaptation of the \CONTEXT font loading system. - It allows for loading \OpenType fonts with an extended syntax and adds - support for a variety of font features. -\endabstractcontent + \typesetdocumenttitle -\tableofcontents + \beginabstractcontent + This package is an adaptation of the \CONTEXT font loading system. + It allows for loading \OpenType fonts with an extended syntax and adds + support for a variety of font features. + \endabstractcontent + +\endfrontmatter + +\typesetcontent %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \beginsection {Introduction} @@ -140,10 +131,10 @@ in the task and willingness to respond to our suggestions. \identifier{luaotfload} supports an extended font request syntax: \beginnarrower - \inlinecode{\\font\\foo=\{}% - \meta{prefix}\inlinecode{:}% - \meta{font name}\inlinecode{:}% - \meta{font features}\inlinecode{\}}% + \nonproportional{\string\font\string\foo\space= \string{}% + \meta{prefix}\nonproportional{:}% + \meta{font name}\nonproportional{:}% + \meta{font features}\nonproportional{\string}}% \meta{\TEX font features} \endnarrower @@ -165,18 +156,18 @@ for a more formal description see figure \ref{font-syntax}. In addition to the font style modifiers (\emphasis{slash-notation}) given above, there are others that are recognized but will be silently - ignored: {\ttfamily aat}, - {\ttfamily icu}, and - {\ttfamily gr}. + ignored: \nonproportional{aat}, + \nonproportional{icu}, and + \nonproportional{gr}. The special terminals are: - {\sc feature\textunderscore id} for a valid font + \smallcaps {feature\textunderscore id} for a valid font feature name and - {\sc feature\textunderscore value} for the corresponding + \smallcaps {feature\textunderscore value} for the corresponding value. - {\sc tfmname} is the name of a \abbrev{tfm} file. - {\sc digit} again refers to bytes 48--57, and - {\sc all\textunderscore characters} to all byte values. - {\sc csname} and {\sc dimension} are the \TEX concepts.} + \smallcaps {tfmname} is the name of a \abbrev{tfm} file. + \smallcaps {digit} again refers to bytes 48--57, and + \smallcaps {all\textunderscore characters} to all byte values. + \smallcaps {csname} and \smallcaps {dimension} are the \TEX concepts.} % ::= `\\font', {\sc csname}, `=', , [ ] ; @@ -223,7 +214,11 @@ In \identifier{luaotfload}, the canonical syntax for font requests requires a \emphasis{prefix}: % \beginnarrower - \inlinecode{\\font\\fontname=}\meta{prefix}\inlinecode{:}\meta{fontname}\dots + \nonproportional{\string\font\string\fontname\space= }% + \meta{prefix}% + \nonproportional{:}% + \meta{fontname}% + \dots \endnarrower % where \meta{prefix} is either \inlinecode{file:} or \inlinecode {name:}.\footnote{% @@ -318,7 +313,9 @@ There are again two modes: bracketed and unbracketed. A bracketed request looks as follows. \beginnarrower - \inlinecode{\\font\\fontname=[}\meta{path to file}\inlinecode{]} + \nonproportional{\string\font\string\fontname\space = [}% + \meta{/path/to/file}% + \nonproportional{]} \endnarrower \noindent @@ -329,7 +326,9 @@ Naturally, path-less file names are equally valid and processed the same way as an ordinary \inlinecode {file:} lookup. \beginnarrower - \inlinecode{\\font\\fontname=}\meta{font name} \dots + \nonproportional{\string\font\string\fontname\space= }% + \meta{font name} + \dots \endnarrower Unbracketed (or, for lack of a better word: \emphasis{anonymous}) @@ -347,7 +346,11 @@ Furthermore, \identifier{luaotfload} supports the slashed (shorthand) font style notation from \XETEX. \beginnarrower - \inlinecode{\\font\\fontname=}\meta{font name}\inlinecode{/}\meta{modifier}\dots + \nonproportional{\string\font\string\fontname\space= }% + \meta{font name}% + \nonproportional{/}% + \meta{modifier} + \dots \endnarrower \noindent @@ -470,11 +473,14 @@ which is equivalent to these full names: general scheme for font requests: \beginnarrower - \inlinecode{\\font\\foo=\{}% - \meta{prefix}\inlinecode{:}% - \meta{font name}\inlinecode{:}% - \meta{font features}\inlinecode{\}}% - \meta{\TEX font features} + \nonproportional{\string\font\string\foo\space= "}% + \meta{prefix}% + \nonproportional{:}% + \meta{font name}% + \nonproportional{:}% + \meta{font features}% + \meta{\TEX font features}% + \nonproportional{"} \endnarrower \noindent @@ -680,6 +686,12 @@ obviously, \inlinecode{random}. user. \endaltitem +\ifcontextmkiv + \startbuffer [printvectors] + \directlua{inspect(fonts.protrusions.setups.default) + inspect(fonts.expansions.setups.default)} + \stopbuffer +\fi \beginaltitem {protrusion \& expansion} These keys control microtypographic features of the font, @@ -694,10 +706,14 @@ obviously, \inlinecode{random}. % Alternatively and with loss of information, you can dump those tables into your terminal by issuing - \beginlisting -\directlua{inspect(fonts.protrusions.setups.default) - inspect(fonts.expansions.setups.default)} - \endlisting + \unless \ifcontextmkiv + \beginlisting + \directlua{inspect(fonts.protrusions.setups.default) + inspect(fonts.expansions.setups.default)} + \endlisting + \else + \typebuffer [printvectors] + \fi at some point after loading \fileent{luaotfload.sty}. } % @@ -740,13 +756,28 @@ Currently (2014) there are three of them: \beginaltitem {tlig} Applies legacy \TEX ligatures: - \begintabulate [rlrl] - \beginrow `` \newcell \inlinecode {``} \newcell '' \newcell \inlinecode {''} \endrow - \beginrow ` \newcell \inlinecode {`} \newcell ' \newcell \inlinecode {'} \endrow - \beginrow " \newcell \inlinecode {"} \newcell -- \newcell \inlinecode {--} \endrow - \beginrow --- \newcell \inlinecode {---} \newcell !` \newcell \inlinecode {!`} \endrow - \beginrow ?` \newcell \inlinecode {?`} \newcell \newcell \endrow - \endtabulate + \unless \ifcontextmkiv + \begintabulate [rlrl] + \beginrow `` \newcell \inlinecode {``} \newcell '' \newcell \inlinecode {''} \endrow + \beginrow ` \newcell \inlinecode {`} \newcell ' \newcell \inlinecode {'} \endrow + \beginrow " \newcell \inlinecode {"} \newcell -- \newcell \inlinecode {--} \endrow + \beginrow --- \newcell \inlinecode {---} \newcell !` \newcell \inlinecode {!`} \endrow + \beginrow ?` \newcell \inlinecode {?`} \newcell \newcell \endrow + \endtabulate + \else + %% XXX find a way to wrap these in the tabulate environment + \startframed [frame=off,width=broad,align=middle] + \startframed [frame=off,width=\dimexpr(\textwidth/2)] + \startxtable [align=middle] + \startxrow \startxcell `` \stopxcell \startxcell \inlinecode {``} \stopxcell \startxcell '' \stopxcell \startxcell \inlinecode {''} \stopxcell \stopxrow + \startxrow \startxcell ` \stopxcell \startxcell \inlinecode {`} \stopxcell \startxcell ' \stopxcell \startxcell \inlinecode {'} \stopxcell \stopxrow + \startxrow \startxcell " \stopxcell \startxcell \inlinecode {"} \stopxcell \startxcell -- \stopxcell \startxcell \inlinecode {--} \stopxcell \stopxrow + \startxrow \startxcell --- \stopxcell \startxcell \inlinecode {---} \stopxcell \startxcell !` \stopxcell \startxcell \inlinecode {!`} \stopxcell \stopxrow + \startxrow \startxcell ?` \stopxcell \startxcell \inlinecode {?`} \stopxcell \startxcell \stopxcell \startxcell \stopxcell \stopxrow + \stopxtable + \stopframed + \stopframed + \fi \footnote{% These contain the feature set \inlinecode {trep} of earlier @@ -873,26 +904,41 @@ Other paths can be specified by setting the environment variable If it is non-empty, then search will be extended to the included directories. -\begintablefloat {table-searchpaths} +\tablefloat {table-searchpaths} {List of paths searched for each supported operating system.} - \begincentered - \begintabulate [lp{.5\textwidth}] - \beginrow - Windows \newcell \inlinecode {\% WINDIR\%\\ Fonts} - \endrow - \beginrow - Linux \newcell \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break - \fileent{/etc/fonts/fonts.conf} - \endrow - \beginrow - Mac \newcell \fileent{\textasciitilde/Library/Fonts},\break - \fileent{/Library/Fonts},\break - \fileent{/System/Library/Fonts}, and\hfill\break - \fileent{/Network/Library/Fonts} - \endrow - \endtabulate - \endcentered -\endtablefloat + {% + \unless \ifcontextmkiv + \begincentered + \begintabulate [lp{.5\textwidth}] + \beginrow + Windows \newcell \inlinecode {\% WINDIR\%\\ Fonts} + \endrow + \beginrow + Linux \newcell \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break + \fileent{/etc/fonts/fonts.conf} + \endrow + \beginrow + Mac \newcell \fileent{\textasciitilde/Library/Fonts},\break + \fileent{/Library/Fonts},\break + \fileent{/System/Library/Fonts}, and\hfill\break + \fileent{/Network/Library/Fonts} + \endrow + \endtabulate + \endcentered + \else + \setuplocalinterlinespace [14pt] + \starttabulate [|l|p(.5\textwidth)|] + \NC Windows \NC \inlinecode {\% WINDIR\%\\ Fonts} \NC \NR + \NC Linux \NC \fileent{/usr/local/etc/fonts/fonts.conf} and\crlf + \fileent{/etc/fonts/fonts.conf} \NC \NR + \NC + Mac \NC \fileent{\textasciitilde/Library/Fonts},\crlf + \fileent{/Library/Fonts},\break + \fileent{/System/Library/Fonts}, and\crlf + \fileent{/Network/Library/Fonts} \NC \NR + \stoptabulate + \fi% + } \endsubsection @@ -1016,7 +1062,7 @@ An example with explicit paths: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \identifier{luaotfload} relies on code originally written by Hans -Hagen for the \hyperlink[\identifier\CONTEXT]{http://wiki.contextgarden.net} +Hagen for the \hyperlink[\identifier{\CONTEXT}]{http://wiki.contextgarden.net} format. % It integrates the font loader as distributed in @@ -1067,64 +1113,70 @@ It houses several \LUA files that can be classed in three categories. \begindefinitions - \normalitem \emphasis{\LUA utility libraries}, a subset - of what is provided by the \identifier{lualibs} - package. - - \begindoublecolumns - \begindefinitions - \beginaltitem {l-lua.lua} \endaltitem - \beginaltitem {l-lpeg.lua} \endaltitem - \beginaltitem {l-function.lua} \endaltitem - \beginaltitem {l-string.lua} \endaltitem - \beginaltitem {l-table.lua} \endaltitem - \beginaltitem {l-io.lua} \endaltitem - \beginaltitem {l-file.lua} \endaltitem - \beginaltitem {l-boolean.lua} \endaltitem - \beginaltitem {l-math.lua} \endaltitem - \beginaltitem {util-str.lua} \endaltitem - \enddefinitions - \enddoublecolumns - - \normalitem The \emphasis{font loader} itself. - These files have been written for - \LUATEX-Fonts and they are distributed along - with \identifier{luaotfload}. - \begindoublecolumns - \begindefinitions - \beginaltitem{luatex-basics-gen.lua} \endaltitem - \beginaltitem{luatex-basics-nod.lua} \endaltitem - \beginaltitem{luatex-fonts-enc.lua} \endaltitem - \beginaltitem{luatex-fonts-syn.lua} \endaltitem - \beginaltitem{luatex-fonts-tfm.lua} \endaltitem - \beginaltitem{luatex-fonts-chr.lua} \endaltitem - \beginaltitem{luatex-fonts-lua.lua} \endaltitem - \beginaltitem{luatex-fonts-inj.lua} \endaltitem - \beginaltitem{luatex-fonts-otn.lua} \endaltitem - \beginaltitem{luatex-fonts-def.lua} \endaltitem - \beginaltitem{luatex-fonts-ext.lua} \endaltitem - \beginaltitem{luatex-fonts-cbk.lua} \endaltitem - \enddefinitions - \enddoublecolumns - - \normalitem Code related to \emphasis{font handling and - node processing}, taken directly from - \CONTEXT. - \begindoublecolumns - \begindefinitions - \beginaltitem{data-con.lua} \endaltitem - \beginaltitem{font-ini.lua} \endaltitem - \beginaltitem{font-con.lua} \endaltitem - \beginaltitem{font-cid.lua} \endaltitem - \beginaltitem{font-map.lua} \endaltitem - \beginaltitem{font-oti.lua} \endaltitem - \beginaltitem{font-otf.lua} \endaltitem - \beginaltitem{font-otb.lua} \endaltitem - \beginaltitem{font-ota.lua} \endaltitem - \beginaltitem{font-def.lua} \endaltitem - \beginaltitem{font-otp.lua} \endaltitem - \enddefinitions - \enddoublecolumns + \beginnormalitem + \emphasis{\LUA utility libraries}, a subset + of what is provided by the \identifier{lualibs} + package. + + \begindoublecolumns + \begindefinitions + \beginaltitem {l-lua.lua} \endaltitem + \beginaltitem {l-lpeg.lua} \endaltitem + \beginaltitem {l-function.lua} \endaltitem + \beginaltitem {l-string.lua} \endaltitem + \beginaltitem {l-table.lua} \endaltitem + \beginaltitem {l-io.lua} \endaltitem + \beginaltitem {l-file.lua} \endaltitem + \beginaltitem {l-boolean.lua} \endaltitem + \beginaltitem {l-math.lua} \endaltitem + \beginaltitem {util-str.lua} \endaltitem + \enddefinitions + \enddoublecolumns + \endnormalitem + + \beginnormalitem + The \emphasis{font loader} itself. + These files have been written for + \LUATEX-Fonts and they are distributed along + with \identifier{luaotfload}. + \begindoublecolumns + \begindefinitions + \beginaltitem{luatex-basics-gen.lua} \endaltitem + \beginaltitem{luatex-basics-nod.lua} \endaltitem + \beginaltitem{luatex-fonts-enc.lua} \endaltitem + \beginaltitem{luatex-fonts-syn.lua} \endaltitem + \beginaltitem{luatex-fonts-tfm.lua} \endaltitem + \beginaltitem{luatex-fonts-chr.lua} \endaltitem + \beginaltitem{luatex-fonts-lua.lua} \endaltitem + \beginaltitem{luatex-fonts-inj.lua} \endaltitem + \beginaltitem{luatex-fonts-otn.lua} \endaltitem + \beginaltitem{luatex-fonts-def.lua} \endaltitem + \beginaltitem{luatex-fonts-ext.lua} \endaltitem + \beginaltitem{luatex-fonts-cbk.lua} \endaltitem + \enddefinitions + \enddoublecolumns + \endnormalitem + + \beginnormalitem + Code related to \emphasis{font handling and + node processing}, taken directly from + \CONTEXT. + \begindoublecolumns + \begindefinitions + \beginaltitem{data-con.lua} \endaltitem + \beginaltitem{font-ini.lua} \endaltitem + \beginaltitem{font-con.lua} \endaltitem + \beginaltitem{font-cid.lua} \endaltitem + \beginaltitem{font-map.lua} \endaltitem + \beginaltitem{font-oti.lua} \endaltitem + \beginaltitem{font-otf.lua} \endaltitem + \beginaltitem{font-otb.lua} \endaltitem + \beginaltitem{font-ota.lua} \endaltitem + \beginaltitem{font-def.lua} \endaltitem + \beginaltitem{font-otp.lua} \endaltitem + \enddefinitions + \enddoublecolumns + \endnormalitem \enddefinitions Note that if \identifier{luaotfload} cannot locate the @@ -1184,11 +1236,10 @@ files not contained in the merge. Some of these have no equivalent in \endaltitem \endfilelist -\beginfigurefloat +\figurefloat {file-graph} {Schematic of the files in \identifier{Luaotfload}} - \includegraphics[width=\textwidth]{filegraph.pdf} -\endfigurefloat + {filegraph.pdf} \endsection -- cgit v1.2.3 From 48f5c2154a3d8ab6954263140570a77909c2c458 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 20 Mar 2014 07:28:50 +0100 Subject: [*] update .gitignore --- .gitignore | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 850a861..d9cba93 100644 --- a/.gitignore +++ b/.gitignore @@ -39,5 +39,22 @@ tests/*.ovf tests/*.sty tests/luaotfload* -# temporary directory +# temporary directories +build/* +testing/* +tests/* tmp/* + +# tex side-effects +doc/*.aux +doc/*.log +doc/*.out +doc/*.pdf +doc/*.tmp +doc/*.toc +doc/*.tuc +doc/*.vimout + +# valgrind log +src/*.log + -- cgit v1.2.3 From 3cfff4cfd2a082eb29334dd97e005b953c155d8a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 21 Mar 2014 08:05:59 +0100 Subject: [db] retrieve list of font files before scanning them --- src/luaotfload-database.lua | 284 +++++++++++++++++++++----------------------- 1 file changed, 137 insertions(+), 147 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 54738df..9e34668 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -560,7 +560,6 @@ local update_names --- state of the database local fonts_reloaded = false -local fonts_read = 0 --- limit output when approximate font matching (luaotfload-tool -F) local fuzzy_limit = 1 --- display closest only @@ -2189,73 +2188,44 @@ local truncate_string = function (str, restrict) return str end ---[[doc-- - scan_dir() scans a directory and populates the list of fonts - with all the fonts it finds. +--[[doc-- - · dirname : name of the directory to scan - · currentnames : current font db object - · targetnames : font db object to fill - · dry_run : don’t touch anything + collect_font_filenames_dir -- Traverse the directory root at + ``dirname`` looking for font files. Returns a list of {*filename*; + *location*} pairs. --doc]]-- ---- string -> dbobj -> dbobj -> bool -> bool -> (int * int) - -local scan_dir = function (dirname, currentnames, targetnames, - dry_run, location) +--- string -> string -> string * string list +local collect_font_filenames_dir = function (dirname, location) if lpegmatch (p_blacklist, dirname) then report ("both", 4, "db", "Skipping blacklisted directory %s.", dirname) --- ignore - return 0, 0 + return { } end local found = find_font_files (dirname, location ~= "texmf" and location ~= "local") if not found then report ("both", 4, "db", "No such directory: %q; skipping.", dirname) - return 0, 0 + return { } end - report ("both", 4, "db", "Scanning directory %s.", dirname) - local n_new = 0 --- total of fonts collected - local n_found = #found - local max_fonts = luaotfloadconfig.max_fonts - - report ("both", 4, "db", "%d font files detected.", n_found) - for j=1, n_found do - if max_fonts and fonts_read >= max_fonts then - break - end + local nfound = #found + local files = { } + report ("both", 4, "db", + "%d font files detected in %s.", + nfound, dirname) + for j = 1, nfound do local fullname = found[j] - fullname = path_normalize(fullname) - local new - - if dry_run == true then - local truncated = truncate_string (fullname, 43) - report ("log", 2, "db", - "Would have been loading %s.", fullname) - report_status ("term", "db", - "Would have been loading %s", truncated) - else - local truncated = truncate_string (fullname, 32) - report ("log", 2, "db", "Loading font %s.", fullname) - report_status ("term", "db", "Loading font %s", truncated) - local new = read_font_names (fullname, currentnames, - targetnames, texmf) - if new == true then - fonts_read = fonts_read + 1 - n_new = n_new + 1 - end - end + files[#files + 1] = { path_normalize (fullname), location } end - report ("both", 4, "db", "Done. %d fonts indexed in %q.", - n_found, dirname) - return n_found, n_new + return files end + --- string list -> string list local filter_out_pwd = function (dirs) local result = { } @@ -2275,28 +2245,28 @@ local path_separator = ostype == "windows" and ";" or ":" --[[doc-- - scan_texmf_fonts() scans all fonts in the texmf tree through the - kpathsea variables OPENTYPEFONTS and TTFONTS of texmf.cnf. + collect_font_filenames_texmf -- Scan texmf tree for font files + relying on the kpathsea variables $OPENTYPEFONTS and $TTFONTS of + texmf.cnf. The current working directory comes as “.” (texlive) or absolute path (miktex) and will always be filtered out. ---doc]]-- + Returns a list of { *filename*; *location* } pairs. ---- dbobj -> dbobj -> bool? -> (int * int) +--doc]]-- -local scan_texmf_fonts = function (currentnames, targetnames, dry_run) +--- unit -> string * string list +local collect_font_filenames_texmf = function () - local n_scanned, n_new, fontdirs = 0, 0 local osfontdir = kpseexpand_path "$OSFONTDIR" if stringis_empty (osfontdir) then - report ("info", 1, "db", "Scanning TEXMF fonts...") + report ("info", 1, "db", "Scanning TEXMF for fonts...") else - report ("info", 1, "db", "Scanning TEXMF and OS fonts...") + report ("info", 1, "db", "Scanning TEXMF and $OSFONTDIR for fonts...") if log.get_loglevel () > 3 then local osdirs = filesplitpath (osfontdir) - report ("info", 0, "db", - "$OSFONTDIR has %d entries:", #osdirs) + report ("info", 0, "db", "$OSFONTDIR has %d entries:", #osdirs) for i = 1, #osdirs do report ("info", 0, "db", "[%d] %s", i, osdirs[i]) end @@ -2307,24 +2277,22 @@ local scan_texmf_fonts = function (currentnames, targetnames, dry_run) fontdirs = fontdirs .. path_separator .. kpseexpand_path "$TTFONTS" fontdirs = fontdirs .. path_separator .. kpseexpand_path "$T1FONTS" - if not stringis_empty (fontdirs) then - local tasks = filter_out_pwd (filesplitpath (fontdirs)) - report ("info", 3, "db", - "Initiating scan of %d directories.", #tasks) - report_status_start (2, 4) - for _, d in next, tasks do - local found, new = scan_dir (d, currentnames, targetnames, - dry_run, "texmf") - n_scanned = n_scanned + found - n_new = n_new + new - end - report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new) + if stringis_empty (fontdirs) then + return { } end - return n_scanned, n_new + local tasks = filter_out_pwd (filesplitpath (fontdirs)) + report ("info", 3, "db", + "Initiating scan of %d directories.", #tasks) + + local files = { } + for _, dir in next, tasks do + files = tableappend (files, collect_font_filenames_dir (dir, "texmf")) + end + report ("term", 3, "db", "Collected %d files.", #files) + return files end ---- TODO stuff those paths into some writable table --- unit -> string list local function get_os_dirs () if os.name == 'macosx' then @@ -2350,38 +2318,75 @@ end --[[doc-- - scan_os_fonts() scans the OS fonts through - - fontconfig for Unix (reads the fonts.conf file[s] and scans the - directories) - - a static set of directories for Windows and MacOSX + retrieve_namedata -- Scan the list of collected fonts and populate + the list of namedata. - **NB**: If $OSFONTDIR is nonempty, as it appears to be by default - on Windows setups, the system fonts will have already been - processed while scanning the TEXMF. Thus, this function is - never called. + · dirname : name of the directory to scan + · currentnames : current font db object + · targetnames : font db object to fill + · dry_run : don’t touch anything + + Returns the number of fonts that were actually added to the index. --doc]]-- ---- dbobj -> dbobj -> bool? -> (int * int) -local scan_os_fonts = function (currentnames, - targetnames, - dry_run) +--- string * string list -> dbobj -> dbobj -> bool? -> int +local retrieve_namedata = function (files, currentnames, targetnames, dry_run) + + local nfiles = #files + local nnew = 0 + local max_fonts = luaotfloadconfig.max_fonts or 2^51 + + report ("info", 1, "db", "Scanning %d collected font files ...", nfiles) + + local bylocation = { texmf = { 0, 0 } + , ["local"] = { 0, 0 } + , system = { 0, 0 } + } + report_status_start (2, 4) + for i = 1, (nfiles < max_fonts) and nfiles or max_fonts do + local fullname, location = unpack (files[i]) + local count = bylocation[location] + count[1] = count[1] + 1 + if dry_run == true then + local truncated = truncate_string (fullname, 43) + report ("log", 2, "db", "Would have been loading %s.", fullname) + report_status ("term", "db", "Would have been loading %s", truncated) + --- skip the read_font_names part + else + local truncated = truncate_string (fullname, 32) + report ("log", 2, "db", "Loading font %s.", fullname) + report_status ("term", "db", "Loading font %s", truncated) + local new = read_font_names (fullname, currentnames, + targetnames, location) + if new == true then + nnew = nnew + 1 + count[2] = count[2] + 1 + end + end + end + report_status_stop ("term", "db", "Scanned %d files, %d new.", nfiles, nnew) + for location, count in next, bylocation do + report ("term", 4, "db", " * %s: %d files, %d new", + location, count[1], count[2]) + end + return nnew +end + +--- unit -> string * string list +local collect_font_filenames_system = function () local n_scanned, n_new = 0, 0 - report ("info", 1, "db", "Scanning OS fonts...") + report ("info", 1, "db", "Scanning system fonts...") report ("info", 2, "db", "Searching in static system directories...") - report_status_start (2, 4) - for _, d in next, get_os_dirs () do - local found, new = scan_dir (d, currentnames, - targetnames, dry_run) - n_scanned = n_scanned + found - n_new = n_new + new + local files = { } + for _, dir in next, get_os_dirs () do + tableappend (files, collect_font_filenames_dir (dir, "system")) end - report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new) - - return n_scanned, n_new + report ("term", 3, "db", "Collected %d files.", #files) + return files end --- unit -> bool @@ -2393,29 +2398,32 @@ end --[[doc-- - scan_local_fonts() -- Scan font files in $PWD (during a TeX run) - and add them to the database. + collect_font_filenames_local -- Scan $PWD (during a TeX run) + for font files. + + Side effect: This sets the “local” flag in the subtable “meta” to + prevent the merged table from being saved to disk. - This sets the “local” flag in the subtable “meta” to prevent the - merged table from being saved to disk. - TODO the local tree could be cached in $PWD. --doc]]-- -local scan_local_fonts = function (currentnames, - targetnames, - dry_run) - local n_scanned, n_new = 0, 0 - local pwd = lfscurrentdir () - report ("both", 1, "db", "Scanning fonts in $PWD (%q) ...", pwd) +--- unit -> string * string list +local collect_font_filenames_local = function () + local pwd = lfscurrentdir () + report ("both", 1, "db", "Scanning for fonts in $PWD (%q) ...", pwd) - n_scanned, n_new = scan_dir (pwd, currentnames, targetnames, false, "local") - if n_new > 0 then + local files = collect_font_filenames_dir (pwd, "local") + local nfiles = #files + if nfiles > 0 then targetnames.meta["local"] = true --- prevent saving to disk + report ("term", 1, "db", "Found %d files.", pwd) + else + report ("term", 1, "db", + "Couldn’t find a thing here. What a waste.", pwd) end - - return n_scanned, n_new + report ("term", 3, "db", "Collected %d files.", #files) + return files end --- dbobj -> dbobj -> int * int @@ -2912,35 +2920,18 @@ order_design_sizes = function (families) return families end -local retrieve_namedata = function (currentnames, - targetnames, - dry_run, - n_rawnames, - n_newnames) - - local rawnames, new = scan_texmf_fonts (currentnames, - targetnames, - dry_run) - - n_rawnames = n_rawnames + rawnames - n_newnames = n_newnames + new - - rawnames, new = scan_os_fonts (currentnames, targetnames, dry_run) - - n_rawnames = n_rawnames + rawnames - n_newnames = n_newnames + new - - if luaotfloadconfig.scan_local == true then - rawnames, new = scan_local_fonts (currentnames, targetnames, dry_run) - - n_rawnames = n_rawnames + rawnames - n_newnames = n_newnames + new +--- dbobj -> dbobj -> int -> int -> string * bool list +local collect_font_filenames = function () + --- + local filenames = { } + tableappend (filenames, collect_font_filenames_texmf ()) + tableappend (filenames, collect_font_filenames_system ()) + if luaotfloadconfig.scan_local == true then + tableappend (filenames, collect_font_filenames_local ()) end - - return n_rawnames, n_newnames + return filenames end - --- dbobj -> stats local collect_statistics = function (mappings) @@ -3113,7 +3104,6 @@ end --- dbobj? -> bool? -> bool? -> dbobj update_names = function (currentnames, force, dry_run) local targetnames - local n_raw, n_new = 0, 0 if luaotfloadconfig.update_live == false then report ("info", 2, "db", @@ -3132,9 +3122,6 @@ update_names = function (currentnames, force, dry_run) report("both", 1, "db", "Updating the font names database" .. (force and " forcefully." or ".")) - --- pass 1 get raw data: read font files (normal case) or reuse - --- information present in index - if luaotfloadconfig.skip_read == true then --- the difference to a “dry run” is that we don’t search --- for font files entirely. we also ignore the “force” @@ -3161,14 +3148,19 @@ update_names = function (currentnames, force, dry_run) read_blacklist () - n_raw, n_new = retrieve_namedata (currentnames, - targetnames, - dry_run, - 0, - 0) + --- pass 1: Collect the names of all fonts we are going to process. + local font_filenames = collect_font_filenames () + + --- pass 1 get raw data: read font files (normal case) or reuse + --- information present in index + + n_new = retrieve_namedata (font_filenames, + currentnames, + targetnames, + dry_run) report ("info", 3, "db", - "Scanned %d font files; %d new entries.", - n_raw, n_new) + "Found %d font files; %d new entries.", + #font_filenames, n_new) end --- pass 2 (optional): collect some stats about the raw font info @@ -3454,7 +3446,6 @@ end --- export functionality to the namespace “fonts.names” ----------------------------------------------------------------------- -names.scan_dir = scan_dir names.set_font_filter = set_font_filter names.flush_lookup_cache = flush_lookup_cache names.save_lookups = save_lookups @@ -3468,7 +3459,6 @@ names.read_blacklist = read_blacklist names.sanitize_fontname = sanitize_fontname names.getfilename = resolve_fullpath names.set_location_precedence = set_location_precedence -names.scan_local_fonts = scan_local_fonts --- font cache names.purge_cache = purge_cache -- cgit v1.2.3 From 95cdecde2d3415604cd8194eacd11544fe985aeb Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 21 Mar 2014 08:07:23 +0100 Subject: [db] update multi-pass description --- src/luaotfload-database.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 9e34668..17cb4fd 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -3151,8 +3151,8 @@ update_names = function (currentnames, force, dry_run) --- pass 1: Collect the names of all fonts we are going to process. local font_filenames = collect_font_filenames () - --- pass 1 get raw data: read font files (normal case) or reuse - --- information present in index + --- pass 2: read font files (normal case) or reuse information + --- present in index n_new = retrieve_namedata (font_filenames, currentnames, @@ -3163,7 +3163,7 @@ update_names = function (currentnames, force, dry_run) #font_filenames, n_new) end - --- pass 2 (optional): collect some stats about the raw font info + --- pass 3 (optional): collect some stats about the raw font info if luaotfloadconfig.statistics == true then targetnames.meta.statistics = collect_statistics (targetnames.mappings) @@ -3173,17 +3173,17 @@ update_names = function (currentnames, force, dry_run) --- non-texmf entries are redirected there and the mapping --- needs to be 100% consistent - --- pass 3: build filename table + --- pass 4: build filename table targetnames.files = generate_filedata (targetnames.mappings) - --- pass 4: build family lookup table + --- pass 5: build family lookup table targetnames.families = collect_families (targetnames.mappings) - --- pass 5: arrange style and size info + --- pass 6: arrange style and size info targetnames.families = group_modifiers (targetnames.mappings, targetnames.families) - --- pass 6: order design size tables + --- pass 7: order design size tables targetnames.families = order_design_sizes (targetnames.families) -- cgit v1.2.3 From 8e9e1d02df416400bd3454f8f0aee279c3898f0e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 22 Mar 2014 21:42:58 +0100 Subject: [fontloader] sync with Context as of 2014-03-22 --- src/luaotfload-fontloader.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 196fc61..655aedf 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 03/16/14 11:40:51 +-- merge date : 03/22/14 15:31:38 do -- begin closure to overcome local limits and interference @@ -2574,9 +2574,11 @@ end if not number then number={} end local stripper=patterns.stripzeros local function points(n) + n=tonumber(n) return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) end local function basepoints(n) + n=tonumber(n) return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536)) end number.points=points @@ -6569,7 +6571,7 @@ local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf otf.glists={ "gsub","gpos" } -otf.version=2.754 +otf.version=2.755 otf.cache=containers.define("fonts","otf",otf.version,true) local fontdata=fonts.hashes.identifiers local chardata=characters and characters.data -- cgit v1.2.3 From 7d5a6d63019e5dfc750a63dc957439a83d4f3307 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 22 Mar 2014 22:05:38 +0100 Subject: [tool] add dummies for --bisect --- src/luaotfload-tool.lua | 64 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 9e75944..03e7db3 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -725,8 +725,8 @@ set. local action_sequence = { "loglevel", "help", "version", "diagnose", - "blacklist", "cache", "flush", "generate", - "list", "query", + "blacklist", "cache", "flush", "bisect", + "generate", "list", "query", } local action_pending = tabletohash(action_sequence, false) @@ -771,6 +771,61 @@ actions.generate = function (job) return false, false end +--[[doc-- + + bisect_start -- Begin a bisect session. Determines the number of + fonts and sets the initial high, low, and pivot values. +--doc]]-- + +local bisect_start = function () +end + +--[[doc-- + + bisect_stop -- Terminate bisection session by removing all state info. + +--doc]]-- + +local bisect_stop = function () +end + +--[[doc-- + + bisect_set -- Prepare the next bisection step by setting high, low, + and pivot to new values. + +--doc]]-- + +local bisect_set = function (outcome) +end + +--[[doc-- + + bisect_status -- Output information about the current bisect session. + +--doc]]-- + +local bisect_status = function () +end + +local bisect_modes = { + start = bisect_start, + good = function () bisect_set "good" end, + bad = function () bisect_set "bad" end, + stop = bisect_stop, + status = bisect_status, +} + +actions.bisect = function (job) + local mode = job.bisect + local runner = bisect_modes[mode] + if not runner then + report ("info", 0, "cache", "Unknown bisect directive %q.", mode) + return false, false + end + return true, false +end + actions.flush = function (job) local success = names.flush_lookup_cache() if success then @@ -1076,6 +1131,7 @@ local process_cmdline = function ( ) -- unit -> jobspec criterion = "", query = "", log_level = 0, --- 2 is approx. the old behavior + bisect = nil, } local long_options = { @@ -1097,6 +1153,7 @@ local process_cmdline = function ( ) -- unit -> jobspec ["local"] = "L", log = 1, ["max-fonts"] = 1, + ["bisect"] = 1, ["no-reload"] = "n", ["no-strip"] = 0, ["skip-read"] = "R", @@ -1211,6 +1268,9 @@ local process_cmdline = function ( ) -- unit -> jobspec luaotfloadconfig.max_fonts = n end end + elseif v == "bisect" then + result.bisect = optarg[n] + actions_pending.bisect = true end end -- cgit v1.2.3 From a026ac6112b4a22aedc9c20a867da02afb734f56 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 23 Mar 2014 14:40:21 +0100 Subject: [db,tool] add bisection initialization routines --- src/luaotfload-database.lua | 22 +++++++++++- src/luaotfload-tool.lua | 87 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 17cb4fd..3cccc17 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -2922,7 +2922,9 @@ end --- dbobj -> dbobj -> int -> int -> string * bool list local collect_font_filenames = function () - --- + + report ("info", 4, "db", "Scanning the filesystem for font files.") + local filenames = { } tableappend (filenames, collect_font_filenames_texmf ()) tableappend (filenames, collect_font_filenames_system ()) @@ -2932,6 +2934,23 @@ local collect_font_filenames = function () return filenames end +--[[doc + + count_font_files -- Return the number of files found by + collect_font_filenames. This function is exported primarily + for use with luaotfload-tool.lua in bisect mode. + +--doc]]-- + +--- unit -> int +local count_font_files = function () + report ("info", 4, "db", "Counting font files.") + if not p_blacklist then + read_blacklist () + end + return #collect_font_filenames () +end + --- dbobj -> stats local collect_statistics = function (mappings) @@ -3459,6 +3478,7 @@ names.read_blacklist = read_blacklist names.sanitize_fontname = sanitize_fontname names.getfilename = resolve_fullpath names.set_location_precedence = set_location_precedence +names.count_font_files = count_font_files --- font cache names.purge_cache = purge_cache diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 03e7db3..5c7cb1d 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -48,6 +48,7 @@ kpse.set_program_name "luatex" local ioopen = io.open local iowrite = io.write local kpsefind_file = kpse.find_file +local mathfloor = math.floor local next = next local osdate = os.date local ostype = os.type @@ -113,8 +114,11 @@ config.lualibs.prefer_merged = true config.lualibs.load_extended = true require "lualibs" -local tabletohash = table.tohash +local iosavedata = io.savedata +local lfsisfile = lfs.isfile local stringsplit = string.split +local tableserialize = table.serialize +local tabletohash = table.tohash --[[doc-- \fileent{luatex-basics-gen.lua} calls functions from the @@ -256,7 +260,7 @@ local help_msg = function (version) names_gzip, names_bin, caches.getwritablepath ( - luaotfloadconfig.cache_dir))) + luaotfloadconfig.cache_dir))) end local about = [[ @@ -288,7 +292,7 @@ local head_adornchars = { } local textwidth = 80 -local wd_leftcolumn = math.floor(textwidth * .25) +local wd_leftcolumn = mathfloor(textwidth * .25) local key_fmt = stringformat([[%%%ds]], wd_leftcolumn) local val_fmt = [[%s]] local fieldseparator = ":" @@ -771,13 +775,56 @@ actions.generate = function (job) return false, false end +------------------------------------------------------------------------------- +--- bisect mode +------------------------------------------------------------------------------- + +local bisect_status_path = caches.getwritablepath "bisect" +local bisect_status_file = bisect_status_path .."/" .. "luaotfload-bisect-status.lua" +local bisect_status_fmt = [[ +--[==[------------------------------------------------------------------------- + This file is generated by Luaotfload. It can be safely deleted. + Creation date: %s. +-------------------------------------------------------------------------]==]-- + +%s + +--- vim:ft=lua:ts=8:et:sw=2 +]] + +local write_bisect_status = function (data) + local payload = tableserialize (data, true) + local status = stringformat (bisect_status_fmt, + osdate ("%Y-%m-d %H:%M:%S", os.time ()), + payload) + if status and iosavedata (bisect_status_file, status) then + report ("info", 4, "bisect", + "Bisection state written to %s.", bisect_status_file) + return true + end + report ("info", 0, "bisect", + "Failed to write bisection state to %s.", bisect_status_file) + return false +end + --[[doc-- bisect_start -- Begin a bisect session. Determines the number of fonts and sets the initial high, low, and pivot values. + --doc]]-- local bisect_start = function () + report ("info", 2, "bisect", + "Starting bisection of font database %q.", bisect_status_file) + local n = names.count_font_files () + local pivot = mathfloor (n / 2) + local data = { { 1, n, pivot } } + report ("info", 0, "bisect", "Initializing pivot to %d.", pivot) + if write_bisect_status (data) then + return true + end + return false end --[[doc-- @@ -806,6 +853,25 @@ end --doc]]-- local bisect_status = function () + report ("info", 4, "bisect", "Testing for status file: %q.", bisect_status_file) + if not lfsisfile (bisect_status_file) then + report ("info", 2, "bisect", "No such file: %q.", bisect_status_file) + report ("info", 0, "bisect", "Not in bisect mode.") + return true + end + report ("info", 4, "bisect", "Reading status file: %q.", bisect_status_file) + local status = pcall (dofile, bisect_status_file) + if not status then + report ("info", 0, "bisect", "Could not read status file.") + return false + end + report ("info", 0, "bisect", "Bisecting through %d font files.", #status) + for i = #status, 1, -1 do + local step = status[i] + report ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.", + i, unpack (step)) + end + return true end local bisect_modes = { @@ -820,10 +886,13 @@ actions.bisect = function (job) local mode = job.bisect local runner = bisect_modes[mode] if not runner then - report ("info", 0, "cache", "Unknown bisect directive %q.", mode) + report ("info", 0, "bisect", "Unknown directive %q.", mode) return false, false end - return true, false + if runner (job) then + return true, false + end + return false, false end actions.flush = function (job) @@ -1270,7 +1339,7 @@ local process_cmdline = function ( ) -- unit -> jobspec end elseif v == "bisect" then result.bisect = optarg[n] - actions_pending.bisect = true + action_pending.bisect = true end end @@ -1299,16 +1368,16 @@ local main = function ( ) -- unit -> int if not success then report (false, 0, "util", - "Could not finish task", "%s", actionname) + "Failed to execute task.", "%s", actionname) retval = -1 exit = true elseif not continue then report (false, 3, "util", - "Task completed, exiting", "%s", actionname) + "Task completed, exiting.", "%s", actionname) exit = true else report (false, 3, "util", - "Task completed successfully", "%s", actionname) + "Task completed successfully.", "%s", actionname) end end if exit then break end -- cgit v1.2.3 From c28953f53ada2a3ba5eb0891d0bb59d28d2cef51 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 23 Mar 2014 14:48:54 +0100 Subject: [tool] add stop directive to bisect mode --- src/luaotfload-tool.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 5c7cb1d..4c915cf 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -115,6 +115,7 @@ config.lualibs.load_extended = true require "lualibs" local iosavedata = io.savedata +local lfsisdir = lfs.isdir local lfsisfile = lfs.isfile local stringsplit = string.split local tableserialize = table.serialize @@ -834,6 +835,27 @@ end --doc]]-- local bisect_stop = function () + report ("info", 3, "bisect", "Erasing bisection state at %s.", bisect_status_file) + if lfsisfile (bisect_status_file) then + local success, msg = os.remove (bisect_status_file) + if not success then + report ("info", 2, "bisect", + "Failed to erase file %s (%s).", + bisect_status_file, msg) + end + end + if lfsisdir (bisect_status_path) then + local success, msg = os.remove (bisect_status_path) + if not success then + report ("info", 2, "bisect", + "Failed to erase directory %s (%s).", + bisect_status_path, msg) + end + end + if lfsisfile (bisect_status_file) then + return false + end + return true end --[[doc-- -- cgit v1.2.3 From 22dc6f817a3ec342996359fd3b39acea65601c53 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 23 Mar 2014 15:15:49 +0100 Subject: [tool] reimplement bisect status directive --- src/luaotfload-tool.lua | 55 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 4c915cf..6f1f0a1 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -793,6 +793,13 @@ local bisect_status_fmt = [[ --- vim:ft=lua:ts=8:et:sw=2 ]] +--[[doc-- + + write_bisect_status -- Write the history of the current bisection to disk. + +--doc]]-- + +--- state list -> bool local write_bisect_status = function (data) local payload = tableserialize (data, true) local status = stringformat (bisect_status_fmt, @@ -808,6 +815,29 @@ local write_bisect_status = function (data) return false end +--[[doc-- + + read_bisect_status -- Read the bisect log from disk. + +--doc]]-- + +--- unit -> state list +local read_bisect_status = function () + report ("info", 4, "bisect", "Testing for status file: %q.", bisect_status_file) + if not lfsisfile (bisect_status_file) then + report ("info", 2, "bisect", "No such file: %q.", bisect_status_file) + report ("info", 0, "bisect", "Not in bisect mode.") + return false + end + report ("info", 4, "bisect", "Reading status file: %q.", bisect_status_file) + local success, status = pcall (dofile, bisect_status_file) + if not success then + report ("info", 0, "bisect", "Could not read status file.") + return false + end + return status +end + --[[doc-- bisect_start -- Begin a bisect session. Determines the number of @@ -875,24 +905,21 @@ end --doc]]-- local bisect_status = function () - report ("info", 4, "bisect", "Testing for status file: %q.", bisect_status_file) - if not lfsisfile (bisect_status_file) then - report ("info", 2, "bisect", "No such file: %q.", bisect_status_file) - report ("info", 0, "bisect", "Not in bisect mode.") - return true - end - report ("info", 4, "bisect", "Reading status file: %q.", bisect_status_file) - local status = pcall (dofile, bisect_status_file) + local status = read_bisect_status () if not status then - report ("info", 0, "bisect", "Could not read status file.") return false end - report ("info", 0, "bisect", "Bisecting through %d font files.", #status) - for i = #status, 1, -1 do - local step = status[i] - report ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.", - i, unpack (step)) + local nsteps = #status + if nsteps > 1 then + for i = nsteps - 1, 1, -1 do + local step = status[i] + report ("info", 2, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.", + i, unpack (step)) + end end + local current = status[nsteps] + report ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.", + nsteps, unpack (current)) return true end -- cgit v1.2.3 From 68d04a338b74a585e12df7ab3200cda389b7c964 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 23 Mar 2014 16:52:27 +0100 Subject: [db,tool,main] implement run directive for bisection mode --- src/luaotfload-database.lua | 13 ++++++++++++- src/luaotfload-main.lua | 34 ++++++++++++++++++---------------- src/luaotfload-tool.lua | 40 +++++++++++++++++++++++++++++++++------- 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 3cccc17..de1594a 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -2920,17 +2920,28 @@ order_design_sizes = function (families) return families end ---- dbobj -> dbobj -> int -> int -> string * bool list +--[[doc-- + + collect_font_filenames -- Scan the three search path categories for + font files. This constitutes the first pass of the update mode. + +--doc]]-- + +--- unit -> string * bool list local collect_font_filenames = function () report ("info", 4, "db", "Scanning the filesystem for font files.") local filenames = { } + local bisect = luaotfloadconfig.bisect tableappend (filenames, collect_font_filenames_texmf ()) tableappend (filenames, collect_font_filenames_system ()) if luaotfloadconfig.scan_local == true then tableappend (filenames, collect_font_filenames_local ()) end + if bisect then + return { unpack (filenames, bisect[1], bisect[2]) } + end return filenames end diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 3c4c770..fe4e792 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -49,20 +49,22 @@ luaotfload.log = luaotfload.log or { } config = config or { } config.luaotfload = config.luaotfload or { } -------.luaotfload.resolver = config.luaotfload.resolver or "normal" -config.luaotfload.resolver = config.luaotfload.resolver or "cached" -config.luaotfload.definer = config.luaotfload.definer or "patch" -config.luaotfload.loglevel = config.luaotfload.loglevel or 2 -config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter" -config.luaotfload.prioritize = config.luaotfload.prioritize or "sys" -config.luaotfload.names_dir = config.luaotfload.names_dir or "names" -config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts" -config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua" -config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont" -config.luaotfload.scan_local = config.luaotfload.scan_local == true - -if config.luaotfload.strip == nil then - config.luaotfload.strip = true +local luaotfloadconfig = config.luaotfload +----------------.resolver = luaotfloadconfig.resolver or "normal" +luaotfloadconfig.resolver = luaotfloadconfig.resolver or "cached" +luaotfloadconfig.definer = luaotfloadconfig.definer or "patch" +luaotfloadconfig.bisect = false --- useless when running TeX +luaotfloadconfig.loglevel = luaotfloadconfig.loglevel or 2 +luaotfloadconfig.color_callback = luaotfloadconfig.color_callback or "pre_linebreak_filter" +luaotfloadconfig.prioritize = luaotfloadconfig.prioritize or "sys" +luaotfloadconfig.names_dir = luaotfloadconfig.names_dir or "names" +luaotfloadconfig.cache_dir = luaotfloadconfig.cache_dir or "fonts" +luaotfloadconfig.index_file = luaotfloadconfig.index_file or "luaotfload-names.lua" +luaotfloadconfig.formats = luaotfloadconfig.formats or "otf,ttf,ttc,dfont" +luaotfloadconfig.scan_local = luaotfloadconfig.scan_local == true + +if luaotfloadconfig.strip == nil then + luaotfloadconfig.strip = true end luaotfload.module = { @@ -151,7 +153,7 @@ loadmodule "log.lua" --- messages; used to be part of -override local log = luaotfload.log local report = log.report -log.set_loglevel(config.luaotfload.loglevel) +log.set_loglevel(luaotfloadconfig.loglevel) --[[doc-- @@ -689,7 +691,7 @@ reset_callback "define_font" --doc]]-- -local font_definer = config.luaotfload.definer +local font_definer = luaotfloadconfig.definer if font_definer == "generic" then add_to_callback("define_font", diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 6f1f0a1..e3f4516 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -96,6 +96,7 @@ config = config or { } local config = config local luaotfloadconfig = config.luaotfload or { } config.luaotfload = luaotfloadconfig +luaotfloadconfig.bisect = false luaotfloadconfig.version = luaotfloadconfig.version or version luaotfloadconfig.names_dir = luaotfloadconfig.names_dir or "names" luaotfloadconfig.cache_dir = luaotfloadconfig.cache_dir or "fonts" @@ -855,7 +856,7 @@ local bisect_start = function () if write_bisect_status (data) then return true end - return false + return false, false end --[[doc-- @@ -885,7 +886,7 @@ local bisect_stop = function () if lfsisfile (bisect_status_file) then return false end - return true + return true, false end --[[doc-- @@ -920,7 +921,34 @@ local bisect_status = function () local current = status[nsteps] report ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.", nsteps, unpack (current)) - return true + return true, false +end + +--[[doc-- + + bisect_run -- Run Luaotfload utilizing the current bisection state. + This should be combined with the --update mode, possibly with the + --force option. + + Luaotfload always tests the segment below the pivot first. + +--doc]]-- + +local bisect_run = function () + local status = read_bisect_status () + if not status then + return false + end + local nsteps = #status + local currentstep = nsteps + 1 + local current = status[nsteps] + local lo, hi, pivot = unpack (current) + report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.", + nsteps, lo, hi, pivot) + report ("info", 1, "bisect", "Step %d: Testing fonts from %d to %d.", + currentstep, lo, pivot) + luaotfloadconfig.bisect = { lo, pivot } + return true, true end local bisect_modes = { @@ -929,6 +957,7 @@ local bisect_modes = { bad = function () bisect_set "bad" end, stop = bisect_stop, status = bisect_status, + run = bisect_run, } actions.bisect = function (job) @@ -938,10 +967,7 @@ actions.bisect = function (job) report ("info", 0, "bisect", "Unknown directive %q.", mode) return false, false end - if runner (job) then - return true, false - end - return false, false + return runner (job) end actions.flush = function (job) -- cgit v1.2.3 From c160c40265b5eb5670730906614997ad2545f94d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 23 Mar 2014 17:21:35 +0100 Subject: [tool] implement good/bad directives for bisection mode --- src/luaotfload-tool.lua | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index e3f4516..9529a4d 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -894,9 +894,42 @@ end bisect_set -- Prepare the next bisection step by setting high, low, and pivot to new values. + The “run” directive always picks the segment below the pivot so we + can rely on the “outcome parameter” to be referring to that. + --doc]]-- local bisect_set = function (outcome) + local status = read_bisect_status () + if not status then + return false, false + end + + local nsteps = #status + local lo, hi, pivot = unpack (status[nsteps]) + report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.", + nsteps, lo, hi, pivot) + + if outcome == "bad" then + hi = pivot + pivot = mathfloor ((lo + hi) / 2) + report ("info", 0, "bisect", + "Continuing with the lower segment: lo=%d, hi=%d, pivot=%d.", + lo, hi, pivot) + elseif outcome == "good" then + lo = pivot + pivot = mathfloor ((lo + hi) / 2) + report ("info", 0, "bisect", + "Continuing with the upper segment: lo=%d, hi=%d, pivot=%d.", + lo, hi, pivot) + else -- can’t happen + report ("info", 0, "bisect", "What the hell?", lo, hi, pivot) + return false, false + end + + status[nsteps + 1] = { lo, hi, pivot } + write_bisect_status (status) + return true, false end --[[doc-- @@ -908,7 +941,7 @@ end local bisect_status = function () local status = read_bisect_status () if not status then - return false + return false, false end local nsteps = #status if nsteps > 1 then @@ -953,8 +986,8 @@ end local bisect_modes = { start = bisect_start, - good = function () bisect_set "good" end, - bad = function () bisect_set "bad" end, + good = function () return bisect_set "good" end, + bad = function () return bisect_set "bad" end, stop = bisect_stop, status = bisect_status, run = bisect_run, -- cgit v1.2.3 From 2d7567b653cabaa0d259996a47227cefa9b54563 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 23 Mar 2014 17:57:42 +0100 Subject: [db,tool] implement termination of bisect session --- src/luaotfload-database.lua | 17 +++++++++++++++++ src/luaotfload-tool.lua | 27 ++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index de1594a..a369d8e 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -2945,6 +2945,22 @@ local collect_font_filenames = function () return filenames end +--[[doc-- + + nth_font_file -- Return the filename of the nth font. + +--doc]]-- + +--- int -> string +local nth_font_filename = function (n) + report ("info", 4, "db", "Picking the %d font file.", n) + if not p_blacklist then + read_blacklist () + end + local filenames = collect_font_filenames () + return filenames[n] and filenames[n][1] or "" +end + --[[doc count_font_files -- Return the number of files found by @@ -3490,6 +3506,7 @@ names.sanitize_fontname = sanitize_fontname names.getfilename = resolve_fullpath names.set_location_precedence = set_location_precedence names.count_font_files = count_font_files +names.nth_font_filename = nth_font_filename --- font cache names.purge_cache = purge_cache diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 9529a4d..2301636 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -889,6 +889,21 @@ local bisect_stop = function () return true, false end +--[[doc-- + + bisect_terminate -- Wrap up a bisect session by printing the + offending font and removing the state file. + +--doc]]-- + +local bisect_terminate = function (nsteps, culprit) + report ("info", 1, "bisect", + "Bisection completed after %d steps.", nsteps) + report ("info", 0, "bisect", + "Bad file: %s.", names.nth_font_filename (culprit)) + return bisect_stop () +end + --[[doc-- bisect_set -- Prepare the next bisection step by setting high, low, @@ -911,13 +926,19 @@ local bisect_set = function (outcome) nsteps, lo, hi, pivot) if outcome == "bad" then - hi = pivot + hi = pivot + if lo >= hi then --- complete + return bisect_terminate (nsteps, lo) + end pivot = mathfloor ((lo + hi) / 2) report ("info", 0, "bisect", "Continuing with the lower segment: lo=%d, hi=%d, pivot=%d.", lo, hi, pivot) elseif outcome == "good" then - lo = pivot + lo = pivot + 1 + if lo >= hi then --- complete + return bisect_terminate (nsteps, lo) + end pivot = mathfloor ((lo + hi) / 2) report ("info", 0, "bisect", "Continuing with the upper segment: lo=%d, hi=%d, pivot=%d.", @@ -987,7 +1008,7 @@ end local bisect_modes = { start = bisect_start, good = function () return bisect_set "good" end, - bad = function () return bisect_set "bad" end, + bad = function () return bisect_set "bad" end, stop = bisect_stop, status = bisect_status, run = bisect_run, -- cgit v1.2.3 From 82a06a10cef0ab4ee3fbe8c83facb1b017fbf6ef Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 23 Mar 2014 21:58:16 +0100 Subject: [db,tool] display list of remaining files if less than ten --- src/luaotfload-database.lua | 22 +++++++++++++++++++++- src/luaotfload-tool.lua | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index a369d8e..e4188dc 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -2953,7 +2953,7 @@ end --- int -> string local nth_font_filename = function (n) - report ("info", 4, "db", "Picking the %d font file.", n) + report ("info", 4, "db", "Picking font file no. %d.", n) if not p_blacklist then read_blacklist () end @@ -2961,6 +2961,25 @@ local nth_font_filename = function (n) return filenames[n] and filenames[n][1] or "" end +--[[doc-- + + font_slice -- Return the fonts in the range from lo to hi. + +--doc]]-- + +local font_slice = function (lo, hi) + report ("info", 4, "db", "Retrieving font files nos. %d--%d.", lo, hi) + if not p_blacklist then + read_blacklist () + end + local filenames = collect_font_filenames () + local result = { } + for i = lo, hi do + result[#result + 1] = filenames[i][1] + end + return result +end + --[[doc count_font_files -- Return the number of files found by @@ -3507,6 +3526,7 @@ names.getfilename = resolve_fullpath names.set_location_precedence = set_location_precedence names.count_font_files = count_font_files names.nth_font_filename = nth_font_filename +names.font_slice = font_slice --- font cache names.purge_cache = purge_cache diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 2301636..60d2c2c 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -904,6 +904,21 @@ local bisect_terminate = function (nsteps, culprit) return bisect_stop () end +--[[doc-- + + list_remainder -- Show remaining fonts in bisect slice. + +--doc]]-- + +local list_remainder = function (lo, hi) + local fonts = names.font_slice (lo, hi) + report ("info", 0, "bisect", "%d fonts left.", hi - lo) + for i = 1, #fonts do + report ("info", 1, "bisect", " · %2d: %s", lo, fonts[i]) + lo = lo + 1 + end +end + --[[doc-- bisect_set -- Prepare the next bisection step by setting high, low, @@ -950,6 +965,9 @@ local bisect_set = function (outcome) status[nsteps + 1] = { lo, hi, pivot } write_bisect_status (status) + if hi - lo <= 10 then + list_remainder (lo, hi) + end return true, false end -- cgit v1.2.3 From 2d555b40fc580e3ef9328a1f47544d4286b8b860 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 24 Mar 2014 07:55:34 +0100 Subject: [db,tool] reconcile --bisect with --max-fonts --- src/luaotfload-database.lua | 10 ++++++++-- src/luaotfload-tool.lua | 38 ++++++++++++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index e4188dc..936e380 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -2335,7 +2335,6 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run) local nfiles = #files local nnew = 0 - local max_fonts = luaotfloadconfig.max_fonts or 2^51 report ("info", 1, "db", "Scanning %d collected font files ...", nfiles) @@ -2344,7 +2343,7 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run) , system = { 0, 0 } } report_status_start (2, 4) - for i = 1, (nfiles < max_fonts) and nfiles or max_fonts do + for i = 1, nfiles do local fullname, location = unpack (files[i]) local count = bylocation[location] count[1] = count[1] + 1 @@ -2934,11 +2933,18 @@ local collect_font_filenames = function () local filenames = { } local bisect = luaotfloadconfig.bisect + local max_fonts = luaotfloadconfig.max_fonts or 2^51 --- XXX revisit for lua 5.3 wrt integers + tableappend (filenames, collect_font_filenames_texmf ()) tableappend (filenames, collect_font_filenames_system ()) if luaotfloadconfig.scan_local == true then tableappend (filenames, collect_font_filenames_local ()) end + --- Now drop everything above max_fonts. + if max_fonts < #filenames then + filenames = { unpack (filenames, 1, max_fonts) } + end + --- And choose the requested slice if in bisect mode. if bisect then return { unpack (filenames, bisect[1], bisect[2]) } end diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 60d2c2c..47e7ccc 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -847,6 +847,14 @@ end --doc]]-- local bisect_start = function () + if lfsisfile (bisect_status_file) then + report ("info", 0, "bisect", + "Bisect session in progress.", + bisect_status_file) + report ("info", 0, "bisect", + "Use --bisect=stop to erase it before starting over.") + return false, false + end report ("info", 2, "bisect", "Starting bisection of font database %q.", bisect_status_file) local n = names.count_font_files () @@ -854,7 +862,7 @@ local bisect_start = function () local data = { { 1, n, pivot } } report ("info", 0, "bisect", "Initializing pivot to %d.", pivot) if write_bisect_status (data) then - return true + return true, false end return false, false end @@ -884,7 +892,7 @@ local bisect_stop = function () end end if lfsisfile (bisect_status_file) then - return false + return false, false end return true, false end @@ -901,7 +909,9 @@ local bisect_terminate = function (nsteps, culprit) "Bisection completed after %d steps.", nsteps) report ("info", 0, "bisect", "Bad file: %s.", names.nth_font_filename (culprit)) - return bisect_stop () + report ("info", 0, "bisect", + "Run with --bisect=stop to finish bisection.") + return true, false end --[[doc-- @@ -912,7 +922,7 @@ end local list_remainder = function (lo, hi) local fonts = names.font_slice (lo, hi) - report ("info", 0, "bisect", "%d fonts left.", hi - lo) + report ("info", 0, "bisect", "%d fonts left.", hi - lo + 1) for i = 1, #fonts do report ("info", 1, "bisect", " · %2d: %s", lo, fonts[i]) lo = lo + 1 @@ -936,13 +946,26 @@ local bisect_set = function (outcome) end local nsteps = #status - local lo, hi, pivot = unpack (status[nsteps]) + local previous = status[nsteps] + if previous == true then + --- Bisection already completed; we exit early through + --- bisect_terminate() to avoid further writes to the + --- state files that mess up step counting. + nsteps = nsteps - 1 + return bisect_terminate (nsteps, status[nsteps][1]) + end + + local lo, hi, pivot = unpack (previous) + report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.", nsteps, lo, hi, pivot) if outcome == "bad" then hi = pivot if lo >= hi then --- complete + status[nsteps + 1] = { lo, lo, lo } + status[nsteps + 1] = true + write_bisect_status (status) return bisect_terminate (nsteps, lo) end pivot = mathfloor ((lo + hi) / 2) @@ -952,6 +975,9 @@ local bisect_set = function (outcome) elseif outcome == "good" then lo = pivot + 1 if lo >= hi then --- complete + status[nsteps + 1] = { lo, lo, lo } + write_bisect_status (status) + status[nsteps + 1] = true return bisect_terminate (nsteps, lo) end pivot = mathfloor ((lo + hi) / 2) @@ -1009,7 +1035,7 @@ end local bisect_run = function () local status = read_bisect_status () if not status then - return false + return false, false end local nsteps = #status local currentstep = nsteps + 1 -- cgit v1.2.3 From e2966b1932f0cb00da4acf6dc8678366dd180e62 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 25 Mar 2014 07:55:47 +0100 Subject: [fontloader] sync with Context as of 2014-03-25 --- src/luaotfload-fontloader.lua | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 655aedf..ca538df 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 03/22/14 15:31:38 +-- merge date : 03/25/14 02:17:04 do -- begin closure to overcome local limits and interference @@ -11499,17 +11499,20 @@ local autofeatures=fonts.analyzers.features local function initialize(sequence,script,language,enabled) local features=sequence.features if features then - local order=features.order - for i=1,#order do - local kind=order[i] - local valid=enabled[kind] - if valid then - local scripts=features[kind] - local languages=scripts[script] or scripts[wildcard] - if languages and (languages[language] or languages[wildcard]) then - return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence } + local order=sequence.order + if order then + for i=1,#order do + local kind=order[i] + local valid=enabled[kind] + if valid then + local scripts=features[kind] + local languages=scripts[script] or scripts[wildcard] + if languages and (languages[language] or languages[wildcard]) then + return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence } + end end end + else end end return false -- cgit v1.2.3 From 0e84e53f47e859ff8d4617747a9075947e79c017 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 27 Mar 2014 08:21:10 +0100 Subject: [tool] fix final stage of bisection --- src/luaotfload-tool.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 47e7ccc..41e61f2 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -964,7 +964,7 @@ local bisect_set = function (outcome) hi = pivot if lo >= hi then --- complete status[nsteps + 1] = { lo, lo, lo } - status[nsteps + 1] = true + status[nsteps + 2] = true write_bisect_status (status) return bisect_terminate (nsteps, lo) end @@ -977,7 +977,7 @@ local bisect_set = function (outcome) if lo >= hi then --- complete status[nsteps + 1] = { lo, lo, lo } write_bisect_status (status) - status[nsteps + 1] = true + status[nsteps + 2] = true return bisect_terminate (nsteps, lo) end pivot = mathfloor ((lo + hi) / 2) @@ -1040,6 +1040,9 @@ local bisect_run = function () local nsteps = #status local currentstep = nsteps + 1 local current = status[nsteps] + if current == true then -- final step + current = status[nsteps - 1] + end local lo, hi, pivot = unpack (current) report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.", nsteps, lo, hi, pivot) -- cgit v1.2.3 From d0d63530acf762f080e8c6255949398a918bf0bc Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 30 Mar 2014 11:54:45 +0200 Subject: [doc] workaround for inline code in tables --- doc/luaotfload-latex.tex | 6 ++---- doc/luaotfload-main.tex | 15 +++++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index 409adcf..c886462 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -208,12 +208,10 @@ \usepackage {listings} \lstset { basicstyle=\ttfamily, - escapechar=Ö, } -\def \inlinecode #1{% - \lstinline {#1}% -} +%\let \inlinecode \lstinline +\protected \def \inlinecode {\lstinline} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% codelistings; this sucks hard since we lack access to buffers diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 5b033c0..5e7c1bc 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -757,12 +757,15 @@ Currently (2014) there are three of them: Applies legacy \TEX ligatures: \unless \ifcontextmkiv + %% Using braced arg syntax with inline code appears to be + %% impossible within Latex tables -- just ignore the weird + %% exclamation points below. \begintabulate [rlrl] - \beginrow `` \newcell \inlinecode {``} \newcell '' \newcell \inlinecode {''} \endrow - \beginrow ` \newcell \inlinecode {`} \newcell ' \newcell \inlinecode {'} \endrow - \beginrow " \newcell \inlinecode {"} \newcell -- \newcell \inlinecode {--} \endrow - \beginrow --- \newcell \inlinecode {---} \newcell !` \newcell \inlinecode {!`} \endrow - \beginrow ?` \newcell \inlinecode {?`} \newcell \newcell \endrow + \beginrow `` \newcell {\inlinecode !``! } \newcell '' \newcell {\inlinecode !''!} \endrow + \beginrow ` \newcell {\inlinecode !`! } \newcell ' \newcell {\inlinecode !'! } \endrow + \beginrow " \newcell {\inlinecode !"! } \newcell -- \newcell {\inlinecode !--!} \endrow + \beginrow --- \newcell {\inlinecode !---!} \newcell !` \newcell {\inlinecode ?!`?} \endrow + \beginrow ?` \newcell {\inlinecode !?`! } \newcell \newcell \endrow \endtabulate \else %% XXX find a way to wrap these in the tabulate environment @@ -911,7 +914,7 @@ directories. \begincentered \begintabulate [lp{.5\textwidth}] \beginrow - Windows \newcell \inlinecode {\% WINDIR\%\\ Fonts} + Windows \newcell \inlinecode !\% WINDIR\%\\ Fonts! \endrow \beginrow Linux \newcell \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break -- cgit v1.2.3 From 9005dd121c14e97b2fe001a5543596047a9c141b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 30 Mar 2014 12:01:33 +0200 Subject: =?UTF-8?q?[doc]=20move=20up=20footnote=20concerning=20the=20forme?= =?UTF-8?q?r=20=E2=80=9Ctrep=E2=80=9D=20set?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/luaotfload-main.tex | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index 5e7c1bc..a0df7f0 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -754,7 +754,14 @@ Currently (2014) there are three of them: \endaltitem \beginaltitem {tlig} - Applies legacy \TEX ligatures: + Applies legacy \TEX ligatures\footnote{% + These contain the feature set \inlinecode {trep} of earlier + versions of \identifier{luaotfload}. + + Note to \XETEX users: this is the equivalent of the + assignment \inlinecode {mapping=text-tex} using \XETEX's input + remapping feature. + }: \unless \ifcontextmkiv %% Using braced arg syntax with inline code appears to be @@ -781,15 +788,6 @@ Currently (2014) there are three of them: \stopframed \stopframed \fi - - \footnote{% - These contain the feature set \inlinecode {trep} of earlier - versions of \identifier{luaotfload}. - - Note to \XETEX users: this is the equivalent of the - assignment \inlinecode {mapping=text-tex} using \XETEX's input - remapping feature. - } \endaltitem \beginaltitem {itlc} -- cgit v1.2.3 From 086a3111ea33c65de1e527d4e21e403d489eec23 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 30 Mar 2014 12:39:45 +0200 Subject: [doc] document bisection in manpage --- doc/luaotfload-tool.rst | 66 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/doc/luaotfload-tool.rst b/doc/luaotfload-tool.rst index 761b0ec..da88b21 100644 --- a/doc/luaotfload-tool.rst +++ b/doc/luaotfload-tool.rst @@ -6,16 +6,16 @@ generate and query the Luaotfload font names database ----------------------------------------------------------------------- -:Date: 2014-01-02 -:Copyright: GPL v2.0 -:Version: 2.5 -:Manual section: 1 -:Manual group: text processing +:Date: 2014-03-30 +:Copyright: GPL v2.0 +:Version: 2.5 +:Manual section: 1 +:Manual group: text processing SYNOPSIS ======================================================================= -**luaotfload-tool** [ -bDfFiIlnpquvVhw ] +**luaotfload-tool** [ -bcDfFiIlLnpqRSuvVhw ] **luaotfload-tool** --update [ --force ] [ --quiet ] [ --verbose ] [ --prefer-texmf ] [ --dry-run ] @@ -32,6 +32,8 @@ SYNOPSIS **luaotfload-tool** --list=CRITERION[:VALUE] [ --fields=F1,F2,...,Fn ] +**luaotfload-tool** --bisect=DIRECTIVE + **luaotfload-tool** --help **luaotfload-tool** --version @@ -187,6 +189,58 @@ font and lookup caches cache; 3) ``show`` -> print stats. +bisection +----------------------------------------------------------------------- +--bisect=DIRECTIVE Bisection of the font database. + This mode is intended as assistance in + debugging the Luatex engine, especially when + tracking memleaks or buggy fonts. + + *DIRECTIVE* can be one of the following: + + 1) ``run`` -> Make ``luaotfload-tool`` respect + the bisection progress when running. + Combined with ``--update`` and possibly + ``--force`` this will only process the files + from the start up until the pivot and ignore + the rest. + 2) ``start`` -> Start bisection: create a + bisection state file and initialize the low, + high, and pivot indices. + 3) ``stop`` -> Terminate the current bisection + session by deleting the state file. + 4) ``good`` | ``bad`` -> Mark the section + processed last as “good” or “bad”, + respectively. The next bisection step will + continue with the bad section. + 5) ``status`` -> Print status information about + the current bisection session. Hint: Use + with higher verbosity settings for more + output. + + A bisection session is initiated by issuing the + ``start`` directive. This sets the pivot to the + middle of the list of available font files. + Now run *luaotfload-tool* with the ``--update`` + flag set as well as ``--bisect=run``: only the + fonts up to the pivot will be considered. If + that task exhibited the issue you are tracking, + then tell Luaotfload using ``--bisect=bad``. + The next step of ``--bisect=run`` will continue + bisection with the part of the files below the + pivot. + Likewise, issue ``--bisect=good`` in order to + continue with the fonts above the pivot, + assuming the tested part of the list did not + trigger the bug. + + Once the culprit font is tracked down, ``good`` + or ``bad`` will have no effect anymore. ``run`` + will always end up processing the single font + file that was left. + Use ``--bisect=stop`` to clear the bisection + state. + miscellaneous ----------------------------------------------------------------------- --verbose=N, -v Set verbosity level to *n* or the number of -- cgit v1.2.3 From 1d53adbda253158348c4e921357114140a54c127 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 30 Mar 2014 12:44:57 +0200 Subject: [doc] group debugging related functionality in manpage --- doc/luaotfload-tool.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/luaotfload-tool.rst b/doc/luaotfload-tool.rst index da88b21..4b1a934 100644 --- a/doc/luaotfload-tool.rst +++ b/doc/luaotfload-tool.rst @@ -21,7 +21,7 @@ SYNOPSIS [ --prefer-texmf ] [ --dry-run ] [ --formats=[+|-]EXTENSIONS ] [ --no-compress ] [ --no-strip ] - [ --local ] + [ --local ] [ --max-fonts=N ] **luaotfload-tool** --find=FONTNAME [ --fuzzy ] [ --info ] [ --inspect ] [ --no-reload ] @@ -69,10 +69,6 @@ update mode with every invocation of ``luaotfload-tool``. --no-reload, -n Suppress auto-updates to the database (e.g. when ``--find`` is passed an unknown name). ---no-strip Do not strip redundant information after - building the database. Warning: this will - inflate the index to about two to three times - the normal size. --no-compress, -c Do not filter the plain text version of the font index through gzip. Useful for debugging if your editor is built without zlib. @@ -80,8 +76,6 @@ update mode --prefer-texmf, -p Organize the file name database in a way so that it prefer fonts in the *TEXMF* tree over system fonts if they are installed in both. ---max-fonts=N Process at most *N* font files, including fonts - already indexed in the count. --formats=EXTENSIONS Extensions of the font files to index. Where *EXTENSIONS* is a comma-separated list of supported file extensions (otf, ttf, ttc, @@ -101,9 +95,6 @@ update mode grow the database considerably and slow down font indexing. ---dry-run, -D Don’t load fonts, scan directories only. - (For debugging file system related issues.) - query mode ----------------------------------------------------------------------- --find=NAME Resolve a font name; this looks up in @@ -127,7 +118,6 @@ query mode library (assumes ``-I``). Automatically enabled if the verbosity level exceeds 2. ---show-blacklist, -b Show blacklisted files (not directories). --list=CRITERION Show entries, where *CRITERION* is one of the following: @@ -189,8 +179,18 @@ font and lookup caches cache; 3) ``show`` -> print stats. -bisection +debugging methods ----------------------------------------------------------------------- +--show-blacklist, -b Show blacklisted files (not directories). +--dry-run, -D Don’t load fonts when updating the database; + scan directories only. + (For debugging file system related issues.) +--no-strip Do not strip redundant information after + building the database. Warning: this will + inflate the index to about two to three times + the normal size. +--max-fonts=N Process at most *N* font files, including fonts + already indexed in the count. --bisect=DIRECTIVE Bisection of the font database. This mode is intended as assistance in debugging the Luatex engine, especially when -- cgit v1.2.3 From 60478137d15bec36b66276a4bfbfe0f1b1f20269 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 30 Mar 2014 12:50:38 +0200 Subject: [tool] update usage and help messages to include bisection --- src/luaotfload-tool.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 41e61f2..1923040 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -222,6 +222,10 @@ Usage: %s [OPTIONS...] --fields=,,…, which fields to print with --list -b --show-blacklist show blacklisted files + --bisect= control database bisection: valid + directives are "start", "stop", "run", "status", + "good", "bad" + The font database will be saved to %s %s @@ -249,6 +253,8 @@ Usage: luaotfload-tool [--help] [--version] [--verbose=] [--list=] [--fields=] [--cache=] [--flush-lookups] [--show-blacklist] [--diagnose=] + [--no-compress] [--no-strip] [--local] + [--max-fonts=] [--bisect=] Enter 'luaotfload-tool --help' for a larger list of options. ]] -- cgit v1.2.3 From 6a6075b138019c1d8a48a1a063cd8f89aabe6de9 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 5 Apr 2014 14:31:51 +0200 Subject: [doc] update NEWS --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 1966c03..2aa319e 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,7 @@ Change History * Move logging routines from luaotfload-override in to luaotfload-log. * Scan local font files (``--local`` flag to luaotfload-tool, flag ``scan_local`` during TeX run). + * Add bisection mode (``--bisect``) to luaotfload-tool. 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) -- cgit v1.2.3 From e6a3a269577fb715079fa185e4d96f1a04b5e1ae Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 5 Apr 2014 14:33:02 +0200 Subject: [fontloader] sync with Context as of 2014-04-05 --- src/luaotfload-fontloader.lua | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index ca538df..fa61668 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 03/25/14 02:17:04 +-- merge date : 04/04/14 00:08:59 do -- begin closure to overcome local limits and interference @@ -3394,6 +3394,17 @@ function caches.loaddata(paths,name) for i=1,#paths do local data=false local luaname,lucname=makefullname(paths[i],name) + if lucname and not lfs.isfile(lucname) and type(caches.compile)=="function" then + texio.write(string.format("(compiling luc: %s)",lucname)) + data=loadfile(luaname) + if data then + data=data() + end + if data then + caches.compile(data,luaname,lucname) + return data + end + end if lucname and lfs.isfile(lucname) then texio.write(string.format("(load luc: %s)",lucname)) data=loadfile(lucname) @@ -5764,7 +5775,6 @@ unify=function(data,filename) if unicode then krn[unicode]=kern else - print(unicode,name) end end description.kerns=krn -- cgit v1.2.3 From f2408bde64eaa2d4081a394bca3991bb7f47dbd3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 6 Apr 2014 12:24:13 +0200 Subject: [db] update index structure documentation --- src/luaotfload-database.lua | 65 ++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 936e380..dc783d8 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -338,7 +338,6 @@ This is a sketch of the luaotfload db: status : filestatus; mappings : fontentry list; meta : metadata; - names : namedata; // TODO: check for relevance after db is finalized } and familytable = { local : (format, familyentry) hash; // specified with include dir @@ -346,10 +345,10 @@ This is a sketch of the luaotfload db: system : (format, familyentry) hash; } and familyentry = { - regular : sizes; - italic : sizes; - bold : sizes; - bolditalic : sizes; + r : sizes; // regular + i : sizes; // italic + b : sizes; // bold + bi : sizes; // bold italic } and sizes = { default : int; // points into mappings or names @@ -358,10 +357,10 @@ This is a sketch of the luaotfload db: and metadata = { local : bool; (* set if local fonts were added to the db *) formats : string list; // { "otf", "ttf", "ttc", "dfont" } - statistics : TODO; - version : float; + statistics : TODO; // created when built with "--stats" + version : float; // index version } - and filemap = { + and filemap = { // created by generate_filedata() base : { local : (string, int) hash; // basename -> idx system : (string, int) hash; @@ -374,36 +373,36 @@ This is a sketch of the luaotfload db: }; full : (int, string) hash; // idx -> full path } - and fontentry = { - barename : string; - familyname : string; - filename : string; - fontname : string; // <- metadata - fullname : string; // <- metadata - sanitized : { - family : string; - fontstyle_name : string; // <- new in 2.4 - fontname : string; // <- metadata - fullname : string; // <- namedata.names - metafamily : string; - pfullname : string; - prefmodifiers : string; - psname : string; - subfamily : string; - }; - size : int list; - slant : int; - subfont : int; - location : local | system | texmf; - weight : int; - width : int; - units_per_em : int; // mainly 1000, but also 2048 or 256 + and fontentry = { // finalized by collect_families() + basename : string; // file name without path "foo.otf" + conflicts : { barename : int; basename : int }; // filename conflict with font at index; happens with subfonts + familyname : string; // sanitized name of the font family the font belongs to, usually from the names table + fontname : string; // sanitized name of the font + fontstyle_name : string; // the fontstyle_name field returned by fontloader.info() + format : string; // "otf" | "ttf" | "dfont" | "pfa" | "pfb" | "afm" + fullname : string; // sanitized full name of the font including style modifiers + fullpath : string; // path to font in filesystem + index : int; // index in the mappings table + italicangle : float; // italic angle; non-zero with oblique faces + location : string; // "texmf" | "system" | "local" + metafamily : string; // alternative family identifier if appropriate, sanitized + plainname : string; // unsanitized font name + prefmodifiers : string; // sanitized preferred subfamily (names table 14) + psname : string; // PostScript name + size : (false | float * float * float); // if available, size info from the size table converted from decipoints + splainname : string; // sanitized version of the “plainname” field + splitstyle : string; // style information obtained by splitting the full name at the last dash + subfamily : string; // sanitized subfamily (names table 2) + subfont : (int | bool); // integer if font is part of a TrueType collection ("ttc") + version : string; // font version string + weight : int; // usWeightClass } and filestatus = (string, // fullname { index : int list; // pointer into mappings timestamp : int; }) dict -beware that this is a reconstruction and may be incomplete. +beware that this is a reconstruction and may be incomplete or out of +date. Last update: 2014-04-06, describing version 2.51. mtx-fonts has in names.tma: -- cgit v1.2.3 From 35523f88fccd87a52395d73570a13d56bdf177ca Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 16 Apr 2014 21:37:39 +0200 Subject: [fontloader] sync with Context as of 2014-04-16 --- src/luaotfload-fontloader.lua | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index fa61668..d4311b2 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 04/04/14 00:08:59 +-- merge date : 04/15/14 09:51:32 do -- begin closure to overcome local limits and interference @@ -3628,9 +3628,17 @@ local free_node=node.free local remove_node=node.remove local new_node=node.new local traverse_id=node.traverse_id -local math_code=nodecodes.math nodes.handlers.protectglyphs=node.protect_glyphs nodes.handlers.unprotectglyphs=node.unprotect_glyphs +local math_code=nodecodes.math +local end_of_math=node.end_of_math +function node.end_of_math(n) + if n.id==math_code and n.subtype==1 then + return n + else + return end_of_math(n) + end +end function nodes.remove(head,current,free_too) local t=current head,current=remove_node(head,current) @@ -3924,14 +3932,15 @@ constructors.sharefonts=false constructors.nofsharedfonts=0 local sharednames={} function constructors.trytosharefont(target,tfmdata) - if constructors.sharefonts then + if constructors.sharefonts then local characters=target.characters local n=1 local t={ target.psname } local u=sortedkeys(characters) for i=1,#u do + local k=u[i] n=n+1;t[n]=k - n=n+1;t[n]=characters[u[i]].index or k + n=n+1;t[n]=characters[k].index or k end local h=md5.HEX(concat(t," ")) local s=sharednames[h] -- cgit v1.2.3 From 0eabdd275af9d743f886907636188c5febbdbb50 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 19 Apr 2014 14:56:44 +0200 Subject: [misc] make valgrind suppressions more strict --- misc/valgrind-kpse-suppression.sup | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/misc/valgrind-kpse-suppression.sup b/misc/valgrind-kpse-suppression.sup index e1cc5f5..63dee22 100644 --- a/misc/valgrind-kpse-suppression.sup +++ b/misc/valgrind-kpse-suppression.sup @@ -1,7 +1,7 @@ { kpathsea-garbage-1 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:kpathsea_cnf_get } @@ -9,7 +9,7 @@ { kpathsea-garbage-2 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:kpse_program_basename } @@ -18,7 +18,7 @@ { kpathsea-garbage-3 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:kpse_find_file } @@ -27,7 +27,7 @@ { kpathsea-garbage-4 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:find_file } @@ -35,7 +35,7 @@ { kpathsea-garbage-5 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:lua_kpse_lookup } @@ -43,7 +43,7 @@ { kpathsea-garbage-6 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:find_file } @@ -52,7 +52,7 @@ { kpathsea-garbage-7 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:expand_path } @@ -60,7 +60,7 @@ { kpathsea-garbage-8 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:do_lua_kpathsea_lookup } @@ -69,7 +69,7 @@ { kpathsea-garbage-9 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:kpathsea_find_file } @@ -78,7 +78,7 @@ { kpathsea-garbage-10 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:kpathsea_init_db } @@ -87,7 +87,7 @@ { kpathsea-garbage-11 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:kpathsea_find_file_generic } @@ -96,7 +96,7 @@ { kpathsea-garbage-12 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:expand_var } @@ -105,7 +105,7 @@ { kpathsea-garbage-13 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:init_path } @@ -114,7 +114,7 @@ { kpathsea-garbage-14 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:kpse_in_name_ok } @@ -123,7 +123,7 @@ { kpathsea-garbage-15 Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: all ... fun:kpathsea_var_value } -- cgit v1.2.3 From d44fc0f4c351e3301d1573888faae922a94cb3e4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 21 Apr 2014 12:14:25 +0200 Subject: [misc] add valgrind suppression covering kpse_set_program_name --- misc/valgrind-kpse-suppression.sup | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/misc/valgrind-kpse-suppression.sup b/misc/valgrind-kpse-suppression.sup index 63dee22..dc32586 100644 --- a/misc/valgrind-kpse-suppression.sup +++ b/misc/valgrind-kpse-suppression.sup @@ -129,3 +129,12 @@ } +{ + kpathsea-garbage-16 + Memcheck:Leak + match-leak-kinds: all + ... + fun:kpse_set_program_name +} + + -- cgit v1.2.3 From 1189a46556c80c9d98b21e3f2dd71bf6a540b78e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 21 Apr 2014 14:10:42 +0200 Subject: [parsers] use less ambiguous identifier for locals --- src/luaotfload-database.lua | 2 +- src/luaotfload-parsers.lua | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index dc783d8..7165d07 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -2789,7 +2789,7 @@ end --[[doc-- - add_bold_spectrum -- For not-quite-bold faces, determine whether + group_modifiers -- For not-quite-bold faces, determine whether they can fill in for a missing bold face slot in a matching family. Some families like Lucida do not contain real bold / bold italic diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua index 1048e1d..c3c3843 100644 --- a/src/luaotfload-parsers.lua +++ b/src/luaotfload-parsers.lua @@ -39,7 +39,7 @@ local io = io local ioopen = io.open local log = luaotfload.log -local report = log.report +local logreport = log.report local string = string local stringsub = string.sub @@ -182,7 +182,7 @@ local p_cheapxml = header * root local fonts_conf_scanner = function (path) local fh = ioopen(path, "r") if not fh then - report("both", 3, "db", "Cannot open fontconfig file %s.", path) + logreport("both", 3, "db", "Cannot open fontconfig file %s.", path) return end local raw = fh:read"*all" @@ -190,7 +190,7 @@ local fonts_conf_scanner = function (path) local confdata = lpegmatch(p_cheapxml, raw) if not confdata then - report("both", 3, "db", "Cannot scan fontconfig file %s.", path) + logreport("both", 3, "db", "Cannot scan fontconfig file %s.", path) return end return confdata @@ -454,7 +454,7 @@ end --doc]]-- local handle_invalid_option = function (opt) - report("log", 0, "load", "font option %q unknown.", opt) + logreport("log", 0, "load", "font option %q unknown.", opt) return "", false end @@ -468,12 +468,12 @@ end local check_garbage = function (_,i, garbage) if stringfind(garbage, "/") then - report("log", 0, "load", --- ffs use path! - "warning: path in file: lookups is deprecated; ") - report("log", 0, "load", "use bracket syntax instead!") - report("log", 0, "load", - "position: %d; full match: %q", - i, garbage) + logreport("log", 0, "load", --- ffs use path! + "warning: path in file: lookups is deprecated; ") + logreport("log", 0, "load", "use bracket syntax instead!") + logreport("log", 0, "load", + "position: %d; full match: %q", + i, garbage) return true end return false -- cgit v1.2.3 From f8d43e62f3927545d06e39d01499442b0f64fe79 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 21 Apr 2014 16:48:02 +0200 Subject: [parsers] add INI file parser --- src/luaotfload-parsers.lua | 121 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua index c3c3843..a45fcb8 100644 --- a/src/luaotfload-parsers.lua +++ b/src/luaotfload-parsers.lua @@ -21,6 +21,8 @@ luaotfload = luaotfload or { } luaotfload.parsers = luaotfload.parsers or { } local parsers = luaotfload.parsers +local rawset = rawset + local lpeg = require "lpeg" local P, R, S = lpeg.P, lpeg.R, lpeg.S local lpegmatch = lpeg.match @@ -62,14 +64,22 @@ local semicolon = P";" local comma = P"," local noncomma = 1 - comma local slash = P"/" +local backslash = P"\\" local equals = P"=" +local dash = P"-" +local gartenzaun = P"#" local lbrk, rbrk = P"[", P"]" +local squote = P"'" +local dquote = P"\"" +local newline = P"\n" +local returnchar = P"\r" local spacing = S" \t\v" local linebreak = S"\n\r" local whitespace = spacing + linebreak local ws = spacing^0 local xmlws = whitespace^1 +local eol = P"\n\r" + P"\r\n" + linebreak local digit = R"09" local alpha = R("az", "AZ") @@ -339,7 +349,7 @@ parsers.splitcomma = splitcomma ------------------------------------------------------------------------------- ---- FONT REQUEST +--- FONT REQUEST ------------------------------------------------------------------------------- @@ -576,3 +586,112 @@ local font_request = Ct(path_lookup * (colon^-1 * features)^-1 luaotfload.parsers.font_request = font_request +------------------------------------------------------------------------------- +--- INI FILES +------------------------------------------------------------------------------- + +--[[doc-- + + Luaotfload uses the pervasive flavor of the INI files that allows '#' in + addition to ';' to indicate comment lines (see git-config(1) for a + description of the syntax we’re targeting). + +--doc]]-- + +local truth_ids = { + ["true"] = true, + ["1"] = true, + yes = true, + on = true, + ["false"] = false, + ["2"] = false, + no = false, + off = false, +} + +local maybe_cast = function (var) + local bool = truth_ids[var] + if bool ~= nil then + return bool + end + return tonumber (var) or var +end +local escape = function (chr, repl) + return (backslash * P(chr) / (repl or chr)) +end +local valid_escapes = escape "\"" + + escape "\\" + + escape ("n", "\n") + + escape ("t", "\t") + + escape ("b", "\b") +local comment_char = semicolon + gartenzaun +local comment_line = ws * comment_char * (1 - eol)^0 * eol +local blank_line = ws * eol +local skip_line = comment_line + blank_line +local ini_id_char = alpha + dash +local ini_id = (alpha * ini_id_char^0) / stringlower +local ini_value_char = (valid_escapes + (1 - newline - backslash - comment_char)) +local ini_value = (Cs (ini_value_char^0) / string.strip) + * (comment_char * (1 - eol)^0)^-1 +local ini_string_char = (valid_escapes + (1 - newline - dquote - backslash)) +local ini_string = dquote + * Cs (ini_string_char^0) + * dquote + +local ini_heading_title = Ct (Cg (ini_id, "title") + * (ws * Cg (ini_string / stringlower, "subtitle"))^-1) +local ini_heading = lbrk * ws + * Cg (ini_heading_title, "section") + * ws * rbrk * ws * eol + +local ini_variable_full = Cg (ws + * ini_id + * ws + * equals + * ws + * (ini_string + (ini_value / maybe_cast)) + * ws + * eol) +local ini_variable_true = Cg (ws * ini_id * ws * eol * Cc (true)) +local ini_variable = ini_variable_full + + ini_variable_true + + skip_line +local ini_variables = Cg (Cf (Ct "" * ini_variable^0, rawset), "variables") + +local ini_section = Ct (ini_heading * ini_variables) +local ini_sections = skip_line^0 * ini_section^0 +local config = Ct (ini_sections) + +--[[doc-- + + The INI parser converts an input of the form + + [==[ + [foo] + bar = baz + xyzzy = no + buzz + + [lavernica "brutalitops"] + # It’s a locomotive that runs on us. + laan-ev = zip zop zooey ; jib-jab + Crouton = "Fibrosis \"\\ # " + + ]==] + + to a Lua table of the form + + { { section = { title = "foo" }, + variables = { bar = "baz", + xyzzy = false, + buzz = true } }, + { section = { title = "boing", + subtitle = "brutalitops" }, + variables = { ["laan-ev"] = "zip zop zooey", + crouton = "Fibrosis \"\\ # " } } } + +--doc]]-- + +luaotfload.parsers.config = config + +-- vim:ft=lua:tw=71:et:sts=4:ts=8 -- cgit v1.2.3 From 70a6425e1b6041d5c476117fa7c5a3acb9bc386a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 21 Apr 2014 18:22:24 +0200 Subject: [tests] add test for config file syntax --- scripts/mktests | 60 +++++++++++++++++++++++++++++++++++++++++++++- src/luaotfload-parsers.lua | 4 ++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/scripts/mktests b/scripts/mktests index 0bf3f64..2fed723 100755 --- a/scripts/mktests +++ b/scripts/mktests @@ -26,6 +26,9 @@ config = { luaotfload = { update_live = true, --- suppress db updates }} +local lpeg = require "lpeg" +local lpegmatch = lpeg.match + kpse.set_program_name "luatex" require "lualibs" @@ -66,10 +69,64 @@ local pprint_spec = function (spec) end ----------------------------------------------------------------------- ---- tool tests +--- parser tests ----------------------------------------------------------------------- +local test_config_input = [==[ + + +[foo] +bar = baz +xyzzy = no +buzz + +[lavernica "brutalitops"] +# It’s a locomotive that runs on us. + laan-ev = zip zop zooey ; jib-jab +Crouton = "Fibrosis \"\\ # " +]==] + +local test_config_output = { + { section = { title = "foo" }, + variables = { bar = "baz", + xyzzy = false, + buzz = true } }, + { section = { title = "lavernica", + subtitle = "brutalitops" }, + variables = { ["laan-ev"] = "zip zop zooey", + crouton = "Fibrosis \"\\ # " } } +} + +local parse_config = function () + local parser = luaotfload.parsers.config + local result = lpegmatch (parser, test_config_input) + --- compare values recursively + local aux aux = function (t1, t2) + --- cheaply non-tail recursive + local k1 = table.keys (t1) + local k2 = table.keys (t2) + if #k1 ~= #k2 then + return false + end + for i = 1, #k1 do + local k = k1[i] + local v1 = t1[k] + local v2 = t2[k] + if type (v1) == "table" then + if type (v2) ~= "table" or not aux (v1, v2) then + return false + end + elseif v1 ~= v2 then + return false + end + end + return true + end + return aux (result, test_config_output) and 0 or 1, 1 +end + +tests["parse_config"] = parse_config ----------------------------------------------------------------------- --- font tests @@ -228,6 +285,7 @@ end tests ["resolve_font_name"] = resolve_font_name + ----------------------------------------------------------------------- --- runner ----------------------------------------------------------------------- diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua index a45fcb8..6a99ad7 100644 --- a/src/luaotfload-parsers.lua +++ b/src/luaotfload-parsers.lua @@ -662,7 +662,7 @@ local ini_section = Ct (ini_heading * ini_variables) local ini_sections = skip_line^0 * ini_section^0 local config = Ct (ini_sections) ---[[doc-- +--[=[doc-- The INI parser converts an input of the form @@ -690,7 +690,7 @@ local config = Ct (ini_sections) variables = { ["laan-ev"] = "zip zop zooey", crouton = "Fibrosis \"\\ # " } } } ---doc]]-- +--doc]=]-- luaotfload.parsers.config = config -- cgit v1.2.3 From a84ec1cd4b7cc06fec1d2cf5e5f5e0cbd9115637 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 21 Apr 2014 21:40:21 +0200 Subject: [conf,tool] integrate configuration parser --- src/luaotfload-configuration.lua | 255 +++++++++++++++++++++++++++++++++++++++ src/luaotfload-tool.lua | 41 +++++-- 2 files changed, 288 insertions(+), 8 deletions(-) create mode 100644 src/luaotfload-configuration.lua diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua new file mode 100644 index 0000000..973810a --- /dev/null +++ b/src/luaotfload-configuration.lua @@ -0,0 +1,255 @@ +#!/usr/bin/env texlua +------------------------------------------------------------------------------- +-- FILE: luaotfload-configuration.lua +-- DESCRIPTION: config file reader +-- REQUIREMENTS: Luaotfload > 2.4 +-- AUTHOR: Philipp Gesang (Phg), +-- VERSION: same as Luaotfload +-- CREATED: 2014-04-21 14:03:52+0200 +------------------------------------------------------------------------------- +-- + +if not modules then modules = { } end modules ["luaotfload-configuration"] = { + version = "2.5", + comment = "part of Luaotfload", + author = "Philipp Gesang", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2.0" +} + +luaotfload = luaotfload or { } +luaotfload.config = luaotfload.config or { } + +local string = string +local stringsub = string.sub +local stringexplode = string.explode + +local io = io +local ioloaddata = io.loaddata + +local os = os +local osgetenv = os.getenv + +local lpeg = require "lpeg" +local lpegmatch = lpeg.match + +local kpse = kpse +local kpselookup = kpse.lookup + +local lfs = lfs +local lfsisfile = lfs.isfile +local lfsisdir = lfs.isdir + +local file = file +local filejoin = file.join + +local parsers = luaotfload.parsers +local config = luaotfload.config +local log = luaotfload.log +local logreport = log.report + +local config_parser = parsers.config + +------------------------------------------------------------------------------- +--- SETTINGS +------------------------------------------------------------------------------- + +local path_t = 0 +local kpse_t = 1 + +local config_paths = { + --- needs adapting for those other OS + { path_t, "./luaotfloadrc" }, + { path_t, "~/.config/luaotfload/luaotfloadrc" }, + { path_t, "~/.luaotfloadrc" }, + { kpse_t, "luaotfloadrc" }, + { kpse_t, "luaotfload.conf" }, +} + +------------------------------------------------------------------------------- +--- OPTION SPECIFICATION +------------------------------------------------------------------------------- + +local string_t = "string" +local table_t = "table" +local boolean_t = "boolean" +local function_t = "function" + +local option_spec = { + db = { + formats = { + --- e.g. "otf ttf" -> { "otf", "ttf" } + in_t = string_t, + out_t = table_t, + transform = function (str) return stringexplode (str, " +") end + }, + reload = { + in_t = boolean_t, + }, + }, +} + +------------------------------------------------------------------------------- +--- MAIN FUNCTIONALITY +------------------------------------------------------------------------------- + +--[[doc-- + + tilde_expand -- Rudimentary tilde expansion; covers just the “substitute ‘~’ + by the current users’s $HOME” part. + +--doc]]-- + +local tilde_expand = function (p) + if #p > 2 then + if stringsub (p, 1, 2) == "~/" then + local homedir = osgetenv "HOME" + if homedir and lfsisdir (homedir) then + p = filejoin (homedir, stringsub (p, 3)) + end + end + end + return p +end + +local resolve_config_path = function () + inspect (config_paths) + for i = 1, #config_paths do + local t, p = unpack (config_paths[i]) + local fullname + if t == kpse_t then + fullname = kpse.lookup (p) + logreport ("both", 6, "conf", "kpse lookup: %s -> %s.", p, fullname) + elseif t == path_t then + local expanded = tilde_expand (p) + if lfsisfile (expanded) then + fullname = expanded + end + logreport ("both", 6, "conf", "path lookup: %s -> %s.", p, fullname) + end + if fullname then + logreport ("both", 3, "conf", "Reading configuration file at %q.", fullname) + return fullname + end + end + logreport ("both", 2, "conf", "No configuration file found.") + return false +end + +local add_config_paths = function (t) + if not next (t) then + return + end + local result = { } + for i = 1, #t do + local path = t[i] + result[#result + 1] = { path_t, path } + end + config_paths = table.append (result, config_paths) +end + +local process_options = function (opts) + local new = { } + for i = 1, #opts do + local section = opts[i] + local title = section.section.title + local vars = section.variables + + if not title then --- trigger warning: arrow code ahead + logreport ("both", 2, "conf", "Section %d lacks a title; skipping.", i) + elseif not vars then + logreport ("both", 2, "conf", "Section %d (%s) lacks a variable section; skipping.", i, title) + else + local spec = option_spec[title] + if not spec then + logreport ("both", 2, "conf", "Section %d (%s) unknown; skipping.", i, title) + else + local newsection = new[title] + if not newsection then + newsection = { } + new[title] = newsection + end + + for var, val in next, vars do + local vspec = spec[var] + local t_val = type (val) + if t_val ~= vspec.in_t then + logreport ("both", 2, "conf", + "Section %d (%s): type mismatch of input value %q (%q, %s != %s); ignoring.", + i, title, + var, tostring (val), t_val, vspec.in_t) + else --- type matches + local transform = vspec.transform + if transform then + local dval + local t_transform = type (transform) + if t_transform == function_t then + dval = transform (val) + elseif t_transform == table_t then + dval = transform[val] + end + if dval then + local out_t = vspec.out_t + if out_t then + local t_dval = type (dval) + if t_dval == out_t then + newsection[var] = dval + else + logreport ("both", 2, "conf", + "Section %d (%s): type mismatch of derived value of %q (%q, %s != %s); ignoring.", + i, title, + var, tostring (dval), t_dval, out_t) + end + else + newsection[var] = dval + end + else + logreport ("both", 2, "conf", + "Section %d (%s): value of %q could not be derived via %s from input %q; ignoring.", + i, title, var, t_transform, tostring (val)) + end + else --- insert as is + newsection[var] = val + end + end + end + end + end + end + return new +end + +local read = function (extra) + if extra then + add_config_paths (extra) + end + + local readme = resolve_config_path () + if readme == false then + logreport ("both", 2, "conf", "No configuration file.") + return false + end + + local raw = ioloaddata (readme) + if not raw then + logreport ("both", 2, "conf", "Error reading the configuration file %q.", readme) + return false + end + + local parsed = lpegmatch (parsers.config, raw) + if not parsed then + logreport ("both", 2, "conf", "Error parsing configuration file %q.", readme) + return false + end + + local ret, msg = process_options (parsed) + if not ret then + logreport ("both", 2, "conf", "File %q is not a valid configuration file.", readme) + logreport ("both", 2, "conf", "Error: %s", msg) + return false + end + return ret +end + +config.read = read + diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 1923040..5893d42 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -45,7 +45,6 @@ kpse.set_program_name "luatex" --doc]]-- -local ioopen = io.open local iowrite = io.write local kpsefind_file = kpse.find_file local mathfloor = math.floor @@ -149,7 +148,8 @@ texio.write, texio.write_nl = backup.write, backup.write_nl utilities = backup.utilities require"luaotfload-log.lua" --- this populates the luaotfload.log.* namespace -require"luaotfload-parsers" --- fonts.conf and request syntax +require"luaotfload-parsers" --- fonts.conf, configuration, and request syntax +require"luaotfload-configuration" --- configuration file handling require"luaotfload-database" require"alt_getopt" @@ -736,13 +736,14 @@ set. --]]-- local action_sequence = { - "loglevel", "help", "version", "diagnose", - "blacklist", "cache", "flush", "bisect", - "generate", "list", "query", + "loglevel", "config", "help", "version", + "diagnose", "blacklist", "cache", "flush", + "bisect", "generate", "list", "query", } local action_pending = tabletohash(action_sequence, false) +action_pending.config = true --- always read the configuration action_pending.loglevel = true --- always set the loglevel action_pending.generate = false --- this is the default action @@ -755,6 +756,16 @@ actions.loglevel = function (job) return true, true end +actions.config = function (job) + local config = luaotfload.config.read (job.extra_config) + --if job.print_config == true then + if true then + -- inspect (config) + return true, false + end + return true, true +end + actions.version = function (job) version_msg() return true, false @@ -1386,8 +1397,9 @@ local process_cmdline = function ( ) -- unit -> jobspec } local long_options = { + ["bisect"] = 1, cache = 1, - ["no-compress"] = "c", + conf = 1, diagnose = 1, ["dry-run"] = "D", ["flush-lookups"] = "l", @@ -1404,11 +1416,12 @@ local process_cmdline = function ( ) -- unit -> jobspec ["local"] = "L", log = 1, ["max-fonts"] = 1, - ["bisect"] = 1, + ["no-compress"] = "c", ["no-reload"] = "n", ["no-strip"] = 0, ["skip-read"] = "R", ["prefer-texmf"] = "p", + ["print-conf"] = 0, quiet = "q", ["show-blacklist"] = "b", stats = "S", @@ -1520,8 +1533,20 @@ local process_cmdline = function ( ) -- unit -> jobspec end end elseif v == "bisect" then - result.bisect = optarg[n] + result.bisect = optarg[n] action_pending.bisect = true + elseif v == "conf" then + local extra = stringexplode (optarg[n], ",+") + if extra then + local extra_config = result.extra_config + if extra_config then + table.append (extra_config, extra) + else + result.extra_config = extra + end + end + elseif v == "print-conf" then + result.print_config = true end end -- cgit v1.2.3 From 3657b9c19c70eb0b52dfa1c67956776a6fd77d4a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 22 Apr 2014 07:15:12 +0200 Subject: [tool,db,conf] integrate defaults into new config model --- src/luaotfload-configuration.lua | 67 +++++++++++++++++++++++++++++++++------- src/luaotfload-database.lua | 10 +++--- src/luaotfload-tool.lua | 29 ++++++----------- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 973810a..d05a48b 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -24,6 +24,10 @@ local string = string local stringsub = string.sub local stringexplode = string.explode +local table = table +local tableappend = table.append +local tablecopy = table.copy + local io = io local ioloaddata = io.loaddata @@ -66,6 +70,27 @@ local config_paths = { { kpse_t, "luaotfload.conf" }, } +------------------------------------------------------------------------------- +--- DEFAULTS +------------------------------------------------------------------------------- + +local luaotfload_defaults = { + misc = { + bisect = false, + version = luaotfload.version, + }, + paths = { + names_dir = "names", + cache_dir = "fonts", + index_file = "luaotfload-names.lua", + }, + db = { + formats = "otf,ttf,ttc,dfont", + reload = false, + strip = true, + }, +} + ------------------------------------------------------------------------------- --- OPTION SPECIFICATION ------------------------------------------------------------------------------- @@ -77,15 +102,18 @@ local function_t = "function" local option_spec = { db = { - formats = { - --- e.g. "otf ttf" -> { "otf", "ttf" } - in_t = string_t, - out_t = table_t, - transform = function (str) return stringexplode (str, " +") end - }, - reload = { - in_t = boolean_t, - }, + formats = { in_t = string_t, }, + reload = { in_t = boolean_t, }, + strip = { in_t = boolean_t, }, + }, + misc = { + bisect = { in_t = boolean_t, }, + version = { in_t = string_t, }, + }, + paths = { + names_dir = { in_t = string_t, }, + cache_dir = { in_t = string_t, }, + index_file = { in_t = string_t, }, }, } @@ -145,7 +173,7 @@ local add_config_paths = function (t) local path = t[i] result[#result + 1] = { path_t, path } end - config_paths = table.append (result, config_paths) + config_paths = tableappend (result, config_paths) end local process_options = function (opts) @@ -219,6 +247,17 @@ local process_options = function (opts) return new end +local apply = function (old, new) + if not old then + return tablecopy (new) + end + local result = tablecopy (old) + for var, val in next, new do + result[var] = val + end + return result +end + local read = function (extra) if extra then add_config_paths (extra) @@ -251,5 +290,11 @@ local read = function (extra) return ret end -config.read = read +------------------------------------------------------------------------------- +--- EXPORTS +------------------------------------------------------------------------------- + +config.defaults = luaotfload_defaults +config.read = read +config.apply = apply diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 7165d07..013e6e8 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -650,7 +650,7 @@ local style_category = { i = "italic", } -local type1_formats = { "tfm", "ofm", } +local type1_metrics = { "tfm", "ofm", } local dummy_findfile = resolvers.findfile -- from basics-gen @@ -692,8 +692,8 @@ crude_file_lookup_verbose = function (filename) end --- ofm and tfm, returns pair - for i=1, #type1_formats do - local format = type1_formats[i] + for i=1, #type1_metrics do + local format = type1_metrics[i] if resolvers.findfile(filename, format) then return file.addsuffix(filename, format), format, true end @@ -748,8 +748,8 @@ crude_file_lookup = function (filename) return found, nil, true end - for i=1, #type1_formats do - local format = type1_formats[i] + for i=1, #type1_metrics do + local format = type1_metrics[i] if resolvers.findfile(filename, format) then return file.addsuffix(filename, format), format, true end diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 5893d42..9b06ba9 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -95,18 +95,6 @@ config = config or { } local config = config local luaotfloadconfig = config.luaotfload or { } config.luaotfload = luaotfloadconfig -luaotfloadconfig.bisect = false -luaotfloadconfig.version = luaotfloadconfig.version or version -luaotfloadconfig.names_dir = luaotfloadconfig.names_dir or "names" -luaotfloadconfig.cache_dir = luaotfloadconfig.cache_dir or "fonts" -luaotfloadconfig.index_file = luaotfloadconfig.index_file - or "luaotfload-names.lua" -luaotfloadconfig.formats = luaotfloadconfig.formats - or "otf,ttf,ttc,dfont" -luaotfloadconfig.reload = false -if not luaotfloadconfig.strip then - luaotfloadconfig.strip = true -end config.lualibs = config.lualibs or { } config.lualibs.verbose = false @@ -147,11 +135,11 @@ require"luaotfload-basics-gen.lua" texio.write, texio.write_nl = backup.write, backup.write_nl utilities = backup.utilities -require"luaotfload-log.lua" --- this populates the luaotfload.log.* namespace -require"luaotfload-parsers" --- fonts.conf, configuration, and request syntax -require"luaotfload-configuration" --- configuration file handling -require"luaotfload-database" -require"alt_getopt" +require "luaotfload-log.lua" --- this populates the luaotfload.log.* namespace +require "luaotfload-parsers" --- fonts.conf, configuration, and request syntax +require "luaotfload-configuration" --- configuration file handling +require "luaotfload-database" +require "alt_getopt" local names = fonts.names local status_file = "luaotfload-status" @@ -757,10 +745,13 @@ actions.loglevel = function (job) end actions.config = function (job) - local config = luaotfload.config.read (job.extra_config) + local defaults = luaotfload.config.defaults + local vars = luaotfload.config.read (job.extra_config) + config.luaotfload = luaotfload.config.apply (defaults, vars) --if job.print_config == true then if true then - -- inspect (config) + --inspect (vars) + inspect (config.luaotfload) return true, false end return true, true -- cgit v1.2.3 From 2237a2bc833d46a1631112fb4c220e3f4941bcc3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 22 Apr 2014 23:46:48 +0200 Subject: [db,conf,main,tool] move primary configuration table to new configuration model (ignore those initialization stubs for now) --- src/luaotfload-configuration.lua | 160 ++++++++++++++++++++++++++++++++++----- src/luaotfload-database.lua | 131 ++++++++++---------------------- src/luaotfload-main.lua | 10 ++- src/luaotfload-tool.lua | 44 ++++++----- 4 files changed, 211 insertions(+), 134 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index d05a48b..aa91b27 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -28,6 +28,9 @@ local table = table local tableappend = table.append local tablecopy = table.copy +local math = math +local mathfloor = math.floor + local io = io local ioloaddata = io.loaddata @@ -76,39 +79,146 @@ local config_paths = { local luaotfload_defaults = { misc = { - bisect = false, - version = luaotfload.version, + bisect = false, + version = luaotfload.version, + termwidth = nil, + statistics = false, }, paths = { - names_dir = "names", - cache_dir = "fonts", - index_file = "luaotfload-names.lua", + names_dir = "names", + cache_dir = "fonts", + index_file = "luaotfload-names.lua", + lookups_file = "luaotfload-lookup-cache.lua", }, db = { - formats = "otf,ttf,ttc,dfont", - reload = false, - strip = true, + formats = "otf,ttf,ttc,dfont", + reload = false, + strip = true, + update_live = true, + compress = true, + scan_local = false, + skip_read = false, }, } +------------------------------------------------------------------------------- +--- RECONFIGURATION TASKS +------------------------------------------------------------------------------- + +--[[doc-- + + Procedures to be executed in order to put the new configuration into effect. + +--doc]]-- + +local reconf_tasks = { } + +local min_terminal_width = 40 + +--- The “termwidth” value is only considered when printing +--- short status messages, e.g. when building the database +--- online. +reconf_tasks.check_termwidth = function () + if config.luaotfload.misc.termwidth == nil then + local tw = 79 + if not ( os.type == "windows" --- Assume broken terminal. + or osgetenv "TERM" == "dumb") + then + local p = iopopen "tput cols" + if p then + result = tonumber (p:read "*all") + p:close () + if result then + tw = result + else + logreport ("log", 2, "db", "tput returned non-number.") + end + else + logreport ("log", 2, "db", "Shell escape disabled or tput executable missing.") + logreport ("log", 2, "db", "Assuming 79 cols terminal width.") + end + end + config.luaotfload.misc.termwidth = tw + end + return true +end + +reconf_tasks.set_font_filters = function () + fonts.names.set_font_filter (config.luaotfload.db.formats) + return true +end + +reconf_tasks.set_name_resolver = function () + local names = fonts.names + --- replace the resolver from luatex-fonts + if config.luaotfload.db.resolver == "cached" then + logreport("both", 2, "cache", "Caching of name: lookups active.") + names.resolvespec = resolve_cached + names.resolve_name = resolve_cached + else + names.resolvespec = resolve_name + names.resolve_name = resolve_name + end + return true +end + ------------------------------------------------------------------------------- --- OPTION SPECIFICATION ------------------------------------------------------------------------------- local string_t = "string" local table_t = "table" +local number_t = "number" local boolean_t = "boolean" local function_t = "function" +local tointeger = function (n) + n = tonumber (n) + if n then + return mathfloor (n + 0.5) + end +end + local option_spec = { db = { - formats = { in_t = string_t, }, - reload = { in_t = boolean_t, }, - strip = { in_t = boolean_t, }, + formats = { in_t = string_t, }, + reload = { in_t = boolean_t, }, + scan_local = { in_t = boolean_t, }, + skip_read = { in_t = boolean_t, }, + strip = { in_t = boolean_t, }, + update_live = { in_t = boolean_t, }, + compress = { in_t = boolean_t, }, + max_fonts = { + in_t = number_t, + out_t = number_t, --- TODO int_t from 5.3.x on + transform = tointeger, + }, + resolver = { + in_t = string_t, + out_t = string_t, + transform = function (r) + if r == "normal" then + return "normal" + end + return "cached" + end, + } }, misc = { - bisect = { in_t = boolean_t, }, - version = { in_t = string_t, }, + bisect = { in_t = boolean_t, }, --- doesn’t make sense in a config file + version = { in_t = string_t, }, + statistics = { in_t = boolean_t, }, + termwidth = { + in_t = number_t, + out_t = number_t, + transform = function (w) + w = tointeger (w) + if w < min_terminal_width then + return min_terminal_width + end + return w + end, + }, }, paths = { names_dir = { in_t = string_t, }, @@ -248,7 +358,12 @@ local process_options = function (opts) end local apply = function (old, new) - if not old then + if not new then + if not old then + return false + end + return tablecopy (old) + elseif not old then return tablecopy (new) end local result = tablecopy (old) @@ -258,6 +373,16 @@ local apply = function (old, new) return result end +local reconfigure = function () + for i = 1, #reconf_tasks do + local task = reconf_tasks[i] + if not task () then + return false + end + end + return true +end + local read = function (extra) if extra then add_config_paths (extra) @@ -294,7 +419,8 @@ end --- EXPORTS ------------------------------------------------------------------------------- -config.defaults = luaotfload_defaults -config.read = read -config.apply = apply +config.defaults = luaotfload_defaults +config.read = read +config.apply = apply +config.reconfigure = reconfigure diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 013e6e8..3a4b8b4 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -127,16 +127,6 @@ fonts = fonts or { } fonts.names = fonts.names or { } fonts.definers = fonts.definers or { } -local luaotfloadconfig = config.luaotfload --- always present -luaotfloadconfig.resolver = luaotfloadconfig.resolver or "normal" -luaotfloadconfig.formats = luaotfloadconfig.formats or "otf,ttf,ttc,dfont" -luaotfloadconfig.strip = luaotfloadconfig.strip == true - ---- this option allows for disabling updates ---- during a TeX run -luaotfloadconfig.update_live = luaotfloadconfig.update_live ~= false -luaotfloadconfig.compress = luaotfloadconfig.compress ~= false - local names = fonts.names local name_index = nil --> upvalue for names.data local lookup_cache = nil --> for names.lookups @@ -145,13 +135,6 @@ names.data = nil --- contains the loaded database names.lookups = nil --- contains the lookup cache names.path = { index = { }, lookups = { } } -names.path.globals = { - prefix = "", --- writable_path/names_dir - names_dir = luaotfloadconfig.names_dir or "names", - index_file = luaotfloadconfig.index_file - or "luaotfload-names.lua", - lookups_file = "luaotfload-lookup-cache.lua", -} --- string -> (string * string) local make_luanames = function (path) @@ -159,31 +142,6 @@ local make_luanames = function (path) filereplacesuffix(path, "luc") end ---- The “termwidth” value is only considered when printing ---- short status messages, e.g. when building the database ---- online. -if not luaotfloadconfig.termwidth then - local tw = 79 - if not ( os.type == "windows" --- Assume broken terminal. - or osgetenv "TERM" == "dumb") - then - local p = iopopen "tput cols" - if p then - result = tonumber (p:read "*all") - p:close () - if result then - tw = result - else - report ("log", 2, "db", "tput returned non-number.") - end - else - report ("log", 2, "db", "Shell escape disabled or tput executable missing.") - report ("log", 2, "db", "Assuming 79 cols terminal width.") - end - end - luaotfloadconfig.termwidth = tw -end - local format_precedence = { "otf", "ttc", "ttf", "dfont", "afm", "pfb", @@ -211,34 +169,35 @@ end created by different user. --doc]]-- -if not runasscript then - local globals = names.path.globals - local names_dir = globals.names_dir +--- XXX this belongs into the initialization file! +local initialize_env = function () + if not runasscript then + local paths = config.luaotfload.paths - prefix = getwritablepath (names_dir, "") - if not prefix then - luaotfload.error - ("Impossible to find a suitable writeable cache...") - else - prefix = lpegmatch (stripslashes, prefix) - report ("log", 0, "db", - "Root cache directory is %s.", prefix) - end + local prefix = getwritablepath (paths.names_dir, "") + if not prefix then + luaotfload.error + ("Impossible to find a suitable writeable cache...") + else + prefix = lpegmatch (stripslashes, prefix) + report ("log", 0, "db", + "Root cache directory is %s.", prefix) + end - globals.prefix = prefix - local lookup_path = names.path.lookups - local index = names.path.index - local lookups_file = filejoin (prefix, globals.lookups_file) - local index_file = filejoin (prefix, globals.index_file) - lookup_path.lua, lookup_path.luc = make_luanames (lookups_file) - index.lua, index.luc = make_luanames (index_file) -else --- running as script, inject some dummies - caches = { } - local dummy_function = function () end - log = { report = dummy_function, - report_status = dummy_function, - report_status_start = dummy_function, - report_status_stop = dummy_function, } + local lookup_path = names.path.lookups + local index = names.path.index + local lookups_file = filejoin (prefix, paths.lookups_file) + local index_file = filejoin (prefix, paths.index_file) + lookup_path.lua, lookup_path.luc = make_luanames (lookups_file) + index.lua, index.luc = make_luanames (index_file) + else --- running as script, inject some dummies + caches = { } + local dummy_function = function () end + log = { report = dummy_function, + report_status = dummy_function, + report_status_start = dummy_function, + report_status_stop = dummy_function, } + end end @@ -2072,9 +2031,6 @@ do get_font_filter = function (formats) return tablefastcopy (current_formats) end - - --- initialize - set_font_filter (luaotfloadconfig.formats) end local process_dir_tree @@ -2177,7 +2133,7 @@ end --- indicates the number of characters already consumed on the --- line. local truncate_string = function (str, restrict) - local tw = luaotfloadconfig.termwidth + local tw = config.luaotfload.misc.termwidth local wd = tw - restrict local len = utf8len (str) if wd - len < 0 then @@ -2670,7 +2626,7 @@ local pull_values = function (entry) entry.splitstyle = style.split entry.weight = style.weight - if luaotfloadconfig.strip == true then + if config.luaotfload.db.strip == true then entry.file = nil entry.names = nil entry.style = nil @@ -2931,12 +2887,12 @@ local collect_font_filenames = function () report ("info", 4, "db", "Scanning the filesystem for font files.") local filenames = { } - local bisect = luaotfloadconfig.bisect - local max_fonts = luaotfloadconfig.max_fonts or 2^51 --- XXX revisit for lua 5.3 wrt integers + local bisect = config.luaotfload.misc.bisect + local max_fonts = config.luaotfload.db.max_fonts or 2^51 --- XXX revisit for lua 5.3 wrt integers tableappend (filenames, collect_font_filenames_texmf ()) tableappend (filenames, collect_font_filenames_system ()) - if luaotfloadconfig.scan_local == true then + if config.luaotfload.db.scan_local == true then tableappend (filenames, collect_font_filenames_local ()) end --- Now drop everything above max_fonts. @@ -3175,7 +3131,7 @@ end update_names = function (currentnames, force, dry_run) local targetnames - if luaotfloadconfig.update_live == false then + if config.luaotfload.db.update_live == false then report ("info", 2, "db", "Skipping database update.") --- skip all db updates @@ -3192,7 +3148,7 @@ update_names = function (currentnames, force, dry_run) report("both", 1, "db", "Updating the font names database" .. (force and " forcefully." or ".")) - if luaotfloadconfig.skip_read == true then + if config.luaotfload.db.skip_read == true then --- the difference to a “dry run” is that we don’t search --- for font files entirely. we also ignore the “force” --- parameter since it concerns only the font files. @@ -3234,7 +3190,7 @@ update_names = function (currentnames, force, dry_run) end --- pass 3 (optional): collect some stats about the raw font info - if luaotfloadconfig.statistics == true then + if config.luaotfload.misc.statistics == true then targetnames.meta.statistics = collect_statistics (targetnames.mappings) end @@ -3325,7 +3281,7 @@ save_names = function (currentnames) if fileiswritable (luaname) and fileiswritable (lucname) then osremove (lucname) local gzname = luaname .. ".gz" - if luaotfloadconfig.compress then + if config.luaotfload.db.compress then local serialized = tableserialize (currentnames, true) save_gzipped (gzname, serialized) caches.compile (currentnames, "", lucname) @@ -3443,7 +3399,7 @@ end local getwritablecachepath = function ( ) --- fonts.handlers.otf doesn’t exist outside a Luatex run, --- so we have to improvise - local writable = getwritablepath (luaotfloadconfig.cache_dir) + local writable = getwritablepath (config.luaotfload.paths.cache_dir) if writable then return writable end @@ -3451,7 +3407,7 @@ end local getreadablecachepaths = function ( ) local readables = caches.getreadablepaths - (luaotfloadconfig.cache_dir) + (config.luaotfload.paths.cache_dir) local result = { } if readables then for i=1, #readables do @@ -3516,6 +3472,7 @@ end --- export functionality to the namespace “fonts.names” ----------------------------------------------------------------------- +names.initialize_env = initialize_env names.set_font_filter = set_font_filter names.flush_lookup_cache = flush_lookup_cache names.save_lookups = save_lookups @@ -3538,16 +3495,6 @@ names.purge_cache = purge_cache names.erase_cache = erase_cache names.show_cache = show_cache ---- replace the resolver from luatex-fonts -if luaotfloadconfig.resolver == "cached" then - report("both", 2, "cache", "Caching of name: lookups active.") - names.resolvespec = resolve_cached - names.resolve_name = resolve_cached -else - names.resolvespec = resolve_name - names.resolve_name = resolve_name -end - names.find_closest = find_closest -- for testing purpose diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index fe4e792..495001b 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -431,10 +431,12 @@ loadmodule "override.lua" --- load glyphlist on demand Now we load the modules written for \identifier{luaotfload}. --doc]]-- -loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax -loadmodule "loaders.lua" --- “font-pfb” new in 2.0, added 2011 -loadmodule "database.lua" --- “font-nms” -loadmodule "colors.lua" --- “font-clr” +loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax +loadmodule "loaders.lua" --- “font-pfb” new in 2.0, added 2011 +loadmodule "configuration.lua" --- configuration options +loadmodule "database.lua" --- “font-nms” +names.initialize_env () --- XXX hack hack hack; we need common initialization +loadmodule "colors.lua" --- “font-clr” --[[doc-- diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 9b06ba9..04fa67d 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -93,8 +93,7 @@ require(loader_path) config = config or { } local config = config -local luaotfloadconfig = config.luaotfload or { } -config.luaotfload = luaotfloadconfig +config.luaotfload = config.luaotfload or { } config.lualibs = config.lualibs or { } config.lualibs.verbose = false @@ -144,17 +143,12 @@ require "alt_getopt" local names = fonts.names local status_file = "luaotfload-status" local luaotfloadstatus = require (status_file) -luaotfloadconfig.status = luaotfloadstatus +config.luaotfload.status = luaotfloadstatus local sanitize_fontname = names.sanitize_fontname local log = luaotfload.log local report = log.report -local pathdata = names.path -local names_plain = pathdata.index.lua -local names_gzip = names_plain .. ".gz" -local names_bin = pathdata.index.luc - local help_messages = { ["luaotfload-tool"] = [[ @@ -249,14 +243,19 @@ Enter 'luaotfload-tool --help' for a larger list of options. } local help_msg = function (version) - local template = help_messages[version] + local template = help_messages[version] + local pathdata = names.path + local paths = config.luaotfload.paths + local names_plain = paths.index.lua + local names_gzip = names_plain .. ".gz" + local names_bin = paths.index.luc + iowrite(stringformat(template, luaotfload.self, -- names_plain, names_gzip, names_bin, - caches.getwritablepath ( - luaotfloadconfig.cache_dir))) + caches.getwritablepath (config.luaotfload.cache_dir))) end local about = [[ @@ -748,12 +747,15 @@ actions.config = function (job) local defaults = luaotfload.config.defaults local vars = luaotfload.config.read (job.extra_config) config.luaotfload = luaotfload.config.apply (defaults, vars) - --if job.print_config == true then - if true then + + if luaotfload.config.reconfigure () then --inspect (vars) inspect (config.luaotfload) return true, false + else + return false, false end + names.initialize_env () return true, true end @@ -1056,7 +1058,7 @@ local bisect_run = function () nsteps, lo, hi, pivot) report ("info", 1, "bisect", "Step %d: Testing fonts from %d to %d.", currentstep, lo, pivot) - luaotfloadconfig.bisect = { lo, pivot } + config.luaotfload.misc.bisect = { lo, pivot } return true, true end @@ -1482,7 +1484,7 @@ local process_cmdline = function ( ) -- unit -> jobspec action_pending["flush"] = true elseif v == "L" then action_pending["generate"] = true - luaotfloadconfig.scan_local = true + config.luaotfload.db.scan_local = true elseif v == "list" then action_pending["list"] = true result.criterion = optarg[n] @@ -1505,22 +1507,22 @@ local process_cmdline = function ( ) -- unit -> jobspec elseif v == "formats" then names.set_font_filter (optarg[n]) elseif v == "n" then - luaotfloadconfig.update_live = false + config.luaotfload.db.update_live = false elseif v == "S" then - luaotfloadconfig.statistics = true + config.luaotfload.misc.statistics = true elseif v == "R" then --- dev only, undocumented - luaotfloadconfig.skip_read = true + config.luaotfload.db.skip_read = true elseif v == "c" then - luaotfloadconfig.compress = false + config.luaotfload.db.compress = false elseif v == "no-strip" then - luaotfloadconfig.strip = false + config.luaotfload.db.strip = false elseif v == "max-fonts" then local n = optarg[n] if n then n = tonumber(n) if n and n > 0 then - luaotfloadconfig.max_fonts = n + config.luaotfload.db.max_fonts = n end end elseif v == "bisect" then -- cgit v1.2.3 From ff15eeb08a3148f961b0535ee2fea7ceea85bc1a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 23 Apr 2014 08:17:51 +0200 Subject: [conf] fix configuration import --- src/luaotfload-configuration.lua | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index aa91b27..fed003e 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -357,7 +357,8 @@ local process_options = function (opts) return new end -local apply = function (old, new) +local apply +apply = function (old, new) if not new then if not old then return false @@ -367,8 +368,19 @@ local apply = function (old, new) return tablecopy (new) end local result = tablecopy (old) - for var, val in next, new do - result[var] = val + for name, section in next, new do + local t_section = type (section) + if t_section ~= table_t then + logreport ("both", 1, "conf", + "Error applying configuration: entry %s is %s, expected table.", + section, t_section) + --- ignore + else + local currentsection = result[name] + for var, val in next, section do + currentsection[var] = val + end + end end return result end -- cgit v1.2.3 From 226a72e66462faeaa5052cfd8aed6011d2545247 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 24 Apr 2014 23:29:52 +0200 Subject: [colors,conf,db,main,tool] adapt the TeX run code to new configuration --- src/luaotfload-colors.lua | 2 +- src/luaotfload-configuration.lua | 155 ++++++++++++++++++++++++++------------- src/luaotfload-database.lua | 7 +- src/luaotfload-main.lua | 100 +++++++++++-------------- src/luaotfload-tool.lua | 28 ++++--- 5 files changed, 168 insertions(+), 124 deletions(-) diff --git a/src/luaotfload-colors.lua b/src/luaotfload-colors.lua index d999df6..56acfee 100644 --- a/src/luaotfload-colors.lua +++ b/src/luaotfload-colors.lua @@ -20,7 +20,7 @@ explanation: http://tug.org/pipermail/luatex/2013-May/004305.html --doc]]-- -local color_callback = config.luaotfload.color_callback +local color_callback = config.luaotfload.run.color_callback if not color_callback then --- maybe this would be better as a method: "early" | "late" color_callback = "pre_linebreak_filter" diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index fed003e..93a358f 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -18,7 +18,8 @@ if not modules then modules = { } end modules ["luaotfload-configuration"] = { } luaotfload = luaotfload or { } -luaotfload.config = luaotfload.config or { } +config = config or { } +config.luaotfload = { } local string = string local stringsub = string.sub @@ -33,6 +34,7 @@ local mathfloor = math.floor local io = io local ioloaddata = io.loaddata +local iopopen = io.popen local os = os local osgetenv = os.getenv @@ -51,7 +53,7 @@ local file = file local filejoin = file.join local parsers = luaotfload.parsers -local config = luaotfload.config + local log = luaotfload.log local logreport = log.report @@ -77,12 +79,28 @@ local config_paths = { --- DEFAULTS ------------------------------------------------------------------------------- -local luaotfload_defaults = { +local default_config = { + db = { + formats = "otf,ttf,ttc,dfont", + reload = false, + scan_local = false, + skip_read = false, + strip = true, + update_live = true, + compress = true, + max_fonts = 2^51, + }, + run = { + resolver = "cached", + definer = "patch", + log_level = 0, + color_callback = "pre_linebreak_filter", + }, misc = { - bisect = false, - version = luaotfload.version, - termwidth = nil, - statistics = false, + bisect = false, + version = luaotfload.version, + statistics = false, + termwidth = nil, }, paths = { names_dir = "names", @@ -90,15 +108,6 @@ local luaotfload_defaults = { index_file = "luaotfload-names.lua", lookups_file = "luaotfload-lookup-cache.lua", }, - db = { - formats = "otf,ttf,ttc,dfont", - reload = false, - strip = true, - update_live = true, - compress = true, - scan_local = false, - skip_read = false, - }, } ------------------------------------------------------------------------------- @@ -111,17 +120,17 @@ local luaotfload_defaults = { --doc]]-- -local reconf_tasks = { } +local reconf_tasks = nil local min_terminal_width = 40 --- The “termwidth” value is only considered when printing --- short status messages, e.g. when building the database --- online. -reconf_tasks.check_termwidth = function () +local check_termwidth = function () if config.luaotfload.misc.termwidth == nil then local tw = 79 - if not ( os.type == "windows" --- Assume broken terminal. + if not ( os.type == "windows" --- Assume broken terminal. or osgetenv "TERM" == "dumb") then local p = iopopen "tput cols" @@ -143,25 +152,42 @@ reconf_tasks.check_termwidth = function () return true end -reconf_tasks.set_font_filters = function () - fonts.names.set_font_filter (config.luaotfload.db.formats) +local set_font_filter = function () + local names = fonts.names + if names and names.set_font_filter then + names.set_font_filter (config.luaotfload.db.formats) + end return true end -reconf_tasks.set_name_resolver = function () +local set_name_resolver = function () local names = fonts.names - --- replace the resolver from luatex-fonts - if config.luaotfload.db.resolver == "cached" then - logreport("both", 2, "cache", "Caching of name: lookups active.") - names.resolvespec = resolve_cached - names.resolve_name = resolve_cached - else - names.resolvespec = resolve_name - names.resolve_name = resolve_name + if names and names.resolve_cached then + --- replace the resolver from luatex-fonts + if config.luaotfload.db.resolver == "cached" then + logreport ("both", 2, "cache", "Caching of name: lookups active.") + names.resolvespec = names.resolve_cached + names.resolve_name = names.resolve_cached + else + names.resolvespec = names.resolve_name + names.resolve_name = names.resolve_name + end end return true end +local set_loglevel = function () + log.set_loglevel (config.luaotfload.run.log_level) + return true +end + +reconf_tasks = { + { "Set the log level" , set_loglevel }, + { "Check terminal dimensions" , check_termwidth }, + { "Set the font filter" , set_font_filter }, + { "Install font name resolver", set_name_resolver }, +} + ------------------------------------------------------------------------------- --- OPTION SPECIFICATION ------------------------------------------------------------------------------- @@ -193,22 +219,37 @@ local option_spec = { out_t = number_t, --- TODO int_t from 5.3.x on transform = tointeger, }, - resolver = { + }, + run = { + resolver = { in_t = string_t, out_t = string_t, - transform = function (r) - if r == "normal" then - return "normal" - end - return "cached" + transform = function (r) return r == "normal" and r or "cached" end, + }, + definer = { + in_t = string_t, + out_t = string_t, + transform = function (d) return d == "generic" and d or "patch" end, + }, + log_level = { + in_t = number_t, + out_t = number_t, --- TODO int_t from 5.3.x on + transform = tointeger, + }, + color_callback = { + in_t = string_t, + out_t = string_t, + transform = function (cb) + --- These are the two that make sense. + return cb == "pre_output_filter" and cb or "pre_linebreak_filter" end, - } + }, }, misc = { - bisect = { in_t = boolean_t, }, --- doesn’t make sense in a config file - version = { in_t = string_t, }, - statistics = { in_t = boolean_t, }, - termwidth = { + bisect = { in_t = boolean_t, }, --- doesn’t make sense in a config file + version = { in_t = string_t, }, + statistics = { in_t = boolean_t, }, + termwidth = { in_t = number_t, out_t = number_t, transform = function (w) @@ -221,9 +262,10 @@ local option_spec = { }, }, paths = { - names_dir = { in_t = string_t, }, - cache_dir = { in_t = string_t, }, - index_file = { in_t = string_t, }, + names_dir = { in_t = string_t, }, + cache_dir = { in_t = string_t, }, + index_file = { in_t = string_t, }, + lookups_file = { in_t = string_t, }, }, } @@ -251,7 +293,6 @@ local tilde_expand = function (p) end local resolve_config_path = function () - inspect (config_paths) for i = 1, #config_paths do local t, p = unpack (config_paths[i]) local fullname @@ -387,8 +428,10 @@ end local reconfigure = function () for i = 1, #reconf_tasks do - local task = reconf_tasks[i] + local name, task = unpack (reconf_tasks[i]) + logreport ("both", 3, "conf", "Launch post-configuration task %q.", name) if not task () then + logreport ("both", 0, "conf", "Post-configuration task %q failed.", name) return false end end @@ -427,12 +470,24 @@ local read = function (extra) return ret end +local apply_defaults = function () + local defaults = default_config + local vars = read () + --- Side-effects galore ... + config.luaotfload = apply (defaults, vars) + return reconfigure () +end + ------------------------------------------------------------------------------- --- EXPORTS ------------------------------------------------------------------------------- -config.defaults = luaotfload_defaults -config.read = read -config.apply = apply -config.reconfigure = reconfigure +luaotfload.default_config = default_config + +config.actions = { + read = read, + apply = apply, + apply_defaults = apply_defaults, + reconfigure = reconfigure, +} diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 3a4b8b4..6b59aac 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -172,8 +172,7 @@ end --- XXX this belongs into the initialization file! local initialize_env = function () if not runasscript then - local paths = config.luaotfload.paths - + local paths = config.luaotfload.paths local prefix = getwritablepath (paths.names_dir, "") if not prefix then luaotfload.error @@ -189,7 +188,7 @@ local initialize_env = function () local lookups_file = filejoin (prefix, paths.lookups_file) local index_file = filejoin (prefix, paths.index_file) lookup_path.lua, lookup_path.luc = make_luanames (lookups_file) - index.lua, index.luc = make_luanames (index_file) + index.lua, index.luc = make_luanames (index_file) else --- running as script, inject some dummies caches = { } local dummy_function = function () end @@ -2888,7 +2887,7 @@ local collect_font_filenames = function () local filenames = { } local bisect = config.luaotfload.misc.bisect - local max_fonts = config.luaotfload.db.max_fonts or 2^51 --- XXX revisit for lua 5.3 wrt integers + local max_fonts = config.luaotfload.db.max_fonts --- XXX revisit for lua 5.3 wrt integers tableappend (filenames, collect_font_filenames_texmf ()) tableappend (filenames, collect_font_filenames_system ()) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 495001b..836768a 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -43,30 +43,12 @@ if not modules then modules = { } end modules ["luaotfload-main"] = { --doc]]-- +local initial_log_level = 0 + luaotfload = luaotfload or { } local luaotfload = luaotfload luaotfload.log = luaotfload.log or { } -config = config or { } -config.luaotfload = config.luaotfload or { } -local luaotfloadconfig = config.luaotfload -----------------.resolver = luaotfloadconfig.resolver or "normal" -luaotfloadconfig.resolver = luaotfloadconfig.resolver or "cached" -luaotfloadconfig.definer = luaotfloadconfig.definer or "patch" -luaotfloadconfig.bisect = false --- useless when running TeX -luaotfloadconfig.loglevel = luaotfloadconfig.loglevel or 2 -luaotfloadconfig.color_callback = luaotfloadconfig.color_callback or "pre_linebreak_filter" -luaotfloadconfig.prioritize = luaotfloadconfig.prioritize or "sys" -luaotfloadconfig.names_dir = luaotfloadconfig.names_dir or "names" -luaotfloadconfig.cache_dir = luaotfloadconfig.cache_dir or "fonts" -luaotfloadconfig.index_file = luaotfloadconfig.index_file or "luaotfload-names.lua" -luaotfloadconfig.formats = luaotfloadconfig.formats or "otf,ttf,ttc,dfont" -luaotfloadconfig.scan_local = luaotfloadconfig.scan_local == true - -if luaotfloadconfig.strip == nil then - luaotfloadconfig.strip = true -end - luaotfload.module = { name = "luaotfload", version = 2.50000, @@ -90,7 +72,7 @@ local add_to_callback, create_callback = local reset_callback, call_callback = luatexbase.reset_callback, luatexbase.call_callback -local dummy_function = function () end +local dummy_function = function () end --- XXX this will be moved to the luaotfload namespace when we have the init module local error, warning, info, log = luatexbase.provides_module(luaotfload.module) @@ -146,14 +128,17 @@ end local fl_prefix = "luaotfload" -- “luatex” for luatex-plain local loadmodule = function (name) - require(fl_prefix .."-"..name) + require (fl_prefix .."-"..name) end -loadmodule "log.lua" --- messages; used to be part of -override +loadmodule "log.lua" --- log messages +--loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax +--loadmodule "configuration.lua" --- configuration options + local log = luaotfload.log local report = log.report -log.set_loglevel(luaotfloadconfig.loglevel) +log.set_loglevel (default_log_level) --[[doc-- @@ -216,8 +201,7 @@ end --doc]]-- -local starttime = os.gettimeofday() - +local starttime = os.gettimeofday () local trapped_register = callback.register callback.register = dummy_function @@ -431,12 +415,22 @@ loadmodule "override.lua" --- load glyphlist on demand Now we load the modules written for \identifier{luaotfload}. --doc]]-- -loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax -loadmodule "loaders.lua" --- “font-pfb” new in 2.0, added 2011 + +loadmodule "parsers.lua" --- fonts.conf and syntax loadmodule "configuration.lua" --- configuration options -loadmodule "database.lua" --- “font-nms” -names.initialize_env () --- XXX hack hack hack; we need common initialization -loadmodule "colors.lua" --- “font-clr” + +if not config.actions.apply_defaults () then + report ("log", 0, "load", "Configuration unsuccessful.") +end + +loadmodule "loaders.lua" --- Type1 font wrappers +loadmodule "database.lua" --- Font management. +fonts.names.initialize_env () --- XXX hack hack hack; we need common initialization +loadmodule "colors.lua" --- Per-font colors. + +if not config.actions.reconfigure () then + report ("log", 0, "load", "Post-configuration hooks failed.") +end --[[doc-- @@ -673,17 +667,20 @@ create_callback("luaotfload.patch_font", "simple", dummy_function) local read_font_file = fonts.definers.read ---- spec -> size -> id -> tmfdata -local patch_defined_font = function (specification, size, id) - local tfmdata = read_font_file(specification, size, id) - if type(tfmdata) == "table" and tfmdata.shared then - --- We need to test for the “shared” field here - --- or else the fontspec capheight callback will - --- operate on tfm fonts. - call_callback("luaotfload.patch_font", tfmdata, specification) - end - return tfmdata -end +local definers = { + generic = read_font_file, + --- spec -> size -> id -> tmfdata + patch = function (specification, size, id) + local tfmdata = read_font_file (specification, size, id) + if type (tfmdata) == "table" and tfmdata.shared then + --- We need to test for the “shared” field here + --- or else the fontspec capheight callback will + --- operate on tfm fonts. + call_callback ("luaotfload.patch_font", tfmdata, specification) + end + return tfmdata + end, +} reset_callback "define_font" @@ -693,23 +690,12 @@ reset_callback "define_font" --doc]]-- -local font_definer = luaotfloadconfig.definer - -if font_definer == "generic" then - add_to_callback("define_font", - fonts.definers.read, - "luaotfload.define_font", - 1) -elseif font_definer == "patch" then - add_to_callback("define_font", - patch_defined_font, - "luaotfload.define_font", - 1) -end +local definer = config.luaotfload.run.definer +add_to_callback ("define_font", definers[definer], "luaotfload.define_font", 1) -loadmodule "features.lua" --- contains what was “font-ltx” and “font-otc” +loadmodule "features.lua" --- font request and feature handling loadmodule "letterspace.lua" --- extra character kerning -loadmodule "auxiliary.lua" --- additionaly high-level functionality (new) +loadmodule "auxiliary.lua" --- additional high-level functionality luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 04fa67d..f54c1ff 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -89,11 +89,19 @@ string.quoted = string.quoted or function (str) return string.format("%q",str) end -require(loader_path) +require (loader_path) -config = config or { } -local config = config -config.luaotfload = config.luaotfload or { } +--[[doc-- + + XXX: + Creating the config table will be moved to the common + initialization when the times comes. + +--doc]]-- + +config = config or { } +local config = config +config.luaotfload = config.luaotfload or { } config.lualibs = config.lualibs or { } config.lualibs.verbose = false @@ -744,15 +752,11 @@ actions.loglevel = function (job) end actions.config = function (job) - local defaults = luaotfload.config.defaults - local vars = luaotfload.config.read (job.extra_config) - config.luaotfload = luaotfload.config.apply (defaults, vars) + local defaults = luaotfload.default_config + local vars = config.actions.read (job.extra_config) + config.luaotfload = config.actions.apply (defaults, vars) - if luaotfload.config.reconfigure () then - --inspect (vars) - inspect (config.luaotfload) - return true, false - else + if not config.actions.reconfigure () then return false, false end names.initialize_env () -- cgit v1.2.3 From b04717c34e036929a6efff04eaa3aab27581baa7 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 24 Apr 2014 23:34:07 +0200 Subject: [tool] adapt help message --- src/luaotfload-tool.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index f54c1ff..acddcb6 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -253,10 +253,9 @@ Enter 'luaotfload-tool --help' for a larger list of options. local help_msg = function (version) local template = help_messages[version] local pathdata = names.path - local paths = config.luaotfload.paths - local names_plain = paths.index.lua + local names_plain = pathdata.index.lua local names_gzip = names_plain .. ".gz" - local names_bin = paths.index.luc + local names_bin = pathdata.index.luc iowrite(stringformat(template, luaotfload.self, -- cgit v1.2.3 From 30100703fb61f7ca421a41615f7b9db32bd362e2 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 24 Apr 2014 23:36:07 +0200 Subject: =?UTF-8?q?[main]=20register=20as=20=E2=80=9Cluaotfload-main?= =?UTF-8?q?=E2=80=9D=20to=20silence=20luatexbase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/luaotfload-main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 836768a..a8d6ef9 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -50,7 +50,7 @@ local luaotfload = luaotfload luaotfload.log = luaotfload.log or { } luaotfload.module = { - name = "luaotfload", + name = "luaotfload-main", version = 2.50000, date = "2014/**/**", description = "OpenType layout system.", -- cgit v1.2.3 From b1856d2be074479aac62f6e89e1fafabb809e590 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 24 Apr 2014 23:38:03 +0200 Subject: [main] trivial: make local imports legible --- src/luaotfload-main.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index a8d6ef9..a752c9a 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -67,10 +67,10 @@ local type, next = type, next local kpsefind_file = kpse.find_file local lfsisfile = lfs.isfile -local add_to_callback, create_callback = - luatexbase.add_to_callback, luatexbase.create_callback -local reset_callback, call_callback = - luatexbase.reset_callback, luatexbase.call_callback +local add_to_callback = luatexbase.add_to_callback +local create_callback = luatexbase.create_callback +local reset_callback = luatexbase.reset_callback +local call_callback = luatexbase.call_callback local dummy_function = function () end --- XXX this will be moved to the luaotfload namespace when we have the init module -- cgit v1.2.3 From 2cdf631f45646e7e2f4a5cf05045cc6f5167d51e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 24 Apr 2014 23:42:35 +0200 Subject: [main] trivial: improve Luatex minimum version test --- src/luaotfload-main.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index a752c9a..2e65788 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -98,18 +98,18 @@ luaotfload.log.tex = { --doc]]-- -local luatex_version = 76 +local min_luatex_version = 76 -if tex.luatexversion < luatex_version then - warning("LuaTeX v%.2f is old, v%.2f is recommended.", - tex.luatexversion/100, - luatex_version /100) +if tex.luatexversion < min_luatex_version then + warning ("LuaTeX v%.2f is old, v%.2f or later is recommended.", + tex.luatexversion / 100, + min_luatex_version / 100) --- we install a fallback for older versions as a safety if not node.end_of_math then - local math_t = node.id"math" + local math_t = node.id "math" local traverse_nodes = node.traverse_id node.end_of_math = function (n) - for n in traverse_nodes(math_t, n.next) do + for n in traverse_nodes (math_t, n.next) do return n end end -- cgit v1.2.3 From 00a1594b5ba192ab99df328eff5fa5781ecc4d2b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 24 Apr 2014 23:49:14 +0200 Subject: [db] add resolvers to fonts.names namespace --- src/luaotfload-database.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 6b59aac..3191a0b 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -3488,6 +3488,8 @@ names.set_location_precedence = set_location_precedence names.count_font_files = count_font_files names.nth_font_filename = nth_font_filename names.font_slice = font_slice +names.resolve_cached = resolve_cached +names.resolve_name = resolve_name --- font cache names.purge_cache = purge_cache -- cgit v1.2.3 From 35e7b448e25f9e51a1299dc5cc60d2427d1ba818 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 24 Apr 2014 23:49:32 +0200 Subject: [conf] remove no-op --- src/luaotfload-configuration.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 93a358f..9c634d4 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -167,10 +167,8 @@ local set_name_resolver = function () if config.luaotfload.db.resolver == "cached" then logreport ("both", 2, "cache", "Caching of name: lookups active.") names.resolvespec = names.resolve_cached - names.resolve_name = names.resolve_cached else names.resolvespec = names.resolve_name - names.resolve_name = names.resolve_name end end return true -- cgit v1.2.3 From 87a18b1385337140635efc843ed37746487f10f9 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 25 Apr 2014 07:50:00 +0200 Subject: [tool] set up interface between the configuration system and the command line parser --- src/luaotfload-tool.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index acddcb6..ba05411 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -259,7 +259,6 @@ local help_msg = function (version) iowrite(stringformat(template, luaotfload.self, --- names_plain, names_gzip, names_bin, caches.getwritablepath (config.luaotfload.cache_dir))) @@ -754,7 +753,10 @@ actions.config = function (job) local defaults = luaotfload.default_config local vars = config.actions.read (job.extra_config) config.luaotfload = config.actions.apply (defaults, vars) + config.luaotfload = config.actions.apply (config.luaotfload, job.config) + --inspect(config.luaotfload) + --os.exit() if not config.actions.reconfigure () then return false, false end @@ -1390,6 +1392,7 @@ local process_cmdline = function ( ) -- unit -> jobspec query = "", log_level = 0, --- 2 is approx. the old behavior bisect = nil, + config = { db = { }, misc = { }, run = { }, paths = { } }, } local long_options = { @@ -1508,7 +1511,8 @@ local process_cmdline = function ( ) -- unit -> jobspec action_pending["diagnose"] = true result.asked_diagnostics = optarg[n] elseif v == "formats" then - names.set_font_filter (optarg[n]) + result.config.db.formats = optarg[n] + --names.set_font_filter (optarg[n]) elseif v == "n" then config.luaotfload.db.update_live = false elseif v == "S" then -- cgit v1.2.3 From 9d65d315f712d3a5bd710639dad96cf273123746 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 26 Apr 2014 12:34:17 +0200 Subject: [features] respect feature order --- src/luaotfload-features.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua index 4237d71..90487c0 100644 --- a/src/luaotfload-features.lua +++ b/src/luaotfload-features.lua @@ -1039,6 +1039,7 @@ local function addfeature(data,feature,specifications) local subtables = specification.subtables or { specification.data } or { } local featuretype = types[specification.type or "substitution"] local featureflags = specification.flags or noflags + local featureorder = specification.order or { feature } local added = false local featurename = stringformat("ctx_%s_%s",feature,s) local st = { } @@ -1107,6 +1108,7 @@ local function addfeature(data,feature,specifications) features = { [feature] = askedfeatures }, flags = featureflags, name = featurename, + order = featureorder, subtables = st, type = featuretype, } @@ -1139,6 +1141,7 @@ local function addfeature(data,feature,specifications) end end + otf.enhancers.addfeature = addfeature local extrafeatures = { } @@ -1167,6 +1170,7 @@ local tlig = { [0x0060] = 0x2018, -- quoteright }, flags = { }, + order = { "tlig" }, }, { type = "ligature", @@ -1185,6 +1189,7 @@ local tlig = { [0x00BB] = {0x003E, 0x003E}, -- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK }, flags = { }, + order = { "tlig" }, }, { type = "ligature", @@ -1196,6 +1201,7 @@ local tlig = { [0x00BF] = {0x003F, 0x0060}, -- questiondown }, flags = { }, + order = { "tlig" }, }, } @@ -1247,6 +1253,7 @@ local anum_specification = { features = { arab = { far = true, urd = true, snd = true } }, data = anum_persian, flags = { }, + order = { "anum" }, valid = valid, }, { @@ -1254,23 +1261,16 @@ local anum_specification = { features = { arab = { ["*"] = true } }, data = anum_arabic, flags = { }, + order = { "anum" }, valid = valid, }, } ---[[doc-- - - Below the specifications as given in the removed font-otc.lua. - The rest was identical to what this file had from the beginning. - Both make the “anum.tex” test pass anyways. - ---doc]]-- - -otf.addfeature("anum",anum_specification) +otf.addfeature ("anum", anum_specification) registerotffeature { - name = 'anum', - description = 'arabic digits', + name = "anum", + description = "arabic digits", } -- vim:tw=71:sw=4:ts=4:expandtab -- cgit v1.2.3 From eb98a810de3fe122becdbe2390fec7dd87399970 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 5 May 2014 22:15:25 +0200 Subject: db: enforce db.update_live option --- src/luaotfload-database.lua | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 3191a0b..4fbf797 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -656,6 +656,12 @@ crude_file_lookup_verbose = function (filename) return file.addsuffix(filename, format), format, true end end + + if not fonts_reloaded and config.luaotfload.db.update_live == true then + return reload_db (stringformat ("File not found: %s.", filename), + crude_file_lookup_verbose, + filename) + end return filename, nil, false end @@ -713,6 +719,11 @@ crude_file_lookup = function (filename) end end + if not fonts_reloaded and config.luaotfload.db.update_live == true then + return reload_db (stringformat ("File not found: %s.", filename), + crude_file_lookup_verbose, + filename) + end return filename, nil, false end @@ -1129,8 +1140,9 @@ resolve_name = function (specification) end if not resolved then - if not fonts_reloaded then - return reload_db ("Font not found.", + if not fonts_reloaded and config.luaotfload.db.update_live == true then + return reload_db (stringformat ("Font %s not found.", + specification.name or ""), resolve_name, specification) end @@ -1171,7 +1183,7 @@ reload_db = function (why, caller, ...) local namedata = name_index local formats = tableconcat (namedata.meta.formats, ",") - report ("both", 1, "db", + report ("both", 0, "db", "Reload initiated (formats: %s); reason: %q.", formats, why) @@ -1223,7 +1235,7 @@ find_closest = function (name, limit) if not name_index then name_index = load_names () end if not name_index or type (name_index) ~= "table" then if not fonts_reloaded then - return reload_db("no database", find_closest, name) + return reload_db("Font index missing.", find_closest, name) end return false end -- cgit v1.2.3 From 741914e55050601ea7aa79a25487ab84ff4d35ae Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 5 May 2014 22:51:17 +0200 Subject: tool: include host info in version message --- src/luaotfload-tool.lua | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index ba05411..df19ca1 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -114,6 +114,7 @@ local lfsisdir = lfs.isdir local lfsisfile = lfs.isfile local stringsplit = string.split local tableserialize = table.serialize +local tablesortedkeys = table.sortedkeys local tabletohash = table.tohash --[[doc-- @@ -272,7 +273,8 @@ local about = [[ ]] local version_msg = function ( ) - local out = function (...) texiowrite_nl (stringformat (...)) end + local out = function (...) texiowrite_nl (stringformat (...)) end + local uname = os.uname () out (about, luaotfload.self) out ("%s version %q", luaotfload.self, version) out ("revision %q", luaotfloadstatus.notes.revision) @@ -282,6 +284,13 @@ local version_msg = function ( ) out ("Luatex version %.2f.%d", status.luatex_version / 100, status.luatex_revision) + out ("Platform: type=%s name=%s", os.type, os.name) + + local uname_vars = tablesortedkeys (uname) + for i = 1, #uname_vars do + local var = uname_vars[i] + out (" + %8s: %s", var, uname[var]) + end out "" end @@ -354,7 +363,7 @@ local baseindent = " " local show_info_table show_info_table = function (t, depth) depth = depth or 0 local indent = stringrep (baseindent, depth) - local keys = table.sortedkeys (t) + local keys = tablesortedkeys (t) for n = 1, #keys do local key = keys [n] local val = t [key] @@ -777,7 +786,7 @@ end actions.blacklist = function (job) names.read_blacklist() local n = 0 - for n, entry in next, table.sortedkeys(names.blacklist) do + for n, entry in next, tablesortedkeys(names.blacklist) do iowrite (stringformat("(%d %s)\n", n, entry)) end return true, false -- cgit v1.2.3 From bcb51bf7ed08f1055b96d9f3287a36b656ac0bc5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 12 May 2014 08:04:16 +0200 Subject: [fontloader] sync with Context as of 2014-05-12 --- src/luaotfload-fontloader.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index d4311b2..b15b235 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 04/15/14 09:51:32 +-- merge date : 05/08/14 11:18:23 do -- begin closure to overcome local limits and interference @@ -4858,6 +4858,7 @@ end local fonts=fonts fonts.encodings={} fonts.encodings.agl={} +fonts.encodings.known={} setmetatable(fonts.encodings.agl,{ __index=function(t,k) if k=="unicodes" then texio.write(" ") -- cgit v1.2.3 From f6a700f3e8149558b8fbadf5a7ebfc9e78ba1b7d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 12 May 2014 18:58:38 +0200 Subject: [fontloader] sync with Context as of 2014-05-12 --- src/luaotfload-fontloader.lua | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index b15b235..381c5ca 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 05/08/14 11:18:23 +-- merge date : 05/12/14 16:53:28 do -- begin closure to overcome local limits and interference @@ -4170,6 +4170,7 @@ function constructors.scale(tfmdata,specification) if changed then local c=changed[unicode] if c then +local ligatures=character.ligatures description=descriptions[c] or descriptions[unicode] or character character=characters[c] or character index=description.index or c @@ -4181,6 +4182,9 @@ function constructors.scale(tfmdata,specification) touni=tounicode[i] end end +if ligatures and not character.ligatures then + character.ligatures=ligatures +end else description=descriptions[unicode] or character index=description.index or unicode @@ -8543,13 +8547,14 @@ local function gref(descriptions,n) return f_unicode(n) end elseif n then - local num,nam={},{} - for i=2,#n do + local num,nam,j={},{},0 + for i=1,#n do local ni=n[i] if tonumber(ni) then + j=j+1 local di=descriptions[ni] - num[i]=f_unicode(ni) - nam[i]=di and di.name or "-" + num[j]=f_unicode(ni) + nam[j]=di and di.name or "-" end end return f_unilist(num,nam) @@ -8632,8 +8637,8 @@ local function finalize_ligatures(tfmdata,ligatures) local ligature=ligatures[i] if ligature then local unicode,lookupdata=ligature[1],ligature[2] - if trace then - trace_ligatures_detail("building % a into %a",lookupdata,unicode) + if trace_ligatures_detail then + report_prepare("building % a into %a",lookupdata,unicode) end local size=#lookupdata local firstcode=lookupdata[1] @@ -8645,8 +8650,8 @@ local function finalize_ligatures(tfmdata,ligatures) local firstdata=characters[firstcode] if not firstdata then firstcode=private - if trace then - trace_ligatures_detail("defining %a as %a",firstname,firstcode) + if trace_ligatures_detail then + report_prepare("defining %a as %a",firstname,firstcode) end unicodes[firstname]=firstcode firstdata={ intermediate=true,ligatures={} } @@ -8669,8 +8674,8 @@ local function finalize_ligatures(tfmdata,ligatures) break end end - if trace then - trace_ligatures_detail("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target) + if trace_ligatures_detail then + report_prepare("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target) end local firstligs=firstdata.ligatures if firstligs then @@ -8681,6 +8686,8 @@ local function finalize_ligatures(tfmdata,ligatures) firstcode=target firstname=secondname end + elseif trace_ligatures_detail then + report_prepare("no glyph (%a,%a) for building %a",firstname,firstcode,target) end if okay then ligatures[i]=false @@ -8690,12 +8697,14 @@ local function finalize_ligatures(tfmdata,ligatures) end alldone=done==0 end - if trace then - for k,v in next,characters do - if v.ligatures then table.print(v,k) end + if trace_ligatures_detail then + for k,v in table.sortedhash(characters) do + if v.ligatures then + table.print(v,k) + end end end - tfmdata.resources.private=private + resources.private=private end end local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) @@ -8914,7 +8923,7 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis end changed[unicode]=data elseif lookuptype=="alternate" then - local replacement=data[alternate] + local replacement=data[alternate] if replacement then changed[unicode]=replacement if trace_alternatives then -- cgit v1.2.3 From de38f10275c160589d0cbbcac4d9141dd6a3ab72 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 12 May 2014 19:27:14 +0200 Subject: [features] adapt tlig feature to extended ligature spec --- src/luaotfload-features.lua | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua index 90487c0..1200d9e 100644 --- a/src/luaotfload-features.lua +++ b/src/luaotfload-features.lua @@ -6,7 +6,8 @@ if not modules then modules = { } end modules ["features"] = { license = "see context related readme files" } -local type, next = type, next +local type = type +local next = next local tonumber = tonumber local tostring = tostring @@ -16,6 +17,11 @@ local P = lpeg.P local R = lpeg.R local C = lpeg.C +local table = table +local tabletohash = table.tohash +local setmetatableindex = table.setmetatableindex +local insert = table.insert + ---[[ begin included font-ltx.lua ]] --- this appears to be based in part on luatex-fonts-def.lua @@ -755,7 +761,7 @@ local verbosebaselines = swapped(baselines) --doc]]-- -local support_incomplete = table.tohash({ +local support_incomplete = tabletohash({ "deva", "beng", "guru", "gujr", "orya", "taml", "telu", "knda", "mlym", "sinh", @@ -983,7 +989,6 @@ local report_otf = logs.reporter("fonts","otf loading") local otf = fonts.handlers.otf local registerotffeature = otf.features.register -local setmetatableindex = table.setmetatableindex --[[HH-- @@ -1100,10 +1105,10 @@ local function addfeature(data,feature,specifications) -- script = { lang1, lang2, lang3 } or script = { lang1 = true, ... } for k, v in next, askedfeatures do if v[1] then - askedfeatures[k] = table.tohash(v) + askedfeatures[k] = tabletohash(v) end end - sequences[#sequences+1] = { + local sequence = { chain = 0, features = { [feature] = askedfeatures }, flags = featureflags, @@ -1112,6 +1117,11 @@ local function addfeature(data,feature,specifications) subtables = st, type = featuretype, } + if specification.prepend then + insert(sequences,1,sequence) + else + insert(sequences,sequence) + end -- register in metadata (merge as there can be a few) if not gsubfeatures then gsubfeatures = { } @@ -1171,6 +1181,7 @@ local tlig = { }, flags = { }, order = { "tlig" }, + prepend = true, }, { type = "ligature", @@ -1190,6 +1201,7 @@ local tlig = { }, flags = { }, order = { "tlig" }, + prepend = true, }, { type = "ligature", @@ -1202,6 +1214,7 @@ local tlig = { }, flags = { }, order = { "tlig" }, + prepend = true, }, } -- cgit v1.2.3 From 12547b5d6f57b0c8032b1029ed90352f5ac39bdb Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 14 May 2014 22:34:48 +0200 Subject: [conf,tool] move status import to configuration --- src/luaotfload-configuration.lua | 4 ++++ src/luaotfload-tool.lua | 11 ++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 9c634d4..535db30 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -21,6 +21,9 @@ luaotfload = luaotfload or { } config = config or { } config.luaotfload = { } +local status_file = "luaotfload-status" +local luaotfloadstatus = require (status_file) + local string = string local stringsub = string.sub local stringexplode = string.explode @@ -421,6 +424,7 @@ apply = function (old, new) end end end + result.status = luaotfloadstatus return result end diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index df19ca1..d2b6800 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -150,9 +150,6 @@ require "luaotfload-database" require "alt_getopt" local names = fonts.names -local status_file = "luaotfload-status" -local luaotfloadstatus = require (status_file) -config.luaotfload.status = luaotfloadstatus local sanitize_fontname = names.sanitize_fontname local log = luaotfload.log @@ -759,10 +756,10 @@ actions.loglevel = function (job) end actions.config = function (job) - local defaults = luaotfload.default_config - local vars = config.actions.read (job.extra_config) - config.luaotfload = config.actions.apply (defaults, vars) - config.luaotfload = config.actions.apply (config.luaotfload, job.config) + local defaults = luaotfload.default_config + local vars = config.actions.read (job.extra_config) + config.luaotfload = config.actions.apply (defaults, vars) + config.luaotfload = config.actions.apply (config.luaotfload, job.config) --inspect(config.luaotfload) --os.exit() -- cgit v1.2.3 From de2ebfd1e897e9bea9aee9ecffe1f146dd457adf Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 14 May 2014 22:59:50 +0200 Subject: [conf,db,diagnose] reimplement runtime cache path handling as configuration task --- src/luaotfload-configuration.lua | 53 ++++++++++++++++++++++++++++++++++------ src/luaotfload-database.lua | 40 ++++++++++++++---------------- src/luaotfload-diagnostics.lua | 32 +++++++++++++----------- 3 files changed, 82 insertions(+), 43 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 535db30..c806915 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -54,6 +54,8 @@ local lfsisdir = lfs.isdir local file = file local filejoin = file.join +local filereplacesuffix = file.replacesuffix + local parsers = luaotfload.parsers @@ -61,6 +63,9 @@ local log = luaotfload.log local logreport = log.report local config_parser = parsers.config +local stripslashes = parsers.stripslashes + +local getwritablepath = caches.getwritablepath ------------------------------------------------------------------------------- --- SETTINGS @@ -106,10 +111,14 @@ local default_config = { termwidth = nil, }, paths = { - names_dir = "names", - cache_dir = "fonts", - index_file = "luaotfload-names.lua", - lookups_file = "luaotfload-lookup-cache.lua", + names_dir = "names", + cache_dir = "fonts", + index_file = "luaotfload-names.lua", + lookups_file = "luaotfload-lookup-cache.lua", + lookup_path_lua = nil, + lookup_path_luc = nil, + index_path_lua = nil, + index_path_luc = nil, }, } @@ -182,8 +191,32 @@ local set_loglevel = function () return true end +local build_cache_paths = function () + local paths = config.luaotfload.paths + local prefix = getwritablepath (paths.names_dir, "") + + if not prefix then + luaotfload.error ("Impossible to find a suitable writeable cache...") + return false + end + + prefix = lpegmatch (stripslashes, prefix) + logreport ("log", 0, "conf", "Root cache directory is %s.", prefix) + + local index_file = filejoin (prefix, paths.index_file) + local lookups_file = filejoin (prefix, paths.lookups_file) + + paths.prefix = prefix + paths.index_path_lua = filereplacesuffix (index_file, "lua") + paths.index_path_luc = filereplacesuffix (index_file, "luc") + paths.lookup_path_lua = filereplacesuffix (lookups_file, "lua") + paths.lookup_path_luc = filereplacesuffix (lookups_file, "luc") + return true +end + reconf_tasks = { { "Set the log level" , set_loglevel }, + { "Build cache paths" , build_cache_paths }, { "Check terminal dimensions" , check_termwidth }, { "Set the font filter" , set_font_filter }, { "Install font name resolver", set_name_resolver }, @@ -263,10 +296,14 @@ local option_spec = { }, }, paths = { - names_dir = { in_t = string_t, }, - cache_dir = { in_t = string_t, }, - index_file = { in_t = string_t, }, - lookups_file = { in_t = string_t, }, + names_dir = { in_t = string_t, }, + cache_dir = { in_t = string_t, }, + index_file = { in_t = string_t, }, + lookups_file = { in_t = string_t, }, + lookup_path_lua = { in_t = string_t, }, + lookup_path_luc = { in_t = string_t, }, + index_path_lua = { in_t = string_t, }, + index_path_luc = { in_t = string_t, }, }, } diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 4fbf797..d903941 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -119,7 +119,6 @@ local tablefastcopy = table.fastcopy local tabletofile = table.tofile local tabletohash = table.tohash local tableserialize = table.serialize -local runasscript = caches == nil --- the font loader namespace is “fonts”, same as in Context --- we need to put some fallbacks into place for when running --- as a script @@ -171,25 +170,24 @@ end --- XXX this belongs into the initialization file! local initialize_env = function () - if not runasscript then - local paths = config.luaotfload.paths - local prefix = getwritablepath (paths.names_dir, "") - if not prefix then - luaotfload.error - ("Impossible to find a suitable writeable cache...") - else - prefix = lpegmatch (stripslashes, prefix) - report ("log", 0, "db", - "Root cache directory is %s.", prefix) - end - - local lookup_path = names.path.lookups - local index = names.path.index - local lookups_file = filejoin (prefix, paths.lookups_file) - local index_file = filejoin (prefix, paths.index_file) - lookup_path.lua, lookup_path.luc = make_luanames (lookups_file) - index.lua, index.luc = make_luanames (index_file) - else --- running as script, inject some dummies + local paths = config.luaotfload.paths + local prefix = getwritablepath (paths.names_dir, "") + if not prefix then + luaotfload.error + ("Impossible to find a suitable writeable cache...") + else + prefix = lpegmatch (stripslashes, prefix) + report ("log", 0, "db", + "Root cache directory is %s.", prefix) + end + + local lookup_path = names.path.lookups + local index = names.path.index + local lookups_file = filejoin (prefix, paths.lookups_file) + local index_file = filejoin (prefix, paths.index_file) + lookup_path.lua, lookup_path.luc = make_luanames (lookups_file) + index.lua, index.luc = make_luanames (index_file) + if not caches then caches = { } local dummy_function = function () end log = { report = dummy_function, @@ -3252,7 +3250,7 @@ end --- unit -> bool save_lookups = function ( ) - local path = names.path.lookups + local path = names.path.lookups local luaname, lucname = path.lua, path.luc if fileiswritable (luaname) and fileiswritable (lucname) then tabletofile (luaname, lookup_cache, true) diff --git a/src/luaotfload-diagnostics.lua b/src/luaotfload-diagnostics.lua index 67119de..1293122 100644 --- a/src/luaotfload-diagnostics.lua +++ b/src/luaotfload-diagnostics.lua @@ -9,8 +9,6 @@ ----------------------------------------------------------------------- -- local names = fonts.names -local luatexstatus = status -local status = config.luaotfload.status local kpse = require "kpse" local kpseexpand_path = kpse.expand_path @@ -99,8 +97,9 @@ local check_index = function (errcnt) return errcnt end -local verify_files = function (errcnt, status) +local verify_files = function (errcnt) out "================ verify files =================" + local status = config.luaotfload.status local hashes = status.hashes local notes = status.notes if not hashes or #hashes == 0 then @@ -243,17 +242,23 @@ end local path = names.path -local desired_permissions = { - { "d", {"r","w"}, function () return caches.getwritablepath () end }, - { "d", {"r","w"}, path.globals.prefix }, - { "f", {"r","w"}, path.index.lua .. ".gz" }, - { "f", {"r","w"}, path.index.luc }, - { "f", {"r","w"}, path.lookups.lua }, - { "f", {"r","w"}, path.lookups.luc }, -} +local desired_permissions +local init_desired_permissions = function () + inspect(config.luaotfload.paths) + local paths = config.luaotfload.paths + desired_permissions = { + { "d", {"r","w"}, function () return caches.getwritablepath () end }, + { "d", {"r","w"}, paths.prefix }, + { "f", {"r","w"}, paths.index_path_lua .. ".gz" }, + { "f", {"r","w"}, paths.index_path_luc }, + { "f", {"r","w"}, paths.lookup_path_lua }, + { "f", {"r","w"}, paths.lookup_path_luc }, + } +end local check_permissions = function (errcnt) out [[=============== file permissions ==============]] + if not desired_permissions then init_desired_permissions () end for i = 1, #desired_permissions do local t, spec, path = unpack (desired_permissions[i]) if type (path) == "function" then @@ -337,8 +342,7 @@ else end out ("Requesting <%s>.", request) - local response, code, headers, status - = https.request (request) + local response, code, headers, status = https.request (request) if status ~= alright then out "Request failed!" return false @@ -629,7 +633,7 @@ local diagnose = function (job) end if asked.files == true then - errcnt = verify_files (errcnt, status) + errcnt = verify_files (errcnt) asked.files = nil end -- cgit v1.2.3 From a5164d10bd1205a645e13080d69cdb1a2cc3a155 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 14 May 2014 23:08:40 +0200 Subject: [db,tool,diagnose] eliminat static config hack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Good riddance. We’re now fully migrated to the new, dynamic configuration subsystem with respect to cache paths. --- src/luaotfload-database.lua | 57 ++++++------------------------------------ src/luaotfload-diagnostics.lua | 2 -- src/luaotfload-tool.lua | 9 +++---- 3 files changed, 11 insertions(+), 57 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index d903941..5d89aab 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -133,8 +133,6 @@ names.version = 2.51 names.data = nil --- contains the loaded database names.lookups = nil --- contains the lookup cache -names.path = { index = { }, lookups = { } } - --- string -> (string * string) local make_luanames = function (path) return filereplacesuffix(path, "lua"), @@ -156,50 +154,9 @@ local set_location_precedence = function (precedence) end --[[doc-- - We use the functions in the cache.* namespace that come with the - fontloader (see luat-basics-gen). it’s safe to use for the most part - since most checks and directory creations are already done. It - uses TEXMFCACHE or TEXMFVAR as starting points. - - There is one quirk, though: ``getwritablepath()`` will always - assume that files in subdirectories of the cache tree are writable. - It gives no feedback at all if it fails to open a file in write - mode. This may cause trouble when the index or lookup cache were - created by different user. ---doc]]-- ---- XXX this belongs into the initialization file! -local initialize_env = function () - local paths = config.luaotfload.paths - local prefix = getwritablepath (paths.names_dir, "") - if not prefix then - luaotfload.error - ("Impossible to find a suitable writeable cache...") - else - prefix = lpegmatch (stripslashes, prefix) - report ("log", 0, "db", - "Root cache directory is %s.", prefix) - end + Auxiliary functions - local lookup_path = names.path.lookups - local index = names.path.index - local lookups_file = filejoin (prefix, paths.lookups_file) - local index_file = filejoin (prefix, paths.index_file) - lookup_path.lua, lookup_path.luc = make_luanames (lookups_file) - index.lua, index.luc = make_luanames (index_file) - if not caches then - caches = { } - local dummy_function = function () end - log = { report = dummy_function, - report_status = dummy_function, - report_status_start = dummy_function, - report_status_stop = dummy_function, } - end -end - - ---[[doc-- -Auxiliary functions --doc]]-- --- fontnames contain all kinds of garbage; as a precaution we @@ -522,7 +479,7 @@ local fuzzy_limit = 1 --- display closest only --- bool? -> dbobj load_names = function (dry_run) local starttime = osgettimeofday () - local foundname, data = load_lua_file (names.path.index.lua) + local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua) if data then report ("both", 2, "db", @@ -569,7 +526,7 @@ end --- unit -> unit load_lookups = function ( ) - local foundname, data = load_lua_file(names.path.lookups.lua) + local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua) if data then report("both", 3, "cache", "Lookup cache loaded from %s.", foundname) @@ -3250,8 +3207,8 @@ end --- unit -> bool save_lookups = function ( ) - local path = names.path.lookups - local luaname, lucname = path.lua, path.luc + local paths = config.luaotfload.paths + local luaname, lucname = paths.lookup_path_lua, paths.lookup_path_luc if fileiswritable (luaname) and fileiswritable (lucname) then tabletofile (luaname, lookup_cache, true) osremove (lucname) @@ -3285,8 +3242,8 @@ save_names = function (currentnames) elseif currentnames.meta and currentnames.meta["local"] then return false, "table contains local entries" end - local path = names.path.index - local luaname, lucname = path.lua, path.luc + local paths = config.luaotfload.paths + local luaname, lucname = paths.index_path_lua, paths.index_path_luc if fileiswritable (luaname) and fileiswritable (lucname) then osremove (lucname) local gzname = luaname .. ".gz" diff --git a/src/luaotfload-diagnostics.lua b/src/luaotfload-diagnostics.lua index 1293122..d9b13f5 100644 --- a/src/luaotfload-diagnostics.lua +++ b/src/luaotfload-diagnostics.lua @@ -240,8 +240,6 @@ local check_conformance = function (spec, permissions, errcnt) return errcnt end -local path = names.path - local desired_permissions local init_desired_permissions = function () inspect(config.luaotfload.paths) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index d2b6800..fe6057c 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -250,10 +250,10 @@ Enter 'luaotfload-tool --help' for a larger list of options. local help_msg = function (version) local template = help_messages[version] - local pathdata = names.path - local names_plain = pathdata.index.lua + local paths = config.luaotfload.paths + local names_plain = paths.index_path_lua local names_gzip = names_plain .. ".gz" - local names_bin = pathdata.index.luc + local names_bin = paths.index_path_luc iowrite(stringformat(template, luaotfload.self, @@ -274,7 +274,7 @@ local version_msg = function ( ) local uname = os.uname () out (about, luaotfload.self) out ("%s version %q", luaotfload.self, version) - out ("revision %q", luaotfloadstatus.notes.revision) + out ("revision %q", config.luaotfload.status.notes.revision) out ("database version %q", names.version) out ("Lua interpreter: %s; version %q", runtime[1], runtime[2]) out ("Luatex SVN revision %d", status.luatex_svn) @@ -766,7 +766,6 @@ actions.config = function (job) if not config.actions.reconfigure () then return false, false end - names.initialize_env () return true, true end -- cgit v1.2.3 From 996f42e8bf5413665adaff02f188fbf8a0be7a2d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 08:05:06 +0200 Subject: [main] remove config hack --- src/luaotfload-main.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 2e65788..82192f8 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -425,7 +425,6 @@ end loadmodule "loaders.lua" --- Type1 font wrappers loadmodule "database.lua" --- Font management. -fonts.names.initialize_env () --- XXX hack hack hack; we need common initialization loadmodule "colors.lua" --- Per-font colors. if not config.actions.reconfigure () then -- cgit v1.2.3 From 34ee3fabd8fe483e7773b127d9d7fca96379f07d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 08:12:24 +0200 Subject: [db] always log index and lookup cache locations --- src/luaotfload-database.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 5d89aab..c4075dc 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -482,8 +482,8 @@ load_names = function (dry_run) local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua) if data then - report ("both", 2, "db", - "Font names database loaded", "%s", foundname) + report ("log", 0, "db", + "Font names database loaded from %s", foundname) report ("info", 3, "db", "Loading took %0.f ms.", 1000 * (osgettimeofday () - starttime)) @@ -528,7 +528,8 @@ end load_lookups = function ( ) local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua) if data then - report("both", 3, "cache", + report("log", 0, "cache", "Lookup cache loaded from %s.", foundname) + report("term", 3, "cache", "Lookup cache loaded from %s.", foundname) else report("both", 1, "cache", @@ -3438,7 +3439,6 @@ end --- export functionality to the namespace “fonts.names” ----------------------------------------------------------------------- -names.initialize_env = initialize_env names.set_font_filter = set_font_filter names.flush_lookup_cache = flush_lookup_cache names.save_lookups = save_lookups -- cgit v1.2.3 From 60e5dfe5a271997fe530197bbd810862125fb795 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 08:17:17 +0200 Subject: [main] choose resolver at runtime --- src/luaotfload-database.lua | 2 ++ src/luaotfload-main.lua | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index c4075dc..59e2a4d 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -484,6 +484,8 @@ load_names = function (dry_run) if data then report ("log", 0, "db", "Font names database loaded from %s", foundname) + report ("term", 3, "db", + "Font names database loaded from %s", foundname) report ("info", 3, "db", "Loading took %0.f ms.", 1000 * (osgettimeofday () - starttime)) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 82192f8..d8dc1d1 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -479,7 +479,6 @@ fonts.encodings.known = fonts.encodings.known or { } local resolve_file = names.crude_file_lookup --local resolve_file = names.crude_file_lookup_verbose -local resolve_name = names.resolve_name local file_resolver = function (specification) local name = resolve_file (specification.name) @@ -612,8 +611,7 @@ end --[[doc-- - The \verb|name:| resolver wraps the database function - \luafunction{resolve_name}. + The \verb|name:| resolver. --doc]]-- @@ -621,7 +619,11 @@ end --- generic name resolver. request_resolvers.name = function (specification) - local resolved, subfont = resolve_name (specification) + local resolver = names.resolve_cached + if config.luaotfload.run.resolver == "normal" then + resolver = names.resolve_name + end + local resolved, subfont = resolver (specification) if resolved then specification.resolved = resolved specification.sub = subfont -- cgit v1.2.3 From dfffd3b6d1e0acc5d7bf11457debcc8506d199b4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 20:15:30 +0200 Subject: [fontloader] sync with Context as of 2014-05-15 --- src/luaotfload-fontloader.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 381c5ca..51d861a 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 05/12/14 16:53:28 +-- merge date : 05/15/14 19:52:15 do -- begin closure to overcome local limits and interference @@ -125,7 +125,7 @@ local uppercase=R("AZ") local underscore=P("_") local hexdigit=digit+lowercase+uppercase local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") -local newline=crlf+S("\r\n") +local newline=P("\r")*(P("\n")+P(true))+P("\n") local escaped=P("\\")*anything local squote=P("'") local dquote=P('"') @@ -147,8 +147,8 @@ patterns.utfbom_32_le=utfbom_32_le patterns.utfbom_16_be=utfbom_16_be patterns.utfbom_16_le=utfbom_16_le patterns.utfbom_8=utfbom_8 -patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") -patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") +patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") patterns.utf8one=R("\000\127") patterns.utf8two=R("\194\223")*utf8next patterns.utf8three=R("\224\239")*utf8next*utf8next @@ -4170,7 +4170,7 @@ function constructors.scale(tfmdata,specification) if changed then local c=changed[unicode] if c then -local ligatures=character.ligatures + local ligatures=character.ligatures description=descriptions[c] or descriptions[unicode] or character character=characters[c] or character index=description.index or c @@ -4182,9 +4182,9 @@ local ligatures=character.ligatures touni=tounicode[i] end end -if ligatures and not character.ligatures then - character.ligatures=ligatures -end + if ligatures and not character.ligatures then + character.ligatures=ligatures + end else description=descriptions[unicode] or character index=description.index or unicode -- cgit v1.2.3 From 37a229fdcc8c191ab2603c314cfbcd450a63c6f0 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 21:25:28 +0200 Subject: [tests] integrate new configuration system --- scripts/mktests | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/mktests b/scripts/mktests index 2fed723..703a6b8 100755 --- a/scripts/mktests +++ b/scripts/mktests @@ -35,6 +35,7 @@ require "lualibs" require "luaotfload-basics-gen.lua" require "luaotfload-log.lua" require "luaotfload-parsers" +require "luaotfload-configuration" require "luaotfload-database" local names = fonts.names @@ -292,6 +293,7 @@ tests ["resolve_font_name"] = resolve_font_name local main = function () local failed, total = 0, 0 + config.actions.apply_defaults () for name, test in next, tests do texio.write_nl ("[" .. name .. "]") local newfailed, newtotal = test () -- cgit v1.2.3 From 42591e0bc48cad87eae5194cc165b510085fdae8 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 21:46:50 +0200 Subject: [db] store creation and modification time in meta table --- src/luaotfload-database.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index 59e2a4d..a290e7d 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -268,8 +268,10 @@ This is a sketch of the luaotfload db: optical : (int, int) list; // design size -> index entry } and metadata = { - local : bool; (* set if local fonts were added to the db *) + created : string // creation time formats : string list; // { "otf", "ttf", "ttc", "dfont" } + local : bool; (* set if local fonts were added to the db *) + modified : string // modification time statistics : TODO; // created when built with "--stats" version : float; // index version } @@ -352,9 +354,10 @@ mtx-fonts has in names.tma: --doc]]-- ---- string list -> dbobj +--- string list -> string option -> dbobj -local initialize_namedata = function (formats) +local initialize_namedata = function (formats, created) + local now = os.date "%F %T" return { --families = { }, status = { }, -- was: status; map abspath -> mapping @@ -362,8 +365,10 @@ local initialize_namedata = function (formats) names = { }, -- files = { }, -- created later meta = { - ["local"] = false, + created = created or now, formats = formats, + ["local"] = false, + modified = now, statistics = { }, version = names.version, }, @@ -3139,7 +3144,8 @@ update_names = function (currentnames, force, dry_run) end end - targetnames = initialize_namedata (get_font_filter ()) + targetnames = initialize_namedata (get_font_filter (), + currentnames.meta and currentnames.meta.created) read_blacklist () -- cgit v1.2.3 From 967a1634498f1030924ab8161ed564a9ff9ac538 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 21:56:54 +0200 Subject: [db,tool] add db creation and modification time to version message --- src/luaotfload-database.lua | 7 +++++++ src/luaotfload-tool.lua | 16 ++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index a290e7d..f216f79 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -462,6 +462,7 @@ local get_font_filter local group_modifiers local load_lookups local load_names +local getmetadata local order_design_sizes local ot_fullinfo local read_blacklist @@ -531,6 +532,11 @@ load_names = function (dry_run) return data end +getmetadata = function () + if not name_index then name_index = load_names() end + return tablefastcopy (name_index.meta) +end + --- unit -> unit load_lookups = function ( ) local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua) @@ -3459,6 +3465,7 @@ names.crude_file_lookup_verbose = crude_file_lookup_verbose names.read_blacklist = read_blacklist names.sanitize_fontname = sanitize_fontname names.getfilename = resolve_fullpath +names.getmetadata = getmetadata names.set_location_precedence = set_location_precedence names.count_font_files = count_font_files names.nth_font_filename = nth_font_filename diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index fe6057c..0db3eca 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -6,7 +6,7 @@ -- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang -- VERSION: 2.5 -- LICENSE: GPL v2.0 --- MODIFIED: 2014-01-14 13:17:04+0100 +-- MODIFIED: 2014-05-15 21:47:39+0200 ----------------------------------------------------------------------- luaotfload = luaotfload or { } @@ -272,13 +272,13 @@ local about = [[ local version_msg = function ( ) local out = function (...) texiowrite_nl (stringformat (...)) end local uname = os.uname () + local meta = names.getmetadata () out (about, luaotfload.self) - out ("%s version %q", luaotfload.self, version) - out ("revision %q", config.luaotfload.status.notes.revision) - out ("database version %q", names.version) + out ("%s version: %q", luaotfload.self, version) + out ("Revision: %q", config.luaotfload.status.notes.revision) out ("Lua interpreter: %s; version %q", runtime[1], runtime[2]) - out ("Luatex SVN revision %d", status.luatex_svn) - out ("Luatex version %.2f.%d", + out ("Luatex SVN revision: %d", status.luatex_svn) + out ("Luatex version: %.2f.%d", status.luatex_version / 100, status.luatex_revision) out ("Platform: type=%s name=%s", os.type, os.name) @@ -288,6 +288,10 @@ local version_msg = function ( ) local var = uname_vars[i] out (" + %8s: %s", var, uname[var]) end + out ("Index: version=%q created=%q modified=%q", + config.luaotfload.status.notes.revision, + meta.created or "ages ago", + meta.modified or "ages ago") out "" end -- cgit v1.2.3 From b48fae97f2a4f37bbe46553032d35c6ceb96d1f3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 22:14:44 +0200 Subject: [tests] remove obsolete config --- scripts/mktests | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/scripts/mktests b/scripts/mktests index 703a6b8..b36b6dd 100755 --- a/scripts/mktests +++ b/scripts/mktests @@ -6,7 +6,7 @@ -- REQUIREMENTS: Luatex > 0.76, Luaotfload -- AUTHOR: Philipp Gesang (Phg), -- VERSION: 2.4 --- MODIFIED: 2013-08-26 09:31:22+0200 +-- MODIFIED: 2014-05-15 22:16:47+0200 ----------------------------------------------------------------------- -- --===================================================================-- @@ -16,16 +16,7 @@ --===================================================================-- -local tests = { } - -config = { luaotfload = { - names_dir = "names", - cache_dir = "fonts", - index_file = "luaotfload-names.lua", - resolver = "normal", - update_live = true, --- suppress db updates -}} - +local tests = { } local lpeg = require "lpeg" local lpegmatch = lpeg.match -- cgit v1.2.3 From d36629e9b1e2bd0f4b0740d3d797534a04edd729 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 22:32:38 +0200 Subject: [doc] remove dependency on mdwlist.sty --- doc/luaotfload-latex.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index c886462..3aab22b 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -34,7 +34,7 @@ \makeatletter -\usepackage {metalogo,multicol,mdwlist,fancyvrb,xspace} +\usepackage {metalogo,multicol,fancyvrb,xspace} \usepackage [x11names] {xcolor} \def \primarycolor {DodgerBlue4} %%-> rgb 16 78 139 | #104e8b -- cgit v1.2.3 From d167ce3c9134150a2da2287f162e56690995419e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 22:36:26 +0200 Subject: [doc] remove obsolete paragraph --- doc/luaotfload-main.tex | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index a0df7f0..e4b28dc 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -877,9 +877,6 @@ rebuild of the database. \beginlisting luaotfload-tool --update --force \endlisting -Whenever it is run under this name, it will update the database -first, mimicking the behavior of earlier versions of -\identifier{luaotfload}. \endsubsection -- cgit v1.2.3 From d435ea827827dcb4494ddff8583e5b15d27625b4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 15 May 2014 22:41:12 +0200 Subject: [conf] use default formats if variable empty --- src/luaotfload-configuration.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index c806915..db42f99 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -167,7 +167,11 @@ end local set_font_filter = function () local names = fonts.names if names and names.set_font_filter then - names.set_font_filter (config.luaotfload.db.formats) + local formats = config.luaotfload.db.formats + if not formats or formats == "" then + formats = default_config.db.formats + end + names.set_font_filter (formats) end return true end -- cgit v1.2.3 From d7fc0762b087bcfae22823dc77844db89009a563 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 18 May 2014 08:50:21 +0200 Subject: [fontloader] sync with Context as of 2014-05-18 --- src/luaotfload-basics-gen.lua | 25 +++++++++++++++++++++++++ src/luaotfload-basics-nod.lua | 15 +++++++++++++-- src/luaotfload-fontloader.lua | 11 ++++++++++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/luaotfload-basics-gen.lua b/src/luaotfload-basics-gen.lua index 9cf5b93..c19a49a 100644 --- a/src/luaotfload-basics-gen.lua +++ b/src/luaotfload-basics-gen.lua @@ -254,6 +254,18 @@ function caches.loaddata(paths,name) for i=1,#paths do local data = false local luaname, lucname = makefullname(paths[i],name) + if lucname and not lfs.isfile(lucname) and type(caches.compile) == "function" then + -- in case we used luatex and luajittex mixed ... lub or luc file + texio.write(string.format("(compiling luc: %s)",lucname)) + data = loadfile(luaname) + if data then + data = data() + end + if data then + caches.compile(data,luaname,lucname) + return data + end + end if lucname and lfs.isfile(lucname) then -- maybe also check for size texio.write(string.format("(load luc: %s)",lucname)) data = loadfile(lucname) @@ -341,3 +353,16 @@ end function table.setmetatableindex(t,f) setmetatable(t,{ __index = f }) end + +-- helper for plain: + +arguments = { } + +if arg then + for i=1,#arg do + local k, v = string.match(arg[i],"^%-%-([^=]+)=?(.-)$") + if k and v then + arguments[k] = v + end + end +end diff --git a/src/luaotfload-basics-nod.lua b/src/luaotfload-basics-nod.lua index 50a1e86..373dab5 100644 --- a/src/luaotfload-basics-nod.lua +++ b/src/luaotfload-basics-nod.lua @@ -54,22 +54,33 @@ nodes.handlers = { } local nodecodes = { } for k,v in next, node.types () do nodecodes[string.gsub(v,"_","")] = k end local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gsub(v,"_","")] = k end local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" } +local disccodes = { [0] = "discretionary", "explicit", "automatic", "regular", "first", "second" } nodes.nodecodes = nodecodes nodes.whatcodes = whatcodes nodes.whatsitcodes = whatcodes nodes.glyphcodes = glyphcodes +nodes.disccodes = disccodes local free_node = node.free local remove_node = node.remove local new_node = node.new local traverse_id = node.traverse_id -local math_code = nodecodes.math - nodes.handlers.protectglyphs = node.protect_glyphs nodes.handlers.unprotectglyphs = node.unprotect_glyphs +local math_code = nodecodes.math +local end_of_math = node.end_of_math + +function node.end_of_math(n) + if n.id == math_code and n.subtype == 1 then + return n + else + return end_of_math(n) + end +end + function nodes.remove(head, current, free_too) local t = current head, current = remove_node(head,current) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 51d861a..f7801ea 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 05/15/14 19:52:15 +-- merge date : 05/17/14 23:46:22 do -- begin closure to overcome local limits and interference @@ -3460,6 +3460,15 @@ end function table.setmetatableindex(t,f) setmetatable(t,{ __index=f }) end +arguments={} +if arg then + for i=1,#arg do + local k,v=string.match(arg[i],"^%-%-([^=]+)=?(.-)$") + if k and v then + arguments[k]=v + end + end +end end -- closure -- cgit v1.2.3 From 773ad91399a0e4c8c1c9525f2454ee7c8e99b03e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 18 May 2014 19:28:55 +0200 Subject: [fontloader] sync with Context as of 2014-05-18 --- src/luaotfload-fontloader.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index f7801ea..4dfefd6 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 05/17/14 23:46:22 +-- merge date : 05/18/14 15:57:13 do -- begin closure to overcome local limits and interference @@ -2853,7 +2853,11 @@ local format_f=function(f) end local format_F=function(f) n=n+1 - return format("((a%s == 0 and '0') or (a%s == 1 and '1') or format('%%%sf',a%s))",n,n,f,n) + if not f or f=="" then + return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or (a%s == 1 and '1') or format('%%.9f',a%s))",n,n,n,n) + else + return format("((a%s == 0 and '0') or (a%s == 1 and '1') or format('%%%sf',a%s))",n,n,f,n) + end end local format_g=function(f) n=n+1 -- cgit v1.2.3 From aca3f50f6806f838b6acbaa2f7012fc6b12d5080 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 18 May 2014 20:57:45 +0200 Subject: [main] log name: requests independent of fontloader --- src/luaotfload-auxiliary.lua | 4 ++-- src/luaotfload-features.lua | 4 ++-- src/luaotfload-main.lua | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/luaotfload-auxiliary.lua b/src/luaotfload-auxiliary.lua index 716af98..14c2ab5 100644 --- a/src/luaotfload-auxiliary.lua +++ b/src/luaotfload-auxiliary.lua @@ -54,7 +54,7 @@ local start_rewrite_fontname = function () rewrite_fontname, "luaotfload.rewrite_fontname") rewriting = true - report ("log", 0, "aux", + report ("log", 1, "aux", "start rewriting tfmdata.name field") end end @@ -66,7 +66,7 @@ local stop_rewrite_fontname = function () luatexbase.remove_fromt_callback ("luaotfload.patch_font", "luaotfload.rewrite_fontname") rewriting = false - report ("log", 0, "aux", + report ("log", 1, "aux", "stop rewriting tfmdata.name field") end end diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua index 1200d9e..4ad188f 100644 --- a/src/luaotfload-features.lua +++ b/src/luaotfload-features.lua @@ -808,13 +808,13 @@ local set_default_features = function (speclist) end speclist.script = script - report("log", 0, "load", + report("log", 1, "load", "auto-selecting default features for script: %s", script) local requested = defaults[script] if not requested then - report("log", 0, "load", + report("log", 1, "load", "no defaults for script %q, falling back to \"dflt\"", script) requested = defaults.dflt diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index d8dc1d1..dba1f52 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -565,7 +565,7 @@ request_resolvers.path = function (specification) local name = specification.name local exists, _ = lfsisfile(name) if not exists then -- resort to file: lookup - report ("log", 1, "load", + report ("log", 0, "load", "path lookup of %q unsuccessful, falling back to file:", name) file_resolver (specification) @@ -625,6 +625,8 @@ request_resolvers.name = function (specification) end local resolved, subfont = resolver (specification) if resolved then + report ("log", 0, "load", "Lookup/name: %q -> \"%s(%d)\"", + specification.name, resolved, subfont) specification.resolved = resolved specification.sub = subfont specification.forced = filesuffix (resolved) -- cgit v1.2.3 From 0e06af55b50b2314c5d99db4843c1881dd89a88b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 18 May 2014 21:25:24 +0200 Subject: [main] lowercase file suffix when forcing file format (thanks, Bryan!) Closing https://github.com/lualatex/luaotfload/issues/215 --- src/luaotfload-main.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index dba1f52..4baa225 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -63,6 +63,7 @@ local luatexbase = luatexbase local setmetatable = setmetatable local type, next = type, next +local stringlower = string.lower local kpsefind_file = kpse.find_file local lfsisfile = lfs.isfile @@ -477,14 +478,14 @@ fonts.encodings.known = fonts.encodings.known or { } --doc]]-- -local resolve_file = names.crude_file_lookup ---local resolve_file = names.crude_file_lookup_verbose +--local resolve_file = names.crude_file_lookup +local resolve_file = names.crude_file_lookup_verbose local file_resolver = function (specification) local name = resolve_file (specification.name) local suffix = filesuffix(name) if formats[suffix] then - specification.forced = suffix + specification.forced = stringlower (suffix) specification.forcedname = file.removesuffix(name) else specification.name = name @@ -522,7 +523,7 @@ request_resolvers.file = file_resolver --doc]]-- -local type1_formats = { "tfm", "ofm", } +local type1_formats = { "tfm", "ofm", "TFM", "OFM", } request_resolvers.anon = function (specification) local name = specification.name @@ -572,7 +573,7 @@ request_resolvers.path = function (specification) else local suffix = filesuffix (name) if formats[suffix] then - specification.forced = suffix + specification.forced = stringlower (suffix) specification.name = file.removesuffix(name) specification.forcedname = name else @@ -595,7 +596,7 @@ request_resolvers.kpse = function (specification) if suffix and formats[suffix] then name = file.removesuffix(name) if resolvers.findfile(name, suffix) then - specification.forced = suffix + specification.forced = stringlower (suffix) specification.forcedname = name return end @@ -629,7 +630,7 @@ request_resolvers.name = function (specification) specification.name, resolved, subfont) specification.resolved = resolved specification.sub = subfont - specification.forced = filesuffix (resolved) + specification.forced = stringlower (filesuffix (resolved) or "") specification.forcedname = resolved specification.name = fileremovesuffix (resolved) else -- cgit v1.2.3 From 84619758adf39e49553662b4a5af806c1527001e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 31 May 2014 13:32:46 +0200 Subject: [fontloader] sync with Context as of 2014-05-31 --- src/luaotfload-fontloader.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 4dfefd6..1732a23 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 05/18/14 15:57:13 +-- merge date : 05/30/14 23:26:41 do -- begin closure to overcome local limits and interference @@ -2851,12 +2851,12 @@ local format_f=function(f) n=n+1 return format("format('%%%sf',a%s)",f,n) end -local format_F=function(f) +local format_F=function() n=n+1 if not f or f=="" then - return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or (a%s == 1 and '1') or format('%%.9f',a%s))",n,n,n,n) + return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n) else - return format("((a%s == 0 and '0') or (a%s == 1 and '1') or format('%%%sf',a%s))",n,n,f,n) + return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n) end end local format_g=function(f) -- cgit v1.2.3 From 467bddf27f4c158e04cfefa3d68d64b58a290a72 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 4 Jun 2014 22:15:12 +0200 Subject: aux: add helpers for accessing the index table/index file --- src/luaotfload-auxiliary.lua | 41 +++++++++++++++++++++++++++++++++++++++-- src/luaotfload-database.lua | 14 ++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-auxiliary.lua b/src/luaotfload-auxiliary.lua index 14c2ab5..89bf51b 100644 --- a/src/luaotfload-auxiliary.lua +++ b/src/luaotfload-auxiliary.lua @@ -20,6 +20,7 @@ local log = luaotfload.log local report = log.report local fonthashes = fonts.hashes local identifiers = fonthashes.identifiers +local fontnames = fonts.names local fontid = font.id local texsprint = tex.sprint @@ -590,8 +591,8 @@ aux.sprint_math_dimension = sprint_math_dimension --- extra database functions ----------------------------------------------------------------------- -local namesresolve = fonts.names.resolve -local namesscan_dir = fonts.names.scan_dir +local namesresolve = fontnames.resolve +local namesscan_dir = fontnames.scan_dir --[====[-- TODO -> port this to new db model @@ -663,6 +664,42 @@ end aux.resolve_fontlist = resolve_fontlist +--- index access ------------------------------------------------------ + +--- Based on a discussion on the Luatex mailing list: +--- http://tug.org/pipermail/luatex/2014-June/004881.html + +--[[doc-- + + aux.read_font_index -- Read the names index from the canonical + location and return its contents. This does not affect the behavior + of Luaotfload: The returned table is independent of what the font + resolvers use internally. Access is raw: each call to the function + will result in the entire table being re-read from disk. + +--doc]]-- + +local load_names = fontnames.load +local access_font_index = fontnames.access_font_index + +local read_font_index = function () + return load_names (true) or { } +end + +--[[doc-- + + aux.font_index -- Access Luaotfload’s internal database. If the + database hasn’t been loaded yet this will cause it to be loaded, with + all the possible side-effects like for instance creating the index + file if it doesn’t exist, reading all font files, &c. + +--doc]]-- + +local font_index = function () return access_font_index () end + +aux.read_font_index = read_font_index +aux.font_index = font_index + --- loaded fonts ------------------------------------------------------ --- just a proof of concept diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index f216f79..e171c52 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -452,6 +452,7 @@ local load_lua_file = function (path) end --- define locals in scope +local access_font_index local collect_families local crude_file_lookup local crude_file_lookup_verbose @@ -532,6 +533,18 @@ load_names = function (dry_run) return data end +--[[doc-- + + access_font_index -- Provide a reference of the index table. Will + cause the index to be loaded if not present. + +--doc]]-- + +access_font_index = function () + if not name_index then name_index = load_names () end + return name_index +end + getmetadata = function () if not name_index then name_index = load_names() end return tablefastcopy (name_index.meta) @@ -3457,6 +3470,7 @@ names.set_font_filter = set_font_filter names.flush_lookup_cache = flush_lookup_cache names.save_lookups = save_lookups names.load = load_names +names.access_font_index = access_font_index names.data = function () return name_index end names.save = save_names names.update = update_names -- cgit v1.2.3 From 50e033bd254a087fccbe1a0bcaef208f5e106b2a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 4 Jun 2014 22:58:08 +0200 Subject: doc: document user access to font index --- doc/luaotfload-main.tex | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex index e4b28dc..034b704 100644 --- a/doc/luaotfload-main.tex +++ b/doc/luaotfload-main.tex @@ -1412,17 +1412,35 @@ are defined for which scripts. \endsubsubsection +\beginsubsubsection{Database} + %% not implemented, may come back later -% \beginsubsubsection{Database} -% -% \beginfunctionlist +\beginfunctionlist % \beginaltitem {aux.scan_external_dir(dir : string)} % Include fonts in directory \luafunction{dir} in font lookups without % adding them to the database. % -% \endfunctionlist -% -% \endsubsubsection + \beginaltitem {aux.read_font_index (void)} + Read the index file from the appropriate location (usually + the bytecode file \fileent{luaotfload-names.luc} somewhere + in the \fileent{texmf-var} tree) and return the result as a + table. The file is processed with each call so it is up to + the user to store the result for later access. + \endaltitem + + \beginaltitem {aux.font_index (void)} + Return a reference of the font names table used internally + by \identifier{luaotfload}. The index will be read if it + has not been loaded up to this point. Also a font scan that + overwrites the current index file might be triggered. Since + the return value points to the actual index, any + modifications to the table might influence runtime behavior + of \identifier{luaotfload}. + \endaltitem + +\endfunctionlist + +\endsubsubsection \endsubsection \endsection @@ -1440,8 +1458,7 @@ target. % The development takes place on \identifier{github} at \hyperlink {https://github.com/lualatex/luaotfload} where there is an issue -tracker for submitting bug reports, feature requests and the likes -requests and the likes. +tracker for submitting bug reports, feature requests and the likes. Bug reports are more likely to be addressed if they contain the output of -- cgit v1.2.3 From 458e2556a1ea7426735486be11523cba8dff177d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 4 Jun 2014 23:16:49 +0200 Subject: doc: fix indentation in listings --- doc/luaotfload-latex.tex | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex index 3aab22b..34c494d 100644 --- a/doc/luaotfload-latex.tex +++ b/doc/luaotfload-latex.tex @@ -244,9 +244,14 @@ \directlua { local texprint = tex.print + local stringsub = string.sub + local backslash = string.byte (0x5c) document = document or { } document.printlines = function (buffer) for _, line in next, string.explode (buffer, "\noexpand\n") do + if stringsub (line, 1, 1) == " " then + line = backslash .. line + end texprint (-1, line) texprint (-1, "") end -- cgit v1.2.3 From 3e542973ad5965223d1da8550a3c573990b2d9d1 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 5 Jun 2014 19:20:31 +0200 Subject: doc: update news --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 2aa319e..4a8da57 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ Change History * Scan local font files (``--local`` flag to luaotfload-tool, flag ``scan_local`` during TeX run). * Add bisection mode (``--bisect``) to luaotfload-tool. + * Add functions for accessing the database: ``aux.font_index()`` and + ``aux.read_font_index()``. 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) -- cgit v1.2.3 From 4edd98f422b10c7f24cab72f0eb9a64c02ab9305 Mon Sep 17 00:00:00 2001 From: Reuben Thomas Date: Fri, 6 Jun 2014 22:45:57 +0100 Subject: Fix issue #218: support XDG_DATA_HOME for nodes --- src/luaotfload-parsers.lua | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua index 1048e1d..d5f64fc 100644 --- a/src/luaotfload-parsers.lua +++ b/src/luaotfload-parsers.lua @@ -207,17 +207,19 @@ local conf_filter = function (path) end --[[doc-- - read_fonts_conf_indeed() is called with six arguments; the + read_fonts_conf_indeed() is called with seven arguments; the latter three are tables that represent the state and are always returned. - The first three are + The first four are · the path to the file · the expanded $HOME - · the expanded $XDG_CONFIG_DIR + · the expanded $XDG_CONFIG_HOME + · the expanded $XDG_DATA_HOME --doc]]-- --- string -> string -> string -> tab -> tab -> (tab * tab * tab) local read_fonts_conf_indeed -read_fonts_conf_indeed = function (start, home, xdg_home, +read_fonts_conf_indeed = function (start, home, xdg_config_home, + xdg_data_home, acc, done, dirs_done, find_files) @@ -230,12 +232,11 @@ read_fonts_conf_indeed = function (start, home, xdg_home, local pathobj = paths[i] local kind, path = pathobj[1], pathobj[2] local attributes = pathobj.attributes - if attributes and attributes.prefix == "xdg" then - --- this prepends the xdg root (usually ~/.config) - path = filejoin(xdg_home, path) - end if kind == "dir" then + if attributes and attributes.prefix == "xdg" then + path = filejoin(xdg_data_home, path) + end if stringsub(path, 1, 1) == "~" then path = filejoin(home, stringsub(path, 2)) end @@ -252,6 +253,9 @@ read_fonts_conf_indeed = function (start, home, xdg_home, end elseif kind == "include" then + if attributes and attributes.prefix == "xdg" then + path = filejoin(xdg_config_home, path) + end --- here the path can be four things: a directory or a file, --- in absolute or relative path. if stringsub(path, 1, 1) == "~" then @@ -268,14 +272,14 @@ read_fonts_conf_indeed = function (start, home, xdg_home, --- we exclude path with texmf in them, as they should --- be found otherwise acc = read_fonts_conf_indeed( - path, home, xdg_home, + path, home, xdg_config_home, xdg_data_home, acc, done, dirs_done) elseif lfsisdir(path) then --- arrow code ahead local config_files = find_files (path, conf_filter) for _, filename in next, config_files do if not done[filename] then acc = read_fonts_conf_indeed( - filename, home, xdg_home, + filename, home, xdg_config_home, xdg_data_home, acc, done, dirs_done) end end @@ -292,10 +296,10 @@ read_fonts_conf_indeed = function (start, home, xdg_home, read_fonts_conf() sets up an accumulator and two sets for tracking what’s been done. - Also, the environment variables HOME and XDG_CONFIG_HOME -- - which are constants anyways -- are expanded so don’t have to - repeat that over and over again as with the old parser. - Now they’re just passed on to every call of + Also, the environment variables HOME, XDG_DATA_HOME and + XDG_CONFIG_HOME -- which are constants anyways -- are expanded + so we don’t have to repeat that over and over again as with the + old parser. Now they’re just passed on to every call of read_fonts_conf_indeed(). read_fonts_conf() is also the only reference visible outside @@ -306,14 +310,17 @@ read_fonts_conf_indeed = function (start, home, xdg_home, local read_fonts_conf = function (path_list, find_files) local home = kpseexpand_path"~" --- could be os.getenv"HOME" - local xdg_home = kpseexpand_path"$XDG_CONFIG_HOME" - if xdg_home == "" then xdg_home = filejoin(home, ".config") end + local xdg_config_home = kpseexpand_path"$XDG_CONFIG_HOME" + if xdg_config_home == "" then xdg_config_home = filejoin(home, ".config") end + local xdg_data_home = kpseexpand_path"$XDG_DATA_HOME" + if xdg_data_home == "" then xdg_data_home = filejoin(home, ".local/share") end local acc = { } ---> list: paths collected local done = { } ---> set: files inspected local dirs_done = { } ---> set: dirs in list for i=1, #path_list do --- we keep the state between files acc, done, dirs_done = read_fonts_conf_indeed( - path_list[i], home, xdg_home, + path_list[i], home, xdg_config_home, + xdg_data_home, acc, done, dirs_done, find_files) end -- cgit v1.2.3 From 335f0d5013de2bb7052b2afb3a6cee3d6c6d18f5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 7 Jun 2014 22:43:04 +0200 Subject: [doc] update news and list of contributors Update for applied PR https://github.com/lualatex/luaotfload/pull/219 --- NEWS | 1 + README | 1 + 2 files changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 4a8da57..b244feb 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ Change History * Add bisection mode (``--bisect``) to luaotfload-tool. * Add functions for accessing the database: ``aux.font_index()`` and ``aux.read_font_index()``. + * Distinguish XDG configuration paths (Reuben Thomas) 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) diff --git a/README b/README index 903551f..75575d2 100644 --- a/README +++ b/README @@ -32,6 +32,7 @@ Elie Roux Will Robertson Philipp Gesang Dohyun Kim +Reuben Thomas Installation -- cgit v1.2.3 From a7a45c7bdb89d3ca81ea5924cdf58d1b33ee45e8 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 9 Jun 2014 22:55:21 +0200 Subject: parsers: remove obsolete comment --- src/luaotfload-configuration.lua | 2 +- src/luaotfload-parsers.lua | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index db42f99..c1db424 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -77,7 +77,7 @@ local kpse_t = 1 local config_paths = { --- needs adapting for those other OS { path_t, "./luaotfloadrc" }, - { path_t, "~/.config/luaotfload/luaotfloadrc" }, + { path_t, "~/.config/luaotfload/luaotfload.conf" }, { path_t, "~/.luaotfloadrc" }, { kpse_t, "luaotfloadrc" }, { kpse_t, "luaotfload.conf" }, diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua index 73be67a..180adac 100644 --- a/src/luaotfload-parsers.lua +++ b/src/luaotfload-parsers.lua @@ -311,9 +311,6 @@ read_fonts_conf_indeed = function (start, home, xdg_config_home, so we don’t have to repeat that over and over again as with the old parser. Now they’re just passed on to every call of read_fonts_conf_indeed(). - - read_fonts_conf() is also the only reference visible outside - the closure. --doc]]-- --- list -> (string -> function option -> string list) -> list -- cgit v1.2.3 From 1780d5eede861e35d05387278e90412ddac13d8f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 9 Jun 2014 23:14:05 +0200 Subject: [main] fix error message --- src/luaotfload-main.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 4baa225..36cbaf6 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -626,8 +626,10 @@ request_resolvers.name = function (specification) end local resolved, subfont = resolver (specification) if resolved then - report ("log", 0, "load", "Lookup/name: %q -> \"%s(%d)\"", - specification.name, resolved, subfont) + report ("log", 0, "load", "Lookup/name: %q -> \"%s%s\"", + specification.name, + resolved, + subfont and stringformat ("(%d)", subfont) or "") specification.resolved = resolved specification.sub = subfont specification.forced = stringlower (filesuffix (resolved) or "") -- cgit v1.2.3 From f35290839badf0df18f46f548c24d6b493c27a90 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 9 Jun 2014 23:16:21 +0200 Subject: [conf] use actual XDG paths for configuration directories --- src/luaotfload-configuration.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index c1db424..ff90f68 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -46,6 +46,7 @@ local lpeg = require "lpeg" local lpegmatch = lpeg.match local kpse = kpse +local kpseexpand_path = kpse.expand_path local kpselookup = kpse.lookup local lfs = lfs @@ -74,11 +75,18 @@ local getwritablepath = caches.getwritablepath local path_t = 0 local kpse_t = 1 +local val_home = kpseexpand_path "~" +local val_xdg_config_home = kpseexpand_path "$XDG_CONFIG_HOME" + +if val_xdg_config_home == "" then val_xdg_config_home = "~/.config" end + local config_paths = { --- needs adapting for those other OS + { path_t, "./luaotfload.conf" }, { path_t, "./luaotfloadrc" }, - { path_t, "~/.config/luaotfload/luaotfload.conf" }, - { path_t, "~/.luaotfloadrc" }, + { path_t, filejoin (val_xdg_config_home, "luaotfload/luaotfload.conf") }, + { path_t, filejoin (val_xdg_config_home, "luaotfload/luaotfloadrc") }, + { path_t, filejoin (val_home, ".luaotfloadrc") }, { kpse_t, "luaotfloadrc" }, { kpse_t, "luaotfload.conf" }, } -- cgit v1.2.3 From 170f83f02682c3e91e784a303de0efb8e90d133d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 9 Jun 2014 23:33:57 +0200 Subject: [doc] add skeleton for luaotfloadrc manpage --- doc/luaotfload.conf.rst | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 doc/luaotfload.conf.rst diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst new file mode 100644 index 0000000..932afad --- /dev/null +++ b/doc/luaotfload.conf.rst @@ -0,0 +1,68 @@ +======================================================================= + luaotfload.conf +======================================================================= + Luaotfload configuration +----------------------------------------------------------------------- + +:Date: 2014-06-09 +:Copyright: GPL v2.0 +:Version: 2.5 +:Manual section: 5 +:Manual group: text processing + +SYNOPSIS +======================================================================= + +**XDG_CONFIG_HOME/luaotfload/luaotfload.conf** +**XDG_CONFIG_HOME/luaotfload/luaotfloadrc** +**~/.luaotfloadrc** + +DESCRIPTION +======================================================================= + + +EXAMPLE +======================================================================= + +* TODO, small example snippet + + +SYNTAX +======================================================================= + +* TODO, short intro to ``.ini`` file syntax + +VARIABLES +======================================================================= + +* TODO, list variables + + +FILES +======================================================================= + +* file locations + + +SEE ALSO +======================================================================= + +**luaotfload-tool** (1), **luatex** (1), **lua** (1) + +* ``texdoc luaotfload`` to display the PDF manual for the *Luaotfload* + package +* Luaotfload development ``_ +* LuaLaTeX mailing list ``_ +* LuaTeX ``_ +* Luaotfload on CTAN ``_ + + +AUTHORS +======================================================================= + +*Luaotfload* is maintained by the LuaLaTeX dev team +(``_). + +This manual page was written by Philipp Gesang +. + -- cgit v1.2.3 From fdea22314cb51cf514f6f1004f75b9d6bacafe2d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 9 Jun 2014 23:47:01 +0200 Subject: [*] add rules for building config man page to makefiles --- Makefile | 26 ++++++++++++++++---------- doc/Makefile | 24 +++++++++++++++++------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 2dc68bd..fd3eefb 100644 --- a/Makefile +++ b/Makefile @@ -25,16 +25,20 @@ RESOURCESCRIPTS = $(GLYPHSCRIPT) $(CHARSCRIPT) $(STATUSSCRIPT) TOOLNAME = luaotfload-tool TOOL = $(SRCSRCDIR)/$(TOOLNAME).lua +CONFNAME = luaotfload.conf + GRAPH = filegraph DOCSRC = $(addprefix $(DOCSRCDIR)/$(NAME), -main.tex -latex.tex) GRAPHSRC = $(DOCSRCDIR)/$(GRAPH).dot -MANSRC = $(DOCSRCDIR)/$(TOOLNAME).rst +MANSRC = $(DOCSRCDIR)/$(TOOLNAME).rst $(DOCSRCDIR)/$(CONFNAME).rst DOCPDF = $(DOCSRCDIR)/$(NAME).pdf DOTPDF = $(DOCSRCDIR)/$(GRAPH).pdf -MANPAGE = $(DOCSRCDIR)/$(TOOLNAME).1 +TOOLMAN = $(DOCSRCDIR)/$(TOOLNAME).1 +CONFMAN = $(DOCSRCDIR)/$(CONFNAME).5 +MANPAGES = $(TOOLMAN) $(CONFMAN) -DOCS = $(DOCPDF) $(DOTPDF) $(MANPAGE) +DOCS = $(DOCPDF) $(DOTPDF) $(MANPAGES) # Files grouped by generation mode GLYPHS = $(BUILDDIR)/$(NAME)-glyphlist.lua @@ -47,7 +51,6 @@ SOURCE = $(DOCSRC) $(MANSRC) $(SRC) README COPYING Makefile NEWS $(RESOURCESCRI SCRIPTSTATUS = $(TOOL) $(RESOURCESCRIPTS) RUNSTATUS = $(filter-out $(SCRIPTSTATUS),$(SRC)) DOCSTATUS = $(DOCPDF) $(DOTPDF) README NEWS COPYING -MANSTATUS = $(MANPAGE) SRCSTATUS = $(DOCSRC) $(MANSRC) $(GRAPHSRC) Makefile # The following definitions should be equivalent @@ -59,7 +62,8 @@ FORMAT = luatex SCRIPTDIR = $(TEXMFROOT)/scripts/$(NAME) RUNDIR = $(TEXMFROOT)/tex/$(FORMAT)/$(NAME) DOCDIR = $(TEXMFROOT)/doc/$(FORMAT)/$(NAME) -MANDIR = $(TEXMFROOT)/doc/man/man1/ +MAN1DIR = $(TEXMFROOT)/doc/man/man1/ +MAN5DIR = $(TEXMFROOT)/doc/man/man5/ SRCDIR = $(TEXMFROOT)/source/$(FORMAT)/$(NAME) TEXMFROOT = $(shell kpsewhich --var-value TEXMFHOME) @@ -95,7 +99,7 @@ world: all ctan graph: $(DOTPDF) doc: $(DOCS) pdf: $(DOCPDF) -manual: $(MANPAGE) +manual: $(MANPAGES) $(DOTPDF): @$(MAKE) -C $(DOCSRCDIR) graph @@ -103,8 +107,8 @@ $(DOTPDF): $(DOCPDF): @$(MAKE) -C $(DOCSRCDIR) doc -$(MANPAGE): - @$(MAKE) -C $(DOCSRCDIR) manual +$(MANPAGES): + @$(MAKE) -C $(DOCSRCDIR) manuals $(GLYPHS): builddir $(DO_GLYPHS) @@ -132,7 +136,8 @@ $(CTAN_ZIP): $(DOCS) $(SOURCE) $(COMPILED) $(TDS_ZIP) define run-install-doc @mkdir -p $(DOCDIR) && cp -- $(DOCSTATUS) $(VGND) $(DOCDIR) @mkdir -p $(SRCDIR) && cp -- $(SRCSTATUS) $(SRCDIR) -@mkdir -p $(MANDIR) && cp -- $(MANSTATUS) $(MANDIR) +@mkdir -p $(MAN1DIR) && cp -- $(TOOLMAN) $(MAN1DIR) +@mkdir -p $(MAN5DIR) && cp -- $(CONFMAN) $(MAN5DIR) endef define run-install @@ -186,7 +191,8 @@ showtargets: @echo " resources generate resource files (chars, glyphs)" @echo @echo " pdf build luaotfload.pdf" - @echo " manual crate manpage for luaotfload-tool (requires Docutils)" + @echo " manual crate manpages for luaotfload-tool(1) and" + @echo " luaotfload.conf(5) (requires Docutils)" @echo " graph generate file graph (requires GraphViz)" @echo @echo " chars import char-def.lua as luaotfload-characters.lua" diff --git a/doc/Makefile b/doc/Makefile index 9e2d591..ed340a4 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -3,14 +3,20 @@ DOCPDF = $(NAME).pdf DOCSRC = $(NAME)-latex.tex SCRIPTNAME = luaotfload-tool -MANSOURCE = $(SCRIPTNAME).rst -MANPAGE = $(SCRIPTNAME).1 +TOOLMANSRC = $(SCRIPTNAME).rst +TOOLMAN = $(SCRIPTNAME).1 + +CONFNAME = luaotfload.conf +CONFMANSRC = $(CONFNAME).rst +CONFMAN = $(CONFNAME).5 + +MANPAGES = $(TOOLMAN) $(CONFMAN) GRAPH = filegraph DOTPDF = $(GRAPH).pdf DOT = $(GRAPH).dot -DOCS = $(DOTPDF) $(DOCPDF) $(MANPAGE) +DOCS = $(DOTPDF) $(DOCPDF) $(MANPAGES) DO_LATEXMK = @latexmk -e '$$max_repeat = 5' -pdf -lualatex -silent $< >/dev/null # latexmk does only one run on my machine, so we’re not going to rely on it @@ -19,9 +25,9 @@ DO_GRAPHVIZ = @dot -Tpdf -o $@ $< > /dev/null DO_DOCUTILS = @rst2man $< >$@ 2>/dev/null doc: graph $(DOCPDF) -all: manual doc +all: manuals doc graph: $(DOTPDF) -manual: $(MANPAGE) +manuals: $(TOOLMAN) $(CONFMAN) $(DOCPDF): $(DOCSRC) @echo "creating PDF documentation ($@)" @@ -29,8 +35,12 @@ $(DOCPDF): $(DOCSRC) $(DO_LATEX) mv -f -- $(<:tex=pdf) $@ -$(MANPAGE): $(MANSOURCE) - @echo "creating man page ($(MANPAGE))" +$(TOOLMAN): $(TOOLMANSRC) + @echo "creating man page ($(TOOLMAN))" + $(DO_DOCUTILS) + +$(CONFMAN): $(CONFMANSRC) + @echo "creating man page ($(CONFMAN))" $(DO_DOCUTILS) $(DOTPDF): $(DOT) -- cgit v1.2.3 From 866d0a23432a936f74cd59eee787808f7968f11b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 9 Jun 2014 23:50:49 +0200 Subject: [doc] make manpage source well-formed reStructuredText --- doc/luaotfload.conf.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst index 932afad..bd080d1 100644 --- a/doc/luaotfload.conf.rst +++ b/doc/luaotfload.conf.rst @@ -1,6 +1,8 @@ ======================================================================= luaotfload.conf ======================================================================= + +----------------------------------------------------------------------- Luaotfload configuration ----------------------------------------------------------------------- -- cgit v1.2.3 From f837641ae86abcd8fa127b0918c7c2389698b3ec Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 3 Jul 2014 21:23:14 +0200 Subject: [fontloader] sync with Context as of 2014-07-03 --- src/luaotfload-fontloader.lua | 140 +++++++++++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 37 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index 1732a23..a62958c 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 05/30/14 23:26:41 +-- merge date : 07/03/14 14:52:08 do -- begin closure to overcome local limits and interference @@ -217,9 +217,12 @@ patterns.integer=sign^-1*digit^1 patterns.unsigned=digit^0*period*digit^1 patterns.float=sign^-1*patterns.unsigned patterns.cunsigned=digit^0*comma*digit^1 +patterns.cpunsigned=digit^0*(period+comma)*digit^1 patterns.cfloat=sign^-1*patterns.cunsigned +patterns.cpfloat=sign^-1*patterns.cpunsigned patterns.number=patterns.float+patterns.integer patterns.cnumber=patterns.cfloat+patterns.integer +patterns.cpnumber=patterns.cpfloat+patterns.integer patterns.oct=zero*octdigit^1 patterns.octal=patterns.oct patterns.HEX=zero*P("X")*(digit+uppercase)^1 @@ -636,21 +639,22 @@ function lpeg.append(list,pp,delayed,checked) end return p end -local function make(t) - local p +local function make(t,hash) + local p=P(false) local keys=sortedkeys(t) for i=1,#keys do local k=keys[i] local v=t[k] - if not p then + local h=hash[v] + if h then if next(v) then - p=P(k)*make(v) + p=p+P(k)*(make(v,hash)+P(true)) else - p=P(k) + p=p+P(k)*P(true) end else if next(v) then - p=p+P(k)*make(v) + p=p+P(k)*make(v,hash) else p=p+P(k) end @@ -660,16 +664,20 @@ local function make(t) end function lpeg.utfchartabletopattern(list) local tree={} + local hash={} for i=1,#list do local t=tree for c in gmatch(list[i],".") do - if not t[c] then - t[c]={} + local tc=t[c] + if not tc then + tc={} + t[c]=tc end - t=t[c] + t=tc end + hash[t]=list[i] end - return make(tree) + return make(tree,hash) end patterns.containseol=lpeg.finder(eol) local function nextstep(n,step,result) @@ -970,14 +978,14 @@ local function sortedhash(t,cmp) end local n=0 local m=#s - local function kv(s) + local function kv() if n=first and k<=last then if tv=="number" then if hexify then - handle(format("%s 0x%04X,",depth,v)) + handle(format("%s 0x%X,",depth,v)) else handle(format("%s %s,",depth,v)) end @@ -1253,25 +1261,25 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv=="number" then if tk=="number" then if hexify then - handle(format("%s [0x%04X]=0x%04X,",depth,k,v)) + handle(format("%s [0x%X]=0x%X,",depth,k,v)) else handle(format("%s [%s]=%s,",depth,k,v)) end elseif tk=="boolean" then if hexify then - handle(format("%s [%s]=0x%04X,",depth,k and "true" or "false",v)) + handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v)) else handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) end elseif noquotes and not reserved[k] and lpegmatch(propername,k) then if hexify then - handle(format("%s %s=0x%04X,",depth,k,v)) + handle(format("%s %s=0x%X,",depth,k,v)) else handle(format("%s %s=%s,",depth,k,v)) end else if hexify then - handle(format("%s [%q]=0x%04X,",depth,k,v)) + handle(format("%s [%q]=0x%X,",depth,k,v)) else handle(format("%s [%q]=%s,",depth,k,v)) end @@ -1280,7 +1288,7 @@ local function do_serialize(root,name,depth,level,indexed) if reduce and tonumber(v) then if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%s,",depth,k,v)) + handle(format("%s [0x%X]=%s,",depth,k,v)) else handle(format("%s [%s]=%s,",depth,k,v)) end @@ -1294,7 +1302,7 @@ local function do_serialize(root,name,depth,level,indexed) else if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%q,",depth,k,v)) + handle(format("%s [0x%X]=%q,",depth,k,v)) else handle(format("%s [%s]=%q,",depth,k,v)) end @@ -1310,7 +1318,7 @@ local function do_serialize(root,name,depth,level,indexed) if not next(v) then if tk=="number" then if hexify then - handle(format("%s [0x%04X]={},",depth,k)) + handle(format("%s [0x%X]={},",depth,k)) else handle(format("%s [%s]={},",depth,k)) end @@ -1326,7 +1334,7 @@ local function do_serialize(root,name,depth,level,indexed) if st then if tk=="number" then if hexify then - handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", "))) + handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", "))) else handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) end @@ -1346,7 +1354,7 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv=="boolean" then if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%s,",depth,k,v and "true" or "false")) + handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false")) else handle(format("%s [%s]=%s,",depth,k,v and "true" or "false")) end @@ -1362,7 +1370,7 @@ local function do_serialize(root,name,depth,level,indexed) local f=getinfo(v).what=="C" and dump(dummy) or dump(v) if tk=="number" then if hexify then - handle(format("%s [0x%04X]=load(%q),",depth,k,f)) + handle(format("%s [0x%X]=load(%q),",depth,k,f)) else handle(format("%s [%s]=load(%q),",depth,k,f)) end @@ -1377,7 +1385,7 @@ local function do_serialize(root,name,depth,level,indexed) else if tk=="number" then if hexify then - handle(format("%s [0x%04X]=%q,",depth,k,tostring(v))) + handle(format("%s [0x%X]=%q,",depth,k,tostring(v))) else handle(format("%s [%s]=%q,",depth,k,tostring(v))) end @@ -1431,7 +1439,7 @@ local function serialize(_handle,root,name,specification) end elseif tname=="number" then if hexify then - handle(format("[0x%04X]={",name)) + handle(format("[0x%X]={",name)) else handle("["..name.."]={") end @@ -1738,6 +1746,44 @@ function table.values(t,s) return {} end end +function table.filtered(t,pattern,sort,cmp) + if t and type(pattern)=="string" then + if sort then + local s + if cmp then + s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end) + else + s=sortedkeys(t) + end + local n=0 + local m=#s + local function kv(s) + while n -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n) @@ -3202,6 +3249,15 @@ else add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) end +local dquote=patterns.dquote +local equote=patterns.escaped+dquote/'\\"'+1 +local space=patterns.space +local cquote=Cc('"') +local pattern=Cs(dquote*(equote-P(-2))^0*dquote) ++Cs(cquote*(equote-space)^0*space*equote^0*cquote) +function string.optionalquoted(str) + return lpegmatch(pattern,str) or str +end end -- closure @@ -6608,7 +6664,7 @@ local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf otf.glists={ "gsub","gpos" } -otf.version=2.755 +otf.version=2.756 otf.cache=containers.define("fonts","otf",otf.version,true) local fontdata=fonts.hashes.identifiers local chardata=characters and characters.data @@ -7812,6 +7868,14 @@ actions["reorganize lookups"]=function(data,filename,raw) rule.current=s_hashed(names,s_h_cache) end rule.glyphs=nil + local lookups=rule.lookups + if lookups then + for i=1,#names do + if not lookups[i] then + lookups[i]="" + end + end + end end end end @@ -9716,6 +9780,7 @@ local isolated={ [0x0856]=true,[0x0858]=true,[0x0857]=true, [0x07FA]=true, [zwnj]=true, + [0x08AD]=true, } local final={ [0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true, @@ -9733,15 +9798,16 @@ local final={ [0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true, [0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true, [0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true, - [0x0778]=true,[0x0779]=true, + [0x0778]=true,[0x0779]=true, [0x08AA]=true,[0x08AB]=true,[0x08AC]=true, [0xFEF5]=true,[0xFEF7]=true,[0xFEF9]=true,[0xFEFB]=true, - [0x0710]=true,[0x0715]=true,[0x0716]=true,[0x0717]=true, - [0x0718]=true,[0x0719]=true,[0x0728]=true,[0x072A]=true, - [0x072C]=true,[0x071E]=true, + [0x0710]=true,[0x0715]=true,[0x0716]=true,[0x0717]=true, + [0x0718]=true,[0x0719]=true,[0x0728]=true,[0x072A]=true, + [0x072C]=true,[0x071E]=true, [0x072F]=true,[0x074D]=true, [0x0840]=true,[0x0849]=true,[0x0854]=true,[0x0846]=true, - [0x084F]=true + [0x084F]=true, + [0x08AE]=true,[0x08B1]=true,[0x08B2]=true, } local medial={ [0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true, @@ -9801,8 +9867,8 @@ local medial={ [0x07D2]=true,[0x07D0]=true,[0x07CF]=true,[0x07CD]=true, [0x07CB]=true,[0x07D3]=true,[0x07E4]=true,[0x07D5]=true, [0x07E6]=true, - [tatweel]=true, - [zwj]=true, + [tatweel]=true,[zwj]=true, + [0x08A1]=true,[0x08AF]=true,[0x08B0]=true, } local arab_warned={} local function warning(current,what) -- cgit v1.2.3 From 721459ab3de4766f852db44fc0f044037e64dfeb Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 6 Jul 2014 23:20:08 +0200 Subject: [fontloader] sync with Context as of 2014-07-06 --- src/luaotfload-fontloader.lua | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua index a62958c..2f26be7 100644 --- a/src/luaotfload-fontloader.lua +++ b/src/luaotfload-fontloader.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 07/03/14 14:52:08 +-- merge date : 07/06/14 22:50:12 do -- begin closure to overcome local limits and interference @@ -665,17 +665,34 @@ end function lpeg.utfchartabletopattern(list) local tree={} local hash={} - for i=1,#list do - local t=tree - for c in gmatch(list[i],".") do - local tc=t[c] - if not tc then - tc={} - t[c]=tc + local n=#list + if n==0 then + for s in next,list do + local t=tree + for c in gmatch(s,".") do + local tc=t[c] + if not tc then + tc={} + t[c]=tc + end + t=tc + end + hash[t]=s + end + else + for i=1,n do + local t=tree + local s=list[i] + for c in gmatch(s,".") do + local tc=t[c] + if not tc then + tc={} + t[c]=tc + end + t=tc end - t=tc + hash[t]=s end - hash[t]=list[i] end return make(tree,hash) end -- cgit v1.2.3 From 4a9a0cb9382ffc70e68913b9fffd74b5a6d1a3d4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 12 Jul 2014 09:51:52 +0200 Subject: [db] remove the alternative (and buggy) verbose file lookup Also document the file lookup somewhat and rename it to ``font_file_lookup()``. --- src/luaotfload-database.lua | 90 ++++++++++++++------------------------------- src/luaotfload-main.lua | 3 +- src/luaotfload-tool.lua | 4 +- 3 files changed, 31 insertions(+), 66 deletions(-) diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index e171c52..7a01ca6 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -454,8 +454,7 @@ end --- define locals in scope local access_font_index local collect_families -local crude_file_lookup -local crude_file_lookup_verbose +local font_file_lookup local find_closest local flush_lookup_cache local generate_filedata @@ -594,59 +593,6 @@ local type1_metrics = { "tfm", "ofm", } local dummy_findfile = resolvers.findfile -- from basics-gen ---- filemap -> string -> string -> (string | bool) -local verbose_lookup = function (data, kind, filename) - local found = data[kind][filename] - if found ~= nil then - found = data.full[found] - if found == nil then --> texmf - report("info", 0, "db", - "Crude file lookup: req=%s; hit=%s => kpse.", - filename, kind) - found = dummy_findfile(filename) - else - report("info", 0, "db", - "Crude file lookup: req=%s; hit=%s; ret=%s.", - filename, kind, found) - end - return found - end - return false -end - ---- string -> (string * string * bool) -crude_file_lookup_verbose = function (filename) - if not name_index then name_index = load_names() end - local mappings = name_index.mappings - local files = name_index.files - local found - - --- look up in db first ... - found = verbose_lookup(files, "bare", filename) - if found then - return found, nil, true - end - found = verbose_lookup(files, "base", filename) - if found then - return found, nil, true - end - - --- ofm and tfm, returns pair - for i=1, #type1_metrics do - local format = type1_metrics[i] - if resolvers.findfile(filename, format) then - return file.addsuffix(filename, format), format, true - end - end - - if not fonts_reloaded and config.luaotfload.db.update_live == true then - return reload_db (stringformat ("File not found: %s.", filename), - crude_file_lookup_verbose, - filename) - end - return filename, nil, false -end - local lookup_filename = function (filename) if not name_index then name_index = load_names () end local files = name_index.files @@ -682,8 +628,18 @@ local lookup_filename = function (filename) end end ---- string -> (string * string * bool) -crude_file_lookup = function (filename) +--[[doc-- + + font_file_lookup -- The ``file:`` are ultimately delegated here. + The lookups are kind of a blunt instrument since they try locating + the file using every conceivable method, which is quite + inefficient. Nevertheless, resolving files that way is rarely the + bottleneck. + +--doc]]-- + +--- string -> string * string * bool +font_file_lookup = function (filename) local found = lookup_filename (filename) if not found then @@ -703,17 +659,28 @@ crude_file_lookup = function (filename) if not fonts_reloaded and config.luaotfload.db.update_live == true then return reload_db (stringformat ("File not found: %s.", filename), - crude_file_lookup_verbose, + font_file_lookup, filename) end return filename, nil, false end --[[doc-- -Existence of the resolved file name is verified differently depending -on whether the index entry has a texmf flag set. + + get_font_file -- Look up the file of an entry in the mappings + table. If the index is valid, pass on the name and subfont index + after verifing the existence of the resolved file. This + verification differs depending the index entry’s ``location`` + field: + + * ``texmf`` fonts are verified using the (slow) function + ``kpse.lookup()``; + * other locations are tested by resolving the full path and + checking for the presence of a file there. + --doc]]-- +--- int -> bool * (string * int) option local get_font_file = function (index) local entry = name_index.mappings [index] if not entry then @@ -3474,8 +3441,7 @@ names.access_font_index = access_font_index names.data = function () return name_index end names.save = save_names names.update = update_names -names.crude_file_lookup = crude_file_lookup -names.crude_file_lookup_verbose = crude_file_lookup_verbose +names.font_file_lookup = font_file_lookup names.read_blacklist = read_blacklist names.sanitize_fontname = sanitize_fontname names.getfilename = resolve_fullpath diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 36cbaf6..83e12a9 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -478,8 +478,7 @@ fonts.encodings.known = fonts.encodings.known or { } --doc]]-- ---local resolve_file = names.crude_file_lookup -local resolve_file = names.crude_file_lookup_verbose +local resolve_file = names.font_file_lookup local file_resolver = function (specification) local name = resolve_file (specification.name) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 0db3eca..4a75dd1 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -1153,11 +1153,11 @@ actions.query = function (job) then foundname, subfont = names.resolve_name (tmpspec) if foundname then - foundname, _, success = names.crude_file_lookup (foundname) + foundname, _, success = names.font_file_lookup (foundname) end elseif tmpspec.lookup == "file" then foundname, _, success = - names.crude_file_lookup (tmpspec.name) + names.font_file_lookup (tmpspec.name) end if success then -- cgit v1.2.3 From 992a8e8b1967486291cc1f480fd3ff83d904576c Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 12 Jul 2014 13:55:04 +0200 Subject: [doc] describe config file search sequence in manpage --- doc/luaotfload.conf.rst | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst index bd080d1..eb7788e 100644 --- a/doc/luaotfload.conf.rst +++ b/doc/luaotfload.conf.rst @@ -3,7 +3,7 @@ ======================================================================= ----------------------------------------------------------------------- - Luaotfload configuration + Luaotfload configuration file ----------------------------------------------------------------------- :Date: 2014-06-09 @@ -15,13 +15,16 @@ SYNOPSIS ======================================================================= -**XDG_CONFIG_HOME/luaotfload/luaotfload.conf** -**XDG_CONFIG_HOME/luaotfload/luaotfloadrc** -**~/.luaotfloadrc** +- **./luaotfload{.conf,rc}** +- **XDG_CONFIG_HOME/luaotfload/luaotfload{.conf,rc}** +- **~/.luaotfloadrc** DESCRIPTION ======================================================================= +The file ``luaotfload.conf`` contains configuration options for +*Luaotfload*, a font loading and font management component for LuaTeX. + EXAMPLE ======================================================================= @@ -37,13 +40,27 @@ SYNTAX VARIABLES ======================================================================= + * TODO, list variables FILES ======================================================================= -* file locations +Luaotfload only processes the first configuration file it encounters at +one of the search locations. The file name may be either +``luaotfload.conf`` or ``luaotfloadrc``, except for the dotfile in the +user’s home directory which is expected at ``~/.luaotfloadrc``. + +Configuration files are located following a series of steps. The search +terminates as soon as a suitable file is encountered. The sequence of +locations that Luaotfload looks at is + +i. The current working directory of the LuaTeX process. +ii. The subdirectory ``luaotfload/`` inside the XDG configuration + tree, e. g. ``/home/oenothea/config/luaotfload/``. +iii. The dotfile. +iv. The *TEXMF* (using kpathsea). SEE ALSO @@ -59,6 +76,12 @@ SEE ALSO * Luaotfload on CTAN ``_ +REFERENCES +======================================================================= + +* The XDG base specification + ``_. + AUTHORS ======================================================================= -- cgit v1.2.3 From 821e6aa045ecbb22dbc96d2dafe5f7ae1c839762 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 12 Jul 2014 15:09:39 +0200 Subject: =?UTF-8?q?[conf]=20validate=20config=20variable=20=E2=80=9Cformat?= =?UTF-8?q?s=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a validation function that checks whether the value is indeed a list of comma-separated identifiers. Also, duplicate and invalid entries are dropped from the result. --- src/luaotfload-configuration.lua | 46 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index ff90f68..937b050 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -31,6 +31,8 @@ local stringexplode = string.explode local table = table local tableappend = table.append local tablecopy = table.copy +local tableconcat = table.concat +local tabletohash = table.tohash local math = math local mathfloor = math.floor @@ -44,6 +46,7 @@ local osgetenv = os.getenv local lpeg = require "lpeg" local lpegmatch = lpeg.match +local lpegsplitter = lpeg.splitat "," local kpse = kpse local kpseexpand_path = kpse.expand_path @@ -91,6 +94,11 @@ local config_paths = { { kpse_t, "luaotfload.conf" }, } +local valid_formats = tabletohash { + "otf", "ttc", "ttf", "dfont", "afm", "pfb", "pfa", +} + + ------------------------------------------------------------------------------- --- DEFAULTS ------------------------------------------------------------------------------- @@ -253,7 +261,43 @@ end local option_spec = { db = { - formats = { in_t = string_t, }, + formats = { + in_t = string_t, + out_t = string_t, + transform = function (f) + local fields = { lpegmatch (lpegsplitter, f) } + + --- check validity + if not fields then + logreport ("both", 0, "conf", + "Expected list of identifiers, got %q.", f) + return nil + end + + --- strip dupes + local known = { } + local result = { } + for i = 1, #fields do + local field = fields[i] + if known[field] ~= true then + --- yet unknown, tag as seen + known[field] = true + --- include in output if valid + if valid_formats[field] == true then + result[#result + 1] = field + else + logreport ("both", 4, "conf", + "Invalid font format identifier %q, ignoring.", field) + end + end + end + if #result == 0 then + --- force defaults + return nil + end + return tableconcat (result, ",") + end + }, reload = { in_t = boolean_t, }, scan_local = { in_t = boolean_t, }, skip_read = { in_t = boolean_t, }, -- cgit v1.2.3 From 9d92d615262ab0a2286146526db6b935f7a8c980 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 12 Jul 2014 19:05:30 +0200 Subject: [color] fix transparency of colorized fonts Thanks to Robert Schlicht and Will Robertson for the fix. Fixes https://github.com/lualatex/luaotfload/issues/222 [0] http://tex.stackexchange.com/q/185878 --- src/luaotfload-colors.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/luaotfload-colors.lua b/src/luaotfload-colors.lua index 56acfee..476826e 100644 --- a/src/luaotfload-colors.lua +++ b/src/luaotfload-colors.lua @@ -34,6 +34,9 @@ local traverse_nodes = node.traverse local insert_node_before = node.insert_before local insert_node_after = node.insert_after +local texset = tex.set +local texget = tex.get + local stringformat = string.format local stringgsub = string.gsub local stringfind = string.find @@ -276,20 +279,24 @@ local color_handler = function (head) local new_head = node_colorize(head, nil, nil) -- now append our page resources if res then - res["1"] = true - local tpr, t = tex.pdfpageresources, "" + res["1"] = true + local tpr = texget("pdfpageresources") + local t = "" for k in pairs(res) do local str = stringformat("/TransGs%s<>", k, k, k) if not stringfind(tpr,str) then t = t .. str end end + print"" if t ~= "" then + print(">>", tpr, "<<") if not stringfind(tpr,"/ExtGState<<.*>>") then tpr = tpr.."/ExtGState<<>>" end tpr = stringgsub(tpr,"/ExtGState<<","%1"..t) - tex.pdfpageresources = tpr + texset("global", "pdfpageresources", tpr) + print(">>", tpr, "<<") end res = nil -- reset res end -- cgit v1.2.3 From 2668c9eb51bda60075c4c599d62bc8e31b25941b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 12 Jul 2014 21:14:37 +0200 Subject: [conf] allow spaces in formats specification List items are now stripped of leading and trailing spaces before building the formats list. --- src/luaotfload-configuration.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 937b050..3522bef 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -27,6 +27,7 @@ local luaotfloadstatus = require (status_file) local string = string local stringsub = string.sub local stringexplode = string.explode +local stringstrip = string.strip local table = table local tableappend = table.append @@ -278,7 +279,7 @@ local option_spec = { local known = { } local result = { } for i = 1, #fields do - local field = fields[i] + local field = stringstrip (fields[i]) if known[field] ~= true then --- yet unknown, tag as seen known[field] = true @@ -287,7 +288,8 @@ local option_spec = { result[#result + 1] = field else logreport ("both", 4, "conf", - "Invalid font format identifier %q, ignoring.", field) + "Invalid font format identifier %q, ignoring.", + field) end end end -- cgit v1.2.3 From 6a2df3f25e05f6a30c4789bd2c2daed999617784 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 12 Jul 2014 21:34:45 +0200 Subject: [conf] strip unused configuration variable What the hell was that for? --- src/luaotfload-configuration.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 3522bef..69f4069 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -107,7 +107,6 @@ local valid_formats = tabletohash { local default_config = { db = { formats = "otf,ttf,ttc,dfont", - reload = false, scan_local = false, skip_read = false, strip = true, @@ -300,7 +299,6 @@ local option_spec = { return tableconcat (result, ",") end }, - reload = { in_t = boolean_t, }, scan_local = { in_t = boolean_t, }, skip_read = { in_t = boolean_t, }, strip = { in_t = boolean_t, }, -- cgit v1.2.3 From cff9bd20679420af3e56b43d035f08c03f6a257e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 12 Jul 2014 22:21:21 +0200 Subject: [doc] describe configuration options --- doc/luaotfload.conf.rst | 176 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 2 deletions(-) diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst index eb7788e..9c8153a 100644 --- a/doc/luaotfload.conf.rst +++ b/doc/luaotfload.conf.rst @@ -40,8 +40,180 @@ SYNTAX VARIABLES ======================================================================= +Variables in belong into a configuration section and their values must +be of a certain type. Some of them have further constraints. For +example, the “color callback” must be a string of one of the values +``pre_linebreak_filter`` or ``pre_output_filter``, defined in the +section *run*. -* TODO, list variables +Currently, the configuration is organized into four sections: + +db + Options relating to the font index. + +misc + Options without a clearly defined category. + +paths + Path and file name settings. + +run + Options controlling runtime behavior of Luaotfload. + +The list of valid variables, the sections they are part of and their +type is given below. Types represent Lua types that the values must be +convertible to; they are abbreviated as follows: ``s`` for the *string* +type, ``n`` for *number*, ``b`` for *boolean*. A value of ``nil`` means +the variable is unset. + + +Section ``db`` +----------------------------------------------------------------------- + ++---------------+--------+---------------------------+ +| variable | type | default | ++---------------+--------+---------------------------+ +| compress | b | ``true`` | ++---------------+--------+---------------------------+ +| formats | s | ``"otf,ttf,ttc,dfont"`` | ++---------------+--------+---------------------------+ +| max_fonts | n | ``2^51`` | ++---------------+--------+---------------------------+ +| scan_local | b | ``false`` | ++---------------+--------+---------------------------+ +| skip_read | b | ``false`` | ++---------------+--------+---------------------------+ +| strip | b | ``true`` | ++---------------+--------+---------------------------+ +| update_live | b | ``true`` | ++---------------+--------+---------------------------+ + +The flag ``compress`` determines whether the font index (usually +``luaotfload-names.lua[.gz]`` will be stored in compressed forms. +If unset it is equivalent of passing ``--no-compress`` to +**luaotfload-tool**. Since the file is only created for convenience +and has no effect on the runtime behavior of Luaotfload, the flag +should remain set. Most editors come with zlib support anyways. + +The list of ``formats`` must be a comma separated sequence of strings +containing one or more of these elements: + +* ``otf`` (OpenType format), +* ``ttf`` and ``ttc`` (TrueType format), +* ``dfont`` (Macintosh TrueType format), +* ``afm`` (Adobe Font Metrics), +* ``pfb`` and ``pfa`` (PostScript format). + +It corresponds loosely to the ``--formats`` option to +**luaotfload-tool**. Invalid or duplicate members are ignored; if the +list does not contain any useful identifiers, the default list +``"otf,ttf,ttc,dfont"`` will be used. + +The variable ``max_fonts`` determines after processing how many font +files the font scanner will terminate the search. This is useful for +debugging issues with the font index and has the same effect as the +option ``--max-fonts`` to **luaotfload-tools**. + +The ``scan_local`` flag, if set, will incorporate the current working +directory as a font search location. NB: This will potentially slow +down document processing because a font index with local fonts will not +be saved to disk, so these fonts will have to be re-indexed whenever +the document is built. + +The ``skip_read`` flag is only useful for debugging: It makes +Luaotfload skip reading fonts. The font information for rebuilding the +index is taken from the presently existing one. + +Unsetting the ``strip`` flag prevents Luaotfload from removing data +from the index that is only useful when processing font files. NB: this +can increase the size of the index files significantly and has no +effect on the runtime behavior. + +If ``update_live`` is set, Luaotfload will reload the database if it +cannot find a requested font. Those who prefer to update manually using +**luaotfload-tool** should unset this flag. + + +Section ``misc`` +----------------------------------------------------------------------- + ++---------------+--------+-------------------------+ +| variable | type | default | ++---------------+--------+-------------------------+ +| statistics | b | ``false`` | ++---------------+--------+-------------------------+ +| termwidth | n | ``nil`` | ++---------------+--------+-------------------------+ +| version | s | ``luaotfload.version`` | ++---------------+--------+-------------------------+ + +With ``statistics`` enabled, extra statistics will be collected during +index creation and appended to the index file. It may then be queried +at the Lua end or inspected by reading the file itself. + +The value of ``termwidth``, if set, overrides the value retrieved by +querying the properties of the terminal in which Luatex runs. This is +useful if the engine runs with ``shell_escape`` disabled and the actual +terminal dimensions cannot be retrieved. + +The value of ``version`` is derived from the version string hard-coded +in the Luaotfload source. Override at your own risk. + + +Section ``paths`` +----------------------------------------------------------------------- + ++------------------+--------+------------------------------------+ +| variable | type | default | ++------------------+--------+------------------------------------+ +| cache_dir | s | ``"fonts"`` | ++------------------+--------+------------------------------------+ +| names_dir | s | ``"names"`` | ++------------------+--------+------------------------------------+ +| index_file | s | ``"luaotfload-names.lua"`` | ++------------------+--------+------------------------------------+ +| lookups_file | s | ``"luaotfload-lookup-cache.lua"`` | ++------------------+--------+------------------------------------+ + +The paths ``cache_dir`` and ``names_dir`` determine the subdirectory +inside the Luaotfload subtree of the ``luatex-cache`` directory where +the font cache and the font index will be stored, respectively. + +Inside the index directory, the names of the index file and the font +lookup cache will be derived from the respective values of +``index_file`` and ``lookups_file``. This is the filename base for the +bytecode compiled version as well as -- for the index -- the gzipped +version. + + +Section ``run`` +----------------------------------------------------------------------- + ++------------------+--------+------------------------------+ +| variable | type | default | ++------------------+--------+------------------------------+ +| color_callback | s | ``"pre_linebreak_filter"`` | ++------------------+--------+------------------------------+ +| definer | s | ``"patch"`` | ++------------------+--------+------------------------------+ +| log_level | n | ``0`` | ++------------------+--------+------------------------------+ +| resolver | s | ``"cached"`` | ++------------------+--------+------------------------------+ + +The ``color_callback`` option determines the stage at which fonts that +defined with a ``color=xxyyzz`` feature will be colorized. By default +this happens in a ``pre_linebreak_filter`` but alternatively the +``pre_output_filter`` may be chosen, which is faster but might produce +inconsistent output. The latter also was the default in the 1.x series +of Luaotfload. + +The value of ``log_level`` set the default verbosity of messages +printed by Luaotfload. Only messages defined with a verbosity of less +than or equal to the supplied value will be output on the terminal. +At a log level of five Luaotfload can be very noisy. Also, printing too +many messages will slow down the interpreter due to line buffering +being disabled (see **setbuf**\(3)). FILES @@ -66,7 +238,7 @@ iv. The *TEXMF* (using kpathsea). SEE ALSO ======================================================================= -**luaotfload-tool** (1), **luatex** (1), **lua** (1) +**luaotfload-tool**\(1), **luatex**\(1), **lua**\(1) * ``texdoc luaotfload`` to display the PDF manual for the *Luaotfload* package -- cgit v1.2.3 From 4a4edf88b2e69da6f609760c7a3664650b4abc2a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 07:37:55 +0200 Subject: [main] deprioritize fontloader intro message This message originates in ``luatex-fonts.lua`` [0]. The advice given on using the Context libraries directly is counterproductive unless one is already hacking on the Luaotfload code, in which case it should be obvious anyways. Cluttering the log file with those lines is unjustified. [0] http://git.contextgarden.net/context/context/blob/master/tex/generic/context/luatex/luatex-fonts.lua#L139 --- src/luaotfload-main.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 83e12a9..7686c80 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -311,13 +311,13 @@ loadmodule "fontloader.lua" if fonts then if not fonts._merge_loaded_message_done_ then - report ("log", 0, "main", [["I am using the merged fontloader here.]]) - report ("log", 0, "main", [[ If you run into problems or experience unexpected]]) - report ("log", 0, "main", [[ behaviour, and if you have ConTeXt installed you can try]]) - report ("log", 0, "main", [[ to delete the file 'luaotfload-fontloader.lua' as I might]]) - report ("log", 0, "main", [[ then use the possibly updated libraries. The merged]]) - report ("log", 0, "main", [[ version is not supported as it is a frozen instance.]]) - report ("log", 0, "main", [[ Problems can be reported to the ConTeXt mailing list."]]) + report ("log", 5, "main", [["I am using the merged fontloader here.]]) + report ("log", 5, "main", [[ If you run into problems or experience unexpected]]) + report ("log", 5, "main", [[ behaviour, and if you have ConTeXt installed you can try]]) + report ("log", 5, "main", [[ to delete the file 'luaotfload-fontloader.lua' as I might]]) + report ("log", 5, "main", [[ then use the possibly updated libraries. The merged]]) + report ("log", 5, "main", [[ version is not supported as it is a frozen instance.]]) + report ("log", 5, "main", [[ Problems can be reported to the ConTeXt mailing list."]]) end fonts._merge_loaded_message_done_ = true -- cgit v1.2.3 From 7bf83c68f3575e1a49533ecd45178551e62746f9 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 07:51:13 +0200 Subject: [main] adapt local to current terminology --- src/luaotfload-main.lua | 56 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index 7686c80..c7694ea 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -137,7 +137,7 @@ loadmodule "log.lua" --- log messages --loadmodule "configuration.lua" --- configuration options local log = luaotfload.log -local report = log.report +local logreport = log.report log.set_loglevel (default_log_level) @@ -165,8 +165,8 @@ local find_vf_file = function (name) fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf") end if fullname then - report ("log", 0, "main", - "loading virtual font file %s.", fullname) + logreport ("log", 0, "main", + "loading virtual font file %s.", fullname) end return fullname end @@ -249,7 +249,7 @@ end local context_environment = { } local push_namespaces = function () - report ("log", 1, "main", "push namespace for font loader") + logreport ("log", 1, "main", "push namespace for font loader") local normalglobal = { } for k, v in next, _G do normalglobal[k] = v @@ -262,7 +262,7 @@ local pop_namespaces = function (normalglobal, isolate) local _G = _G local mode = "non-destructive" if isolate then mode = "destructive" end - report ("log", 1, "main", "pop namespace from font loader -- " .. mode) + logreport ("log", 1, "main", "pop namespace from font loader -- " .. mode) for k, v in next, _G do if not normalglobal[k] then context_environment[k] = v @@ -277,8 +277,8 @@ local pop_namespaces = function (normalglobal, isolate) -- just to be sure: setmetatable(context_environment,_G) else - report ("both", 0, "main", - "irrecoverable error during pop_namespace: no globals to restore") + logreport ("both", 0, "main", + "irrecoverable error during pop_namespace: no globals to restore") os.exit() end end @@ -311,13 +311,13 @@ loadmodule "fontloader.lua" if fonts then if not fonts._merge_loaded_message_done_ then - report ("log", 5, "main", [["I am using the merged fontloader here.]]) - report ("log", 5, "main", [[ If you run into problems or experience unexpected]]) - report ("log", 5, "main", [[ behaviour, and if you have ConTeXt installed you can try]]) - report ("log", 5, "main", [[ to delete the file 'luaotfload-fontloader.lua' as I might]]) - report ("log", 5, "main", [[ then use the possibly updated libraries. The merged]]) - report ("log", 5, "main", [[ version is not supported as it is a frozen instance.]]) - report ("log", 5, "main", [[ Problems can be reported to the ConTeXt mailing list."]]) + logreport ("log", 5, "main", [["I am using the merged fontloader here.]]) + logreport ("log", 5, "main", [[ If you run into problems or experience unexpected]]) + logreport ("log", 5, "main", [[ behaviour, and if you have ConTeXt installed you can try]]) + logreport ("log", 5, "main", [[ to delete the file 'luaotfload-fontloader.lua' as I might]]) + logreport ("log", 5, "main", [[ then use the possibly updated libraries. The merged]]) + logreport ("log", 5, "main", [[ version is not supported as it is a frozen instance.]]) + logreport ("log", 5, "main", [[ Problems can be reported to the ConTeXt mailing list."]]) end fonts._merge_loaded_message_done_ = true @@ -375,8 +375,8 @@ end --- non-merge fallback scope pop_namespaces(our_environment, false)-- true) -report ("both", 0, "main", - "fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) +logreport ("both", 0, "main", + "fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime) --[[doc-- @@ -421,7 +421,7 @@ loadmodule "parsers.lua" --- fonts.conf and syntax loadmodule "configuration.lua" --- configuration options if not config.actions.apply_defaults () then - report ("log", 0, "load", "Configuration unsuccessful.") + logreport ("log", 0, "load", "Configuration unsuccessful.") end loadmodule "loaders.lua" --- Type1 font wrappers @@ -429,7 +429,7 @@ loadmodule "database.lua" --- Font management. loadmodule "colors.lua" --- Per-font colors. if not config.actions.reconfigure () then - report ("log", 0, "load", "Post-configuration hooks failed.") + logreport ("log", 0, "load", "Post-configuration hooks failed.") end --[[doc-- @@ -541,9 +541,9 @@ request_resolvers.anon = function (specification) local exists, _ = lfsisfile(name) if exists then --- garbage; we do this because we are nice, --- not because it is correct - report ("log", 1, "load", "file %q exists", name) - report ("log", 1, "load", - "... overriding borked anon: lookup with path: lookup") + logreport ("log", 1, "load", "file %q exists", name) + logreport ("log", 1, "load", + "... overriding borked anon: lookup with path: lookup") specification.name = name request_resolvers.path(specification) return @@ -565,9 +565,9 @@ request_resolvers.path = function (specification) local name = specification.name local exists, _ = lfsisfile(name) if not exists then -- resort to file: lookup - report ("log", 0, "load", - "path lookup of %q unsuccessful, falling back to file:", - name) + logreport ("log", 0, "load", + "path lookup of %q unsuccessful, falling back to file:", + name) file_resolver (specification) else local suffix = filesuffix (name) @@ -625,10 +625,10 @@ request_resolvers.name = function (specification) end local resolved, subfont = resolver (specification) if resolved then - report ("log", 0, "load", "Lookup/name: %q -> \"%s%s\"", - specification.name, - resolved, - subfont and stringformat ("(%d)", subfont) or "") + logreport ("log", 0, "load", "Lookup/name: %q -> \"%s%s\"", + specification.name, + resolved, + subfont and stringformat ("(%d)", subfont) or "") specification.resolved = resolved specification.sub = subfont specification.forced = stringlower (filesuffix (resolved) or "") -- cgit v1.2.3 From 2bc0e79bb7362ff57c2414c332e4d2f74ccfd977 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 12:05:52 +0200 Subject: [tool] fix log level status message --- src/luaotfload-tool.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 4a75dd1..daf30e2 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -754,7 +754,7 @@ local actions = { } --- (jobspec -> (bool * bool)) list actions.loglevel = function (job) log.set_loglevel(job.log_level) - report ("info", 3, "util", "Setting log level", "%d", job.log_level) + report ("info", 3, "util", "Setting the log level to %d.", job.log_level) report ("log", 2, "util", "Lua=%q", _VERSION) return true, true end -- cgit v1.2.3 From 441f07d9a08ac3e9654a7ff5daf1bdb426072616 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 12:08:33 +0200 Subject: =?UTF-8?q?=C2=A0[tool]=20reorder=20task=20list=20to=20prevent=20t?= =?UTF-8?q?he=20log=20level=20from=20being=20overridden=20due=20to=20confi?= =?UTF-8?q?guration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/luaotfload-tool.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index daf30e2..7b61675 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -739,7 +739,7 @@ set. --]]-- local action_sequence = { - "loglevel", "config", "help", "version", + "config", "loglevel", "help", "version", "diagnose", "blacklist", "cache", "flush", "bisect", "generate", "list", "query", } -- cgit v1.2.3 From 992b0150c8182c10cd3415d696bf66c10a381dd7 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 12:19:47 +0200 Subject: [doc] extend documentation of config options in manpage --- doc/luaotfload.conf.rst | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst index 9c8153a..f25fc88 100644 --- a/doc/luaotfload.conf.rst +++ b/doc/luaotfload.conf.rst @@ -29,7 +29,7 @@ The file ``luaotfload.conf`` contains configuration options for EXAMPLE ======================================================================= -* TODO, small example snippet + SYNTAX @@ -208,13 +208,27 @@ this happens in a ``pre_linebreak_filter`` but alternatively the inconsistent output. The latter also was the default in the 1.x series of Luaotfload. -The value of ``log_level`` set the default verbosity of messages +The ``definer`` allows for switching the ``define_font`` callback. +Apart from the default ``patch`` one may also choose the ``generic`` +one that comes with the vanilla fontloader. Beware that this might +break tools like Fontspect that rely on the ``patch_font`` callback +provided by Luaotfload to perform important corrections on font data. + +The value of ``log_level`` sets the default verbosity of messages printed by Luaotfload. Only messages defined with a verbosity of less than or equal to the supplied value will be output on the terminal. At a log level of five Luaotfload can be very noisy. Also, printing too many messages will slow down the interpreter due to line buffering being disabled (see **setbuf**\(3)). +The ``resolver`` setting allows choosing the font name resolution +function: With the default value ``cached`` Luaotfload saves the result +of a successful font name request to a cache file to speed up +subsequent lookups. The alternative, ``normal`` circumvents the cache +and resolves every request individually. (Since to the restructuring of +the font name index in Luaotfload 2.4 the performance difference +between the cached and uncached lookups should be marginal.) + FILES ======================================================================= -- cgit v1.2.3 From c381ee8e456001a8ca4e9ceadda080e6a8c7386b Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 12:30:27 +0200 Subject: [tool] fix setting of verbosity --- src/luaotfload-tool.lua | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 7b61675..04237dd 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -753,9 +753,14 @@ action_pending.generate = false --- this is the default action local actions = { } --- (jobspec -> (bool * bool)) list actions.loglevel = function (job) - log.set_loglevel(job.log_level) - report ("info", 3, "util", "Setting the log level to %d.", job.log_level) - report ("log", 2, "util", "Lua=%q", _VERSION) + local lvl = job.log_level + if lvl then + log.set_loglevel(lvl) + report ("info", 3, "util", "Setting the log level to %d.", lvl) + report ("log", 2, "util", "Lua=%q", _VERSION) + else + report ("info", 0, "util", "Invalid loglevel, ignoring.") + end return true, true end @@ -1399,7 +1404,7 @@ local process_cmdline = function ( ) -- unit -> jobspec warnings = false, criterion = "", query = "", - log_level = 0, --- 2 is approx. the old behavior + log_level = nil, bisect = nil, config = { db = { }, misc = { }, run = { }, paths = { } }, } @@ -1452,11 +1457,13 @@ local process_cmdline = function ( ) -- unit -> jobspec elseif v == "u" then action_pending["generate"] = true elseif v == "v" then - if result.log_level > 0 then - result.log_level = result.log_level + 1 + local lvl = result.log_level + if not lvl or lvl < 1 then + lvl = 1 else - result.log_level = 1 + lvl = lvl + 1 end + result.log_level = lvl elseif v == "V" then action_pending["version"] = true elseif v == "h" then -- cgit v1.2.3 From 13799ebd44c93bb67cd10a362a8755175676bb59 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 12:30:51 +0200 Subject: [conf] warn about unknown variables --- src/luaotfload-configuration.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 69f4069..e302ca6 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -446,7 +446,12 @@ local process_options = function (opts) for var, val in next, vars do local vspec = spec[var] local t_val = type (val) - if t_val ~= vspec.in_t then + if not vspec then + logreport ("both", 0, "conf", + "Section %d (%s): invalid configuration variable %q (%q); ignoring.", + i, title, + var, tostring (val)) + elseif t_val ~= vspec.in_t then logreport ("both", 2, "conf", "Section %d (%s): type mismatch of input value %q (%q, %s != %s); ignoring.", i, title, -- cgit v1.2.3 From 51a13c452989be9baf69c67923e655357b0526bd Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 12:46:26 +0200 Subject: [parsers] convert dashes to underscores in ini parser --- src/luaotfload-parsers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua index 180adac..3eeb614 100644 --- a/src/luaotfload-parsers.lua +++ b/src/luaotfload-parsers.lua @@ -632,8 +632,8 @@ local comment_char = semicolon + gartenzaun local comment_line = ws * comment_char * (1 - eol)^0 * eol local blank_line = ws * eol local skip_line = comment_line + blank_line -local ini_id_char = alpha + dash -local ini_id = (alpha * ini_id_char^0) / stringlower +local ini_id_char = alpha + (dash / "_") +local ini_id = Cs(alpha * ini_id_char^0) / stringlower local ini_value_char = (valid_escapes + (1 - newline - backslash - comment_char)) local ini_value = (Cs (ini_value_char^0) / string.strip) * (comment_char * (1 - eol)^0)^-1 -- cgit v1.2.3 From 42554adf7de06013127c9a2d63431456c82f447f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 12:52:18 +0200 Subject: [doc] fix variable identifiers --- doc/luaotfload.conf.rst | 36 ++++++++++++++++++------------------ src/luaotfload-tool.lua | 2 -- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst index f25fc88..762f412 100644 --- a/doc/luaotfload.conf.rst +++ b/doc/luaotfload.conf.rst @@ -77,15 +77,15 @@ Section ``db`` +---------------+--------+---------------------------+ | formats | s | ``"otf,ttf,ttc,dfont"`` | +---------------+--------+---------------------------+ -| max_fonts | n | ``2^51`` | +| max-fonts | n | ``2^51`` | +---------------+--------+---------------------------+ -| scan_local | b | ``false`` | +| scan-local | b | ``false`` | +---------------+--------+---------------------------+ -| skip_read | b | ``false`` | +| skip-read | b | ``false`` | +---------------+--------+---------------------------+ | strip | b | ``true`` | +---------------+--------+---------------------------+ -| update_live | b | ``true`` | +| update-live | b | ``true`` | +---------------+--------+---------------------------+ The flag ``compress`` determines whether the font index (usually @@ -109,18 +109,18 @@ It corresponds loosely to the ``--formats`` option to list does not contain any useful identifiers, the default list ``"otf,ttf,ttc,dfont"`` will be used. -The variable ``max_fonts`` determines after processing how many font +The variable ``max-fonts`` determines after processing how many font files the font scanner will terminate the search. This is useful for debugging issues with the font index and has the same effect as the option ``--max-fonts`` to **luaotfload-tools**. -The ``scan_local`` flag, if set, will incorporate the current working +The ``scan-local`` flag, if set, will incorporate the current working directory as a font search location. NB: This will potentially slow down document processing because a font index with local fonts will not be saved to disk, so these fonts will have to be re-indexed whenever the document is built. -The ``skip_read`` flag is only useful for debugging: It makes +The ``skip-read`` flag is only useful for debugging: It makes Luaotfload skip reading fonts. The font information for rebuilding the index is taken from the presently existing one. @@ -129,7 +129,7 @@ from the index that is only useful when processing font files. NB: this can increase the size of the index files significantly and has no effect on the runtime behavior. -If ``update_live`` is set, Luaotfload will reload the database if it +If ``update-live`` is set, Luaotfload will reload the database if it cannot find a requested font. Those who prefer to update manually using **luaotfload-tool** should unset this flag. @@ -166,22 +166,22 @@ Section ``paths`` +------------------+--------+------------------------------------+ | variable | type | default | +------------------+--------+------------------------------------+ -| cache_dir | s | ``"fonts"`` | +| cache-dir | s | ``"fonts"`` | +------------------+--------+------------------------------------+ -| names_dir | s | ``"names"`` | +| names-dir | s | ``"names"`` | +------------------+--------+------------------------------------+ -| index_file | s | ``"luaotfload-names.lua"`` | +| index-file | s | ``"luaotfload-names.lua"`` | +------------------+--------+------------------------------------+ -| lookups_file | s | ``"luaotfload-lookup-cache.lua"`` | +| lookups-file | s | ``"luaotfload-lookup-cache.lua"`` | +------------------+--------+------------------------------------+ -The paths ``cache_dir`` and ``names_dir`` determine the subdirectory +The paths ``cache-dir`` and ``names-dir`` determine the subdirectory inside the Luaotfload subtree of the ``luatex-cache`` directory where the font cache and the font index will be stored, respectively. Inside the index directory, the names of the index file and the font lookup cache will be derived from the respective values of -``index_file`` and ``lookups_file``. This is the filename base for the +``index-file`` and ``lookups-file``. This is the filename base for the bytecode compiled version as well as -- for the index -- the gzipped version. @@ -192,16 +192,16 @@ Section ``run`` +------------------+--------+------------------------------+ | variable | type | default | +------------------+--------+------------------------------+ -| color_callback | s | ``"pre_linebreak_filter"`` | +| color-callback | s | ``"pre_linebreak_filter"`` | +------------------+--------+------------------------------+ | definer | s | ``"patch"`` | +------------------+--------+------------------------------+ -| log_level | n | ``0`` | +| log-level | n | ``0`` | +------------------+--------+------------------------------+ | resolver | s | ``"cached"`` | +------------------+--------+------------------------------+ -The ``color_callback`` option determines the stage at which fonts that +The ``color-callback`` option determines the stage at which fonts that defined with a ``color=xxyyzz`` feature will be colorized. By default this happens in a ``pre_linebreak_filter`` but alternatively the ``pre_output_filter`` may be chosen, which is faster but might produce @@ -214,7 +214,7 @@ one that comes with the vanilla fontloader. Beware that this might break tools like Fontspect that rely on the ``patch_font`` callback provided by Luaotfload to perform important corrections on font data. -The value of ``log_level`` sets the default verbosity of messages +The value of ``log-level`` sets the default verbosity of messages printed by Luaotfload. Only messages defined with a verbosity of less than or equal to the supplied value will be output on the terminal. At a log level of five Luaotfload can be very noisy. Also, printing too diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 04237dd..8cfcac0 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -758,8 +758,6 @@ actions.loglevel = function (job) log.set_loglevel(lvl) report ("info", 3, "util", "Setting the log level to %d.", lvl) report ("log", 2, "util", "Lua=%q", _VERSION) - else - report ("info", 0, "util", "Invalid loglevel, ignoring.") end return true, true end -- cgit v1.2.3 From 18aceb2612c0352535aa07ca9628d9cbc5285c19 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 13:02:17 +0200 Subject: [doc] add short example file to configuration man page --- doc/luaotfload.conf.rst | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst index 762f412..561ca97 100644 --- a/doc/luaotfload.conf.rst +++ b/doc/luaotfload.conf.rst @@ -29,7 +29,33 @@ The file ``luaotfload.conf`` contains configuration options for EXAMPLE ======================================================================= - +A small Luaotfload configuration file with few customizations could +look as follows: :: + + [db] + formats = afm, pfa, pfb + compress = false + + [misc] + termwidth = 60 + + [run] + log-level = 6 + +This will make Luaotfload ignore all font files except for PostScript +formats. NB: With a default Tex Live install the PS fonts will take +much longer to index than OpenType or TrueType ones. Also, an +uncompressed index file will be dumped which is going to be much larger +due to the huge amount of PostScript fonts indexed. The terminal width +is truncated to 60 characters which influences the verbose output +during indexing. Finally, the verbosity is increased greatly: each font +file being processed will be printed to the stdout on a separate line, +along with lots of other information. + +To observe the difference in behavior, save above snippet to +``./luaotfload.conf`` and update the font index: :: + + luaotfload --update --force SYNTAX -- cgit v1.2.3 From dcbfa2c743d14a60037ac3c61f0d7cba339696cd Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 13:17:18 +0200 Subject: [conf] add example configuration file --- doc/luaotfload.conf.example | 30 ++++++++++++++++++++++++++++++ doc/luaotfload.conf.rst | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 doc/luaotfload.conf.example diff --git a/doc/luaotfload.conf.example b/doc/luaotfload.conf.example new file mode 100644 index 0000000..ddf438e --- /dev/null +++ b/doc/luaotfload.conf.example @@ -0,0 +1,30 @@ +;; Example configuration file for Luaotfload. This file contains the +;; defaults only. + +[db] + compress = true + formats = otf, ttf, ttc, dfont + max-fonts = 2.2517998136852e15 + scan-local = false + skip-read = false + strip = true + update-live = true + +[misc] + statistics = false + termwidth = nil + +[paths] + cache-dir = fonts + names-dir = names + index-file = luaotfload-names.lua + lookups-file = luaotfload-lookup-cache.lua + +[run] + color-callback = pre_linebreak_filter + definer = patch + log-level = 0 + resolver = cached + +;; vim:ft=dosini:et:sw=4:ts=8 + diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst index 561ca97..9940be2 100644 --- a/doc/luaotfload.conf.rst +++ b/doc/luaotfload.conf.rst @@ -170,7 +170,7 @@ Section ``misc`` +---------------+--------+-------------------------+ | termwidth | n | ``nil`` | +---------------+--------+-------------------------+ -| version | s | ``luaotfload.version`` | +| version | s | | +---------------+--------+-------------------------+ With ``statistics`` enabled, extra statistics will be collected during -- cgit v1.2.3 From 16242a76f163e8b5b3d3ddc9076d4b0933e9441f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 13:17:43 +0200 Subject: [conf] add example configuration file --- doc/luaotfload.conf.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/luaotfload.conf.example b/doc/luaotfload.conf.example index ddf438e..2756d62 100644 --- a/doc/luaotfload.conf.example +++ b/doc/luaotfload.conf.example @@ -1,5 +1,5 @@ ;; Example configuration file for Luaotfload. This file contains the -;; defaults only. +;; defaults only, see luaotfload.conf(5) for more information. [db] compress = true -- cgit v1.2.3 From 9491abe57c42b97253c6daffd8720b4251f6ddff Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 13:36:33 +0200 Subject: [*] update news --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index b244feb..f75e61e 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,7 @@ Change History * Add functions for accessing the database: ``aux.font_index()`` and ``aux.read_font_index()``. * Distinguish XDG configuration paths (Reuben Thomas) + * Optional configuration via rc files. 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) -- cgit v1.2.3 From 88f95fa579f23ed08c72eb936d5866edc291edfd Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 14:29:10 +0200 Subject: [features,conf] configure default font features through configuration file --- src/luaotfload-configuration.lua | 141 ++++++++++++++++++++++++++++++++++++--- src/luaotfload-features.lua | 66 ++---------------- 2 files changed, 137 insertions(+), 70 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index e302ca6..1e1e907 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -2,10 +2,10 @@ ------------------------------------------------------------------------------- -- FILE: luaotfload-configuration.lua -- DESCRIPTION: config file reader --- REQUIREMENTS: Luaotfload > 2.4 +-- REQUIREMENTS: Luaotfload 2.5 or above -- AUTHOR: Philipp Gesang (Phg), -- VERSION: same as Luaotfload --- CREATED: 2014-04-21 14:03:52+0200 +-- MODIFIED: 2014-07-13 14:19:32+0200 ------------------------------------------------------------------------------- -- @@ -47,7 +47,8 @@ local osgetenv = os.getenv local lpeg = require "lpeg" local lpegmatch = lpeg.match -local lpegsplitter = lpeg.splitat "," +local commasplitter = lpeg.splitat "," +local equalssplitter = lpeg.splitat "=" local kpse = kpse local kpseexpand_path = kpse.expand_path @@ -99,6 +100,33 @@ local valid_formats = tabletohash { "otf", "ttc", "ttf", "dfont", "afm", "pfb", "pfa", } +local feature_presets = { + arab = { + "ccmp", "locl", "isol", "fina", "fin2", + "fin3", "medi", "med2", "init", "rlig", + "calt", "liga", "cswh", "mset", "curs", + "kern", "mark", "mkmk", + }, + deva = { + "ccmp", "locl", "init", "nukt", "akhn", + "rphf", "blwf", "half", "pstf", "vatu", + "pres", "blws", "abvs", "psts", "haln", + "calt", "blwm", "abvm", "dist", "kern", + "mark", "mkmk", + }, + khmr = { + "ccmp", "locl", "pref", "blwf", "abvf", + "pstf", "pres", "blws", "abvs", "psts", + "clig", "calt", "blwm", "abvm", "dist", + "kern", "mark", "mkmk", + }, + thai = { + "ccmp", "locl", "liga", "kern", "mark", + "mkmk", + }, +} + + ------------------------------------------------------------------------------- --- DEFAULTS @@ -136,6 +164,36 @@ local default_config = { index_path_lua = nil, index_path_luc = nil, }, + default_features = { + global = { mode = "node" }, + dflt = { + "ccmp", "locl", "rlig", "liga", "clig", + "kern", "mark", "mkmk", 'itlc', + }, + + arab = feature_presets.arab, + syrc = feature_presets.arab, + mong = feature_presets.arab, + nko = feature_presets.arab, + + deva = feature_presets.deva, + beng = feature_presets.deva, + guru = feature_presets.deva, + gujr = feature_presets.deva, + orya = feature_presets.deva, + taml = feature_presets.deva, + telu = feature_presets.deva, + knda = feature_presets.deva, + mlym = feature_presets.deva, + sinh = feature_presets.deva, + + khmr = feature_presets.khmr, + tibt = feature_presets.khmr, + thai = feature_presets.thai, + lao = feature_presets.thai, + + hang = { "ccmp", "ljmo", "vjmo", "tjmo", }, + }, } ------------------------------------------------------------------------------- @@ -234,12 +292,32 @@ local build_cache_paths = function () return true end + +local set_default_features = function () + local default_features = config.luaotfload.default_features + luaotfload.features = luaotfload.features or { + global = { }, + defaults = { }, + } + current_features = luaotfload.features + for var, val in next, default_features do + if var == "global" then + current_features.global = val + else + current_features.defaults[var] = val + end + end + return true +end + + reconf_tasks = { - { "Set the log level" , set_loglevel }, - { "Build cache paths" , build_cache_paths }, - { "Check terminal dimensions" , check_termwidth }, - { "Set the font filter" , set_font_filter }, - { "Install font name resolver", set_name_resolver }, + { "Set the log level" , set_loglevel }, + { "Build cache paths" , build_cache_paths }, + { "Check terminal dimensions" , check_termwidth }, + { "Set the font filter" , set_font_filter }, + { "Install font name resolver", set_name_resolver }, + { "Set default features" , set_default_features }, } ------------------------------------------------------------------------------- @@ -259,13 +337,26 @@ local tointeger = function (n) end end +local toarray = function (s) + local fields = { lpegmatch (commasplitter, s) } + local ret = { } + for i = 1, #fields do + local field = stringstrip (fields[i]) + if field and field ~= "" then + ret[#ret + 1] = field + end + end + return ret +end + + local option_spec = { db = { formats = { in_t = string_t, out_t = string_t, transform = function (f) - local fields = { lpegmatch (lpegsplitter, f) } + local fields = toarray (f) --- check validity if not fields then @@ -278,7 +369,7 @@ local option_spec = { local known = { } local result = { } for i = 1, #fields do - local field = stringstrip (fields[i]) + local field = fields[i] if known[field] ~= true then --- yet unknown, tag as seen known[field] = true @@ -361,6 +452,34 @@ local option_spec = { index_path_lua = { in_t = string_t, }, index_path_luc = { in_t = string_t, }, }, + default_features = { + global = { + in_t = string_t, + out_t = table_t, + transform = function (s) + --- Split key=value arguments into hash. + local result = { } + local fields = toarray (s) + for _, field in next, fields do + local var, val = lpegmatch (equalssplitter, field) + if var and val then + if val == "true" then + result[var] = true + else + result[var] = val + end + end + end + return result + end, + }, + dflt = { in_t = string_t, out_t = table_t, transform = toarray, }, + arab = { in_t = string_t, out_t = table_t, transform = toarray, }, + deva = { in_t = string_t, out_t = table_t, transform = toarray, }, + khmr = { in_t = string_t, out_t = table_t, transform = toarray, }, + thai = { in_t = string_t, out_t = table_t, transform = toarray, }, + hang = { in_t = string_t, out_t = table_t, transform = toarray, }, + }, } ------------------------------------------------------------------------------- @@ -447,7 +566,7 @@ local process_options = function (opts) local vspec = spec[var] local t_val = type (val) if not vspec then - logreport ("both", 0, "conf", + logreport ("both", 2, "conf", "Section %d (%s): invalid configuration variable %q (%q); ignoring.", i, title, var, tostring (val)) diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua index 4ad188f..0dc7d0a 100644 --- a/src/luaotfload-features.lua +++ b/src/luaotfload-features.lua @@ -65,58 +65,6 @@ local stringformat = string.format local stringis_empty = string.is_empty local mathceil = math.ceil -local defaults = { - dflt = { - "ccmp", "locl", "rlig", "liga", "clig", - "kern", "mark", "mkmk", 'itlc', - }, - arab = { - "ccmp", "locl", "isol", "fina", "fin2", - "fin3", "medi", "med2", "init", "rlig", - "calt", "liga", "cswh", "mset", "curs", - "kern", "mark", "mkmk", - }, - deva = { - "ccmp", "locl", "init", "nukt", "akhn", - "rphf", "blwf", "half", "pstf", "vatu", - "pres", "blws", "abvs", "psts", "haln", - "calt", "blwm", "abvm", "dist", "kern", - "mark", "mkmk", - }, - khmr = { - "ccmp", "locl", "pref", "blwf", "abvf", - "pstf", "pres", "blws", "abvs", "psts", - "clig", "calt", "blwm", "abvm", "dist", - "kern", "mark", "mkmk", - }, - thai = { - "ccmp", "locl", "liga", "kern", "mark", - "mkmk", - }, - hang = { - "ccmp", "ljmo", "vjmo", "tjmo", - }, -} - -local global_defaults = { mode = "node" } - -defaults.beng = defaults.deva -defaults.guru = defaults.deva -defaults.gujr = defaults.deva -defaults.orya = defaults.deva -defaults.taml = defaults.deva -defaults.telu = defaults.deva -defaults.knda = defaults.deva -defaults.mlym = defaults.deva -defaults.sinh = defaults.deva - -defaults.syrc = defaults.arab -defaults.mong = defaults.arab -defaults.nko = defaults.arab - -defaults.tibt = defaults.khmr - -defaults.lao = defaults.thai ---[[ begin excerpt from font-ott.lua ]] @@ -800,8 +748,8 @@ local set_default_features = function (speclist) or "dflt" if support_incomplete[script] then report("log", 0, "load", - "support for the requested script: " - .. "%q may be incomplete", script) + "Support for the requested script: " + .. "%q may be incomplete.", script) end else script = "dflt" @@ -809,15 +757,15 @@ local set_default_features = function (speclist) speclist.script = script report("log", 1, "load", - "auto-selecting default features for script: %s", + "Auto-selecting default features for script: %s.", script) - local requested = defaults[script] + local requested = luaotfload.features.defaults[script] if not requested then report("log", 1, "load", - "no defaults for script %q, falling back to \"dflt\"", + "No default features for script %q, falling back to \"dflt\".", script) - requested = defaults.dflt + requested = luaotfload.features.defaults.dflt end for i=1, #requested do @@ -825,7 +773,7 @@ local set_default_features = function (speclist) if speclist[feat] ~= false then speclist[feat] = true end end - for feat, state in next, global_defaults do + for feat, state in next, luaotfload.features.global do --- This is primarily intended for setting node --- mode unless “base” is requested, as stated --- in the manual. -- cgit v1.2.3 From 5903e876c61b1e999e03404abf5c7a69bb194482 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 14:29:40 +0200 Subject: [*] update news --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index f75e61e..7fd4268 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,7 @@ Change History ``aux.read_font_index()``. * Distinguish XDG configuration paths (Reuben Thomas) * Optional configuration via rc files. + * Configure default features via configuration file. 2013/12/31, luaotfload v2.4 * Additional self-tests, now in separate file (luaotfload-diagnostics.lua) -- cgit v1.2.3 From 0c8c0abc6c1dafa129ccac2beca02b3c4afbccbf Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 14:36:42 +0200 Subject: [color] set color callback according to configuration --- src/luaotfload-colors.lua | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/luaotfload-colors.lua b/src/luaotfload-colors.lua index 476826e..67041fe 100644 --- a/src/luaotfload-colors.lua +++ b/src/luaotfload-colors.lua @@ -20,14 +20,6 @@ explanation: http://tug.org/pipermail/luatex/2013-May/004305.html --doc]]-- -local color_callback = config.luaotfload.run.color_callback -if not color_callback then - --- maybe this would be better as a method: "early" | "late" - color_callback = "pre_linebreak_filter" --- color_callback = "pre_output_filter" --- old behavior, breaks expansion -end - - local newnode = node.new local nodetype = node.id local traverse_nodes = node.traverse @@ -307,6 +299,11 @@ local color_callback_activated = 0 --- unit -> unit add_color_callback = function ( ) + local color_callback = config.luaotfload.run.color_callback + if not color_callback then + color_callback = "pre_linebreak_filter" + end + if color_callback_activated == 0 then luatexbase.add_to_callback(color_callback, color_handler, -- cgit v1.2.3 From e8b0693cc014c201a600aed32cec71044a72edce Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 15:00:34 +0200 Subject: [colors] fix logging --- src/luaotfload-colors.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/luaotfload-colors.lua b/src/luaotfload-colors.lua index 67041fe..9be2974 100644 --- a/src/luaotfload-colors.lua +++ b/src/luaotfload-colors.lua @@ -19,6 +19,8 @@ explanation: http://tug.org/pipermail/luatex/2013-May/004305.html --doc]]-- +local log = luaotfload.log +local logreport = log.report local newnode = node.new local nodetype = node.id @@ -81,8 +83,9 @@ local sanitize_color_expression = function (digits) digits = tostring(digits) local sanitized = lpegmatch(valid_digits, digits) if not sanitized then - luaotfload.warning( - "%q is not a valid rgb[a] color expression", digits) + logreport("both", 0, "color", + "%q is not a valid rgb[a] color expression", + digits) return nil end return sanitized -- cgit v1.2.3 From d84ce4ec794e634b853ca88925e1d20f6a1287e3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 15:01:59 +0200 Subject: [features,conf] generalize feature option parsing --- src/luaotfload-configuration.lua | 46 +++++++++++++++++----------------------- src/luaotfload-features.lua | 13 ++++++------ src/luaotfload-main.lua | 1 + 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 1e1e907..696bee6 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -28,6 +28,7 @@ local string = string local stringsub = string.sub local stringexplode = string.explode local stringstrip = string.strip +local stringfind = string.find local table = table local tableappend = table.append @@ -349,6 +350,22 @@ local toarray = function (s) return ret end +local tohash = function (s) + local result = { } + local fields = toarray (s) + for _, field in next, fields do + local var, val + if stringfind (field, "=") then + local tmp + var, tmp = lpegmatch (equalssplitter, field) + if tmp == "true" or tmp == "yes" then val = true else val = tmp end + else + var, val = field, true + end + result[var] = val + end + return result +end local option_spec = { db = { @@ -453,32 +470,7 @@ local option_spec = { index_path_luc = { in_t = string_t, }, }, default_features = { - global = { - in_t = string_t, - out_t = table_t, - transform = function (s) - --- Split key=value arguments into hash. - local result = { } - local fields = toarray (s) - for _, field in next, fields do - local var, val = lpegmatch (equalssplitter, field) - if var and val then - if val == "true" then - result[var] = true - else - result[var] = val - end - end - end - return result - end, - }, - dflt = { in_t = string_t, out_t = table_t, transform = toarray, }, - arab = { in_t = string_t, out_t = table_t, transform = toarray, }, - deva = { in_t = string_t, out_t = table_t, transform = toarray, }, - khmr = { in_t = string_t, out_t = table_t, transform = toarray, }, - thai = { in_t = string_t, out_t = table_t, transform = toarray, }, - hang = { in_t = string_t, out_t = table_t, transform = toarray, }, + __default = { in_t = string_t, out_t = table_t, transform = tohash, }, }, } @@ -563,7 +555,7 @@ local process_options = function (opts) end for var, val in next, vars do - local vspec = spec[var] + local vspec = spec[var] or spec.__default local t_val = type (val) if not vspec then logreport ("both", 2, "conf", diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua index 0dc7d0a..1fb6d7c 100644 --- a/src/luaotfload-features.lua +++ b/src/luaotfload-features.lua @@ -724,6 +724,8 @@ local support_incomplete = tabletohash({ --- (string, string) dict -> (string, string) dict local set_default_features = function (speclist) + local default_features = luaotfload.features + speclist = speclist or { } speclist[""] = nil --- invalid options stub @@ -760,20 +762,19 @@ local set_default_features = function (speclist) "Auto-selecting default features for script: %s.", script) - local requested = luaotfload.features.defaults[script] + local requested = default_features.defaults[script] if not requested then report("log", 1, "load", "No default features for script %q, falling back to \"dflt\".", script) - requested = luaotfload.features.defaults.dflt + requested = default_features.defaults.dflt end - for i=1, #requested do - local feat = requested[i] - if speclist[feat] ~= false then speclist[feat] = true end + for feat, state in next, requested do + if not speclist[feat] then speclist[feat] = state end end - for feat, state in next, luaotfload.features.global do + for feat, state in next, default_features.global do --- This is primarily intended for setting node --- mode unless “base” is requested, as stated --- in the manual. diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index c7694ea..6616468 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -64,6 +64,7 @@ local luatexbase = luatexbase local setmetatable = setmetatable local type, next = type, next local stringlower = string.lower +local stringformat = string.format local kpsefind_file = kpse.find_file local lfsisfile = lfs.isfile -- cgit v1.2.3 From f66b2ffe8fdc30e517d00b4796c142acd0e4438d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 15:15:46 +0200 Subject: [doc] describe feature settings and config file syntax in manpage --- doc/luaotfload.conf.rst | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst index 9940be2..774095b 100644 --- a/doc/luaotfload.conf.rst +++ b/doc/luaotfload.conf.rst @@ -61,7 +61,29 @@ To observe the difference in behavior, save above snippet to SYNTAX ======================================================================= -* TODO, short intro to ``.ini`` file syntax +The configuration file syntax follows the common INI format. For a more +detailed description please refer to the section “CONFIGURATION FILE” +of **git-config**\(1). A brief list of rules is given below: + + * Blank lines and lines starting with a semicolon (``;``) are ignored. + + * A configuration file is partitioned into sections that are declared + by specifying the section title in brackets on a separate line: :: + + [some-section] + ... section content ... + + * Sections consist of one or more variable assignments of the form + ``variable-name = value`` E. g.:: + + [foo] + bar = baz + quux = xyzzy + ... + + * Section and variable names may contain only uppercase and lowercase + letters as well as dashes (``-``). + VARIABLES ======================================================================= @@ -160,6 +182,26 @@ cannot find a requested font. Those who prefer to update manually using **luaotfload-tool** should unset this flag. +Section ``default-features`` +----------------------------------------------------------------------- + +By default Luaotfload enables ``node`` mode and picks the default font +features that are prescribed in the OpenType standard. This behavior +may be overridden in the ``default-features`` section. Global defaults +that will be applied for all scripts can be set via the ``global`` +option, others by the script they are to be applied to. For example, +a setting of :: + + [default-features] + global = mode=base,color=0000FF + dflt = smcp,onum + +would force *base* mode, tint all fonts blue and activate small +capitals and text figures globally. Features are specified as a comma +separated list of variables or variable-value pairs. Variables without +an explicit value are set to ``true``. + + Section ``misc`` ----------------------------------------------------------------------- -- cgit v1.2.3 From 16f1a6bde623466e2ffb34e361797b1481ad07d9 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 13 Jul 2014 15:19:22 +0200 Subject: [conf] use hash tables for default feature lists --- src/luaotfload-configuration.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index 696bee6..dfa222c 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -102,26 +102,26 @@ local valid_formats = tabletohash { } local feature_presets = { - arab = { + arab = tabletohash { "ccmp", "locl", "isol", "fina", "fin2", "fin3", "medi", "med2", "init", "rlig", "calt", "liga", "cswh", "mset", "curs", "kern", "mark", "mkmk", }, - deva = { + deva = tabletohash { "ccmp", "locl", "init", "nukt", "akhn", "rphf", "blwf", "half", "pstf", "vatu", "pres", "blws", "abvs", "psts", "haln", "calt", "blwm", "abvm", "dist", "kern", "mark", "mkmk", }, - khmr = { + khmr = tabletohash { "ccmp", "locl", "pref", "blwf", "abvf", "pstf", "pres", "blws", "abvs", "psts", "clig", "calt", "blwm", "abvm", "dist", "kern", "mark", "mkmk", }, - thai = { + thai = tabletohash { "ccmp", "locl", "liga", "kern", "mark", "mkmk", }, @@ -167,7 +167,7 @@ local default_config = { }, default_features = { global = { mode = "node" }, - dflt = { + dflt = tabletohash { "ccmp", "locl", "rlig", "liga", "clig", "kern", "mark", "mkmk", 'itlc', }, @@ -193,7 +193,7 @@ local default_config = { thai = feature_presets.thai, lao = feature_presets.thai, - hang = { "ccmp", "ljmo", "vjmo", "tjmo", }, + hang = tabletohash { "ccmp", "ljmo", "vjmo", "tjmo", }, }, } -- cgit v1.2.3