diff options
Diffstat (limited to 'luaotfload.lua')
-rw-r--r-- | luaotfload.lua | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/luaotfload.lua b/luaotfload.lua new file mode 100644 index 0000000..c85041b --- /dev/null +++ b/luaotfload.lua @@ -0,0 +1,416 @@ +module("luaotfload", package.seeall) + +luaotfload.module = { + name = "luaotfload", + version = 2.2, + date = "2013/04/15", + description = "OpenType layout system.", + author = "Elie Roux & Hans Hagen", + copyright = "Elie Roux", + license = "CC0" +} + +local luatexbase = luatexbase + +local type, next = type, next +local stringfind = string.find +local stringsub = string.sub +local stringmatch = string.match +local find_file = kpse.find_file + +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 + +_G.luaotfload = _G.luaotfload or { } +local luaotfload = _G.luaotfload + +--[[doc-- +No final decision has been made on how to handle font definition. At +the moment, there are three candidates: The \identifier{generic} +callback as hard-coded in the font loader, the \identifier{old} +wrapper, and a simplified version of the latter (\identifier{patch}) +that does nothing besides applying font patches. +--doc]]-- + +luaotfload.font_definer = "patch" --- | “generic” | “old” + +local error, warning, info, log = + luatexbase.provides_module(luaotfload.module) + +--[[doc-- +This is a necessary initalization in order not to rebuild an existing +font. +Maybe 600 should be replaced by \texmacro{pdfpkresolution} %% (why?) +or \luafunction{texconfig.pk_dpi} (and it should be replaced +dynamically), but we don't have access (yet) to the +\identifier{texconfig} table, so we let it be 600. +Anyway, it does still work fine even if \texmacro{pdfpkresolution} is +changed. +--doc]]-- + +kpse.init_prog("", 600, "/") + +--[[doc-- +We set the minimum version requirement for \LUATEX to v0.74, as it was +the first version to include version 5.2 of the \LUA interpreter. +--doc]]-- + +local luatex_version = 74 + +if tex.luatexversion < luatex_version then + warning("LuaTeX v%.2f is old, v%.2f is recommended.", + tex.luatexversion/100, + luatex_version /100) +end + +--[[doc-- +\subsection{Module loading} + +We load the files imported from \CONTEXT with this function. +It automatically prepends the prefix \fileent{otfl-} to its argument, +so we can refer to the files with their actual \CONTEXT name. +--doc]]-- + +local fl_prefix = "otfl" -- “luatex” for luatex-plain +local loadmodule = function (name) + local tofind = fl_prefix .."-"..name + local found = find_file(tofind,"tex") + if found then + log("loading file %s.", found) + dofile(found) + else + error("file %s not found.", tofind) + end +end + +--[[doc-- +Virtual fonts are resolved via a callback. +\luafunction{find_vf_file} derives the name of the virtual font file +from the filename. +(NB: \CONTEXT handles this likewise in \fileent{font-vf.lua}.) +--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 = find_file(name, "ovf") + if not fullname then + --fullname = find_file(file.removesuffix(name), "ovf") + fullname = find_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. +The wrapper file is \fileent{otfl-fonts.lua} which we imported from +\href{http://standalone.contextgarden.net/current/context/experimental/tex/generic/context/luatex/}{\LUATEX-Plain}. +It has roughly two purposes: + +\begin{enumerate} + + \item insert the functionality required for fontloader; and + + \item put it in place via the respective callbacks. + +\end{enumerate} + +How the first step is executed depends on the presence on the +\emphasis{merged font loader code}. +In \identifier{luaotfload} this is contained in the file +\fileent{otfl-fonts-merged.lua}. +If this file cannot be found, the original libraries from \CONTEXT of +which the merged code was composed are loaded instead. + +Hans provides two global tables to control the font loader: + + \begin{itemize} + \item \luafunction{generic_context}: + encapsulation mechanism, callback functions + \item \luafunction{non generic_context}: + customized code insertion + \end{itemize} + + +With \luafunction{non_generic_context} we can tailor the font loader +insertion to our file naming habits (key \luafunction{load_before}). +Additionally, \luafunction{skip_loading} can be unset to force loading +of the original libraries as though the merged code was absent. +Another key, \luafunction{load_after} is called at the time when the +font loader is actually inserted. +In combination with the option \luafunction{no_callbacks_yet} in +\luafunction{generic_context}, we can insert our own, +\identifier{luatexbase}-style callback handling here. +--doc]]-- +if not _G. generic_context then _G. generic_context = { } end +if not _G.non_generic_context then _G.non_generic_context = { } end + +local generic_context = generic_context +local non_generic_context =non_generic_context + +generic_context.no_callbacks_yet = true + +_G.non_generic_context = { luatex_fonts = { + load_before = "otfl-fonts-merged.lua", + -- load_after = nil, --- TODO, this is meant for callbacks + skip_loading = true, +}} + +--[[doc-- +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. +--doc]]-- + +local trapped_register = callback.register +callback.register = dummy_function + +--[[doc-- +Now that things are sorted out we can finally load the fontloader. +--doc]]-- + +loadmodule"fonts.lua" + +--[[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{otfl@}” to +avoid name clashes. +--doc]]-- + +do + local new_attribute = luatexbase.new_attribute + local the_attributes = luatexbase.attributes + + _G.attributes = _G.attributes or { } + + _G.attributes.private = function (name) + local attr = "otfl@" .. name + local number = the_attributes[attr] + if not number then + number = new_attribute(attr) + end + return number + end +end + +--[[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", + generic_context.callback_pre_linebreak_filter, + "luaotfload.node_processor", + 1) +add_to_callback("hpack_filter", + generic_context.callback_hpack_filter, + "luaotfload.node_processor", + 1) +add_to_callback("find_vf_file", + find_vf_file, "luaotfload.find_vf_file") + +loadmodule"font-otc.lua" -- TODO check what we can drop from otfl-features +loadmodule"lib-dir.lua" -- required by font-nms +loadmodule"luat-ovr.lua" + +if fonts and fonts.readers.tfm then + -------------------------------------------------------------------- + --- OFM; read this first + -------------------------------------------------------------------- + --- I can’t quite make out whether this is still relevant + --- as those ofm fonts always fail, even in the 2011 version + --- (mktexpk: don't know how to create bitmap font for omarabb.ofm) + --- the font loader appears to read ofm like tfm so if this + --- hack was supposed achieve that, we should excise it anyways + fonts.readers.ofm = fonts.readers.tfm + fonts.handlers.ofm = fonts.handlers.tfm --- empty anyways + fonts.formats.ofm = fonts.formats.tfm --- “type1” + --- fonts.readers.sequence[#fonts.readers.sequence+1] = "ofm" + -------------------------------------------------------------------- +end + +--[[doc-- + +Now we load the modules written for \identifier{luaotfload}. + +--doc]]-- +loadmodule"font-pfb.lua" --- new in 2.0, added 2011 +loadmodule"font-nms.lua" +loadmodule"font-clr.lua" +loadmodule"font-ltx.lua" --- new in 2.0, added 2011 + +--[[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-- + +This is a wrapper for the imported font loader. +As of 2013, everything it does appear to be redundand, so we won’t use +it unless somebody points out a cogent reason. +Nevertheless, it has been adapted to work with the current structure of +font data objects and will stay here for reference / until breakage is +reported. + +\emphasis{TODO} +This one also enables patching fonts. +The current fontloader apparently comes with a dedicated mechanism for +that already: enhancers. +How those work remains to be figured out. + +--doc]]-- +local define_font_wrapper = function (...) + --- we use “tfmdata” (not “fontdata”) for consistency with the + --- font loader + local tfmdata = fonts.definers.read(...) + if type(tfmdata) == "table" and tfmdata.shared then + local metadata = tfmdata.shared.rawdata.metadata + local mathdata = metadata.math --- do all fonts have this field? + if mathdata then + local mathconstants = { } --- why new hash, not modify in place? + local units_per_em = metadata.units_per_em + local size = tfmdata.size + for k,v in next, mathdata do + --- afaics this is alread taken care of by + --- definers.read + if stringfind(k, "Percent") then + -- keep percent values as is + print(k,v) + mathconstants[k] = v + else + mathconstants[k] = v / units_per_em * size + end + end + --- for \overwithdelims + --- done by definers.read as well + mathconstants.FractionDelimiterSize = 1.01 * size + --- fontloader has 2.4 × size + mathconstants.FractionDelimiterDisplayStyleSize = 2.39 * size + tfmdata.MathConstants = mathconstants + end + call_callback("luaotfload.patch_font", tfmdata) + end + return tfmdata +end + +--[[doc-- + +\subsection{\CONTEXT override} + +We provide a simplified version of the original font definition +callback. + +--doc]]-- + +local read_font_file = fonts.definers.read +local patch_defined_font = function (...) + local tfmdata = read_font_file(...)-- spec -> size -> id -> tmfdata + if type(tfmdata) == "table" then + call_callback("luaotfload.patch_font", tfmdata) + end + -- inspect(table.keys(tfmdata)) + return tfmdata +end + +caches.compilemethod = "both" + +reset_callback("define_font") + +--[[doc-- +Finally we register the callbacks +--doc]]-- + +if luaotfload.font_definer == "old" then + add_to_callback("define_font", + define_font_wrapper, + "luaotfload.define_font", + 1) +elseif luaotfload.font_definer == "generic" then + add_to_callback("define_font", + generic_context.callback_define_font, + "luaotfload.define_font", + 1) +elseif luaotfload.font_definer == "patch" then + add_to_callback("define_font", + patch_defined_font, + "luaotfload.define_font", + 1) +end + +--[[todo-- +--- The manual promises coercion of the file: lookup if +--- the asked name is enclosed in brackets. +--- A couple things make me doubt that this is the case: +--- +--- 1) there doesn’t appear to be code for these cases +--- 2) the brackets remain part of the file name +--- 3) we still get calls to names.resolve which +--- ignores the “lookup” field of the spec it gets +--- +--- For this reason here is some code that a) coerces +--- file: lookups in these cases and b) strips the brackets +--- from the file name. As we *still* get name: lookups even +--- though this code is active I’ll just leave it here +--- for reference, ineffective as it is. +do + local getspecification, makespecification = + fonts.definers.getspecification, fonts.definers.makespecification + + local analyze = function (specification, size) + local lookup, name, sub, method, detail = getspecification(specification or "") + local filename = stringmatch(name, "^%[(.*)%]$") + if filename then + lookup = "file" --> coerce file: + name = filename --> remove brackets + end + return makespecification(specification, lookup, name, sub, method, detail, size) + end + fonts.definers.analyze = analyze +end +--]]-- + +loadmodule"features.lua" + +-- vim:tw=71:sw=4:ts=4:expandtab |