summaryrefslogtreecommitdiff
path: root/luaotfload.lua
diff options
context:
space:
mode:
Diffstat (limited to 'luaotfload.lua')
-rw-r--r--luaotfload.lua416
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