From 5bba2ed9b294bca8c8b7afcbbc1f2dea990c5eb5 Mon Sep 17 00:00:00 2001 From: Olof-Joachim Frahm Date: Tue, 23 Oct 2012 23:42:44 +0300 Subject: Handle missing names properly. For some (possibly broken) fonts, e.g. Gentoo's media-fonts/twmoefonts, the names field is nil, thus breaking lookup even for other fonts. Defaulting to nil if the field is missing fixes this problem. --- otfl-font-nms.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 8290e17..87f4987 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -118,10 +118,10 @@ function names.resolve(_,_,specification) -- the 1st two parameters are used by if data.mappings then local found = { } for _,face in next, data.mappings do - local family = sanitize(face.names.family) - local subfamily = sanitize(face.names.subfamily) - local fullname = sanitize(face.names.fullname) - local psname = sanitize(face.names.psname) + local family = sanitize(face.names and face.names.family) + local subfamily = sanitize(face.names and face.names.subfamily) + local fullname = sanitize(face.names and face.names.fullname) + local psname = sanitize(face.names and face.names.psname) local fontname = sanitize(face.fontname) local pfullname = sanitize(face.fullname) local optsize, dsnsize, maxsize, minsize -- cgit v1.2.3 From 2e59059d0a2e74c55fad59847679f2c61e72f476 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 20 Oct 2012 10:42:53 +0200 Subject: fix redundancy in test alternate_sub --- tests/alternate_sub.tex | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/alternate_sub.tex b/tests/alternate_sub.tex index e646ddb..862c665 100644 --- a/tests/alternate_sub.tex +++ b/tests/alternate_sub.tex @@ -13,4 +13,3 @@ \1 \char"06DD \2 \char"06DD \bye -\bye -- cgit v1.2.3 From a38dcda19542af3e9d27e6792166a027e29a125b Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sun, 7 Apr 2013 19:50:05 +0200 Subject: Fixing character escaping errors Some syntax errors in Lua for character escaping were silently ignore by old versions of LuaTeX, but the new one raises errors. Maybe a side effect of Lua 5.2? --- otfl-basics-gen.lua | 2 +- otfl-font-nms.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/otfl-basics-gen.lua b/otfl-basics-gen.lua index bdbc3cf..bfd81fd 100644 --- a/otfl-basics-gen.lua +++ b/otfl-basics-gen.lua @@ -85,7 +85,7 @@ local remapper = { } function resolvers.findfile(name,fileformat) - name = string.gsub(name,"\\","\/") + name = string.gsub(name,"\\","/") fileformat = fileformat and string.lower(fileformat) local found = kpse.find_file(name,(fileformat and fileformat ~= "" and (remapper[fileformat] or fileformat)) or file.extname(name,"tex")) if not found or found == "" then diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 87f4987..094ebec 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -557,8 +557,8 @@ local function scan_texmf_fonts(fontnames, newfontnames) else logs.info("Scanning TEXMF and OS fonts...") end - local fontdirs = expandpath("$OPENTYPEFONTS"):gsub("^\.", "") - fontdirs = fontdirs .. expandpath("$TTFONTS"):gsub("^\.", "") + 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) -- cgit v1.2.3 From 28eebe8a17b640eb101e684b38efe836aa7ade73 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Mon, 8 Apr 2013 17:38:30 +0200 Subject: Fixing module loading in the script Even though the script should not really be useful anymore, it might be useful for debugging purposes, so I think it's worth fixing it. It was crashing on require("lualibs"), unable to find the module; so I made it load luatexbase.loader.lua, in oder to fix it... --- mkluatexfontdb.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 4275c84..38b9daa 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -11,6 +11,14 @@ kpse.set_program_name("luatex") function string.quoted(s) return string.format("%q",s) end -- XXX +-- First we need to be able to load module (code copied from +-- luatexbase-loader.sty): +local file = "luatexbase.loader.lua" +local path = assert(kpse.find_file(file, 'tex'), + "File '"..file.."' not found") +texio.write_nl("("..path..")") +dofile(path) + require("lualibs") require("otfl-basics-gen.lua") require("otfl-font-nms") -- cgit v1.2.3 From f71beac9ee3f29083c86c0c87049cb54f4ad91aa Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 12:04:32 +0200 Subject: remove merged files --- otfl-data-con.lua | 135 --- otfl-font-cid.lua | 165 ---- otfl-font-con.lua | 1332 -------------------------- otfl-font-ini.lua | 38 - otfl-font-map.lua | 307 ------ otfl-font-ota.lua | 373 -------- otfl-font-otb.lua | 636 ------------- otfl-font-otf.lua | 2080 ---------------------------------------- otfl-font-oti.lua | 92 -- otfl-font-otn.lua | 2712 ----------------------------------------------------- 10 files changed, 7870 deletions(-) delete mode 100644 otfl-data-con.lua delete mode 100644 otfl-font-cid.lua delete mode 100644 otfl-font-con.lua delete mode 100644 otfl-font-ini.lua delete mode 100644 otfl-font-map.lua delete mode 100644 otfl-font-ota.lua delete mode 100644 otfl-font-otb.lua delete mode 100644 otfl-font-otf.lua delete mode 100644 otfl-font-oti.lua delete mode 100644 otfl-font-otn.lua diff --git a/otfl-data-con.lua b/otfl-data-con.lua deleted file mode 100644 index ed4f2de..0000000 --- a/otfl-data-con.lua +++ /dev/null @@ -1,135 +0,0 @@ -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) - ---[[ldx-- -

Once we found ourselves defining similar cache constructs -several times, containers were introduced. Containers are used -to collect tables in memory and reuse them when possible based -on (unique) hashes (to be provided by the calling function).

- -

Caching to disk is disabled by default. Version numbers are -stored in the saved table which makes it possible to change the -table structures without bothering about the disk cache.

- -

Examples of usage can be found in the font related code.

---ldx]]-- - -containers = containers or { } -local containers = containers -containers.usecache = true - -local report_containers = logs.reporter("resolvers","containers") - -local function report(container,tag,name) - if trace_cache or trace_containers then - report_containers("container: %s, tag: %s, name: %s",container.subcategory,tag,name or 'invalid') - end -end - -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, -- after all, this is TeX - trace = false, - -- writable = caches.getwritablepath and caches.getwritablepath (category,subcategory) or { "." }, - -- readables = caches.getreadablepaths and caches.getreadablepaths(category,subcategory) or { "." }, - } - 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 - report(container,"loaded",name) - else - stored = nil - end - storage[name] = stored - elseif stored then - report(container,"reusing",name) - 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) - report(container,"saved",name) - data.unique, data.shared = unique, shared - end - report(container,"stored",name) - 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%d]+","-")) -end diff --git a/otfl-font-cid.lua b/otfl-font-cid.lua deleted file mode 100644 index 4a4c4d2..0000000 --- a/otfl-font-cid.lua +++ /dev/null @@ -1,165 +0,0 @@ -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 trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) - -local report_otf = logs.reporter("fonts","otf loading") - -local fonts = fonts - -local cid = { } -fonts.cid = cid - -local cidmap = { } -local cidmax = 10 - --- original string parser: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap --- --- 18964 18964 (leader) --- 0 /.notdef --- 1..95 0020 --- 99 3000 - -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 = { }, { } -- we could use Carg now - -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 -- we use the frozen variant - -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: %s, ordering: %s, supplement: %s, filename: %s",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 %s",filename) - end - cidmap[hashname] = found - found.usedname = file.basename(filename) - end - end - end - return found -end - --- cf Arthur R. we can safely scan upwards since cids are downward compatible - -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 - -- check for already loaded file - local filename = format(registry,ordering,supplement) - local found = cidmap[lower(filename)] - if found then - return found - end - if trace_loading then - report_otf("needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement) - end - found = locate(registry,ordering,supplement) - if not found then - local supnum = tonumber(supplement) - local cidnum = nil - -- next highest (alternatively we could start high) - if supnum < cidmax then - for s=supnum+1,cidmax do - local c = locate(registry,ordering,s) - if c then - found, cidnum = c, s - break - end - end - end - -- next lowest (least worse fit) - if not found and supnum > 0 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 - -- prevent further lookups -- somewhat tricky - 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 diff --git a/otfl-font-con.lua b/otfl-font-con.lua deleted file mode 100644 index 9280996..0000000 --- a/otfl-font-con.lua +++ /dev/null @@ -1,1332 +0,0 @@ -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" -} - - --- some names of table entries will be changed (no _) - -local utf = unicode.utf8 - -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") - --- watch out: no negative depths and negative eights permitted in regular fonts - ---[[ldx-- -

Here we only implement a few helper functions.

---ldx]]-- - -local fonts = fonts -local constructors = { } -fonts.constructors = constructors -local handlers = { } -fonts.handlers = handlers - -local specifiers = fonts.specifiers -local contextsetups = specifiers.contextsetups -local contextnumbers = specifiers.contextnumbers - -local allocate = utilities.storage.allocate -local setmetatableindex = table.setmetatableindex - --- will be directives - -constructors.dontembed = allocate() -constructors.autocleanup = true -constructors.namemode = "fullpath" -- will be a function - -constructors.version = 1.01 -constructors.cache = containers.define("fonts", "constructors", constructors.version, false) - -constructors.privateoffset = 0xF0000 -- 0x10FFFF - --- Some experimental helpers (handy for tracing): --- --- todo: extra: --- --- extra_space => space.extra --- space => space.width --- space_stretch => space.stretch --- space_shrink => space.shrink - --- We do keep the x-height, extra_space, space_shrink and space_stretch --- around as these are low level official names. - -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", -- might become float - shrink = "integerscale", -- might become float - step = "integerscale", -- might become float - 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", - }, -} - --- This might become an interface: - -local designsizes = allocate() -constructors.designsizes = designsizes -local loadedfonts = allocate() -constructors.loadedfonts = loadedfonts - ---[[ldx-- -

We need to normalize the scale factor (in scaled points). This has to -do with the fact that uses a negative multiple of 1000 as -a signal for a font scaled based on the design size.

---ldx]]-- - -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) -- handles designsize in sp as well - if scaledpoints < 0 then - if designsize then - local factor = constructors.factor - if designsize > factor then -- or just 1000 / when? mp? - return (- scaledpoints/1000) * designsize -- sp's - else - return (- scaledpoints/1000) * designsize * factor - end - else - return (- scaledpoints/1000) * 10 * factor - end - else - return scaledpoints - end -end - ---[[ldx-- -

Beware, the boundingbox is passed as reference so we may not overwrite it -in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to -excessive memory usage in CJK fonts, we no longer pass the boundingbox.)

---ldx]]-- - --- The scaler is only used for otf and afm and virtual fonts. If --- a virtual font has italic correction make sure to set the --- hasitalics flag. Some more flags will be added in --- the future. - ---[[ldx-- -

The reason why the scaler was originally split, is that for a while we experimented -with a helper function. However, in practice the calls are too slow to -make this profitable and the based variant was just faster. A days -wasted day but an experience richer.

---ldx]]-- - --- we can get rid of the tfm instance when we have fast access to the --- scaled character dimensions at the tex end, e.g. a fontobject.width --- actually we already have soem of that now as virtual keys in glyphs --- --- flushing the kern and ligature tables from memory saves a lot (only --- base mode) but it complicates vf building where the new characters --- demand this data .. solution: functions that access them - -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 - -- if v.kerns then v.kerns = nil end - end - end -end - --- experimental, sharing kerns (unscaled and scaled) saves memory --- local sharedkerns, basekerns = constructors.check_base_kerns(tfmdata) --- loop over descriptions (afm and otf have descriptions, tfm not) --- there is no need (yet) to assign a value to chr.tonunicode - --- constructors.prepare_base_kerns(tfmdata) -- optimalization - --- we have target.name=metricfile and target.fullname=RealName and target.filename=diskfilename --- when collapsing fonts, luatex looks as both target.name and target.fullname as ttc files --- can have multiple subfonts - -function constructors.calculatescale(tfmdata,scaledpoints) - local parameters = tfmdata.parameters - if scaledpoints < 0 then - scaledpoints = (- scaledpoints/1000) * (tfmdata.designsize or parameters.designsize) -- already in sp - end - return scaledpoints, scaledpoints / (parameters.units or 1000) -- delta -end - -local unscaled = { - ScriptPercentScaleDown = true, - ScriptScriptPercentScaleDown = true, - RadicalDegreeBottomRaisePercent = true -} - -function constructors.assignmathparameters(target,original) -- simple variant, not used in context - -- when a tfm file is loaded, it has already been scaled - -- and it never enters the scaled so this is otf only and - -- even then we do some extra in the context math plugins - 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) - -- can be used for additional tweaking -end - -function constructors.aftercopyingcharacters(target,original) - -- can be used for additional tweaking -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 = { } -- the new table - -- - if tonumber(specification) then - specification = { size = specification } - end - -- - 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 { } -- bad news if empty - local characters = tfmdata.characters or { } -- bad news if empty - local changed = tfmdata.changed or { } -- for base mode - 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 -- we need to loop so no metatable - target.characters = targetcharacters - target.descriptions = targetdescriptions - target.parameters = targetparameters - -- target.mathparameters = targetmathparameters -- happens elsewhere - target.properties = targetproperties - target.goodies = targetgoodies - target.shared = shared - target.resources = resources - target.unscaled = tfmdata -- the original unscaled one - -- - -- specification.mathsize : 1=text 2=script 3=scriptscript - -- specification.textsize : natural (text)size - -- parameters.mathsize : 1=text 2=script 3=scriptscript >1000 enforced size (feature value other than yes) - -- - 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 -- safeguard - scaledpoints = forcedsize - end - -- - 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) -- maybe we virtualize more afterwards - end - -- - -- boundary keys are no longer needed as we now have a string 'right_boundary' - -- that can be used in relevant tables (kerns and ligatures) ... not that I ever - -- used them - -- - -- boundarychar_label = 0, -- not needed - -- boundarychar = 65536, -- there is now a string 'right_boundary' - -- false_boundarychar = 65536, -- produces invalid tfm in luatex - -- - targetproperties.language = properties.language or "dflt" -- inherited - targetproperties.script = properties.script or "dflt" -- inherited - targetproperties.mode = properties.mode or "base" -- inherited - -- - local askedscaledpoints = scaledpoints - local scaledpoints, delta = constructors.calculatescale(tfmdata,scaledpoints) -- no shortcut, dan be redefined - -- - local hdelta = delta - local vdelta = delta - -- - target.designsize = parameters.designsize -- not really needed so it muight become obsolete - target.units_per_em = units -- just a trigger for the backend (does luatex use this? if not it will go) - -- - local direction = properties.direction or tfmdata.direction or 0 -- pointless, as we don't use omf fonts at all - 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 -- for the moment we fall back on - local fullname = properties.fullname or tfmdata.fullname -- names in the tfmdata although - local filename = properties.filename or tfmdata.filename -- that is not the right place to - local psname = properties.psname or tfmdata.psname -- pass them - local name = properties.name or tfmdata.name - -- - if not psname or psname == "" then - -- name used in pdf file as well as for selecting subfont in ttc/dfont - 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 - -- expansion (hz) - 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 - -- protrusion - local protrusion = parameters.protrusion - if protrusion then - target.auto_protrude = protrusion.auto - end - -- widening - local extendfactor = parameters.extendfactor or 0 - if extendfactor ~= 0 and extendfactor ~= 1 then - hdelta = hdelta * extendfactor - target.extend = extendfactor * 1000 -- extent ? - else - target.extend = 1000 -- extent ? - end - -- slanting - 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) - 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 -- slantperpoint - 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) -- official copies for us - -- - local protrusionfactor = (targetquad ~= 0 and 1000/targetquad) or 0 - local scaledwidth = defaultwidth * hdelta - local scaledheight = defaultheight * vdelta - local scaleddepth = defaultdepth * vdelta - -- - if trace_defining then - report_defining("scaling by (%s,%s): name '%s', fullname: '%s', filename: '%s'", - hdelta,vdelta,name or "noname",fullname or "nofullname",filename or "nofilename") - end - -- - local hasmath = (properties.hasmath or next(mathparameters)) and true - if hasmath then - if trace_defining then - report_defining("math enabled for: name '%s', fullname: '%s', filename: '%s'", - name or "noname",fullname or "nofullname",filename or "nofilename") - end - constructors.assignmathparameters(target,tfmdata) -- does scaling and whatever is needed - properties.hasmath = true - target.nomath = false - target.MathConstants = target.mathparameters - else - if trace_defining then - report_defining("math disabled for: name '%s', fullname: '%s', filename: '%s'", - name or "noname",fullname or "nofullname",filename or "nofilename") - end - properties.hasmath = false - target.nomath = true - target.mathparameters = nil -- nop - end - -- - local italickey = "italic" - -- - -- some context specific trickery (this will move to a plugin) - -- - if hasmath then - if properties.mathitalics then - italickey = "italic_correction" - if trace_defining then - report_defining("math italics disabled for: name '%s', fullname: '%s', filename: '%s'", - name or "noname",fullname or "nofullname",filename or "nofilename") - end - end - autoitalicamount = false -- new - else - if properties.textitalics then - italickey = "italic_correction" - if trace_defining then - report_defining("text italics disabled for: name '%s', fullname: '%s', filename: '%s'", - name or "noname",fullname or "nofullname",filename or "nofilename") - end - if properties.delaytextitalics then - autoitalicamount = false - end - end - end - -- - -- end of context specific trickery - -- - constructors.beforecopyingcharacters(target,tfmdata) - -- - local sharedkerns = { } - -- - -- we can have a dumb mode (basemode without math etc) that skips most - -- - for unicode, character in next, characters do - local chr, description, index, touni - if changed then - -- basemode hack (we try to catch missing tounicodes, e.g. needed for ssty in math cambria) - 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] -- nb: index! - if not touni then -- goodie - local d = descriptions[unicode] or characters[unicode] - local i = d.index or unicode - touni = tounicode[i] -- nb: index! - end - end - else - description = descriptions[unicode] or character - index = description.index or unicode - if tounicode then - touni = tounicode[index] -- nb: index! - end - end - else - description = descriptions[unicode] or character - index = description.index or unicode - if tounicode then - touni = tounicode[index] -- nb: 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 then depth = vdelta*depth else depth = scaleddepth 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 - -- this saves a little bit of memory time and memory, esp for big cjk fonts - 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 trace_scaling then - -- report_defining("t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or "",index or 0,description.name or '-',description.class or '-') - -- end - if hasquality then - -- we could move these calculations elsewhere (saves calculations) - local ve = character.expansion_factor - if ve then - chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere - 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 -- < 0 indicates no overshoot or a very small auto italic - 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 - -- to be tested - if hasmath then - -- todo, just operate on descriptions.math - local vn = character.next - if vn then - chr.next = vn - -- if character.vert_variants or character.horiz_variants then - -- report_defining("glyph U+%05X has combination of next, vert_variants and horiz_variants",index) - -- end - 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 -- not in math ? - 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 -- singular -> should be patched in luatex ! - 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 -- shared - 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 - -- we assume non scaled commands here - -- tricky .. we need to scale pseudo math glyphs too - -- which is why we deal with rules too - 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 -- not comment - tt[i] = ivc -- shared since in cache and untouched - end - end - chr.commands = tt - else - chr.commands = vc - end - chr.index = nil - end - end - targetcharacters[unicode] = chr - end - -- - constructors.aftercopyingcharacters(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 = { } -- context specific - 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 655360 - end - -- - if not parameters.units then - parameters.units = tfmdata.units_per_em or 1000 - end - -- - if not tfmdata.descriptions then - local descriptions = { } -- yes or no - 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 - -- - -- tfmdata.fonts - -- tfmdata.unscaled - -- - 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 -- most tricky part - 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 - ---[[ldx-- -

A unique hash value is generated by:

---ldx]]-- - -local hashmethods = { } -constructors.hashmethods = hashmethods - -function constructors.hashfeatures(specification) -- will be overloaded - 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 k ~= "number" and k ~= "features" then -- I need to figure this out, features - 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 - ---[[ldx-- -

In principle we can share tfm tables when we are in node for a font, but then -we need to define a font switch as an id/attr switch which is no fun, so in that -case users can best use dynamic features ... so, we will not use that speedup. Okay, -when we get rid of base mode we can optimize even further by sharing, but then we -loose our testcases for .

---ldx]]-- - -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 - -- local mathsize = specification.mathsize or 0 - -- if mathsize > 0 then - -- local textsize = specification.textsize - -- if fallbacks then - -- return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ] @ ' .. fallbacks - -- else - -- return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ]' - -- end - -- else - if fallbacks then - return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks - else - return hash .. ' @ ' .. tostring(size) - end - -- end -end - -function constructors.setname(tfmdata,specification) -- todo: get specification from tfmdata - if constructors.namemode == "specification" then - -- not to be used in context ! - local specname = specification.specification - if specname then - tfmdata.properties.name = specname - if trace_defining then - report_otf("overloaded fontname: '%s'",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) -- no shortcut - foundfilename = resolvers.findbinfile(askedfilename,"") or "" - if foundfilename == "" then - report_defining("source file '%s' is not found",askedfilename) - foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or "" - if foundfilename ~= "" then - report_defining("using source file '%s' (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.extname(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 '%s', group '%s', mode '%s'",name or "?",group or "?",mode) - os.exit() - elseif position then - -- todo: remove existing - 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 '%s', group '%s'",name or "?",group or "?") - os.exit() - end - local source = source[group] - if not source then - report_defining("fatal source error in setting feature '%s', group '%s'",name or "?",group or "?") - 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.newfeatures(what) - local features = handlers[what].features - if not features then - local tables = handlers[what].tables -- can be preloaded - features = allocate { - defaults = { }, - descriptions = tables and tables.features or { }, - initializers = { base = { }, node = { } }, - processors = { base = { }, node = { } }, - manipulators = { base = { }, node = { } }, - } - features.register = function(specification) return register(features,specification) end - handlers[what].features = features -- will also become hidden - end - return features -end - ---[[ldx-- -

We need to check for default features. For this we provide -a helper function.

---ldx]]-- - -function constructors.checkedfeatures(what,features) - local defaults = handlers[what].features.defaults - if features and next(features) then - features = fastcopy(features) -- can be inherited (mt) but then no loops possible - for key, value in next, defaults do - if features[key] == nil then - features[key] = value - end - end - return features - else - return fastcopy(defaults) -- we can change features in place - end -end - --- before scaling - -function constructors.initializefeatures(what,tfmdata,features,trace,report) - if features and next(features) then - local properties = tfmdata.properties or { } -- brrr - local whathandler = handlers[what] - local whatfeatures = whathandler.features - local whatinitializers = whatfeatures.initializers - local whatmodechecker = whatfeatures.modechecker - -- properties.mode can be enforces (for instance in font-otd) - local mode = properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base" - properties.mode = mode -- also status - features.mode = mode -- both properties.mode or features.mode can be changed - -- - 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 --- we could intercept mode here .. needs a rewrite of this whole loop then but it's cleaner that way - local value = features[feature] - if not value then - -- disabled - elseif done[feature] then - -- already done - else - local action = step.action - if trace then - report("initializing feature %s to %s for mode %s for font %s",feature, - tostring(value),mode or 'unknown', tfmdata.properties.fullname or 'unknown') - end - action(tfmdata,value,features) -- can set mode (e.g. goodies) so it can trigger a restart - if mode ~= properties.mode or mode ~= features.mode then - if whatmodechecker then - properties.mode = whatmodechecker(tfmdata,features,properties.mode) -- force checking - 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 -- to be sure - return true - else - return false - end -end - --- while typesetting - -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 processors = whatprocessors[properties.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 %s for mode %s for font %s",feature, - mode or 'unknown', tfmdata.properties.fullname or 'unknown') - end - if action then - nofprocesses = nofprocesses + 1 - processes[nofprocesses] = action - end - end - end - elseif trace then - report("no feature processors for mode %s for font %s", - mode or 'unknown', tfmdata.properties.fullname or 'unknown') - end - end - return processes -end - --- after scaling - -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 manipulators = whatmanipulators[properties.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 %s for mode %s for font %s",feature, - mode or 'unknown', tfmdata.properties.fullname or 'unknown') - end - if action then - action(tfmdata,feature,value) - end - end - end - end - end -end diff --git a/otfl-font-ini.lua b/otfl-font-ini.lua deleted file mode 100644 index 8eeba0c..0000000 --- a/otfl-font-ini.lua +++ /dev/null @@ -1,38 +0,0 @@ -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" -} - --- basemethods -> can also be in list --- presetcontext -> defaults --- hashfeatures -> ctx version - ---[[ldx-- -

Not much is happening here.

---ldx]]-- - -local lower = string.lower -local allocate, mark = utilities.storage.allocate, utilities.storage.mark - -local report_defining = logs.reporter("fonts","defining") - -fontloader.totable = fontloader.to_table - -fonts = fonts or { } -- already defined in context -local fonts = fonts - --- some of these might move to where they are used first: - -fonts.hashes = { identifiers = allocate() } -fonts.analyzers = { } -- not needed here -fonts.readers = { } -fonts.tables = { } -fonts.definers = { methods = { } } -fonts.specifiers = fonts.specifiers or { } -- in format ! -fonts.loggers = { register = function() end } -fonts.helpers = { } - -fonts.tracers = { } -- for the moment till we have move to moduledata diff --git a/otfl-font-map.lua b/otfl-font-map.lua deleted file mode 100644 index 7f5305f..0000000 --- a/otfl-font-map.lua +++ /dev/null @@ -1,307 +0,0 @@ -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 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 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") -- not otf only - -local fonts = fonts -local mappings = { } -fonts.mappings = mappings - ---[[ldx-- -

Eventually this code will disappear because map files are kind -of obsolete. Some code may move to runtime or auxiliary modules.

-

The name to unciode related code will stay of course.

---ldx]]-- - -local function loadlumtable(filename) -- will move to font goodies - 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("enhance: loading %s ",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^1) / 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 parser = mappings.makenameparser("Japan1") ---~ local parser = mappings.makenameparser() ---~ local function test(str) ---~ local b, a = lpegmatch(parser,str) ---~ print((a and table.serialize(b)) or b) ---~ end ---~ test("a.sc") ---~ test("a") ---~ test("uni1234") ---~ test("uni1234.xx") ---~ test("uni12349876") ---~ test("index1234") ---~ test("Japan1.123") - -local function tounicode16(unicode) - if unicode < 0x10000 then - return format("%04X",unicode) - else - return format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) - end -end - -local function tounicode16sequence(unicodes) - local t = { } - for l=1,#unicodes do - local unicode = unicodes[l] - if unicode < 0x10000 then - t[l] = format("%04X",unicode) - else - t[l] = format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) - 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)- 0xD800)*0x400 + tonumber(r,16) - 0xDC00 - end -end - ---~ This is quite a bit faster but at the cost of some memory but if we ---~ do this we will also use it elsewhere so let's not follow this route ---~ now. I might use this method in the plain variant (no caching there) ---~ but then I need a flag that distinguishes between code branches. ---~ ---~ local cache = { } ---~ ---~ function mappings.tounicode16(unicode) ---~ local s = cache[unicode] ---~ if not s then ---~ if unicode < 0x10000 then ---~ s = format("%04X",unicode) ---~ else ---~ s = format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) ---~ end ---~ cache[unicode] = s ---~ end ---~ return s ---~ end - -mappings.loadlumtable = loadlumtable -mappings.makenameparser = makenameparser -mappings.tounicode16 = tounicode16 -mappings.tounicode16sequence = tounicode16sequence -mappings.fromunicode16 = fromunicode16 - -local separator = S("_.") -local other = C((1 - separator)^1) -local ligsplitter = Ct(other * (separator * other)^0) - ---~ print(table.serialize(lpegmatch(ligsplitter,"this"))) ---~ print(table.serialize(lpegmatch(ligsplitter,"this.that"))) ---~ print(table.serialize(lpegmatch(ligsplitter,"japan1.123"))) ---~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more"))) ---~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more.that"))) - -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 - -- we need to move this code - unicodes['space'] = unicodes['space'] or 32 - unicodes['hyphen'] = unicodes['hyphen'] or 45 - unicodes['zwj'] = unicodes['zwj'] or 0x200D - unicodes['zwnj'] = unicodes['zwnj'] or 0x200C - -- the tounicode mapping is sparse and only needed for alternatives - local private = fonts.constructors.privateoffset - local unknown = format("%04X",utfbyte("?")) - local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context - local tounicode = { } - local originals = { } - resources.tounicode = tounicode - resources.originals = originals - local lumunic, uparser, oparser - local cidinfo, cidnames, cidcodes, usedmap - if false then -- will become an option - 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) - ns = ns + 1 - end - -- cidmap heuristics, beware, there is no guarantee for a match unless - -- the chain resolves - if (not unicode) and usedmap then - local foundindex = lpegmatch(oparser,name) - if foundindex then - unicode = cidcodes[foundindex] -- name to number - if unicode then - originals[index] = unicode - tounicode[index] = tounicode16(unicode) - ns = ns + 1 - else - local reference = cidnames[foundindex] -- number to name - if reference then - local foundindex = lpegmatch(oparser,reference) - if foundindex then - unicode = cidcodes[foundindex] - if unicode then - originals[index] = unicode - tounicode[index] = tounicode16(unicode) - ns = ns + 1 - end - end - if not 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) - ns = ns + 1 - unicode = foundcodes - end - end - end - end - end - end - end - -- a.whatever or a_b_c.whatever or a_b_c (no numbers) - if not unicode then - local split = lpegmatch(ligsplitter,name) - local nplit = split and #split or 0 - if nplit >= 2 then - local t, n = { }, 0 - for l=1,nplit do - local base = split[l] - local u = unicodes[base] or unicodevector[base] - if not u then - break - elseif type(u) == "table" then - n = n + 1 - t[n] = u[1] - else - n = n + 1 - t[n] = u - end - end - if n == 0 then -- done then - -- nothing - elseif n == 1 then - originals[index] = t[1] - tounicode[index] = tounicode16(t[1]) - else - originals[index] = t - tounicode[index] = tounicode16sequence(t) - end - nl = nl + 1 - unicode = true - else - -- skip: already checked and we don't want privates here - end - end - -- last resort (we might need to catch private here as well) - if not unicode then - local foundcodes, multiple = lpegmatch(uparser,name) - if foundcodes then - if multiple then - originals[index] = foundcodes - tounicode[index] = tounicode16sequence(foundcodes) - nl = nl + 1 - unicode = true - else - originals[index] = foundcodes - tounicode[index] = tounicode16(foundcodes) - ns = ns + 1 - unicode = foundcodes - end - end - end - -- if not unicode then - -- originals[index] = 0xFFFD - -- tounicode[index] = "FFFD" - -- 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: 0x%05X, name: %s, unicode: U+%05X, tounicode: %s",index,name,unic,toun) - else - report_fonts("internal: 0x%05X, name: %s, unicode: U+%05X",index,name,unic) - end - end - end - if trace_loading and (ns > 0 or nl > 0) then - report_fonts("enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) - end -end diff --git a/otfl-font-ota.lua b/otfl-font-ota.lua deleted file mode 100644 index c4663e1..0000000 --- a/otfl-font-ota.lua +++ /dev/null @@ -1,373 +0,0 @@ -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" -} - --- this might become scrp-*.lua - -local type, tostring, match, format, concat = type, tostring, string.match, string.format, table.concat - -if not trackers then trackers = { register = function() end } end - -local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v 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 nodecodes = nodes.nodecodes -local glyph_code = nodecodes.glyph - -local set_attribute = node.set_attribute -local has_attribute = node.has_attribute -local traverse_id = node.traverse_id -local traverse_node_list = node.traverse - -local fontdata = fonts.hashes.identifiers -local state = attributes.private('state') -local categories = characters and characters.categories or { } -- sorry, only in context - -local tracers = nodes.tracers -local colortracers = tracers and tracers.colors -local setnodecolor = colortracers and colortracers.set or function() end -local resetnodecolor = colortracers and colortracers.reset or function() end - -local otffeatures = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register - ---[[ldx-- -

Analyzers run per script and/or language and are needed in order to -process features right.

---ldx]]-- - --- todo: analyzers per script/lang, cross font, so we need an font id hash -> script --- e.g. latin -> hyphenate, arab -> 1/2/3 analyze -- its own namespace - -local state = attributes.private('state') - -function analyzers.setstate(head,font) - local useunicodemarks = analyzers.useunicodemarks - local tfmdata = fontdata[font] - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean - while current do - local id = current.id - if id == glyph_code and current.font == font then - local char = current.char - local d = descriptions[char] - if d then - if d.class == "mark" or (useunicodemarks and categories[char] == "mn") then - done = true - set_attribute(current,state,5) -- mark - elseif n == 0 then - first, last, n = current, current, 1 - set_attribute(current,state,1) -- init - else - last, n = current, n+1 - set_attribute(current,state,2) -- medi - end - else -- finish - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina - end - first, last, n = nil, nil, 0 - end - elseif id == disc_code then - -- always in the middle - set_attribute(current,state,2) -- midi - last = current - else -- finish - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina - end - first, last, n = nil, nil, 0 - end - current = current.next - end - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina - end - return head, done -end - --- in the future we will use language/script attributes instead of the --- font related value, but then we also need dynamic features which is --- somewhat slower; and .. we need a chain of them - -local function analyzeinitializer(tfmdata,value) -- attr - local script, language = otf.scriptandlanguage(tfmdata) -- attr - local action = initializers[script] - if action then - if type(action) == "function" then - return action(tfmdata,value) - else - local action = action[language] - if action then - return action(tfmdata,value) - end - 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 action then - if type(action) == "function" then - return action(head,font,attr) - else - action = action[language] - if action then - return action(head,font,attr) - end - end - end - return head, false -end - -registerotffeature { - name = "analyze", - description = "analysis of (for instance) character classes", - default = true, - initializers = { - node = analyzeinitializer, - }, - processors = { - position = 1, - node = analyzeprocessor, - } -} - --- latin - -methods.latn = analyzers.setstate - --- this info eventually will go into char-def and we will have a state --- table for generic then - -local zwnj = 0x200C -local zwj = 0x200D - -local isol = { - [0x0600] = true, [0x0601] = true, [0x0602] = true, [0x0603] = true, - [0x0608] = true, [0x060B] = true, [0x0621] = true, [0x0674] = true, - [0x06DD] = true, [zwnj] = true, -} - -local isol_fina = { - [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, [0xFEF5] = true, [0xFEF7] = true, - [0xFEF9] = true, [0xFEFB] = true, - - -- syriac - - [0x0710] = true, [0x0715] = true, [0x0716] = true, [0x0717] = true, - [0x0718] = true, [0x0719] = true, [0x0728] = true, [0x072A] = true, - [0x072C] = true, [0x071E] = true, -} - -local isol_fina_medi_init = { - [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, - [0x0640] = 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, [zwj] = true, - - -- syriac - - [0x0712] = true, [0x0713] = true, [0x0714] = true, [0x071A] = true, - [0x071B] = true, [0x071C] = true, [0x071D] = true, [0x071F] = true, - [0x0720] = true, [0x0721] = true, [0x0722] = true, [0x0723] = true, - [0x0725] = true, [0x0726] = true, [0x0727] = true, [0x0729] = true, - [0x072B] = true, [0x0724] = true, [0x0706] = true, [0x0707] = true, -} - -local arab_warned = { } - - --- todo: gref - -local function warning(current,what) - local char = current.char - if not arab_warned[char] then - log.report("analyze","arab: character %s (U+%05X) has no %s class", char, char, what) - arab_warned[char] = true - end -end - -function methods.nocolor(head,font,attr) - for n in traverse_id(glyph_code,head) do - if not font or n.font == font then - resetnodecolor(n) - end - end - return head, true -end - -local function finish(first,last) - if last then - if first == last then - local fc = first.char - if isol_fina_medi_init[fc] or isol_fina[fc] then - set_attribute(first,state,4) -- isol - if trace_analyzing then setnodecolor(first,"font:isol") end - else - warning(first,"isol") - set_attribute(first,state,0) -- error - if trace_analyzing then resetnodecolor(first) end - end - else - local lc = last.char - if isol_fina_medi_init[lc] or isol_fina[lc] then -- why isol here ? - -- if laststate == 1 or laststate == 2 or laststate == 4 then - set_attribute(last,state,3) -- fina - if trace_analyzing then setnodecolor(last,"font:fina") end - else - warning(last,"fina") - set_attribute(last,state,0) -- error - if trace_analyzing then resetnodecolor(last) end - end - end - first, last = nil, nil - elseif first then - -- first and last are either both set so we never com here - local fc = first.char - if isol_fina_medi_init[fc] or isol_fina[fc] then - set_attribute(first,state,4) -- isol - if trace_analyzing then setnodecolor(first,"font:isol") end - else - warning(first,"isol") - set_attribute(first,state,0) -- error - if trace_analyzing then resetnodecolor(first) end - end - first = nil - end - return first, last -end - -function methods.arab(head,font,attr) -- maybe make a special version with no trace - 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 - if current.id == glyph_code and current.subtype<256 and current.font == font and not has_attribute(current,state) then - done = true - local char = current.char - if marks[char] or (useunicodemarks and categories[char] == "mn") then - set_attribute(current,state,5) -- mark - if trace_analyzing then setnodecolor(current,"font:mark") end - elseif isol[char] then -- can be zwj or zwnj too - first, last = finish(first,last) - set_attribute(current,state,4) -- isol - if trace_analyzing then setnodecolor(current,"font:isol") end - first, last = nil, nil - elseif not first then - if isol_fina_medi_init[char] then - set_attribute(current,state,1) -- init - if trace_analyzing then setnodecolor(current,"font:init") end - first, last = first or current, current - elseif isol_fina[char] then - set_attribute(current,state,4) -- isol - if trace_analyzing then setnodecolor(current,"font:isol") end - first, last = nil, nil - else -- no arab - first, last = finish(first,last) - end - elseif isol_fina_medi_init[char] then - first, last = first or current, current - set_attribute(current,state,2) -- medi - if trace_analyzing then setnodecolor(current,"font:medi") end - elseif isol_fina[char] then - if not has_attribute(last,state,1) then - -- tricky, we need to check what last may be ! - set_attribute(last,state,2) -- medi - if trace_analyzing then setnodecolor(last,"font:medi") end - end - set_attribute(current,state,3) -- fina - if trace_analyzing then setnodecolor(current,"font:fina") end - first, last = nil, nil - elseif char >= 0x0600 and char <= 0x06FF then - if trace_analyzing then setnodecolor(current,"font:rest") end - first, last = finish(first,last) - else --no - first, last = finish(first,last) - end - else - first, last = finish(first,last) - end - current = current.next - end - first, last = finish(first,last) - return head, done -end - -methods.syrc = methods.arab - -directives.register("otf.analyze.useunicodemarks",function(v) - analyzers.useunicodemarks = v -end) diff --git a/otfl-font-otb.lua b/otfl-font-otb.lua deleted file mode 100644 index 44639a8..0000000 --- a/otfl-font-otb.lua +++ /dev/null @@ -1,636 +0,0 @@ -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_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 = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register - -otf.defaultbasealternate = "none" -- first last - -local wildcard = "*" -local default = "dflt" - -local function gref(descriptions,n) - if type(n) == "number" then - local name = descriptions[n].name - if name then - return format("U+%05X (%s)",n,name) - else - return format("U+%05X") - end - elseif n then - local num, nam = { }, { } - for i=2,#n do -- first is likely a key - local ni = n[i] - num[i] = format("U+%05X",ni) - nam[i] = descriptions[ni].name or "?" - end - return format("%s (%s)",concat(num," "), concat(nam," ")) - else - return "?" - end -end - -local function cref(feature,lookupname) - if lookupname then - return format("feature %s, lookup %s",feature,lookupname) - else - return format("feature %s",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) or "-", - tostring(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 basemethods = { } -local basemethod = "" - -local function applybasemethod(what,...) - local m = basemethods[basemethod][what] - if m then - return m(...) - end -end - --- We need to make sure that luatex sees the difference between --- base fonts that have different glyphs in the same slots in fonts --- that have the same fullname (or filename). LuaTeX will merge fonts --- eventually (and subset later on). If needed we can use a more --- verbose name as long as we don't use <()<>[]{}/%> and the length --- is < 128. - -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 - -- report_prepare("fullname base hash: '%s', featureset '%s'",tfmdata.properties.fullname,hash) - applied = { } -end - -local function registerbasefeature(feature,value) - applied[#applied+1] = feature .. "=" .. tostring(value) -end - --- The original basemode ligature builder used the names of components --- and did some expression juggling to get the chain right. The current --- variant starts with unicodes but still uses names to make the chain. --- This is needed because we have to create intermediates when needed --- but use predefined snippets when available. To some extend the --- current builder is more stupid but I don't worry that much about it --- as ligatures are rather predicatable. --- --- Personally I think that an ff + i == ffi rule as used in for instance --- latin modern is pretty weird as no sane person will key that in and --- expect a glyph for that ligature plus the following character. Anyhow, --- as we need to deal with this, we do, but no guarantes are given. --- --- latin modern dejavu --- --- f+f 102 102 102 102 --- f+i 102 105 102 105 --- f+l 102 108 102 108 --- f+f+i 102 102 105 --- f+f+l 102 102 108 102 102 108 --- ff+i 64256 105 64256 105 --- ff+l 64256 108 --- --- As you can see here, latin modern is less complete than dejavu but --- in practice one will not notice it. --- --- The while loop is needed because we need to resolve for instance --- pseudo names like hyphen_hyphen to endash so in practice we end --- up with a bit too many definitions but the overhead is neglectable. --- --- Todo: if changed[first] or changed[second] then ... 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 - print("BUILDING",concat(lookupdata," "),unicode) - end - local size = #lookupdata - local firstcode = lookupdata[1] -- [2] - local firstdata = characters[firstcode] - local okay = false - if firstdata then - local firstname = "ctx_" .. firstcode - for i=1,size-1 do -- for i=2,size-1 do - local firstdata = characters[firstcode] - if not firstdata then - firstcode = private - if trace then - print(" DEFINING",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 -- map final ligature onto intermediates - end - okay = true - else - target = unicodes[secondname] - if not target then - break - end - end - if trace then - print("CODES",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) -- todo what kind of kerns, currently all - 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 -- shared - if rawkerns then - local s = sharedkerns[rawkerns] - if s == false then - -- skip - 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 - -- maybe no 0 test here - elseif not newkerns then - newkerns = { [otherunicode] = value } - done = true - if traceindeed then - report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), - gref(descriptions,unicode),gref(descriptions,otherunicode),value) - end - elseif not newkerns[otherunicode] then -- first wins - newkerns[otherunicode] = value - done = true - if traceindeed then - report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), - gref(descriptions,unicode),gref(descriptions,otherunicode),value) - end - end - end - end - end - if done then - sharedkerns[rawkerns] = newkerns - character.kerns = newkerns -- no empty assignments - 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 %s: U+%05X (%s), preceding U+%05X (%s)",lookupname,v,utfchar(v),preceding,utfchar(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 - - -- check out this sharedkerns trickery - - 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_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), - gref(descriptions,unicode),gref(descriptions,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 -- value 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 basepositions then - for feature, data in next, basepositions 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 %s",os.clock()-t,tfmdata.properties.fullname or "?") - end - end -end - -registerotffeature { - name = "features", - description = "features", - default = true, - initializers = { - -- position = 1, -- after setscript (temp hack ... we need to force script / language to 1 - base = featuresinitializer, - } -} - --- independent : collect lookups independently (takes more runtime ... neglectable) --- shared : shares lookups with node mode (takes more memory unless also a node mode variant is used ... noticeable) - -directives.register("fonts.otf.loader.basemethod", function(v) - if basemethods[v] then - basemethod = v - end -end) diff --git a/otfl-font-otf.lua b/otfl-font-otf.lua deleted file mode 100644 index e1339ae..0000000 --- a/otfl-font-otf.lua +++ /dev/null @@ -1,2080 +0,0 @@ -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" -} - --- langs -> languages enz --- anchor_classes vs kernclasses --- modification/creationtime in subfont is runtime dus zinloos --- to_table -> totable --- ascent descent - --- more checking against low level calls of functions - -local utf = unicode.utf8 - -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 getn = table.getn -local lpegmatch = lpeg.match -local reversed, concat, remove = table.reversed, table.concat, table.remove -local ioflush = io.flush -local fastcopy, tohash, derivetable = table.fastcopy, table.tohash, table.derive - -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.737 -- beware: also sync font-mis.lua -otf.cache = containers.define("fonts", "otf", otf.version, true) - -local fontdata = fonts.hashes.identifiers -local chardata = characters and characters.data -- not used - -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 -- mk: 0=885M 1=765M 2=735M (regular run 730M) -local usemetatables = false -- .4 slower on mk but 30 M less mem so we might change the default -- will be directive -local packdata = true -local syncspace = true -local forcenotdef = false - -local wildcard = "*" -local default = "dflt" - -local fontloaderfields = fontloader.fields -local mainfields = nil -local glyphfields = nil -- not used yet - -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) - -local function load_featurefile(raw,featurefile) - if featurefile and featurefile ~= "" then - if trace_loading then - report_otf("featurefile: %s", 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 %s 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 [%s]",nos,name,typ,concat(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] = format("[%s: %s]",script,concat(ttt," ")) - end - if trace_loading then - report_otf(" %s: %s",feature,concat(tt," ")) - end - else - if trace_loading then - report_otf(" %s: %s",feature,tostring(scripts)) - end - end - end - end - end - if trace_loading then - report_otf("\n") - end - elseif trace_loading then - report_otf("font %s has no sequences",filename) - end -end - ---[[ldx-- -

We start with a lot of tables and related functions.

---ldx]]-- - -local valid_fields = table.tohash { - -- "anchor_classes", - "ascent", - -- "cache_version", - "cidinfo", - "copyright", - -- "creationtime", - "descent", - "design_range_bottom", - "design_range_top", - "design_size", - "encodingchanged", - "extrema_bound", - "familyname", - "fontname", - "fontname", - "fontstyle_id", - "fontstyle_name", - "fullname", - -- "glyphs", - "hasvmetrics", - -- "head_optimized_for_cleartype", - "horiz_base", - "issans", - "isserif", - "italicangle", - -- "kerns", - -- "lookups", - "macstyle", - -- "modificationtime", - "onlybitmaps", - "origname", - "os2_version", - "pfminfo", - -- "private", - "serifcheck", - "sfd_version", - -- "size", - "strokedfont", - "strokewidth", - -- "subfonts", - "table_version", - -- "tables", - -- "ttf_tab_saved", - "ttf_tables", - "uni_interp", - "uniqueid", - "units_per_em", - "upos", - "use_typo_metrics", - "uwidth", - -- "validation_state", - "version", - "vert_base", - "weight", - "weight_width_slope_only", - -- "xuid", -} - -local ordered_enhancers = { - "prepare tables", - "prepare glyphs", - "prepare lookups", - - "analyze glyphs", - "analyze math", - - "prepare tounicode", -- maybe merge with prepare - - "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", -- after metadata - - "add duplicates", - "check encoding", - - "cleanup tables", -} - ---[[ldx-- -

Here we go.

---ldx]]-- - -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("enhance: %s (%s)",name,filename) - ioflush() - end - enhancer(data,filename,raw) - elseif trace_loading then - -- report_otf("enhance: %s is undefined",name) - end -end - -function enhancers.apply(data,filename,raw) - local basename = file.basename(lower(filename)) - if trace_loading then - report_otf("start enhancing: %s",filename) - end - ioflush() -- we want instant messages - 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() -- we want instant messages - end - if trace_loading then - report_otf("stop enhancing") - end - ioflush() -- we want instant messages -end - --- patches.register("before","migrate metadata","cambria",function() 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: " ..fmt,...) - end -end - -function enhancers.register(what,action) -- only already registered can be overloaded - actions[what] = action -end - -function otf.load(filename,format,sub,featurefile) - local name = file.basename(file.removesuffix(filename)) - 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: no featurefile '%s'",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("loading: forced reload due to hard coded flag") - 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: %s",featurefile or "--") - end - end - if reload then - report_otf("loading: %s (hash: %s)",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",tostring(messages[m])) - end - end - else - report_otf("font loaded okay") - end - if fontdata then - if featurefiles then - for i=1,#featurefiles do - load_featurefile(fontdata,featurefiles[i].name) - end - end - local unicodes = { - -- names to unicodes - } - local splitter = lpeg.splitter(" ",unicodes) - data = { - size = size, - time = time, - format = format, - featuredata = featurefiles, - resources = { - filename = resolvers.unresolve(filename), -- no shortcut - version = otf.version, - creator = "context mkiv", - unicodes = unicodes, - indices = { - -- index to unicodes - }, - duplicates = { - -- alternative unicodes - }, - variants = { - -- alternative unicodes (variants) - }, - lookuptypes = { - }, - }, - metadata = { - -- raw metadata, not to be used - }, - properties = { - -- normalized metadata - }, - descriptions = { - }, - goodies = { - }, - helpers = { - tounicodelist = splitter, - tounicodetable = lpeg.Ct(splitter), - }, - } - starttiming(data) - report_otf("file size: %s", size) - enhancers.apply(data,filename,fontdata) - if packdata then - if cleanup > 0 then - collectgarbage("collect") ---~ lua.collectgarbage() - end - enhance("pack",data,filename,nil) - end - report_otf("saving in cache: %s",filename) - data = containers.write(otf.cache, hash, data) - if cleanup > 1 then - collectgarbage("collect") ---~ lua.collectgarbage() - end - stoptiming(data) - if elapsedtime then -- not in generic - report_otf("preprocessing and caching took %s seconds",elapsedtime(data)) - end - fontloader.close(fontdata) -- free memory - if cleanup > 3 then - collectgarbage("collect") ---~ lua.collectgarbage() - end - data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one - if cleanup > 2 then - collectgarbage("collect") ---~ lua.collectgarbage() - end - else - data = nil - report_otf("loading failed (file read error)") - end - end - if data then - if trace_defining then - report_otf("loading from cache: %s",hash) - end - enhance("unpack",data,filename,nil,false) - 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) -- maybe set it - 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 -- or maybe uni* - return forcenotdef and ".notdef" - end - end -} - -actions["prepare tables"] = function(data,filename,raw) - data.properties.hasitalics = false -end - -actions["add dimensions"] = function(data,filename) - -- todo: forget about the width if it's the defaultwidth (saves mem) - -- we could also build the marks hash here (instead of storing it) - 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 - 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 with width %s (%s) in %s",wd,d.name or "",file.basename(filename)) - -- d.width = -wd - 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 with width %s (%s) in %s",wd,d.name or "",file.basename(filename)) - -- d.width = -wd - end - -- if forcenotdef and not d.name then - -- d.name = ".notdef" - -- end - if bb then - local ht, dp = bb[4], -bb[2] - if ht == 0 or ht < 0 then - -- not set - else - d.height = ht - end - if dp == 0 or dp < 0 then - -- not set - else - d.depth = dp - end - end - end - end - end -end - -local function somecopy(old) -- fast one - if old then - local new = { } - if type(old) == "table" then - for k, v in next, old do - if k == "glyphs" then - -- skip - 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 - -- skip - elseif type(v) == "table" then - new[k] = somecopy(v) - else - new[k] = v - end - end - end - return new - else - return { } - end -end - --- not setting hasitalics and class (when nil) during --- table cronstruction can save some mem - -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 -- name to unicode - local indices = resources.indices -- index to unicode - local duplicates = resources.duplicates - local variants = resources.variants - - if rawsubfonts then - - metadata.subfonts = { } - 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 - metadata.subfonts[cidindex] = somecopy(subfont) - for index=0,subfont.glyphcnt-1 do -- we could take the previous glyphcnt instead of 0 - 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 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("enhance: glyph %s at index 0x%04X is moved to private unicode slot U+%05X",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 -- each index is unique (at least now) - - local description = { - -- width = glyph.width, - boundingbox = glyph.boundingbox, - name = glyph.name or name or "unknown", -- uniXXXX - cidindex = cidindex, - index = index, - glyph = glyph, - } - - descriptions[unicode] = description - else - -- report_otf("potential problem: glyph 0x%04X is used but empty",index) - 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 %s",filename) - end - elseif trace_loading then - report_otf("font %s has no glyphs",filename) - end - - else - - for index=0,raw.glyphcnt-1 do -- not raw.glyphmax-1 (as that will crash) - 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("enhance: glyph %s at index 0x%04X is moved to private unicode slot U+%05X",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] = { - -- width = glyph.width, - boundingbox = glyph.boundingbox, - name = name, - index = index, - glyph = glyph, - } - local altuni = glyph.altuni - if altuni then - local d - 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 -- xits-math has some: - 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 0x%04X is used but empty",index) - end - end - - end - - resources.private = private - -end - --- the next one is still messy but will get better when we have --- flattened map/enc tables in the font loader - -actions["check encoding"] = function(data,filename,raw) - local descriptions = data.descriptions - local resources = data.resources - local properties = data.properties - local unicodes = resources.unicodes -- name to unicode - local indices = resources.indices -- index to unicodes - - -- begin of messy (not needed when cidmap) - - local mapdata = raw.map or { } - local unicodetoindex = mapdata and mapdata.map or { } - -- local encname = lower(data.enc_name or raw.enc_name or mapdata.enc_name or "") - local encname = lower(data.enc_name or mapdata.enc_name or "") - local criterium = 0xFFFF -- for instance cambria has a lot of mess up there - - -- end of messy - - if find(encname,"unicode") then -- unicodebmp, unicodefull, ... - if trace_loading then - report_otf("checking embedded unicode map '%s'",encname) - end - for unicode, index in next, unicodetoindex do -- altuni already covers this - if unicode <= criterium and not descriptions[unicode] then - local parent = indices[index] -- why nil? - if parent then - report_otf("weird, unicode U+%05X points to U+%05X with index 0x%04X",unicode,parent,index) - else - report_otf("weird, unicode U+%05X points to nowhere with index 0x%04X",unicode,index) - end - end - end - elseif properties.cidinfo then - report_otf("warning: no unicode map, used cidmap '%s'",properties.cidinfo.usedname or "?") - else - report_otf("warning: non unicode map '%s', only using glyph unicode data",encname or "whatever") - end - - if mapdata then - mapdata.map = { } -- clear some memory - end -end - --- for the moment we assume that a fotn with lookups will not use --- altuni so we stick to kerns only - -actions["add duplicates"] = function(data,filename,raw) - local descriptions = data.descriptions - local resources = data.resources - local properties = data.properties - local unicodes = resources.unicodes -- name to unicode - local indices = resources.indices -- index to unicodes - 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) -- else packing problem - 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 - end - end - end - -- todo: lookups etc - end - if trace_loading then - report_otf("duplicating U+%05X to U+%05X with index 0x%04X (%s kerns)",unicode,u,description.index,n) - end - end - end - end -end - --- class : nil base mark ligature component (maybe we don't need it in description) --- boundingbox: split into ht/dp takes more memory (larger tables and less sharing) - -actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this in the previous - local descriptions = data.descriptions - local resources = data.resources - local metadata = data.metadata - local properties = data.properties - local hasitalics = false - local widths = { } - local marks = { } -- always present (saves checking) - for unicode, description in next, descriptions do - local glyph = description.glyph - local italic = glyph.italic_correction - if not italic then - -- skip - elseif italic == 0 then - -- skip - 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 - -- flag italic - properties.hasitalics = hasitalics - -- flag marks - resources.marks = marks - -- share most common width for cjk fonts - 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 -- maybe 500 - 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 - -- description.width = nil - 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 -- reversed - 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) -- combine with other - 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 -- anchor classes not in final table - 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_context = 1, - -- gpos_context = 1, - gsub_reversecontextchain = -1, - gpos_reversecontextchain = -1, -} - --- Research by Khaled Hosny has demonstrated that the font loader merges --- regular and AAT features and that these can interfere (especially because --- we dropped checking for valid features elsewhere. So, we just check for --- the special flag and drop the feature if such a tag is found. - -local function supported(features) - for i=1,#features do - if features[i].ismac then - return false - end - end - return true -end - -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 --- if features and supported(features) then - if not features or supported(features) then -- not always 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 = { -- forcing false packs nicer - (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 features then - -- scripts, tag, ismac - 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 -end - --- test this: --- --- for _, what in next, otf.glists do --- raw[what] = nil --- end - -actions["prepare lookups"] = function(data,filename,raw) - local lookups = raw.lookups - if lookups then - data.lookups = lookups - end -end - --- The reverse handler does a bit redundant splitting but it's seldom --- seen so we don' tbother too much. We could store the replacement --- in the current list (value instead of true) but it makes other code --- uglier. Maybe some day. - -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 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_uncover(splitter,cache,cover) - if cover == "" then - return nil - else - local uncovered = cache[cover] - if not uncovered then - uncovered = lpegmatch(splitter,cover) - for i=1,#uncovered do - uncovered[i] = { [uncovered[i]] = true } - end - cache[cover] = uncovered - end - return uncovered - end -end - -local s_hashed = t_hashed - -local function r_uncover(splitter,cache,cover,replacements) - if cover == "" then - return nil - else - -- we always have current as { } even in the case of one - 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) - -- we prefer the before lookups in a normal order - if data.lookups then - local splitter = data.helpers.tounicodetable - local cache, h_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,cache,reversed(before_class)) - end - local current_class = lookup.current_class - if current_class then - current_class = t_uncover(splitter,cache,current_class) - end - local after_class = lookup.after_class - if after_class then - after_class = t_uncover(splitter,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,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] = false -- e.g. we can have two lookups and one replacement - end - end - rule.current = t_hashed(current,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,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,cache,reversed(before)) - rule.before = t_hashed(before,h_cache) - end - local current = coverage.current - if current then - current = t_uncover(splitter,cache,current) - rule.current = t_hashed(current,h_cache) - end - local after = coverage.after - if after then - after = t_uncover(splitter,cache,after) - rule.after = t_hashed(after,h_cache) - end - rule.coverage = nil - end - end - elseif format == "reversecoverage" then -- special case, single substitution only - 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,cache,reversed(before)) - rule.before = t_hashed(before,h_cache) - end - local current = reversecoverage.current - if current then - current = t_uncover(splitter,cache,current) - rule.current = t_hashed(current,h_cache) - end - local after = reversecoverage.after - if after then - after = t_uncover(splitter,cache,after) - rule.after = t_hashed(after,h_cache) - end - local replacements = reversecoverage.replacements - if replacements then - rule.replacements = r_uncover(splitter,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 then - fore = s_uncover(splitter,cache,fore) - rule.before = s_hashed(fore,h_cache) - end - local back = glyphs.back - if back then - back = s_uncover(splitter,cache,back) - rule.after = s_hashed(back,h_cache) - end - local names = glyphs.names - if names then - names = s_uncover(splitter,cache,names) - rule.current = s_hashed(names,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 -- use splitter - 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+%05X in math variant U+%05X",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 -- singular - 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 -- height / kern can be zero - 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 %s of kern %s of glyph U+%05X",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 - for gp=1,#gposlist do - local gpos = gposlist[gp] - local subtables = gpos.subtables - if subtables then - for s=1,#subtables do - local subtable = subtables[s] - local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes - if kernclass then -- the next one is quite slow - local split = { } -- saves time - for k=1,#kernclass do - local kcl = kernclass[k] - local firsts = kcl.firsts - local seconds = kcl.seconds - local offsets = kcl.offsets - local lookups = kcl.lookup -- singular - if type(lookups) ~= "table" then - lookups = { lookups } - end - -- we can check the max in the loop - -- local maxseconds = getn(seconds) - 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 l=1,#lookups do - local lookup = lookups[l] - for fk=1,#firsts do -- maxfirsts ? - 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] - for sk, sv in next, seconds do - local splt = split[sv] - if splt then -- redundant test - 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] - local description = descriptions[first_unicode] - if description then - local kerns = description.kerns - if not kerns then - kerns = { } -- unicode indexed ! - description.kerns = kerns - end - local lookupkerns = kerns[lookup] - if not lookupkerns then - lookupkerns = { } - kerns[lookup] = lookupkerns - end - for second_unicode, kern in next, extrakerns do - lookupkerns[second_unicode] = kern - end - elseif trace_loading then - report_otf("no glyph data for U+%05X", first_unicode) - end - end - end - end - end - end - subtable.kernclass = { } - end - end - end - end - end -end - -actions["check glyphs"] = function(data,filename,raw) - for unicode, description in next, data.descriptions do - description.glyph = nil - end -end - --- future versions will remove _ - -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 - -- metadata.pfminfo = raw.pfminfo -- not already done? - local ttftables = metadata.ttf_tables - if ttftables then - for i=1,#ttftables do - ttftables[i].data = "deleted" - end - end -end - -actions["cleanup tables"] = function(data,filename,raw) - data.resources.indices = nil -- not needed - data.helpers = nil -end - --- kern: ttf has a table with kerns --- --- Weird, as maxfirst and maxseconds can have holes, first seems to be indexed, but --- seconds can start at 2 .. this need to be fixed as getn as well as # are sort of --- unpredictable alternatively we could force an [1] if not set (maybe I will do that --- anyway). - --- we can share { } as it is never set - ---- ligatures have an extra specification.char entry that we don't use - -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: %s => %s and %s",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} } -- maybe nil instead of { } - 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) -- when we replace inplace we safe entries - 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 - --- modes: node, base, none - -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 { } -- will become false - end -end - --- the first version made a top/mid/not extensible table, now we just --- pass on the variants data and deal with it in the tfm scaler (there --- is no longer an extensible table anyway) --- --- we cannot share descriptions as virtual fonts might extend them (ok, --- we could use a cache with a hash --- --- we already assing an empty tabel to characters as we can add for --- instance protruding info and loop over characters; one is not supposed --- to change descriptions and if one does so one should make a copy! - -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 mode = data.mode or "base" - 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 -- use parent table - characters[unicode] = { } - end - if mathspecs then - -- we could move this to the scaler but not that much is saved - -- and this is cleaner - for unicode, character in next, characters do - local d = descriptions[unicode] - local m = d.math - if m then - -- watch out: luatex uses horiz_variants for the parts - local variants = m.horiz_variants - local parts = m.horiz_parts - -- local done = { [unicode] = true } - if variants then - local c = character - for i=1,#variants do - local un = variants[i] - -- if done[un] then - -- -- report_otf("skipping cyclic reference U+%05X in math variant U+%05X",un,unicode) - -- else - c.next = un - c = characters[un] - -- done[un] = true - -- end - end -- c is now last in chain - c.horiz_variants = parts - elseif parts then - character.horiz_variants = parts - end - local variants = m.vert_variants - local parts = m.vert_parts - -- local done = { [unicode] = true } - if variants then - local c = character - for i=1,#variants do - local un = variants[i] - -- if done[un] then - -- -- report_otf("skipping cyclic reference U+%05X in math variant U+%05X",un,unicode) - -- else - c.next = un - c = characters[un] - -- done[un] = true - -- end - end -- c is now last in chain - 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 -- was c. - 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 - -- end math - local monospaced = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced") - local charwidth = pfminfo.avgwidth -- or unset - local italicangle = metadata.italicangle - local charxheight = pfminfo.os2_xheight and pfminfo.os2_xheight > 0 and pfminfo.os2_xheight - properties.monospaced = monospaced - parameters.italicangle = italicangle - parameters.charwidth = charwidth - parameters.charxheight = charxheight - -- - local space = 0x0020 -- unicodes['space'], unicodes['emdash'] - local emdash = 0x2014 -- unicodes['space'], unicodes['emdash'] - 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 -- brrr - -- we need a runtime lookup because of running from cdrom or zip, brrr (shouldn't we use the basename then?) - 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 -- catch bugs in fonts - units = 1000 - metadata.units_per_em = 1000 - end - -- - parameters.slant = 0 - parameters.space = spaceunits -- 3.333 (cmr10) - parameters.space_stretch = units/2 -- 500 -- 1.666 (cmr10) - parameters.space_shrink = 1*units/3 -- 333 -- 1.111 (cmr10) - parameters.x_height = 2*units/5 -- 400 - parameters.quad = units -- 1000 - if spaceunits < 2*units/5 then - -- todo: warning - end - if italicangle then - parameters.italicangle = italicangle - parameters.italicfactor = math.cos(math.rad(90+italicangle)) - parameters.slant = - math.round(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 -- 1.111 (cmr10) - if charxheight then - parameters.x_height = charxheight - else - local x = 0x78 -- unicodes['x'] - 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 fonts.formats[filename] or "opentype" - properties.noglyphnames = true - properties.filename = filename - properties.fontname = fontname - properties.fullname = fullname - properties.psname = fontname or fullname - properties.name = filename or fullname - -- - -- properties.name = specification.name - -- properties.sub = specification.sub - 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 format = specification.format - local features = specification.features.normal - local rawdata = otf.load(filename,format,sub,features and features.featurefile) - if rawdata and next(rawdata) then - rawdata.lookuphash = { } - tfmdata = copytotfm(rawdata,cache_id) - if tfmdata and next(tfmdata) then - -- at this moment no characters are assigned yet, only empty slots - local features = constructors.checkedfeatures("otf",features) - local shared = tfmdata.shared - if not shared then - shared = { } - tfmdata.shared = shared - end - shared.rawdata = rawdata - -- shared.features = features -- default - shared.dynamics = { } - -- shared.processes = { } - 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 - -- this late ? .. needs checking - 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) -- only otf? - fonts.loggers.register(tfmdata,file.extname(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 -- we cannot use mathparameters as luatex will complain - local parameters = tfmdata.parameters - parameters.scriptpercentage = mathdata.ScriptPercentScaleDown - parameters.scriptscriptpercentage = mathdata.ScriptScriptPercentScaleDown - parameters.mathsize = mathsize - end -end - -registerotffeature { - name = "mathsize", - description = "apply mathsize as specified in the font", - initializers = { - base = checkmathsize, - node = checkmathsize, - } -} - --- helpers - -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 - --- readers - -local function check_otf(forced,specification,suffix,what) - local name = specification.name - if forced then - name = file.addsuffix(name,suffix,true) - end - local fullname = findbinfile(name,suffix) or "" - if fullname == "" then - fullname = fonts.names.getfilename(name,suffix) or "" - end - if fullname ~= "" then - specification.filename = fullname - specification.format = what - return read_from_otf(specification) - end -end - -local function opentypereader(specification,suffix,what) - local forced = specification.forced or "" - if forced == "otf" then - return check_otf(true,specification,forced,"opentype") - elseif forced == "ttf" or forced == "ttc" or forced == "dfont" then - return check_otf(true,specification,forced,"truetype") - else - return check_otf(false,specification,suffix,what) - end -end - -readers.opentype = opentypereader - -local formats = fonts.formats - -formats.otf = "opentype" -formats.ttf = "truetype" -formats.ttc = "truetype" -formats.dfont = "truetype" - -function readers.otf (specification) return opentypereader(specification,"otf",formats.otf ) end -function readers.ttf (specification) return opentypereader(specification,"ttf",formats.ttf ) end -function readers.ttc (specification) return opentypereader(specification,"ttf",formats.ttc ) end -function readers.dfont(specification) return opentypereader(specification,"ttf",formats.dfont) end - --- this will be overloaded - -function otf.scriptandlanguage(tfmdata,attr) - local properties = tfmdata.properties - return properties.script or "dflt", properties.language or "dflt" -end diff --git a/otfl-font-oti.lua b/otfl-font-oti.lua deleted file mode 100644 index d6853db..0000000 --- a/otfl-font-oti.lua +++ /dev/null @@ -1,92 +0,0 @@ -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 allocate = utilities.storage.allocate - -local fonts = fonts -local otf = { } -fonts.handlers.otf = otf - -local otffeatures = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register - -registerotffeature { - name = "features", - description = "initialization of feature handler", - default = true, -} - --- these are later hooked into node and base initializaters - -local otftables = otf.tables -- not always defined - -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, - } -} - diff --git a/otfl-font-otn.lua b/otfl-font-otn.lua deleted file mode 100644 index 8e67597..0000000 --- a/otfl-font-otn.lua +++ /dev/null @@ -1,2712 +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" -} - --- 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 --- 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) - ---[[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. - -local concat, insert, remove = table.concat, table.insert, table.remove -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 random = math.random - -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") - -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 set_attribute = node.set_attribute -local has_attribute = node.has_attribute -local flush_node_list = node.flush_list - -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 glyph_code = nodecodes.glyph -local glue_code = nodecodes.glue -local disc_code = nodecodes.disc -local whatsit_code = nodecodes.whatsit - -local dir_code = whatcodes.dir -local localpar_code = whatcodes.localpar - -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 state = privateattribute('state') -local markbase = privateattribute('markbase') -local markmark = privateattribute('markmark') -local markdone = privateattribute('markdone') -- assigned at the injection end -local cursbase = privateattribute('cursbase') -local curscurs = privateattribute('curscurs') -local cursdone = privateattribute('cursdone') -local kernpair = privateattribute('kernpair') -local 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 - -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 - --- we cannot optimize with "start = first_glyph(head)" because then we don't --- know which rlmode we're in which messes up cursive handling later on --- --- 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 function gref(n) - if type(n) == "number" then - local description = descriptions[n] - local name = description and description.name - if name then - return format("U+%05X (%s)",n,name) - else - return format("U+%05X",n) - end - elseif not n then - return "" - else - 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] = format("U+%05X",ni) - nam[i] = di and di.name or "?" - end - end - return format("%s (%s)",concat(num," "), concat(nam," ")) - end -end - -local function cref(kind,chainname,chainlookupname,lookupname,index) - if index then - return format("feature %s, chain %s, sub %s, lookup %s, index %s",kind,chainname,chainlookupname,lookupname,index) - elseif lookupname then - return format("feature %s, chain %s, sub %s, lookup %s",kind,chainname or "?",chainlookupname or "?",lookupname) - elseif chainlookupname then - return format("feature %s, chain %s, sub %s",kind,chainname or "?",chainlookupname) - elseif chainname then - return format("feature %s, chain %s",kind,chainname) - else - return format("feature %s",kind) - end -end - -local function pref(kind,lookupname) - return format("feature %s, lookup %s",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 - -local function markstoligature(kind,lookupname,start,stop,char) - local n = copy_node(start) - local keep = start - local current - current, start = insert_node_after(start,start,n) - local snext = stop.next - current.next = snext - if snext then - snext.prev = current - end - start.prev, stop.next = nil, nil - current.char, current.subtype, current.components = char, ligature_code, start - return keep -end - -local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- brr head - if start == stop then - start.char = char - return start - elseif discfound then - -- print("start->stop",nodes.tosequence(start,stop)) - local components = start.components - if components then - flush_node_list(components) - start.components = nil - end - local lignode = copy_node(start) - lignode.font = start.font - lignode.char = char - lignode.subtype = ligature_code - local next = stop.next - local prev = start.prev - stop.next = nil - start.prev = nil - lignode.components = start - -- print("lignode",nodes.tosequence(lignode)) - -- print("components",nodes.tosequence(lignode.components)) - prev.next = lignode - if next then - next.prev = lignode - end - lignode.next = next - lignode.prev = prev - -- print("start->end",nodes.tosequence(start)) - return lignode - else - -- start is the ligature - local deletemarks = markflag ~= "mark" - local n = copy_node(start) - local current - current, start = insert_node_after(start,start,n) - local snext = stop.next - current.next = snext - if snext then - snext.prev = current - end - start.prev = nil - stop.next = nil - current.char = char - current.subtype = ligature_code - current.components = start - local head = current - -- this is messy ... we should get rid of the components eventually - local i = 0 -- is index of base - while start do - if not marks[start.char] then - i = i + 1 - elseif not deletemarks then -- quite fishy - set_attribute(start,ligacomp,i) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) - end - head, current = insert_node_after(head,current,copy_node(start)) - end - start = start.next - end - start = current.next - while start and start.id == glyph_code do - if marks[start.char] then - set_attribute(start,ligacomp,i) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) - end - else - break - end - start = start.next - end - -- - -- 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 - -- - -- flush_node_list(head.components) - return head - end -end - -function handlers.gsub_single(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 start, true -end - -local function get_alternative_glyph(start,alternatives,value) - -- needs checking: (global value, brrr) - local choice = nil - local n = #alternatives - local char = start.char - -- - if value == "random" then - local r = random(1,n) - value, choice = format("random, choice %s",r), alternatives[r] - elseif value == "first" then - value, choice = format("first, choice %s",1), alternatives[1] - elseif value == "last" then - value, choice = format("last, choice %s",n), alternatives[n] - else - value = tonumber(value) - if type(value) ~= "number" then - value, choice = "default, choice 1", alternatives[1] - elseif value > n then - local defaultalt = otf.defaultnodealternate - if defaultalt == "first" then - value, choice = format("no %s variants, taking %s",value,n), alternatives[n] - elseif defaultalt == "last" then - value, choice = format("no %s variants, taking %s",value,1), alternatives[1] - else - value, choice = format("no %s variants, ignoring",value), false - end - elseif value == 0 then - value, choice = format("choice %s (no change)",value), char - elseif value < 1 then - value, choice = format("no %s variants, taking %s",value,1), alternatives[1] - else - value, choice = format("choice %s",value), alternatives[value] - end - end - return choice -end - -local function multiple_glyphs(start,multiple) -- marks ? - 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 - 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 start, true - else - if trace_multiples then - logprocess("no multiple for %s",gref(start.char)) - end - return start, false - end -end - -function handlers.gsub_alternate(start,kind,lookupname,alternative,sequence) - local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue - local choice = get_alternative_glyph(start,alternative,value) - if choice then - if trace_alternatives then - logprocess("%s: replacing %s by alternative %s (%s)",pref(kind,lookupname),gref(char),gref(choice),choice) - end - start.char = choice - else - if trace_alternatives then - logwarning("%s: no variant %s for %s",pref(kind,lookupname),tostring(value),gref(char)) - end - end - return start, true -end - -function handlers.gsub_multiple(start,kind,lookupname,multiple) - if trace_multiples then - logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) - end - return multiple_glyphs(start,multiple) -end - -function handlers.gsub_ligature(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.subtype<256 and s.font == currentfont 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 - start = markstoligature(kind,lookupname,start,stop,lig) - logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) - else - start = markstoligature(kind,lookupname,start,stop,lig) - end - return 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 - if stop then - local lig = ligature.ligature - if lig then - if trace_ligatures then - local stopchar = stop.char - start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) - logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) - else - start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) - end - return start, true - else - -- ok, goto next lookup - end - end - end - return 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(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.subtype<256 and base.font == currentfont then - local basechar = base.char - if marks[basechar] then - while true do - base = base.prev - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont 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 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 => (%s,%s)", - pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return 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 - else -- if 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 start, false -end - -function handlers.gpos_mark2ligature(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.subtype<256 and base.font == currentfont then - local basechar = base.char - if marks[basechar] then - while true do - base = base.prev - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont 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 start, false - end - end - end - local index = has_attribute(start,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 => (%s,%s)", - pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) - end - return start, true - 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 - else -- if 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 start, false -end - -function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) - local markchar = start.char - if marks[markchar] then - local base = start.prev -- [glyph] [basemark] [start=mark] - -- while base and has_attribute(base,ligacomp) and has_attribute(base,ligacomp) ~= has_attribute(start,ligacomp) do - -- base = base.prev -- KE: prevents mkmk for marks on different components of a ligature - -- end - local slc = has_attribute(start,ligacomp) - if slc then -- a rather messy loop ... needs checking with husayni - while base do - local blc = has_attribute(base,ligacomp) - if blc and blc ~= slc then - base = base.prev - else - break - end - end - end - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont 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) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", - pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return 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 - else -- if 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 start,false -end - -function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to be checked - local alreadydone = cursonce and has_attribute(start,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.subtype<256 and nxt.font == currentfont 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 (%s,%s) 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 - else -- if 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 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 start, false - end -end - -function handlers.gpos_single(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 (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) - end - return start, false -end - -function handlers.gpos_pair(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 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.subtype<256 and snext.font == currentfont do - local nextchar = snext.char - local krn = kerns[nextchar] - if not krn and marks[nextchar] then - prev = snext - snext = snext.next - else - local krn = kerns[nextchar] - 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 (%s,%s) and correction (%s,%s)",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 (%s,%s) and correction (%s,%s)",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 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(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) - logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return start, false -end - -function chainmores.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) - logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return 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(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 start, true - else - return 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(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 - delete_node(start,next) - end - n = n + 1 - until next == stop - else -- start x x x stop => start - repeat - local next = start.next - delete_node(start,next) - n = n + 1 - until next == stop - end - return n -end - ---[[ldx-- -

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

---ldx]]-- - -function chainprocs.gsub_single(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 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 start, true - elseif current == stop then - break - else - current = current.next - end - end - return 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(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - delete_till_stop(start,stop) -- we could pass ignoremarks as #3 .. - 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 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(start,replacements) - end - end - return start, false -end - --- function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) --- logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) --- return 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(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 = get_alternative_glyph(current,alternatives,value) - if choice then - if trace_alternatives then - logprocess("%s: replacing %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),gref(char),gref(choice),choice) - end - start.char = choice - else - if trace_alternatives then - logwarning("%s: no variant %s for %s",cref(kind,chainname,chainlookupname,lookupname),tostring(value),gref(char)) - end - end - elseif trace_bugs then - logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar)) - end - end - return start, true - elseif current == stop then - break - else - current = current.next - end - end - return start, false -end - --- function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) --- logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) --- return 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(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, discfound, last, nofreplacements = start.next, false, stop, 0 - while s do - local id = s.id - if id == disc_code then - s = s.next - discfound = true - else - local schar = s.char - if 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",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) - else - logprocess("%s: replacing character %s upto %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) - end - end - start = toligature(kind,lookupname,start,stop,l2,currentlookup.flags[1],discfound) - return 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 start, false, 0 -end - -chainmores.gsub_ligature = chainprocs.gsub_ligature - -function chainprocs.gpos_mark2base(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.subtype<256 and base.font == currentfont then - local basechar = base.char - if marks[basechar] then - while true do - base = base.prev - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont 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 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 => (%s,%s)", - cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return 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 start, false -end - -function chainprocs.gpos_mark2ligature(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.subtype<256 and base.font == currentfont then - local basechar = base.char - if marks[basechar] then - while true do - base = base.prev - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont 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 start, false - end - end - end - -- todo: like marks a ligatures hash - local index = has_attribute(start,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 => (%s,%s)", - cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) - end - return 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 start, false -end - -function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local markchar = start.char - if marks[markchar] then ---~ local alreadydone = markonce and has_attribute(start,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] - -- while (base and has_attribute(base,ligacomp) and has_attribute(base,ligacomp) ~= has_attribute(start,ligacomp)) do - -- base = base.prev -- KE: prevents mkmk for marks on different components of a ligature - -- end - local slc = has_attribute(start,ligacomp) - if slc then -- a rather messy loop ... needs checking with husayni - while base do - local blc = has_attribute(base,ligacomp) - if blc and blc ~= slc then - base = base.prev - else - break - end - end - end - if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont 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) - if trace_marks then - logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", - cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) - end - return 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 start, false -end - --- ! ! ! untested ! ! ! - -function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) - local alreadydone = cursonce and has_attribute(start,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.subtype<256 and nxt.font == currentfont 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 (%s,%s) 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 - else -- if 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 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 start, false - end - end - return start, false -end - -function chainprocs.gpos_single(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 (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) - end - end - end - return start, false -end - --- when machines become faster i will make a shared function - -function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) --- logwarning("%s: gpos_pair not yet supported",cref(kind,chainname,chainlookupname)) - 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.subtype<256 and snext.font == currentfont 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 (%s,%s) and correction (%s,%s)",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 (%s,%s) and correction (%s,%s)",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 start, done - end - end - end - return start, false -end - --- 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 (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) - else - logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2]) - end -end - -local function normal_handle_contextchain(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.subtype<256 and current.font == currentfont 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.subtype<256 and last.font == currentfont 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.subtype<256 and prev.font == currentfont 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 -- somehat 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.subtype<256 and current.font == currentfont 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 %s (%s=>%s)", - 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 %s", - 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 - start, done = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) - 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] -- can be false (n matches, nofchainlookups - end - else - local replacements = ck[7] - if replacements then - start, done = chainprocs.reversesub(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 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 '%s'",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 %s of type %s in font %s (%s)",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 special_attributes = { - init = 1, - medi = 2, - fina = 3, - isol = 4 -} - -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, special_attributes[kind] or false, sequence.chain or 0, kind, sequence } - end - end - end - end - return false -end - -function otf.dataset(tfmdata,sequences,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 = { } - rs[language] = rl - setmetatableindex(rl, function(t,k) - local v = enabled and initialize(sequences[k],script,language,enabled) - t[k] = v - return v - 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 - -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,sequences,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. - - for s=1,#sequences do - local dataset = datasets[s] - if dataset then - featurevalue = dataset[1] -- todo: pass to function instead of using a global - if featurevalue then - local sequence = 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.subtype<256 and start.font == font then - local a = has_attribute(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 - start, success = handler(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 - while start do - local id = start.id - if id == glyph_code then - if start.subtype<256 and start.font == font then - local a = has_attribute(start,0) - if a then - a = (a == attr) and (not attribute or has_attribute(start,state,attribute)) - else - a = not attribute or has_attribute(start,state,attribute) - end - if a then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - -- sequence kan weg - local ok - start, ok = handler(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 == 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 %s: txtdir=%s:%s, parmode=%s, txtmode=%s",dir,topstack,newdir or "unset",rlparmode,rlmode) - 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 %s: parmode=%s, txtmode=%s",dir,rlparmode,rlmode) - end - end - start = start.next - else - start = start.next - end - end - end - else - while start do - local id = start.id - if id == glyph_code then - if start.subtype<256 and start.font == font then - local a = has_attribute(start,0) - if a then - a = (a == attr) and (not attribute or has_attribute(start,state,attribute)) - else - a = not attribute or has_attribute(start,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 - start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if ok then - success = true - 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 == 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 %s: txtdir=%s:%s, parmode=%s, txtmode=%s",dir,topstack,newdir or "unset",rlparmode,rlmode) - 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 %s: parmode=%s, txtmode=%s",dir,rlparmode,rlmode) - end - end - start = start.next - else - start = start.next - end - end - end - end - if success then - done = true - end - if trace_steps then -- ? - registerstep(head) - end - end - 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 - --- not shared as we hook into lookups now - ---~ local function uncover_1(covers,result) -- multiple covers ---~ local nofresults = #result ---~ for n=1,#covers do ---~ nofresults = nofresults + 1 ---~ local u = { } ---~ local c = covers[n] ---~ for i=1,#c do ---~ u[c[i]] = true ---~ end ---~ result[nofresults] = u ---~ end ---~ end - ---~ local function uncover_2(covers,result) -- single covers (turned into multiple with n=1) ---~ local nofresults = #result ---~ for n=1,#covers do ---~ nofresults = nofresults + 1 ---~ result[nofresults] = { [covers[n]] = true } ---~ end ---~ end - ---~ local function uncover_1(covers,result) -- multiple covers ---~ local nofresults = #result ---~ for n=1,#covers do ---~ nofresults = nofresults + 1 ---~ result[nofresults] = covers[n] ---~ end ---~ end - ---~ 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 not lookuptype then ---~ report_prepare("missing lookuptype for %s",lookupname) ---~ else -- => lookuphash[lookupname][unicode] ---~ local rules = lookupdata.rules ---~ if rules then ---~ local fmt = lookupdata.format ---~ -- if fmt == "coverage" then ---~ if fmt == "coverage" or fmt == "glyphs" then ---~ if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then ---~ -- todo: dejavu-serif has one (but i need to see what use it has) ---~ report_prepare("unsupported coverage %s for %s",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 -- does #rules>1 happen often? ---~ local rule = rules[nofrules] ---~ local current = rule.current ---~ local before = rule.before ---~ local after = rule.after ---~ local sequence = { } ---~ if before then ---~ uncover_1(before,sequence) ---~ end ---~ local start = #sequence + 1 ---~ uncover_1(current,sequence) ---~ local stop = #sequence ---~ if after then ---~ uncover_1(after,sequence) ---~ end ---~ if sequence[1] then ---~ nt = nt + 1 ---~ t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } ---~ for unic, _ in next, sequence[start] do ---~ local cu = contexts[unic] ---~ if not cu then ---~ contexts[unic] = t ---~ end ---~ end ---~ end ---~ end ---~ end ---~ elseif fmt == "reversecoverage" then -- we could combine both branches (only dufference is replacements) ---~ if lookuptype ~= "reversesub" then ---~ report_prepare("unsupported reverse coverage %s for %s",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 = { } ---~ if before then ---~ uncover_1(before,sequence) ---~ end ---~ local start = #sequence + 1 ---~ uncover_1(current,sequence) ---~ local stop = #sequence ---~ if after then ---~ uncover_1(after,sequence) ---~ 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 ---~ -- elseif fmt == "glyphs" then --maybe just make then before = { fore } and share with coverage ---~ -- if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then ---~ -- report_prepare("unsupported coverage %s for %s",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 -- we can make glyphs a special case (less tables) ---~ -- local rule = rules[nofrules] ---~ -- local current = rule.names ---~ -- local before = rule.fore ---~ -- local after = rule.back ---~ -- local sequence = { } ---~ -- if before then ---~ -- uncover_1(before,sequence) ---~ -- end ---~ -- local start = #sequence + 1 ---~ -- uncover_1(current,sequence) ---~ -- local stop = #sequence ---~ -- if after then ---~ -- uncover_1(after,sequence) ---~ -- end ---~ -- if sequence then ---~ -- nt = nt + 1 ---~ -- t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } ---~ -- for unic, _ in next, sequence[start] do ---~ -- local cu = contexts[unic] ---~ -- if not cu then ---~ -- contexts[unic] = t ---~ -- end ---~ -- end ---~ -- end ---~ -- end ---~ -- end ---~ end ---~ end ---~ end ---~ end ---~ end ---~ 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 %s",format) - elseif not validformat[lookuptype] then - -- todo: dejavu-serif has one (but i need to see what use it has) - report_prepare("unsupported %s %s for %s",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 - -- Wventually 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 %s",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 %s",os.clock()-starttime,tfmdata.properties.fullname or "?") - end - end - end -end - -registerotffeature { - name = "features", - description = "features", - default = true, - initializers = { - position = 1, - node = featuresinitializer, - }, - processors = { - node = featuresprocessor, - } -} -- cgit v1.2.3 From f4111f603593803b2ff87c7f5f9cc8e4dd6215d2 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 12:07:45 +0200 Subject: rename otfl-font-def.lua -> otfl-fonts-def.lua --- otfl-font-def.lua | 440 ----------------------------------------------------- otfl-fonts-def.lua | 440 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 440 insertions(+), 440 deletions(-) delete mode 100644 otfl-font-def.lua create mode 100644 otfl-fonts-def.lua diff --git a/otfl-font-def.lua b/otfl-font-def.lua deleted file mode 100644 index 96de480..0000000 --- a/otfl-font-def.lua +++ /dev/null @@ -1,440 +0,0 @@ -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 concat = table.concat -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 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") - ---[[ldx-- -

Here we deal with defining fonts. We do so by intercepting the -default loader that only handles .

---ldx]]-- - -local fonts = fonts -local fontdata = fonts.hashes.identifiers -local readers = fonts.readers -local definers = fonts.definers -local specifiers = fonts.specifiers -local constructors = fonts.constructors - -readers.sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } -- dfont ttc - -local variants = allocate() -specifiers.variants = variants - -definers.methods = definers.methods or { } - -local internalized = allocate() -- internal tex numbers (private) - - -local loadedfonts = constructors.loadedfonts -local designsizes = constructors.designsizes - ---[[ldx-- -

We hardly gain anything when we cache the final (pre scaled) - table. But it can be handy for debugging, so we no -longer carry this code along. Also, we now have quite some reference -to other tables so we would end up with lots of catches.

---ldx]]-- - ---[[ldx-- -

We can prefix a font specification by name: or -file:. The first case will result in a lookup in the -synonym table.

- - -[ name: | file: ] identifier [ separator [ specification ] ] - - -

The following function split the font specification into components -and prepares a table that will move along as we proceed.

---ldx]]-- - --- beware, we discard additional specs --- --- method:name method:name(sub) method:name(sub)*spec method:name*spec --- name name(sub) name(sub)*spec name*spec --- name@spec*oeps - -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) -end - -definers.getspecification = getspecification - -function definers.registersplit(symbol,action,verbosename) - addspecifier(symbol) - variants[symbol] = action - if verbosename then - variants[verbosename] = action - end -end - -function definers.makespecification(specification,lookup,name,sub,method,detail,size) - size = size or 655360 - if trace_defining then - report_defining("%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s", - specification, (lookup ~= "" and lookup) or "[file]", (name ~= "" and name) or "-", - (sub ~= "" and sub) or "-", (method ~= "" and method) or "-", (detail ~= "" and detail) or "-") - end - if not lookup or lookup == "" then - lookup = definers.defaultlookup - end - local t = { - lookup = lookup, -- forced type - specification = specification, -- full specification - size = size, -- size in scaled points or -1000*n - name = name, -- font or filename - sub = sub, -- subfont (eg in ttc) - method = method, -- specification method - detail = detail, -- specification - resolved = "", -- resolved font name - forced = "", -- forced loader - features = { }, -- preprocessed features - } - return t -end - -function definers.analyze(specification, size) - -- can be optimized with locals - local lookup, name, sub, method, detail = getspecification(specification or "") - return definers.makespecification(specification, lookup, name, sub, method, detail, size) -end - ---[[ldx-- -

We can resolve the filename using the next function:

---ldx]]-- - -definers.resolvers = definers.resolvers or { } -local resolvers = definers.resolvers - --- todo: reporter - -function resolvers.file(specification) - local suffix = file.suffix(specification.name) - if fonts.formats[suffix] then - specification.forced = suffix - specification.name = file.removesuffix(specification.name) - end -end - -function resolvers.name(specification) - local resolve = fonts.names.resolve - if resolve then - local resolved, sub = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions - if resolved then - specification.resolved = resolved - specification.sub = sub - local suffix = file.suffix(resolved) - if fonts.formats[suffix] then - specification.forced = suffix - specification.name = file.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) -- we pass specification for overloaded versions - if resolved then - specification.resolved = resolved - specification.sub = sub - specification.forced = file.extname(resolved) - specification.name = file.removesuffix(resolved) - end - else - resolvers.name(specification) - end -end - -function definers.resolve(specification) - if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash - local r = resolvers[specification.lookup] - if r then - r(specification) - end - end - if specification.forced == "" then - specification.forced = nil - else - specification.forced = specification.forced - 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 - ---[[ldx-- -

The main read function either uses a forced reader (as determined by -a lookup) or tries to resolve the name using the list of readers.

- -

We need to cache when possible. We do cache raw tfm data (from , or ). After that we can cache based -on specificstion (name) and size, that is, only needs a number -for an already loaded fonts. However, it may make sense to cache fonts -before they're scaled as well (store 's with applied methods -and features). However, there may be a relation between the size and -features (esp in virtual fonts) so let's not do that now.

- -

Watch out, here we do load a font, but we don't prepare the -specification yet.

---ldx]]-- - --- very experimental: - -function 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 = gsub(lower(extrahash),"[^a-z]","-") - tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) - end - end - end - return tfmdata -end - --- function definers.applypostprocessors(tfmdata) --- 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] -- hashes by size ! - 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 %s of %s not found",forced,specification.name) - end - else - local sequence = readers.sequence -- can be overloaded so only a shortcut here - for s=1,#sequence do - local reader = sequence[s] - if readers[reader] then -- we skip not loaded readers - if trace_defining then - report_defining("trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") - 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) -- todo: general postprocessor - loadedfonts[hash] = tfmdata - designsizes[specification.hash] = tfmdata.parameters.designsize - end - end - if not tfmdata then - report_defining("font with asked name '%s' is not found using lookup '%s'",specification.name,specification.lookup) - end - return tfmdata -end - ---[[ldx-- -

For virtual fonts we need a slightly different approach:

---ldx]]-- - -function constructors.readanddefine(name,size) -- no id -- maybe a dummy first - 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 - id = font.define(tfmdata) - definers.register(tfmdata,id) - else - id = 0 -- signal - end - end - return fontdata[id], id -end - ---[[ldx-- -

So far the specifiers. Now comes the real definer. Here we cache -based on id's. Here we also intercept the virtual font handler. Since -it evolved stepwise I may rewrite this bit (combine code).

- -In the previously defined reader (the one resulting in a -table) we cached the (scaled) instances. Here we cache them again, but -this time based on id. We could combine this in one cache but this does -not gain much. By the way, passing id's back to in the callback was -introduced later in the development.

---ldx]]-- - -local lastdefined = nil -- we don't want this one to end up in s-tra-02 -local internalized = { } - -function definers.current() -- or maybe 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 internalized[hash] then - internalized[hash] = id - if trace_defining then - report_defining("registering font, id: %s, hash: %s",id or "?",hash or "?") - end - fontdata[id] = tfmdata - end - end -end - -function definers.read(specification,size,id) -- id can be optional, name can already be table - 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) -- id - if tfmdata then - if trace_defining then - report_defining("already hashed: %s",hash) - end - else - tfmdata = definers.loadfont(specification) -- can be overloaded - if tfmdata then - if trace_defining then - report_defining("loaded and hashed: %s",hash) - end - --~ constructors.checkvirtualid(tfmdata) -- interferes - 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 -- todo ! ! ! ! ! - if not tfmdata then -- or id? - report_defining( "unknown font %s, 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 %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", - properties.format or "unknown", - id or "?", - properties.name or "?", - parameters.size or "default", - properties.encodingbytes or "?", - properties.encodingname or "unicode", - properties.fullname or "?", - file.basename(properties.filename or "?")) - end - statistics.stoptiming(fonts) - return tfmdata -end - ---[[ldx-- -

We overload the reader.

---ldx]]-- - -callbacks.register('define_font', definers.read, "definition of fonts (tfmdata preparation)") diff --git a/otfl-fonts-def.lua b/otfl-fonts-def.lua new file mode 100644 index 0000000..96de480 --- /dev/null +++ b/otfl-fonts-def.lua @@ -0,0 +1,440 @@ +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 concat = table.concat +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 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") + +--[[ldx-- +

Here we deal with defining fonts. We do so by intercepting the +default loader that only handles .

+--ldx]]-- + +local fonts = fonts +local fontdata = fonts.hashes.identifiers +local readers = fonts.readers +local definers = fonts.definers +local specifiers = fonts.specifiers +local constructors = fonts.constructors + +readers.sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } -- dfont ttc + +local variants = allocate() +specifiers.variants = variants + +definers.methods = definers.methods or { } + +local internalized = allocate() -- internal tex numbers (private) + + +local loadedfonts = constructors.loadedfonts +local designsizes = constructors.designsizes + +--[[ldx-- +

We hardly gain anything when we cache the final (pre scaled) + table. But it can be handy for debugging, so we no +longer carry this code along. Also, we now have quite some reference +to other tables so we would end up with lots of catches.

+--ldx]]-- + +--[[ldx-- +

We can prefix a font specification by name: or +file:. The first case will result in a lookup in the +synonym table.

+ + +[ name: | file: ] identifier [ separator [ specification ] ] + + +

The following function split the font specification into components +and prepares a table that will move along as we proceed.

+--ldx]]-- + +-- beware, we discard additional specs +-- +-- method:name method:name(sub) method:name(sub)*spec method:name*spec +-- name name(sub) name(sub)*spec name*spec +-- name@spec*oeps + +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) +end + +definers.getspecification = getspecification + +function definers.registersplit(symbol,action,verbosename) + addspecifier(symbol) + variants[symbol] = action + if verbosename then + variants[verbosename] = action + end +end + +function definers.makespecification(specification,lookup,name,sub,method,detail,size) + size = size or 655360 + if trace_defining then + report_defining("%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s", + specification, (lookup ~= "" and lookup) or "[file]", (name ~= "" and name) or "-", + (sub ~= "" and sub) or "-", (method ~= "" and method) or "-", (detail ~= "" and detail) or "-") + end + if not lookup or lookup == "" then + lookup = definers.defaultlookup + end + local t = { + lookup = lookup, -- forced type + specification = specification, -- full specification + size = size, -- size in scaled points or -1000*n + name = name, -- font or filename + sub = sub, -- subfont (eg in ttc) + method = method, -- specification method + detail = detail, -- specification + resolved = "", -- resolved font name + forced = "", -- forced loader + features = { }, -- preprocessed features + } + return t +end + +function definers.analyze(specification, size) + -- can be optimized with locals + local lookup, name, sub, method, detail = getspecification(specification or "") + return definers.makespecification(specification, lookup, name, sub, method, detail, size) +end + +--[[ldx-- +

We can resolve the filename using the next function:

+--ldx]]-- + +definers.resolvers = definers.resolvers or { } +local resolvers = definers.resolvers + +-- todo: reporter + +function resolvers.file(specification) + local suffix = file.suffix(specification.name) + if fonts.formats[suffix] then + specification.forced = suffix + specification.name = file.removesuffix(specification.name) + end +end + +function resolvers.name(specification) + local resolve = fonts.names.resolve + if resolve then + local resolved, sub = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions + if resolved then + specification.resolved = resolved + specification.sub = sub + local suffix = file.suffix(resolved) + if fonts.formats[suffix] then + specification.forced = suffix + specification.name = file.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) -- we pass specification for overloaded versions + if resolved then + specification.resolved = resolved + specification.sub = sub + specification.forced = file.extname(resolved) + specification.name = file.removesuffix(resolved) + end + else + resolvers.name(specification) + end +end + +function definers.resolve(specification) + if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash + local r = resolvers[specification.lookup] + if r then + r(specification) + end + end + if specification.forced == "" then + specification.forced = nil + else + specification.forced = specification.forced + 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 + +--[[ldx-- +

The main read function either uses a forced reader (as determined by +a lookup) or tries to resolve the name using the list of readers.

+ +

We need to cache when possible. We do cache raw tfm data (from , or ). After that we can cache based +on specificstion (name) and size, that is, only needs a number +for an already loaded fonts. However, it may make sense to cache fonts +before they're scaled as well (store 's with applied methods +and features). However, there may be a relation between the size and +features (esp in virtual fonts) so let's not do that now.

+ +

Watch out, here we do load a font, but we don't prepare the +specification yet.

+--ldx]]-- + +-- very experimental: + +function 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 = gsub(lower(extrahash),"[^a-z]","-") + tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) + end + end + end + return tfmdata +end + +-- function definers.applypostprocessors(tfmdata) +-- 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] -- hashes by size ! + 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 %s of %s not found",forced,specification.name) + end + else + local sequence = readers.sequence -- can be overloaded so only a shortcut here + for s=1,#sequence do + local reader = sequence[s] + if readers[reader] then -- we skip not loaded readers + if trace_defining then + report_defining("trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") + 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) -- todo: general postprocessor + loadedfonts[hash] = tfmdata + designsizes[specification.hash] = tfmdata.parameters.designsize + end + end + if not tfmdata then + report_defining("font with asked name '%s' is not found using lookup '%s'",specification.name,specification.lookup) + end + return tfmdata +end + +--[[ldx-- +

For virtual fonts we need a slightly different approach:

+--ldx]]-- + +function constructors.readanddefine(name,size) -- no id -- maybe a dummy first + 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 + id = font.define(tfmdata) + definers.register(tfmdata,id) + else + id = 0 -- signal + end + end + return fontdata[id], id +end + +--[[ldx-- +

So far the specifiers. Now comes the real definer. Here we cache +based on id's. Here we also intercept the virtual font handler. Since +it evolved stepwise I may rewrite this bit (combine code).

+ +In the previously defined reader (the one resulting in a +table) we cached the (scaled) instances. Here we cache them again, but +this time based on id. We could combine this in one cache but this does +not gain much. By the way, passing id's back to in the callback was +introduced later in the development.

+--ldx]]-- + +local lastdefined = nil -- we don't want this one to end up in s-tra-02 +local internalized = { } + +function definers.current() -- or maybe 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 internalized[hash] then + internalized[hash] = id + if trace_defining then + report_defining("registering font, id: %s, hash: %s",id or "?",hash or "?") + end + fontdata[id] = tfmdata + end + end +end + +function definers.read(specification,size,id) -- id can be optional, name can already be table + 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) -- id + if tfmdata then + if trace_defining then + report_defining("already hashed: %s",hash) + end + else + tfmdata = definers.loadfont(specification) -- can be overloaded + if tfmdata then + if trace_defining then + report_defining("loaded and hashed: %s",hash) + end + --~ constructors.checkvirtualid(tfmdata) -- interferes + 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 -- todo ! ! ! ! ! + if not tfmdata then -- or id? + report_defining( "unknown font %s, 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 %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", + properties.format or "unknown", + id or "?", + properties.name or "?", + parameters.size or "default", + properties.encodingbytes or "?", + properties.encodingname or "unicode", + properties.fullname or "?", + file.basename(properties.filename or "?")) + end + statistics.stoptiming(fonts) + return tfmdata +end + +--[[ldx-- +

We overload the reader.

+--ldx]]-- + +callbacks.register('define_font', definers.read, "definition of fonts (tfmdata preparation)") -- cgit v1.2.3 From ed2b905f3f19b8e92b2e3d750f9c3f3250327a01 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 12:53:44 +0200 Subject: update basics-gen.lua --- otfl-basics-gen.lua | 82 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/otfl-basics-gen.lua b/otfl-basics-gen.lua index bfd81fd..288cfa2 100644 --- a/otfl-basics-gen.lua +++ b/otfl-basics-gen.lua @@ -12,7 +12,8 @@ if context then end local dummyfunction = function() end -local dummyreporter = function(c) return function(...) texio.write(c .. " : " .. string.format(...)) end end +----- dummyreporter = function(c) return function(...) texio.write_nl(c .. " : " .. string.format(...)) end end +local dummyreporter = function(c) return function(...) texio.write_nl(c .. " : " .. string.formatters(...)) end end statistics = { register = dummyfunction, @@ -74,32 +75,42 @@ 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", - 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! + 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! } function resolvers.findfile(name,fileformat) name = string.gsub(name,"\\","/") - fileformat = fileformat and string.lower(fileformat) - local found = kpse.find_file(name,(fileformat and fileformat ~= "" and (remapper[fileformat] or fileformat)) or file.extname(name,"tex")) + 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.extname(name) -- string.match(name,"%.([^%.]-)$") - end - return resolvers.findfile(name,(fileformat and remapper[fileformat]) or fileformat) -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.resolve(s) return s @@ -206,15 +217,28 @@ 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: %s)",lucname)) + 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 not data and luaname and lfs.isfile(luaname) then - texio.write(string.format("(load: %s)",luaname)) + 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 - return data and data() end end @@ -241,21 +265,25 @@ end -- this) in which case one should limit the method to luac and enable support -- for execution. -caches.compilemethod = "luac" -- luac dump both +caches.compilemethod = "both" function caches.compile(data,luaname,lucname) local done = false if caches.compilemethod == "luac" or caches.compilemethod == "both" then - local command = "-o " .. string.quoted(lucname) .. " -s " .. string.quoted(luaname) - done = os.spawn("texluac " .. command) == 0 + done = os.spawn("texluac -o " .. string.quoted(lucname) .. " -s " .. string.quoted(luaname)) == 0 end if not done and (caches.compilemethod == "dump" or caches.compilemethod == "both") then - local d = table.serialize(data,true) + 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) - f:write(string.dump(s)) + if s then + f:write(string.dump(s,true)) + end f:close() end end -- cgit v1.2.3 From 65481c5210c2bb8137ed8fddbbcc42dcb3a002bc Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 12:59:53 +0200 Subject: update fonts-def --- otfl-fonts-def.lua | 459 +++++++---------------------------------------------- 1 file changed, 58 insertions(+), 401 deletions(-) diff --git a/otfl-fonts-def.lua b/otfl-fonts-def.lua index 96de480..0c2f0db 100644 --- a/otfl-fonts-def.lua +++ b/otfl-fonts-def.lua @@ -1,440 +1,97 @@ -if not modules then modules = { } end modules ['font-def'] = { +if not modules then modules = { } end modules ['luatex-font-def'] = { version = 1.001, - comment = "companion to font-ini.mkiv", + comment = "companion to luatex-*.tex", 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, 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 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") - ---[[ldx-- -

Here we deal with defining fonts. We do so by intercepting the -default loader that only handles .

---ldx]]-- - -local fonts = fonts -local fontdata = fonts.hashes.identifiers -local readers = fonts.readers -local definers = fonts.definers -local specifiers = fonts.specifiers -local constructors = fonts.constructors - -readers.sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } -- dfont ttc - -local variants = allocate() -specifiers.variants = variants - -definers.methods = definers.methods or { } - -local internalized = allocate() -- internal tex numbers (private) - - -local loadedfonts = constructors.loadedfonts -local designsizes = constructors.designsizes - ---[[ldx-- -

We hardly gain anything when we cache the final (pre scaled) - table. But it can be handy for debugging, so we no -longer carry this code along. Also, we now have quite some reference -to other tables so we would end up with lots of catches.

---ldx]]-- - ---[[ldx-- -

We can prefix a font specification by name: or -file:. The first case will result in a lookup in the -synonym table.

- - -[ name: | file: ] identifier [ separator [ specification ] ] - - -

The following function split the font specification into components -and prepares a table that will move along as we proceed.

---ldx]]-- - --- beware, we discard additional specs --- --- method:name method:name(sub) method:name(sub)*spec method:name*spec --- name name(sub) name(sub)*spec name*spec --- name@spec*oeps - -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(""))) +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() end -local function addlookup(str,default) - prefixpattern = prefixpattern + P(str) -end - -definers.addlookup = addlookup +local fonts = fonts -addlookup("file") -addlookup("name") -addlookup("spec") +-- A bit of tuning for definitions. -local function getspecification(str) - return lpegmatch(splitter,str) -end +fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload -definers.getspecification = getspecification +-- tricky: we sort of bypass the parser and directly feed all into +-- the sub parser -function definers.registersplit(symbol,action,verbosename) - addspecifier(symbol) - variants[symbol] = action - if verbosename then - variants[verbosename] = action - end +function fonts.definers.getspecification(str) + return "", str, "", ":", str end -function definers.makespecification(specification,lookup,name,sub,method,detail,size) - size = size or 655360 - if trace_defining then - report_defining("%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s", - specification, (lookup ~= "" and lookup) or "[file]", (name ~= "" and name) or "-", - (sub ~= "" and sub) or "-", (method ~= "" and method) or "-", (detail ~= "" and detail) or "-") - end - if not lookup or lookup == "" then - lookup = definers.defaultlookup - end - local t = { - lookup = lookup, -- forced type - specification = specification, -- full specification - size = size, -- size in scaled points or -1000*n - name = name, -- font or filename - sub = sub, -- subfont (eg in ttc) - method = method, -- specification method - detail = detail, -- specification - resolved = "", -- resolved font name - forced = "", -- forced loader - features = { }, -- preprocessed features - } - return t -end +-- the generic name parser (different from context!) -function definers.analyze(specification, size) - -- can be optimized with locals - local lookup, name, sub, method, detail = getspecification(specification or "") - return definers.makespecification(specification, lookup, name, sub, method, detail, size) -end +local list = { } ---[[ldx-- -

We can resolve the filename using the next function:

---ldx]]-- +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 -definers.resolvers = definers.resolvers or { } -local resolvers = definers.resolvers +local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C --- todo: reporter +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 -function resolvers.file(specification) - local suffix = file.suffix(specification.name) - if fonts.formats[suffix] then - specification.forced = suffix - specification.name = file.removesuffix(specification.name) - end -end +local pattern = (filename_1 + filename_2 + fontname_1 + fontname_2) * subvalue^0 * crapspec^0 * options^0 -function resolvers.name(specification) - local resolve = fonts.names.resolve - if resolve then - local resolved, sub = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions - if resolved then - specification.resolved = resolved - specification.sub = sub - local suffix = file.suffix(resolved) - if fonts.formats[suffix] then - specification.forced = suffix - specification.name = file.removesuffix(resolved) - else - specification.name = resolved - end - end - else - resolvers.file(specification) +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 -end - -function resolvers.spec(specification) - local resolvespec = fonts.names.resolvespec - if resolvespec then - local resolved, sub = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions - if resolved then - specification.resolved = resolved - specification.sub = sub - specification.forced = file.extname(resolved) - specification.name = file.removesuffix(resolved) - end - else - resolvers.name(specification) + if list.lookup then + specification.lookup = list.lookup + list.lookup = nil end -end - -function definers.resolve(specification) - if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash - local r = resolvers[specification.lookup] - if r then - r(specification) - end - end - if specification.forced == "" then - specification.forced = nil - else - specification.forced = specification.forced - end - specification.hash = lower(specification.name .. ' @ ' .. constructors.hashfeatures(specification)) - if specification.sub and specification.sub ~= "" then - specification.hash = specification.sub .. ' @ ' .. specification.hash + if list.sub then + specification.sub = list.sub + list.sub = nil end + specification.features.normal = fonts.handlers.otf.features.normalize(list) return specification end ---[[ldx-- -

The main read function either uses a forced reader (as determined by -a lookup) or tries to resolve the name using the list of readers.

- -

We need to cache when possible. We do cache raw tfm data (from , or ). After that we can cache based -on specificstion (name) and size, that is, only needs a number -for an already loaded fonts. However, it may make sense to cache fonts -before they're scaled as well (store 's with applied methods -and features). However, there may be a relation between the size and -features (esp in virtual fonts) so let's not do that now.

- -

Watch out, here we do load a font, but we don't prepare the -specification yet.

---ldx]]-- - --- very experimental: +fonts.definers.registersplit(":",colonized,"cryptic") +fonts.definers.registersplit("", colonized,"more cryptic") -- catches \font\text=[names] -function definers.applypostprocessors(tfmdata) +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 = gsub(lower(extrahash),"[^a-z]","-") + extrahash = string.gsub(lower(extrahash),"[^a-z]","-") tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) end end end return tfmdata end - --- function definers.applypostprocessors(tfmdata) --- 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] -- hashes by size ! - 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 %s of %s not found",forced,specification.name) - end - else - local sequence = readers.sequence -- can be overloaded so only a shortcut here - for s=1,#sequence do - local reader = sequence[s] - if readers[reader] then -- we skip not loaded readers - if trace_defining then - report_defining("trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") - 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) -- todo: general postprocessor - loadedfonts[hash] = tfmdata - designsizes[specification.hash] = tfmdata.parameters.designsize - end - end - if not tfmdata then - report_defining("font with asked name '%s' is not found using lookup '%s'",specification.name,specification.lookup) - end - return tfmdata -end - ---[[ldx-- -

For virtual fonts we need a slightly different approach:

---ldx]]-- - -function constructors.readanddefine(name,size) -- no id -- maybe a dummy first - 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 - id = font.define(tfmdata) - definers.register(tfmdata,id) - else - id = 0 -- signal - end - end - return fontdata[id], id -end - ---[[ldx-- -

So far the specifiers. Now comes the real definer. Here we cache -based on id's. Here we also intercept the virtual font handler. Since -it evolved stepwise I may rewrite this bit (combine code).

- -In the previously defined reader (the one resulting in a -table) we cached the (scaled) instances. Here we cache them again, but -this time based on id. We could combine this in one cache but this does -not gain much. By the way, passing id's back to in the callback was -introduced later in the development.

---ldx]]-- - -local lastdefined = nil -- we don't want this one to end up in s-tra-02 -local internalized = { } - -function definers.current() -- or maybe 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 internalized[hash] then - internalized[hash] = id - if trace_defining then - report_defining("registering font, id: %s, hash: %s",id or "?",hash or "?") - end - fontdata[id] = tfmdata - end - end -end - -function definers.read(specification,size,id) -- id can be optional, name can already be table - 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) -- id - if tfmdata then - if trace_defining then - report_defining("already hashed: %s",hash) - end - else - tfmdata = definers.loadfont(specification) -- can be overloaded - if tfmdata then - if trace_defining then - report_defining("loaded and hashed: %s",hash) - end - --~ constructors.checkvirtualid(tfmdata) -- interferes - 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 -- todo ! ! ! ! ! - if not tfmdata then -- or id? - report_defining( "unknown font %s, 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 %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", - properties.format or "unknown", - id or "?", - properties.name or "?", - parameters.size or "default", - properties.encodingbytes or "?", - properties.encodingname or "unicode", - properties.fullname or "?", - file.basename(properties.filename or "?")) - end - statistics.stoptiming(fonts) - return tfmdata -end - ---[[ldx-- -

We overload the reader.

---ldx]]-- - -callbacks.register('define_font', definers.read, "definition of fonts (tfmdata preparation)") -- cgit v1.2.3 From b3fe27445a0608084f3a87f1ac9d60a08615aab8 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 13:01:27 +0200 Subject: update fonts-ext --- otfl-fonts-ext.lua | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/otfl-fonts-ext.lua b/otfl-fonts-ext.lua index d8884cc..b60d045 100644 --- a/otfl-fonts-ext.lua +++ b/otfl-fonts-ext.lua @@ -18,18 +18,14 @@ local otffeatures = fonts.constructors.newfeatures("otf") local function initializeitlc(tfmdata,value) if value then - -- the magic 40 and it formula come from Dohyun Kim - local parameters = tfmdata.parameters + -- 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 uwidth = (parameters.uwidth or 40)/2 - for unicode, d in next, tfmdata.descriptions do - local it = d.boundingbox[3] - d.width + uwidth - if it ~= 0 then - d.italic = it - end - end - tfmdata.properties.hasitalics = true + local properties = tfmdata.properties + local factor = tonumber(value) or 1 + properties.hasitalics = true + properties.autoitalicamount = factor * (parameters.uwidth or 40)/2 end end end -- cgit v1.2.3 From 0db05fe38f53f414a459907c9d6f1a6e77bbfd17 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 13:11:09 +0200 Subject: import fresh luatex-fonts.lua and luatex-fonts-merged.lua from Context --- otfl-fonts-merged.lua | 11178 ++++++++++++++++++++++++++++++++++++++++++++++++ otfl-fonts.lua | 233 + 2 files changed, 11411 insertions(+) create mode 100644 otfl-fonts-merged.lua create mode 100644 otfl-fonts.lua diff --git a/otfl-fonts-merged.lua b/otfl-fonts-merged.lua new file mode 100644 index 0000000..2cb036d --- /dev/null +++ b/otfl-fonts-merged.lua @@ -0,0 +1,11178 @@ +-- merged file : luatex-fonts-merged.lua +-- parent file : luatex-fonts.lua +-- merge date : 04/07/13 14:05:24 + +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 +local type=type +local gsub,format=string.gsub,string.format +local package=package +local searchers=package.searchers or package.loaders +local libpaths=nil +local clibpaths=nil +local libhash={} +local clibhash={} +local libextras={} +local clibextras={} +local filejoin=file and file.join or function(path,name) return path.."/"..name end +local isreadable=file and file.is_readable or function(name) local f=io.open(name) if f then f:close() return true end end +local addsuffix=file and file.addsuffix or function(name,suffix) return name.."."..suffix end +local function cleanpath(path) + return path +end +local helpers=package.helpers or { + libpaths=function() return {} end, + clibpaths=function() return {} end, + cleanpath=cleanpath, + trace=false, + report=function(...) print(format(...)) end, +} +package.helpers=helpers +local function getlibpaths() + return libpaths or helpers.libpaths(libhash) +end +local function getclibpaths() + return clibpaths or helpers.clibpaths(clibhash) +end +package.libpaths=getlibpaths +package.clibpaths=getclibpaths +local function addpath(what,paths,extras,hash,...) + local pathlist={... } + local cleanpath=helpers.cleanpath + local trace=helpers.trace + local report=helpers.report + local function add(path) + local path=cleanpath(path) + if not hash[path] then + if trace then + report("extra %s path: %s",what,path) + end + paths [#paths+1]=path + extras[#extras+1]=path + end + end + for p=1,#pathlist do + local path=pathlist[p] + if type(path)=="table" then + for i=1,#path do + add(path[i]) + end + else + add(path) + end + end + return paths,extras +end +function package.extralibpath(...) + libpaths,libextras=addpath("lua",getlibpaths(),libextras,libhash,...) +end +function package.extraclibpath(...) + clibpaths,clibextras=addpath("lib",getclibpaths(),clibextras,clibhash,...) +end +if not searchers[-2] then + searchers[-2]=searchers[2] +end +searchers[2]=function(name) + return helpers.loaded(name) +end +searchers[3]=nil +local function loadedaslib(resolved,rawname) + local init="luaopen_"..gsub(rawname,"%.","_") + if helpers.trace then + helpers.report("calling loadlib with '%s' with init '%s'",resolved,init) + end + return package.loadlib(resolved,init) +end +local function loadedbylua(name) + if helpers.trace then + helpers.report("locating '%s' using normal loader",name) + end + return true,searchers[-2](name) +end +local function loadedbypath(name,rawname,paths,islib,what) + local trace=helpers.trace + local report=helpers.report + if trace then + report("locating '%s' as '%s' on '%s' paths",rawname,name,what) + end + for p=1,#paths do + local path=paths[p] + local resolved=filejoin(path,name) + if trace then + report("checking for '%s' using '%s' path '%s'",name,what,path) + end + if isreadable(resolved) then + if trace then + report("lib '%s' located on '%s'",name,resolved) + end + if islib then + return true,loadedaslib(resolved,rawname) + else + return true,loadfile(resolved) + end + end + end +end +local function notloaded(name) + if helpers.trace then + helpers.report("? unable to locate library '%s'",name) + end +end +helpers.loadedaslib=loadedaslib +helpers.loadedbylua=loadedbylua +helpers.loadedbypath=loadedbypath +helpers.notloaded=notloaded +function helpers.loaded(name) + local thename=gsub(name,"%.","/") + local luaname=addsuffix(thename,"lua") + local libname=addsuffix(thename,os.libsuffix or "so") + local libpaths=getlibpaths() + local clibpaths=getclibpaths() + local done,result=loadedbypath(luaname,name,libpaths,false,"lua") + if done then + return result + end + local done,result=loadedbypath(luaname,name,clibpaths,false,"lua") + if done then + return result + end + local done,result=loadedbypath(libname,name,clibpaths,true,"lib") + if done then + return result + end + local done,result=loadedbylua(name) + if done then + return result + end + return notloaded(name) +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") +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 +setinspector(function(v) if lpegtype(v) then lpegprint(v) return true 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 digit,sign=R('09'),S('+-') +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 utfbom_32_be=P('\000\000\254\255') +local utfbom_32_le=P('\255\254\000\000') +local utfbom_16_be=P('\255\254') +local utfbom_16_le=P('\254\255') +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 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.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.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.digit=digit +patterns.sign=sign +patterns.cardinal=sign^0*digit^1 +patterns.integer=sign^0*digit^1 +patterns.unsigned=digit^0*P('.')*digit^1 +patterns.float=sign^0*patterns.unsigned +patterns.cunsigned=digit^0*P(',')*digit^1 +patterns.cfloat=sign^0*patterns.cunsigned +patterns.number=patterns.float+patterns.integer +patterns.cnumber=patterns.cfloat+patterns.integer +patterns.oct=P("0")*R("07")^1 +patterns.octal=patterns.oct +patterns.HEX=P("0x")*R("09","AF")^1 +patterns.hex=P("0x")*R("09","af")^1 +patterns.hexadecimal=P("0x")*R("09","AF","af")^1 +patterns.lowercase=R("az") +patterns.uppercase=R("AZ") +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=P(",") +patterns.commaspacer=P(",")*spacer^0 +patterns.period=P(".") +patterns.colon=P(":") +patterns.semicolon=P(";") +patterns.underscore=P("_") +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.propername=R("AZ","az","__")*R("09","AZ","az","__")^0*P(-1) +patterns.somecontent=(anything-newline-space)^1 +patterns.beginline=#(1-newline) +patterns.longtostring=Cs(whitespace^0/""*nonwhitespace^0*((whitespace^0/" "*(patterns.quoted+nonwhitespace)^1)^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) + 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 + pattern=(1-pattern)^0*pattern + 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 + separator=P(separator) + splitter=C((1-separator)^0) + splitters_f[separator]=splitter + end + return splitter +end +function lpeg.secondofsplit(separator) + local splitter=splitters_s[separator] + if not splitter then + separator=P(separator) + splitter=(1-separator)^0*separator*C(anything^0) + splitters_s[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 digit=R("09") +local period=P(".") +local zero=P("0") +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=="boolean" then + nt=nt+1 + tt[nt]=tostring(v) + elseif tv=="string" then + nt=nt+1 + tt[nt]=format("%q",v) + 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,tostring(name))) + 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 t,tk=type(v),type(k) + if compact and first and tk=="number" and k>=first and k<=last then + if t=="number" then + if hexify then + handle(format("%s 0x%04X,",depth,v)) + else + handle(format("%s %s,",depth,v)) + end + elseif t=="string" then + if reduce and tonumber(v) then + handle(format("%s %s,",depth,v)) + else + handle(format("%s %q,",depth,v)) + end + elseif t=="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 t=="boolean" then + handle(format("%s %s,",depth,tostring(v))) + elseif t=="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 t=="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,tostring(k),v)) + else + handle(format("%s [%s]=%s,",depth,tostring(k),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 t=="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,tostring(k),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,tostring(k),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 t=="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,tostring(k))) + 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,tostring(k),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 t=="boolean" then + if tk=="number" then + if hexify then + handle(format("%s [0x%04X]=%s,",depth,k,tostring(v))) + else + handle(format("%s [%s]=%s,",depth,k,tostring(v))) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%s,",depth,tostring(k),tostring(v))) + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%s,",depth,k,tostring(v))) + else + handle(format("%s [%q]=%s,",depth,k,tostring(v))) + end + elseif t=="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,tostring(k),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,tostring(k),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[k]=v + end + end + end + local n=#f + for k=1,#t do + local v=t[k] + if depth>0 and type(v)=="table" then + flattened(v,f,depth-1) + n=#f + else + n=n+1 + f[n]=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 +setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true 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 + +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) + 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=string.match +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 +file.pathpart=pathpart +file.basename=basename +file.nameonly=nameonly +file.suffixonly=suffixonly +file.suffix=suffixonly +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) + return str and lpegmatch(pattern_d,str) +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 stripper=Cs(P(fwslash)^0/""*reslasher) +local isnetwork=fwslash*fwslash*(1-fwslash)+(1-fwslash-colon)^1*colon +local isroot=fwslash^1*-1 +local hasroot=fwslash^1 +local deslasher=lpeg.replacer(S("\\/")^1,"/") +function file.join(...) + local lst={... } + local one=lst[1] + if lpegmatch(isnetwork,one) then + local two=lpegmatch(deslasher,concat(lst,"/",2)) + return one.."/"..two + 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 splitstarter=(Cs(drivespec*(bwslash/"/"+fwslash)^0)+Cc(false))*Ct(lpeg.splitat(S("/\\")^1)) +local absolute=fwslash +function file.collapsepath(str,anchor) + if not str then + return + end + if anchor 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 + return concat(newelements,'/') + 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 + +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" then + return true + elseif str=="false" or str=="no" or str=="off" or str=="f" 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=_LUAVERSION<5.2 and load or function(str) + return load(dump(load(str),true)) +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 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 template=[[ +%s +%s +return function(%s) return %s end +]] +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=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("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_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 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("h") ++V("w") ++V("W") ++V("a") ++V("A") ++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_tab*P("l"))/format_l, + ["L"]=(prefix_tab*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, + ["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("%")/""*Cc([[local format = string.format return function(str) return format("%]])*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*Cc([[",str) end]])*P(-1) + ) +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)() + 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 t={ _extensions_={},_preamble_="",_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 preamble then + t._preamble_=preamble.."\n"..t._preamble_ + end + end +end +strings.formatters.add=add +lpeg.patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"""+P(1))^0) +lpeg.patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) +add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]]) +add(formatters,"tex",[[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]]) + +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.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", +} +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.resolve(s) + return s +end +function resolvers.unresolve(s) + return s +end +caches={} +local writable,readables=nil,{} +if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then + caches.namespace='generic' +end +do + local cachepaths=kpse.expand_path('$TEXMFCACHE') or "" + if cachepaths=="" then + cachepaths=kpse.expand_path('$TEXMFVAR') + end + if cachepaths=="" then + cachepaths=kpse.expand_path('$VARTEXMF') + end + if cachepaths=="" then + cachepaths="." + end + cachepaths=string.split(cachepaths,os.type=="windows" and ";" or ":") + for i=1,#cachepaths do + if file.is_writable(cachepaths[i]) then + writable=file.join(cachepaths[i],"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 + name="temp-"..name + return file.addsuffix(file.join(path,name),"lua"),file.addsuffix(file.join(path,name),"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,{ reduce=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 +caches.compilemethod="both" +function caches.compile(data,luaname,lucname) + local done=false + if caches.compilemethod=="luac" or caches.compilemethod=="both" then + done=os.spawn("texluac -o "..string.quoted(lucname).." -s "..string.quoted(luaname))==0 + end + if not done and (caches.compilemethod=="dump" or caches.compilemethod=="both") then + 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,'w') + if f then + local s=loadstring(d) + if s then + f:write(string.dump(s,true)) + end + f:close() + end + 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%d]+","-")) +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.unsetvalue=-0x7FFFFFFF +local numbers,last={},127 +function attributes.private(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" } +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 +nodes.before=node.insert_before +nodes.after=node.insert_after +function nodes.pool.kern(k) + local n=new_node("kern",1) + n.kern=k + return n +end +function nodes.endofmath(n) + for n in traverse_id(math_code,n.next) do + return n + end +end + +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 +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 + 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) + 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) + 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) + 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 655360 + 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 processors=whatprocessors[properties.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,tfmdata.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 manipulators=whatmanipulators[properties.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,tfmdata.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 +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^1)/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) + 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 into tounicode",unicode) + end +end +local function tounicode16sequence(unicodes) + 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 into tounicode",unicode) + 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)- 0xD800)*0x400+tonumber(r,16)-0xDC00 + end +end +mappings.loadlumtable=loadlumtable +mappings.makenameparser=makenameparser +mappings.tounicode16=tounicode16 +mappings.tounicode16sequence=tounicode16sequence +mappings.fromunicode16=fromunicode16 +local separator=S("_.") +local other=C((1-separator)^1) +local ligsplitter=Ct(other*(separator*other)^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) + 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) + 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) + ns=ns+1 + end + end + if not 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) + ns=ns+1 + unicode=foundcodes + end + end + end + end + end + end + end + if not unicode then + local split=lpegmatch(ligsplitter,name) + local nplit=split and #split or 0 + if nplit>=2 then + local t,n={},0 + for l=1,nplit do + local base=split[l] + local u=unicodes[base] or unicodevector[base] + if not u then + break + elseif type(u)=="table" then + n=n+1 + t[n]=u[1] + else + 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]) + else + originals[index]=t + tounicode[index]=tounicode16sequence(t) + end + nl=nl+1 + unicode=true + else + end + end + if not unicode then + local foundcodes,multiple=lpegmatch(uparser,name) + if foundcodes then + if multiple then + originals[index]=foundcodes + tounicode[index]=tounicode16sequence(foundcodes) + nl=nl+1 + unicode=true + else + originals[index]=foundcodes + tounicode[index]=tounicode16(foundcodes) + 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.lua" +fonts.names.new_to_old={} +fonts.names.old_to_new={} +local data,loaded=nil,false +local fileformats={ "lua","tex","other text files" } +function fonts.names.resolve(name,sub) + if not loaded then + local basename=fonts.names.basename + if basename and basename~="" then + 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 + 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 + else + return name,false + end + end +end +fonts.names.resolvespec=fonts.names.resolve +function fonts.names.getfilename(askedname,suffix) + return "" +end + +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 getn=table.getn +local lpegmatch=lpeg.match +local reversed,concat,remove=table.reversed,table.concat,table.remove +local ioflush=io.flush +local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive +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.741 +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 wildcard="*" +local default="dflt" +local fontloaderfields=fontloader.fields +local mainfields=nil +local glyphfields=nil +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) +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", + "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", + "add duplicates", + "check encoding", + "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,format,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=format, + 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) + 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={} + 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 + metadata.subfonts[cidindex]=somecopy(subfont) + 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 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 + local d + 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 + 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) + 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 mapdata=raw.map or {} + local unicodetoindex=mapdata and mapdata.map 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 parent then + report_otf("weird, unicode %U points to %U with index %H",unicode,parent,index) + else + report_otf("weird, unicode %U points to nowhere with index %H",unicode,index) + end + 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={} + 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 + 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 + end + end + end + end + 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 +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, +} +local function supported(features) + for i=1,#features do + if features[i].ismac then + return false + end + end + return true +end +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 + if not features or supported(features) then + 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 +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 s_hashed=t_hashed +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) + 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 + for gp=1,#gposlist do + local gpos=gposlist[gp] + local subtables=gpos.subtables + if subtables then + for s=1,#subtables do + local subtable=subtables[s] + local kernclass=subtable.kernclass + if kernclass then + local split={} + for k=1,#kernclass do + local kcl=kernclass[k] + local firsts=kcl.firsts + local seconds=kcl.seconds + local offsets=kcl.offsets + local lookups=kcl.lookup + if type(lookups)~="table" then + lookups={ lookups } + end + 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 l=1,#lookups do + local lookup=lookups[l] + 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] + 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 + for second_unicode,kern in next,extrakerns do + lookupkerns[second_unicode]=kern + end + elseif trace_loading then + report_otf("no glyph data for %U",first_unicode) + end + end + end + end + end + end + subtable.kernclass={} + end + end + end + 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 +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 monospaced=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced") + local charwidth=pfminfo.avgwidth + local italicangle=metadata.italicangle + local charxheight=pfminfo.os2_xheight and pfminfo.os2_xheight>0 and pfminfo.os2_xheight + 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 + 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 + end + 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 then + parameters.italicangle=italicangle + parameters.italicfactor=math.cos(math.rad(90+italicangle)) + parameters.slant=- math.round(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 fonts.formats[filename] or "opentype" + 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 format=specification.format + local features=specification.features.normal + local rawdata=otf.load(filename,format,sub,features and features.featurefile) + if rawdata and next(rawdata) then + 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 as 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,what) + local name=specification.name + if forced then + name=file.addsuffix(name,suffix,true) + end + local fullname=findbinfile(name,suffix) or "" + if fullname=="" then + fullname=fonts.names.getfilename(name,suffix) or "" + end + if fullname~="" then + specification.filename=fullname + specification.format=what + return read_from_otf(specification) + end +end +local function opentypereader(specification,suffix,what) + local forced=specification.forced or "" + if forced=="otf" then + return check_otf(true,specification,forced,"opentype") + elseif forced=="ttf" or forced=="ttc" or forced=="dfont" then + return check_otf(true,specification,forced,"truetype") + else + return check_otf(false,specification,suffix,what) + end +end +readers.opentype=opentypereader +local formats=fonts.formats +formats.otf="opentype" +formats.ttf="truetype" +formats.ttc="truetype" +formats.dfont="truetype" +function readers.otf (specification) return opentypereader(specification,"otf",formats.otf ) end +function readers.ttf (specification) return opentypereader(specification,"ttf",formats.ttf ) end +function readers.ttc (specification) return opentypereader(specification,"ttf",formats.ttc ) end +function readers.dfont(specification) return opentypereader(specification,"ttf",formats.dfont) 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 basepositions then + for feature,data in next,basepositions 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 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) + 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 } } + 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",char,n.font,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 +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] + if rlmode and rlmode>=0 then + local k=wx[p] + if k then + n.xoffset=p.xoffset-p.width+d[1]-k[2] + else + n.xoffset=p.xoffset-p.width+d[1] + end + else + local k=wx[p] + if k then + n.xoffset=p.xoffset-d[1]-k[2] + else + n.xoffset=p.xoffset-d[1] + 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,w=k[2] or 0,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 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, +} +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_midi + 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 (for instance) 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 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 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 +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)) + end + start=start.next + end + local start=components + 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: keep 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) + 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) + 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) +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 + if stop then + local lig=ligature.ligature + if lig 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 + end + 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 + else + 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 + 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 + else + 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) + 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 + else + 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 + else + 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 + local krn=kerns[nextchar] + 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 +local function delete_till_stop(start,stop,ignoremarks) + local n=1 + if start==stop then + elseif ignoremarks then + repeat + local next=start.next + if not marks[next.char] then + local components=next.components + if components then + flush_node_list(components) + end + delete_node(start,next) + end + n=n+1 + until next==stop + else + repeat + local next=start.next + local components=next.components + if components then + flush_node_list(components) + end + delete_node(start,next) + n=n+1 + until next==stop + end + return n +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) + delete_till_stop(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) + 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) + 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 + else + 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 +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 +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 + head,start,done=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + 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] + local cp=chainlookup and chainmores[chainlookup.type] + if cp then + 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 + else + i=i+1 + 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 + 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 + elseif id==math_code then + start=end_of_math(start).next + else + start=start.next + end + 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 + 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 + 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==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 ['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 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 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) +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=file.suffix(name) + if fonts.formats[suffix] then + specification.forced=suffix + specification.name=file.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=file.suffix(resolved) + if fonts.formats[suffix] then + specification.forced=suffix + specification.name=file.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=file.suffix(resolved) + specification.name=file.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 + else + specification.forced=specification.forced + 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 +local function checkvirtual(tfmdata) + local fonts=tfmdata.fonts + local selfid=font.nextid() + if fonts and #fonts>0 then + for i=1,#fonts do + if fonts[i][2]==0 then + fonts[i][2]=selfid + end + end + else + tfmdata.fonts={ "id",selfid } + end +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 + checkvirtual(tfmdata) + id=font.define(tfmdata) + definers.register(tfmdata,id) + else + id=0 + end + end + return fontdata[id],id +end +local lastdefined=nil +local internalized={} +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 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/otfl-fonts.lua b/otfl-fonts.lua new file mode 100644 index 0000000..fc7b789 --- /dev/null +++ b/otfl-fonts.lua @@ -0,0 +1,233 @@ +if not modules then modules = { } end modules ['luatex-fonts'] = { + version = 1.001, + comment = "companion to luatex-fonts.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- The following code isolates the generic context code from already defined or to be defined +-- namespaces. This is the reference loader for plain, but the generic code is also used in +-- luaotfload (which is is a file meant for latex) and that used to be maintained by Khaled +-- Hosny. We do our best to keep the interface as clean as possible. +-- +-- The code base is rather stable now, especially if you stay away from the non generic code. All +-- relevant data is organized in tables within the main table of a font instance. There are a few +-- places where in context other code is plugged in, but this does not affect the core code. Users +-- can (given that their macro package provides this option) access the font data (characters, +-- descriptions, properties, parameters, etc) of this main table. + +utf = utf or unicode.utf8 + +if not generic_context then + + generic_context = { } + +end + +if not generic_context.push_namespaces then + + function generic_context.push_namespaces() + texio.write(" ") + local normalglobal = { } + for k, v in next, _G do + normalglobal[k] = v + end + return normalglobal + end + + function generic_context.pop_namespaces(normalglobal,isolate) + if normalglobal then + texio.write(" ") + for k, v in next, _G do + if not normalglobal[k] then + generic_context[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(generic_context,_G) + else + texio.write(" ") + os.exit() + end + end + +end + +local whatever = generic_context.push_namespaces() + +-- We keep track of load time by storing the current time. That way we cannot be accused +-- of slowing down loading too much. Anyhow, there is no reason for this library to perform +-- slower in any other package as it does in context. +-- +-- Please don't update to this version without proper testing. It might be that this version +-- lags behind stock context and the only formal release takes place around tex live code +-- freeze. + +local starttime = os.gettimeofday() + +-- As we don't use the context file searching, we need to initialize the kpse library. As the +-- progname can be anything we will temporary switch to the context namespace if needed. Just +-- adding the context paths to the path specification is somewhat faster. +-- +-- Now, with lua 5.2 being used we might create a special ENV for this. + +-- kpse.set_program_name("luatex") + +local ctxkpse = nil +local verbose = true + +local function loadmodule(name,continue) + local foundname = kpse.find_file(name,"tex") or "" + if not foundname then + if not ctxkpse then + ctxkpse = kpse.new("luatex","context") + end + foundname = ctxkpse:find_file(name,"tex") or "" + end + if foundname == "" then + if not continue then + texio.write_nl(string.format(" ",name)) + os.exit() + end + else + if verbose then + texio.write(string.format(" <%s>",foundname)) -- no file.basename yet + end + dofile(foundname) + end +end + +loadmodule('luatex-fonts-merged.lua',true) -- you might comment this line + +if fonts then + + if not fonts._merge_loaded_message_done_ then + texio.write_nl("log", "!") + texio.write_nl("log", "! I am using the merged version of 'luatex-fonts.lua' here. If") + texio.write_nl("log", "! you run into problems or experience unexpected behaviour, and") + texio.write_nl("log", "! if you have ConTeXt installed you can try to delete the file") + texio.write_nl("log", "! 'luatex-font-merged.lua' as I might then use the possibly") + texio.write_nl("log", "! updated libraries. The merged version is not supported as it") + texio.write_nl("log", "! is a frozen instance. Problems can be reported to the ConTeXt") + texio.write_nl("log", "! mailing list.") + texio.write_nl("log", "!") + end + + fonts._merge_loaded_message_done_ = true + +else + + -- The following helpers are a bit overkill but I don't want to mess up context code for the + -- sake of general generality. Around version 1.0 there will be an official api defined. + -- + -- So, I will strip these libraries and see what is really needed so that we don't have this + -- overhead in the generic modules. The next section is only there for the packager, so stick + -- to using luatex-fonts with luatex-fonts-merged.lua and forget about the rest. The following + -- list might change without prior notice (for instance because we shuffled code around). + + loadmodule("l-lua.lua") + loadmodule("l-lpeg.lua") + loadmodule("l-function.lua") + loadmodule("l-string.lua") + loadmodule("l-table.lua") + loadmodule("l-io.lua") + ----------("l-number.lua") + ----------("l-set.lua") + ----------("l-os.lua") + loadmodule("l-file.lua") + ----------("l-md5.lua") + ----------("l-url.lua") + ----------("l-dir.lua") + loadmodule("l-boolean.lua") + ----------("l-unicode.lua") + loadmodule("l-math.lua") + loadmodule("util-str.lua") + + + -- The following modules contain code that is either not used at all outside context or will fail + -- when enabled due to lack of other modules. + + -- First we load a few helper modules. This is about the miminum needed to let the font modules do + -- their work. Don't depend on their functions as we might strip them in future versions of his + -- generic variant. + + loadmodule('luatex-basics-gen.lua') + loadmodule('data-con.lua') + + -- We do need some basic node support. The code in there is not for general use as it might change. + + loadmodule('luatex-basics-nod.lua') + + -- Now come the font modules that deal with traditional tex fonts as well as open type fonts. We only + -- support OpenType fonts here. + -- + -- The font database file (if used at all) must be put someplace visible for kpse and is not shared + -- with context. The mtx-fonts script can be used to genate this file (using the --names option). + + -- in 2013/14 we will merge/move some generic files into luatex-fonts-* files (copies) so that + -- intermediate updates of context not interfere + + loadmodule('font-ini.lua') + loadmodule('font-con.lua') + loadmodule('luatex-fonts-enc.lua') -- will load font-age on demand + loadmodule('font-cid.lua') + loadmodule('font-map.lua') -- for loading lum file (will be stripped) + loadmodule('luatex-fonts-syn.lua') -- deals with font names (synonyms) + loadmodule('luatex-fonts-tfm.lua') + loadmodule('font-oti.lua') + loadmodule('font-otf.lua') + loadmodule('font-otb.lua') + loadmodule('node-inj.lua') -- will be replaced (luatex >= .70) + loadmodule('font-ota.lua') + loadmodule('font-otn.lua') + ----------('luatex-fonts-chr.lua') + loadmodule('luatex-fonts-lua.lua') + loadmodule('font-def.lua') + loadmodule('luatex-fonts-def.lua') + loadmodule('luatex-fonts-ext.lua') -- some extensions + + -- We need to plug into a callback and the following module implements the handlers. Actual plugging + -- in happens later. + + loadmodule('luatex-fonts-cbk.lua') + +end + +resolvers.loadmodule = loadmodule + +-- In order to deal with the fonts we need to initialize some callbacks. One can overload them later on if +-- needed. First a bit of abstraction. + +generic_context.callback_ligaturing = false +generic_context.callback_kerning = false +generic_context.callback_pre_linebreak_filter = nodes.simple_font_handler +generic_context.callback_hpack_filter = nodes.simple_font_handler +generic_context.callback_define_font = fonts.definers.read + +-- The next ones can be done at a different moment if needed. You can create a generic_context namespace +-- and set no_callbacks_yet to true, load this module, and enable the callbacks later. So, there is really +-- *no* need to create a alternative for luatex-fonts.lua and luatex-fonts-merged.lua: just load this one +-- and overload if needed. + +if not generic_context.no_callbacks_yet then + + callback.register('ligaturing', generic_context.callback_ligaturing) + callback.register('kerning', generic_context.callback_kerning) + callback.register('pre_linebreak_filter', generic_context.callback_pre_linebreak_filter) + callback.register('hpack_filter', generic_context.callback_hpack_filter) + callback.register('define_font' , generic_context.callback_define_font) + +end + +-- We're done. + +texio.write(string.format(" ", os.gettimeofday()-starttime)) + +generic_context.pop_namespaces(whatever) -- cgit v1.2.3 From f936db4a7f589afec3586c8e96c69afe3d852bba Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 13:40:20 +0200 Subject: otfl-fonts.lua: s/luatex-fonts-merged/otfl-fonts-merged/ --- otfl-fonts.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/otfl-fonts.lua b/otfl-fonts.lua index fc7b789..c666b6a 100644 --- a/otfl-fonts.lua +++ b/otfl-fonts.lua @@ -104,7 +104,8 @@ local function loadmodule(name,continue) end end -loadmodule('luatex-fonts-merged.lua',true) -- you might comment this line +--loadmodule('luatex-fonts-merged.lua',true) -- you might comment this line +loadmodule('otfl-fonts-merged.lua',true) if fonts then -- cgit v1.2.3 From 07b9f189d66ce826a760e158f539ea6e37a52a58 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 13:41:28 +0200 Subject: preliminarily import l-dir -> otfl-lib-dir.lua to please otfl-font-nms --- otfl-lib-dir.lua | 449 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 449 insertions(+) create mode 100644 otfl-lib-dir.lua diff --git a/otfl-lib-dir.lua b/otfl-lib-dir.lua new file mode 100644 index 0000000..00cda38 --- /dev/null +++ b/otfl-lib-dir.lua @@ -0,0 +1,449 @@ +if not modules then modules = { } end modules ['l-dir'] = { + 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" +} + +-- dir.expandname will be merged with cleanpath and collapsepath + +local type, select = type, select +local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub +local concat, insert, remove = table.concat, table.insert, table.remove +local lpegmatch = lpeg.match + +local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V + +dir = dir or { } +local dir = dir +local lfs = lfs + +local attributes = lfs.attributes +local walkdir = lfs.dir +local isdir = lfs.isdir +local isfile = lfs.isfile +local currentdir = lfs.currentdir +local chdir = lfs.chdir + +-- in case we load outside luatex + +if not isdir then + function isdir(name) + local a = attributes(name) + return a and a.mode == "directory" + end + lfs.isdir = isdir +end + +if not isfile then + function isfile(name) + local a = attributes(name) + return a and a.mode == "file" + end + lfs.isfile = isfile +end + +-- handy + +function dir.current() + return (gsub(currentdir(),"\\","/")) +end + +-- optimizing for no find (*) does not save time + +--~ local function globpattern(path,patt,recurse,action) -- fails in recent luatex due to some change in lfs +--~ local ok, scanner +--~ if path == "/" then +--~ ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe +--~ else +--~ ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe +--~ end +--~ if ok and type(scanner) == "function" then +--~ if not find(path,"/$") then path = path .. '/' end +--~ for name in scanner do +--~ local full = path .. name +--~ local mode = attributes(full,'mode') +--~ if mode == 'file' then +--~ if find(full,patt) then +--~ action(full) +--~ end +--~ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then +--~ globpattern(full,patt,recurse,action) +--~ end +--~ end +--~ end +--~ end + +local lfsisdir = isdir + +local function isdir(path) + path = gsub(path,"[/\\]+$","") + return lfsisdir(path) +end + +lfs.isdir = isdir + +local function globpattern(path,patt,recurse,action) + if path == "/" then + path = path .. "." + elseif not find(path,"/$") then + path = path .. '/' + end + if isdir(path) then -- lfs.isdir does not like trailing / + for name in walkdir(path) do -- lfs.dir accepts trailing / + local full = path .. name + local mode = attributes(full,'mode') + if mode == 'file' then + if find(full,patt) then + action(full) + end + elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then + globpattern(full,patt,recurse,action) + end + end + end +end + +dir.globpattern = globpattern + +local function collectpattern(path,patt,recurse,result) + local ok, scanner + result = result or { } + if path == "/" then + ok, scanner, first = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe + else + ok, scanner, first = xpcall(function() return walkdir(path) end, function() end) -- kepler safe + end + if ok and type(scanner) == "function" then + if not find(path,"/$") then path = path .. '/' end + for name in scanner, first do + local full = path .. name + local attr = attributes(full) + local mode = attr.mode + if mode == 'file' then + if find(full,patt) then + result[name] = attr + end + elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then + attr.list = collectpattern(full,patt,recurse) + result[name] = attr + end + end + end + return result +end + +dir.collectpattern = collectpattern + +local pattern = Ct { + [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3), + [2] = C(((1-S("*?/"))^0 * P("/"))^0), + [3] = C(P(1)^0) +} + +local filter = Cs ( ( + P("**") / ".*" + + P("*") / "[^/]*" + + P("?") / "[^/]" + + P(".") / "%%." + + P("+") / "%%+" + + P("-") / "%%-" + + P(1) +)^0 ) + +local function glob(str,t) + if type(t) == "function" then + if type(str) == "table" then + for s=1,#str do + glob(str[s],t) + end + elseif isfile(str) then + t(str) + else + local split = lpegmatch(pattern,str) -- we could use the file splitter + if split then + local root, path, base = split[1], split[2], split[3] + local recurse = find(base,"%*%*") + local start = root .. path + local result = lpegmatch(filter,start .. base) + globpattern(start,result,recurse,t) + end + end + else + if type(str) == "table" then + local t = t or { } + for s=1,#str do + glob(str[s],t) + end + return t + elseif isfile(str) then + if t then + t[#t+1] = str + return t + else + return { str } + end + else + local split = lpegmatch(pattern,str) -- we could use the file splitter + if split then + local t = t or { } + local action = action or function(name) t[#t+1] = name end + local root, path, base = split[1], split[2], split[3] + local recurse = find(base,"%*%*") + local start = root .. path + local result = lpegmatch(filter,start .. base) + globpattern(start,result,recurse,action) + return t + else + return { } + end + end + end +end + +dir.glob = glob + +--~ list = dir.glob("**/*.tif") +--~ list = dir.glob("/**/*.tif") +--~ list = dir.glob("./**/*.tif") +--~ list = dir.glob("oeps/**/*.tif") +--~ list = dir.glob("/oeps/**/*.tif") + +local function globfiles(path,recurse,func,files) -- func == pattern or function + if type(func) == "string" then + local s = func + func = function(name) return find(name,s) end + end + files = files or { } + local noffiles = #files + for name in walkdir(path) do + if find(name,"^%.") then + --- skip + else + local mode = attributes(name,'mode') + if mode == "directory" then + if recurse then + globfiles(path .. "/" .. name,recurse,func,files) + end + elseif mode == "file" then + if not func or func(name) then + noffiles = noffiles + 1 + files[noffiles] = path .. "/" .. name + end + end + end + end + return files +end + +dir.globfiles = globfiles + +-- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex") +-- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex") +-- t = dir.glob("c:/data/develop/context/texmf/**/*.tex") +-- t = dir.glob("f:/minimal/tex/**/*") +-- print(dir.ls("f:/minimal/tex/**/*")) +-- print(dir.ls("*.tex")) + +function dir.ls(pattern) + return concat(glob(pattern),"\n") +end + +--~ mkdirs("temp") +--~ mkdirs("a/b/c") +--~ mkdirs(".","/a/b/c") +--~ mkdirs("a","b","c") + +local make_indeed = true -- false + +local onwindows = os.type == "windows" or find(os.getenv("PATH"),";") + +if onwindows then + + function dir.mkdirs(...) + local str, pth = "", "" + for i=1,select("#",...) do + local s = select(i,...) + if s == "" then + -- skip + elseif str == "" then + str = s + else + str = str .. "/" .. s + end + end + local first, middle, last + local drive = false + first, middle, last = match(str,"^(//)(//*)(.*)$") + if first then + -- empty network path == local path + else + first, last = match(str,"^(//)/*(.-)$") + if first then + middle, last = match(str,"([^/]+)/+(.-)$") + if middle then + pth = "//" .. middle + else + pth = "//" .. last + last = "" + end + else + first, middle, last = match(str,"^([a-zA-Z]:)(/*)(.-)$") + if first then + pth, drive = first .. middle, true + else + middle, last = match(str,"^(/*)(.-)$") + if not middle then + last = str + end + end + end + end + for s in gmatch(last,"[^/]+") do + if pth == "" then + pth = s + elseif drive then + pth, drive = pth .. s, false + else + pth = pth .. "/" .. s + end + if make_indeed and not isdir(pth) then + lfs.mkdir(pth) + end + end + return pth, (isdir(pth) == true) + end + + --~ print(dir.mkdirs("","","a","c")) + --~ print(dir.mkdirs("a")) + --~ print(dir.mkdirs("a:")) + --~ print(dir.mkdirs("a:/b/c")) + --~ print(dir.mkdirs("a:b/c")) + --~ print(dir.mkdirs("a:/bbb/c")) + --~ print(dir.mkdirs("/a/b/c")) + --~ print(dir.mkdirs("/aaa/b/c")) + --~ print(dir.mkdirs("//a/b/c")) + --~ print(dir.mkdirs("///a/b/c")) + --~ print(dir.mkdirs("a/bbb//ccc/")) + +else + + function dir.mkdirs(...) + local str, pth = "", "" + for i=1,select("#",...) do + local s = select(i,...) + if s and s ~= "" then -- we catch nil and false + if str ~= "" then + str = str .. "/" .. s + else + str = s + end + end + end + str = gsub(str,"/+","/") + if find(str,"^/") then + pth = "/" + for s in gmatch(str,"[^/]+") do + local first = (pth == "/") + if first then + pth = pth .. s + else + pth = pth .. "/" .. s + end + if make_indeed and not first and not isdir(pth) then + lfs.mkdir(pth) + end + end + else + pth = "." + for s in gmatch(str,"[^/]+") do + pth = pth .. "/" .. s + if make_indeed and not isdir(pth) then + lfs.mkdir(pth) + end + end + end + return pth, (isdir(pth) == true) + end + + --~ print(dir.mkdirs("","","a","c")) + --~ print(dir.mkdirs("a")) + --~ print(dir.mkdirs("/a/b/c")) + --~ print(dir.mkdirs("/aaa/b/c")) + --~ print(dir.mkdirs("//a/b/c")) + --~ print(dir.mkdirs("///a/b/c")) + --~ print(dir.mkdirs("a/bbb//ccc/")) + +end + +dir.makedirs = dir.mkdirs + +-- we can only define it here as it uses dir.current + +if onwindows then + + function dir.expandname(str) -- will be merged with cleanpath and collapsepath + local first, nothing, last = match(str,"^(//)(//*)(.*)$") + if first then + first = dir.current() .. "/" -- dir.current sanitizes + end + if not first then + first, last = match(str,"^(//)/*(.*)$") + end + if not first then + first, last = match(str,"^([a-zA-Z]:)(.*)$") + if first and not find(last,"^/") then + local d = currentdir() + if chdir(first) then + first = dir.current() + end + chdir(d) + end + end + if not first then + first, last = dir.current(), str + end + last = gsub(last,"//","/") + last = gsub(last,"/%./","/") + last = gsub(last,"^/*","") + first = gsub(first,"/*$","") + if last == "" or last == "." then + return first + else + return first .. "/" .. last + end + end + +else + + function dir.expandname(str) -- will be merged with cleanpath and collapsepath + if not find(str,"^/") then + str = currentdir() .. "/" .. str + end + str = gsub(str,"//","/") + str = gsub(str,"/%./","/") + str = gsub(str,"(.)/%.$","%1") + return str + end + +end + +file.expandname = dir.expandname -- for convenience + +local stack = { } + +function dir.push(newdir) + insert(stack,currentdir()) + if newdir and newdir ~= "" then + chdir(newdir) + end +end + +function dir.pop() + local d = remove(stack) + if d then + chdir(d) + end + return d +end -- cgit v1.2.3 From 1e3f80d13d7bd10e4f3d62848b2df6a340f83fd4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 13:42:32 +0200 Subject: preliminarily add luaotfload.lua, luaotfload-deferred.lua (move to dtx later) --- luaotfload.lua | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 luaotfload.lua diff --git a/luaotfload.lua b/luaotfload.lua new file mode 100644 index 0000000..f397633 --- /dev/null +++ b/luaotfload.lua @@ -0,0 +1,105 @@ +-- +-- This is file `luaotfload.lua', +-- generated with the docstrip utility. +-- +-- The original source files were: +-- +-- luaotfload.dtx (with options: `lua') +-- This is a generated file. +-- +-- Copyright (C) 2009-2010 by by Elie Roux +-- and Khaled Hosny +-- (Support: .) +-- +-- This work is under the CC0 license. +-- +-- This work consists of the main source file luaotfload.dtx +-- and the derived files +-- luaotfload.sty, luaotfload.lua +-- +module("luaotfload", package.seeall) + +luaotfload.module = { + name = "luaotfload", + version = 1.27, + date = "2012/05/28", + description = "OpenType layout system.", + author = "Elie Roux & Hans Hagen", + copyright = "Elie Roux", + license = "CC0" +} + +--- these will be overloaded later by luatexbase +local error = function(...) print("err", string.format(...)) end +local log = function(...) print("log", string.format(...)) end + +kpse.init_prog("", 600, "/") +local luatex_version = 60 + +if tex.luatexversion < luatex_version then + warning("LuaTeX v%.2f is old, v%.2f is recommended.", + tex.luatexversion/100, + luatex_version /100) +end +function luaotfload.loadmodule(name, prefix) + local prefix = prefix or "otfl" + local tofind = prefix .."-"..name + local found = kpse.find_file(tofind,"tex") + if found then + log("loading file %s.", found) + dofile(found) + else + --error("file %s not found.", tofind) + error("file %s not found.", tofind) + end +end + +--[[-- keep --]] +--- from Hans (all merged): + +--- file name modified include name +--- × basics-gen.lua t luat-basics-gen +--- × font-def -> fonts-def t luatex-font-def (there’s also the normal font-def!) +--- × fonts-enc f luatex-font-enc +--- × fonts-ext t luatex-fonts-ext +--- × fonts-lua f luatex-fonts-lua +--- fonts-tfm f luatex-fonts-tfm +--- × fonts-cbk f luatex-fonts-lua + +--- from luaotfload: +--- otfl-luat-ovr.lua -- override some luat-dum functions +--- otfl-font-clr.lua +--- otfl-font-ltx.lua +--- otfl-font-nms.lua +--- otfl-font-otc.lua +--- otfl-font-pfb.lua -- ? + +--[[-- new --]] +--- basics-nod (merged as fonts-nod !) +--- fonts-demo-vf-1.lua +--- fonts-syn (merged) + +--[[-- merged, to be dropped --]] +--- otfl-data-con.lua +--- otfl-font-cid.lua +--- otfl-font-con.lua +--- otfl-font-ini.lua +--- otfl-font-ota.lua +--- otfl-font-otb.lua +--- otfl-font-otf.lua +--- otfl-font-oti.lua +--- otfl-font-otn.lua + +--[[-- + it all boils down to this: we load otfl-fonts.lua + which takes care loading the merged file. + that’s it, go thank Hans! +--]]-- + +--luaotfload.loadmodule("fonts.lua", "luatex") +luaotfload.loadmodule("fonts.lua") + +--- now load luatexbase (from the TEX end) +--- then continue in luaotfload-deferred.lua + +-- End of File `luaotfload.lua'. -- cgit v1.2.3 From c3f5cbf28f671d888a561aa1dfce1aaa629de12f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 13:45:13 +0200 Subject: truly add deferred --- luaotfload-deferred.lua | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 luaotfload-deferred.lua diff --git a/luaotfload-deferred.lua b/luaotfload-deferred.lua new file mode 100644 index 0000000..2a5880b --- /dev/null +++ b/luaotfload-deferred.lua @@ -0,0 +1,116 @@ +--- TODO integrate into luaotfload.dtx +--- this part is loaded after luatexbase +luaotfload.loadmodule("lib-dir.lua") -- required by font-nms; will change with lualibs update +luaotfload.loadmodule("font-nms.lua") +luaotfload.loadmodule("font-clr.lua") + +luatexbase.create_callback("luaotfload.patch_font", "simple", function() end) + +local function def_font(...) + local fontdata = fonts.define.read(...) + if type(fontdata) == "table" and fontdata.shared then + 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 + luatexbase.call_callback("luaotfload.patch_font", fontdata) + end + return fontdata +end +fonts.mode = "node" + +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 + +--luaotfload.loadmodule("font-otc.lua") -- broken + +--luatexbase.create_callback("luaotfload.patch_font", "simple", function() end) + +--local function def_font(...) + --local fontdata = fonts.define.read(...) + --if type(fontdata) == "table" and fontdata.shared then + --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 + --luatexbase.call_callback("luaotfload.patch_font", fontdata) + --end + --return fontdata +--end +--fonts.mode = "node" + +--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 +--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") +--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") +-- +-- End of File `luaotfload.lua'. -- cgit v1.2.3 From f27aa22b46f1c43d05cfbb5991b5ed3e8e923f63 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 13:46:20 +0200 Subject: temporarily add luatotfload.sty --- luaotfload.sty | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 luaotfload.sty diff --git a/luaotfload.sty b/luaotfload.sty new file mode 100644 index 0000000..a7b3b52 --- /dev/null +++ b/luaotfload.sty @@ -0,0 +1,37 @@ +%% +%% This is file `luaotfload.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% luaotfload.dtx (with options: `package') +%% This is a generated file. +%% +%% Copyright (C) 2009-2011 by by Elie Roux +%% and Khaled Hosny +%% (Support: .) +%% +%% This work is under the CC0 license. +%% +%% This work consists of the main source file luaotfload.dtx +%% and the derived files +%% luaotfload.sty, luaotfload.lua +%% +\csname ifluaotfloadloaded\endcsname +\let\ifluaotfloadloaded\endinput +\bgroup\expandafter\expandafter\expandafter\egroup +%\RequireLuaModule{lualibs} +%\RequireLuaModule{luaotfload} +\directlua{require"luaotfload"} +\expandafter\ifx\csname ProvidesPackage\endcsname\relax + \input luatexbase.sty +\else + \NeedsTeXFormat{LaTeX2e} + \ProvidesPackage{luaotfload}% + [2011/10/06 v2.0 OpenType layout system] + \RequirePackage{luatexbase} +\fi +\directlua{require"luaotfload-deferred"} +\endinput +%% +%% End of file `luaotfload.sty'. -- cgit v1.2.3 From 24b1bdad120fcb18d0aebd66a52f3b2f19677e42 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 14:53:07 +0200 Subject: fix function names --- otfl-font-nms.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 094ebec..3934919 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -30,13 +30,14 @@ else end end -local splitpath, expandpath = file.split_path, kpse.expand_path -local glob, basename = dir.glob, file.basename -local extname = file.extname -local upper, lower, format = string.upper, string.lower, string.format -local gsub, match, rpadd = string.gsub, string.match, string.rpadd -local gmatch, sub, find = string.gmatch, string.sub, string.find -local utfgsub = unicode.utf8.gsub +local splitpath, collapsepath = file.splitpath, file.collapsepath +local expandpath = kpse.expand_path +local glob, basename = dir.glob, file.basename +local extname = file.extname +local upper, lower, format = string.upper, string.lower, string.format +local gsub, match, rpadd = string.gsub, string.match, string.rpadd +local gmatch, sub, find = string.gmatch, string.sub, string.find +local utfgsub = unicode.utf8.gsub local trace_short = false --tracing adapted to rebuilding of the database inside a document local trace_search = false --trackers.register("names.search", function(v) trace_search = v end) @@ -430,7 +431,7 @@ local function path_normalize(path) end end end - path = file.collapse_path(path) + path = collapsepath(path) return path end -- cgit v1.2.3 From e204300571bb3c539cacfd5e142ebd0f66c7309a Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Tue, 9 Apr 2013 15:31:48 +0200 Subject: Fixing issue #52 --- mkluatexfontdb.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 38b9daa..3854840 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -44,7 +44,7 @@ Valid options: -V --version print version and exit -h --help print this message -The output database file is named otfl-fonts.lua and is placed under: +The output database file is named otfl-names(.lua and .luc) and is placed under: %s" ]], name, names.path.dir)) -- cgit v1.2.3 From d9475ae1ca5d90213f195ded7c55457a3fe9ca05 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Tue, 9 Apr 2013 15:33:12 +0200 Subject: Fixing issue #49 --- mkluatexfontdb.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 3854840..2a25b29 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -46,7 +46,7 @@ Valid options: The output database file is named otfl-names(.lua and .luc) and is placed under: - %s" + %s ]], name, names.path.dir)) end -- cgit v1.2.3 From e67643a60422ed265dc5ad8955a83140598385f1 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Tue, 9 Apr 2013 16:56:50 +0200 Subject: Adapting to Lua 5.2 --- otfl-basics-gen.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/otfl-basics-gen.lua b/otfl-basics-gen.lua index bfd81fd..9a328d0 100644 --- a/otfl-basics-gen.lua +++ b/otfl-basics-gen.lua @@ -254,7 +254,8 @@ function caches.compile(data,luaname,lucname) if d and d ~= "" then local f = io.open(lucname,'w') if f then - local s = loadstring(d) + local s + if _G["loadstring"] then s=loadstring(d) else s=load(d) end f:write(string.dump(s)) f:close() end -- cgit v1.2.3 From b4e51c838713797b8a1fbaffc6f104d7fc55bf40 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 9 Apr 2013 23:53:03 +0200 Subject: new font loader initialization code (thanks Hans) --- luaotfload.lua | 9 ++- otfl-fonts.lua | 211 ++++++++++++++++++++++++++++++++------------------------- 2 files changed, 124 insertions(+), 96 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index f397633..a73847c 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -92,11 +92,16 @@ end --[[-- it all boils down to this: we load otfl-fonts.lua - which takes care loading the merged file. + which takes care of loading the merged file. that’s it, go thank Hans! --]]-- ---luaotfload.loadmodule("fonts.lua", "luatex") +_G.non_generic_context = { luatex_fonts = { + load_before = "otfl-fonts-merged.lua", + -- load_after = nil, --- TODO, this is meant for callbacks + skip_loading = true, +}} + luaotfload.loadmodule("fonts.lua") --- now load luatexbase (from the TEX end) diff --git a/otfl-fonts.lua b/otfl-fonts.lua index c666b6a..75bd079 100644 --- a/otfl-fonts.lua +++ b/otfl-fonts.lua @@ -19,10 +19,22 @@ if not modules then modules = { } end modules ['luatex-fonts'] = { utf = utf or unicode.utf8 -if not generic_context then +-- We have some (global) hooks (for latex): - generic_context = { } +if not non_generic_context then + non_generic_context = { } +end + +if not non_generic_context.luatex_fonts then + non_generic_context.luatex_fonts = { + -- load_before = nil, + -- load_after = nil, + -- skip_loading = nil, + } +end +if not generic_context then + generic_context = { } end if not generic_context.push_namespaces then @@ -104,101 +116,112 @@ local function loadmodule(name,continue) end end ---loadmodule('luatex-fonts-merged.lua',true) -- you might comment this line -loadmodule('otfl-fonts-merged.lua',true) - -if fonts then - - if not fonts._merge_loaded_message_done_ then - texio.write_nl("log", "!") - texio.write_nl("log", "! I am using the merged version of 'luatex-fonts.lua' here. If") - texio.write_nl("log", "! you run into problems or experience unexpected behaviour, and") - texio.write_nl("log", "! if you have ConTeXt installed you can try to delete the file") - texio.write_nl("log", "! 'luatex-font-merged.lua' as I might then use the possibly") - texio.write_nl("log", "! updated libraries. The merged version is not supported as it") - texio.write_nl("log", "! is a frozen instance. Problems can be reported to the ConTeXt") - texio.write_nl("log", "! mailing list.") - texio.write_nl("log", "!") +if non_generic_context.luatex_fonts.load_before then + loadmodule(non_generic_context.luatex_fonts.load_before,true) +end + +if non_generic_context.luatex_fonts.skip_loading ~= true then + + loadmodule('luatex-fonts-merged.lua',true) + + if fonts then + + if not fonts._merge_loaded_message_done_ then + texio.write_nl("log", "!") + texio.write_nl("log", "! I am using the merged version of 'luatex-fonts.lua' here. If") + texio.write_nl("log", "! you run into problems or experience unexpected behaviour, and") + texio.write_nl("log", "! if you have ConTeXt installed you can try to delete the file") + texio.write_nl("log", "! 'luatex-font-merged.lua' as I might then use the possibly") + texio.write_nl("log", "! updated libraries. The merged version is not supported as it") + texio.write_nl("log", "! is a frozen instance. Problems can be reported to the ConTeXt") + texio.write_nl("log", "! mailing list.") + texio.write_nl("log", "!") + end + + fonts._merge_loaded_message_done_ = true + + else + + -- The following helpers are a bit overkill but I don't want to mess up context code for the + -- sake of general generality. Around version 1.0 there will be an official api defined. + -- + -- So, I will strip these libraries and see what is really needed so that we don't have this + -- overhead in the generic modules. The next section is only there for the packager, so stick + -- to using luatex-fonts with luatex-fonts-merged.lua and forget about the rest. The following + -- list might change without prior notice (for instance because we shuffled code around). + + loadmodule("l-lua.lua") + loadmodule("l-lpeg.lua") + loadmodule("l-function.lua") + loadmodule("l-string.lua") + loadmodule("l-table.lua") + loadmodule("l-io.lua") + ----------("l-number.lua") + ----------("l-set.lua") + ----------("l-os.lua") + loadmodule("l-file.lua") + ----------("l-md5.lua") + ----------("l-url.lua") + ----------("l-dir.lua") + loadmodule("l-boolean.lua") + ----------("l-unicode.lua") + loadmodule("l-math.lua") + loadmodule("util-str.lua") + + + -- The following modules contain code that is either not used at all outside context or will fail + -- when enabled due to lack of other modules. + + -- First we load a few helper modules. This is about the miminum needed to let the font modules do + -- their work. Don't depend on their functions as we might strip them in future versions of his + -- generic variant. + + loadmodule('luatex-basics-gen.lua') + loadmodule('data-con.lua') + + -- We do need some basic node support. The code in there is not for general use as it might change. + + loadmodule('luatex-basics-nod.lua') + + -- Now come the font modules that deal with traditional tex fonts as well as open type fonts. We only + -- support OpenType fonts here. + -- + -- The font database file (if used at all) must be put someplace visible for kpse and is not shared + -- with context. The mtx-fonts script can be used to genate this file (using the --names option). + + -- in 2013/14 we will merge/move some generic files into luatex-fonts-* files (copies) so that + -- intermediate updates of context not interfere + + loadmodule('font-ini.lua') + loadmodule('font-con.lua') + loadmodule('luatex-fonts-enc.lua') -- will load font-age on demand + loadmodule('font-cid.lua') + loadmodule('font-map.lua') -- for loading lum file (will be stripped) + loadmodule('luatex-fonts-syn.lua') -- deals with font names (synonyms) + loadmodule('luatex-fonts-tfm.lua') + loadmodule('font-oti.lua') + loadmodule('font-otf.lua') + loadmodule('font-otb.lua') + loadmodule('node-inj.lua') -- will be replaced (luatex >= .70) + loadmodule('font-ota.lua') + loadmodule('font-otn.lua') + ----------('luatex-fonts-chr.lua') + loadmodule('luatex-fonts-lua.lua') + loadmodule('font-def.lua') + loadmodule('luatex-fonts-def.lua') + loadmodule('luatex-fonts-ext.lua') -- some extensions + + -- We need to plug into a callback and the following module implements the handlers. Actual plugging + -- in happens later. + + loadmodule('luatex-fonts-cbk.lua') + end - fonts._merge_loaded_message_done_ = true - -else - - -- The following helpers are a bit overkill but I don't want to mess up context code for the - -- sake of general generality. Around version 1.0 there will be an official api defined. - -- - -- So, I will strip these libraries and see what is really needed so that we don't have this - -- overhead in the generic modules. The next section is only there for the packager, so stick - -- to using luatex-fonts with luatex-fonts-merged.lua and forget about the rest. The following - -- list might change without prior notice (for instance because we shuffled code around). - - loadmodule("l-lua.lua") - loadmodule("l-lpeg.lua") - loadmodule("l-function.lua") - loadmodule("l-string.lua") - loadmodule("l-table.lua") - loadmodule("l-io.lua") - ----------("l-number.lua") - ----------("l-set.lua") - ----------("l-os.lua") - loadmodule("l-file.lua") - ----------("l-md5.lua") - ----------("l-url.lua") - ----------("l-dir.lua") - loadmodule("l-boolean.lua") - ----------("l-unicode.lua") - loadmodule("l-math.lua") - loadmodule("util-str.lua") - - - -- The following modules contain code that is either not used at all outside context or will fail - -- when enabled due to lack of other modules. - - -- First we load a few helper modules. This is about the miminum needed to let the font modules do - -- their work. Don't depend on their functions as we might strip them in future versions of his - -- generic variant. - - loadmodule('luatex-basics-gen.lua') - loadmodule('data-con.lua') - - -- We do need some basic node support. The code in there is not for general use as it might change. - - loadmodule('luatex-basics-nod.lua') - - -- Now come the font modules that deal with traditional tex fonts as well as open type fonts. We only - -- support OpenType fonts here. - -- - -- The font database file (if used at all) must be put someplace visible for kpse and is not shared - -- with context. The mtx-fonts script can be used to genate this file (using the --names option). - - -- in 2013/14 we will merge/move some generic files into luatex-fonts-* files (copies) so that - -- intermediate updates of context not interfere - - loadmodule('font-ini.lua') - loadmodule('font-con.lua') - loadmodule('luatex-fonts-enc.lua') -- will load font-age on demand - loadmodule('font-cid.lua') - loadmodule('font-map.lua') -- for loading lum file (will be stripped) - loadmodule('luatex-fonts-syn.lua') -- deals with font names (synonyms) - loadmodule('luatex-fonts-tfm.lua') - loadmodule('font-oti.lua') - loadmodule('font-otf.lua') - loadmodule('font-otb.lua') - loadmodule('node-inj.lua') -- will be replaced (luatex >= .70) - loadmodule('font-ota.lua') - loadmodule('font-otn.lua') - ----------('luatex-fonts-chr.lua') - loadmodule('luatex-fonts-lua.lua') - loadmodule('font-def.lua') - loadmodule('luatex-fonts-def.lua') - loadmodule('luatex-fonts-ext.lua') -- some extensions - - -- We need to plug into a callback and the following module implements the handlers. Actual plugging - -- in happens later. - - loadmodule('luatex-fonts-cbk.lua') +end +if non_generic_context.luatex_fonts.load_after then + loadmodule(non_generic_context.luatex_fonts.load_after,true) end resolvers.loadmodule = loadmodule -- cgit v1.2.3 From 422263516bb273977c8dbc9a038868d6a0f8a6de Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 10:41:12 +0200 Subject: description of loading procedure --- luaotfload-deferred.lua | 14 ++++++++++---- luaotfload.lua | 51 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/luaotfload-deferred.lua b/luaotfload-deferred.lua index 2a5880b..a3026c9 100644 --- a/luaotfload-deferred.lua +++ b/luaotfload-deferred.lua @@ -1,8 +1,12 @@ --- TODO integrate into luaotfload.dtx --- this part is loaded after luatexbase -luaotfload.loadmodule("lib-dir.lua") -- required by font-nms; will change with lualibs update -luaotfload.loadmodule("font-nms.lua") -luaotfload.loadmodule("font-clr.lua") +local loadmodule = luaotfload.loadmodule + +loadmodule("lib-dir.lua") -- required by font-nms; will change with lualibs update +loadmodule("font-nms.lua") +loadmodule("font-clr.lua") +--loadmodule("font-ovr.lua") +loadmodule("font-ltx.lua") luatexbase.create_callback("luaotfload.patch_font", "simple", function() end) @@ -112,5 +116,7 @@ end --end --luatexbase.add_to_callback("luaotfload.patch_font", set_sscale_diments, "unicodemath.set_sscale_diments") --- + +-- vim:tw=71:sw=2:ts=2:expandtab + -- End of File `luaotfload.lua'. diff --git a/luaotfload.lua b/luaotfload.lua index a73847c..5550c79 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -29,6 +29,8 @@ luaotfload.module = { license = "CC0" } +local fl_prefix = "otfl" -- “luatex” for luatex-plain + --- these will be overloaded later by luatexbase local error = function(...) print("err", string.format(...)) end local log = function(...) print("log", string.format(...)) end @@ -41,9 +43,8 @@ if tex.luatexversion < luatex_version then tex.luatexversion/100, luatex_version /100) end -function luaotfload.loadmodule(name, prefix) - local prefix = prefix or "otfl" - local tofind = prefix .."-"..name +local loadmodule = function (name) + local tofind = fl_prefix .."-"..name local found = kpse.find_file(tofind,"tex") if found then log("loading file %s.", found) @@ -53,6 +54,7 @@ function luaotfload.loadmodule(name, prefix) error("file %s not found.", tofind) end end +luaotfload.loadmodule = loadmodule --- required in deferred code --[[-- keep --]] --- from Hans (all merged): @@ -96,15 +98,56 @@ end that’s it, go thank Hans! --]]-- +--[[doc +We treat the fontloader as a black box so behavior is consistent +between formats. +The wrapper file is |otfl-fonts.lua| which we imported from +\LUATEX-Plain. +It has roughly two purposes: +(\textit{1}) insert the functionality required for fontloader, and +(\textit{2}) put it in place via the respective callbacks. +How the first step is executed depends on the presence on the +\emph{merged font loader code}. +In \textsf{luaotfload} this is contained in the file +|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{tabular}{ll} + \texttt{generic\textunderscore context} & + encapsulation mechanism, callback functions + \\ + \texttt{non\textunderscore generic\textunderscore context} & + customized code insertion + \\ +\end{tabular} +With \verb|non_generic_context| we can tailor the font loader insertion +to our file naming habits (key \verb|load_before|). +Additionally, \verb|skip_loading| can be unset to force loading of +the original libraries as though the merged code was absent. +Another key, \verb|load_after| is called at the time when the font +loader is actually inserted. +In combination with the option \verb|no_callbacks_yet| in +\verb|generic_context|, we can insert our own, +\textsf{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 + +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, }} -luaotfload.loadmodule("fonts.lua") +loadmodule("fonts.lua") --- now load luatexbase (from the TEX end) --- then continue in luaotfload-deferred.lua +-- vim:tw=71:sw=2:ts=2:expandtab + -- End of File `luaotfload.lua'. -- cgit v1.2.3 From 82141b63af85b266f0304db468dc60f3d18a4793 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 12:01:43 +0200 Subject: make font definition callback wrapper work with current tfm object structure --- luaotfload-deferred.lua | 147 ++++++++++++++++++++++++++++-------------------- luaotfload.lua | 7 ++- 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/luaotfload-deferred.lua b/luaotfload-deferred.lua index a3026c9..bfbf644 100644 --- a/luaotfload-deferred.lua +++ b/luaotfload-deferred.lua @@ -1,6 +1,33 @@ --- TODO integrate into luaotfload.dtx + +local type, next = type, next + +local stringfind = string.find --- this part is loaded after luatexbase -local loadmodule = luaotfload.loadmodule +local loadmodule = luaotfload.loadmodule +local luatexbase = luatexbase +local generic_context = generic_context --- from fontloader + +local add_to_callback, create_callback = + luatexbase.add_to_callback, luatexbase.create_callback +local reset_callback, call_callback = + luatexbase.reset_callback, luatexbase.call_callback + +--[[doc-- +We do our own callback handling with the means provided by luatexbase. + +Note: \verb|pre_linebreak_filter| and \verb|hpack_filter| are coupled +in \CONTEXT\ in the concept of \emph{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) loadmodule("lib-dir.lua") -- required by font-nms; will change with lualibs update loadmodule("font-nms.lua") @@ -8,31 +35,55 @@ loadmodule("font-clr.lua") --loadmodule("font-ovr.lua") loadmodule("font-ltx.lua") -luatexbase.create_callback("luaotfload.patch_font", "simple", function() end) +local dummy_function = function ( ) end --- upvalue more efficient than lambda +create_callback("luaotfload.patch_font", "simple", dummy_function) -local function def_font(...) - local fontdata = fonts.define.read(...) - if type(fontdata) == "table" and fontdata.shared then - 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 +--[[doc-- +This is a wrapper for the imported font loader. +As of 2013, everything it does appears to be redundand, so we won’t use +it. +Nevertheless, it has been adapted to work with the current structure of +font data objects and will stay here for reference / until somebody +reports breakage. + +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 - mc[k] = v + print(k,v) + mathconstants[k] = v else - mc[k] = v / fontdata.units * fontdata.size + mathconstants[k] = v / units_per_em * size end end - -- for \overwithdelims - mc.FractionDelimiterSize = 1.01 * fontdata.size - mc.FractionDelimiterDisplayStyleSize = 2.39 * fontdata.size - - fontdata.MathConstants = mc + --- 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 - luatexbase.call_callback("luaotfload.patch_font", fontdata) + call_callback("luaotfload.patch_font", tfmdata) end - return fontdata + return tfmdata end fonts.mode = "node" @@ -45,35 +96,21 @@ function attributes.private(name) return number end ---luaotfload.loadmodule("font-otc.lua") -- broken +reset_callback("define_font") ---luatexbase.create_callback("luaotfload.patch_font", "simple", function() end) - ---local function def_font(...) - --local fontdata = fonts.define.read(...) - --if type(fontdata) == "table" and fontdata.shared then - --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 - --luatexbase.call_callback("luaotfload.patch_font", fontdata) - --end - --return fontdata ---end ---fonts.mode = "node" +if luaotfload.old_font_definer then + add_to_callback("define_font", + old_define_font_wrapper, + "luaotfload.define_font", + 1) +else -- use default font loader + add_to_callback("define_font", + generic_context.callback_define_font, + "luaotfload.define_font", + 1) +end + +--luaotfload.loadmodule("font-otc.lua") -- broken --local register_base_sub = fonts.otf.features.register_base_substitution --local gsubs = { @@ -86,19 +123,9 @@ end --for _,v in next, gsubs do --register_base_sub(v) --end ---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") +--add_to_callback("find_vf_file", + --fonts.vf.find, + --"luaotfload.find_vf_file") --local function set_sscale_diments(fontdata) --local mc = fontdata.MathConstants --if mc then diff --git a/luaotfload.lua b/luaotfload.lua index 5550c79..1bfca79 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -29,9 +29,11 @@ luaotfload.module = { license = "CC0" } +luaotfload.old_font_definer = false --- toggle wrapper for font loader + local fl_prefix = "otfl" -- “luatex” for luatex-plain ---- these will be overloaded later by luatexbase +--- these will be provided by luatexbase some time local error = function(...) print("err", string.format(...)) end local log = function(...) print("log", string.format(...)) end @@ -98,7 +100,7 @@ luaotfload.loadmodule = loadmodule --- required in deferred code that’s it, go thank Hans! --]]-- ---[[doc +--[[doc-- We treat the fontloader as a black box so behavior is consistent between formats. The wrapper file is |otfl-fonts.lua| which we imported from @@ -145,7 +147,6 @@ _G.non_generic_context = { luatex_fonts = { loadmodule("fonts.lua") ---- now load luatexbase (from the TEX end) --- then continue in luaotfload-deferred.lua -- vim:tw=71:sw=2:ts=2:expandtab -- cgit v1.2.3 From 223c4fbc3201ce3bd6e5e7ab4ba666e350d97f35 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 12:28:36 +0200 Subject: make font patching work again with font definition wrapper --- luaotfload-deferred.lua | 58 ++++++++++++++++++++++++++++++++++--------------- luaotfload.lua | 9 +++++++- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/luaotfload-deferred.lua b/luaotfload-deferred.lua index bfbf644..93e8bb2 100644 --- a/luaotfload-deferred.lua +++ b/luaotfload-deferred.lua @@ -85,6 +85,19 @@ local define_font_wrapper = function (...) end return tfmdata end + +--[[doc-- +We provide a simplified version of the original font definition +callback. +--doc]]-- +local patch_defined_font = function (...) + local tfmdata = fonts.definers.read(...) + if type(tfmdata) == "table" and tfmdata.shared then + call_callback("luaotfload.patch_font", tfmdata) + end + return tfmdata +end + fonts.mode = "node" function attributes.private(name) @@ -98,16 +111,21 @@ end reset_callback("define_font") -if luaotfload.old_font_definer then +if luaotfload.font_definer == "old" then add_to_callback("define_font", old_define_font_wrapper, "luaotfload.define_font", 1) -else -- use default font loader +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 --luaotfload.loadmodule("font-otc.lua") -- broken @@ -126,23 +144,27 @@ end --add_to_callback("find_vf_file", --fonts.vf.find, --"luaotfload.find_vf_file") ---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") +local set_sscale_diments = function (tfmdata) + local mathconstants = tfmdata.MathConstants + if mathconstants then + local tfmparameters = tfmdata.parameters + if mathconstants.ScriptPercentScaleDown then + tfmparameters[10] = mathconstants.ScriptPercentScaleDown + else -- resort to plain TeX default + tfmparameters[10] = 70 + end + if mathconstants.ScriptScriptPercentScaleDown then + tfmparameters[11] = mathconstants.ScriptScriptPercentScaleDown + else -- resort to plain TeX default + tfmparameters[11] = 50 + end + end +end + +add_to_callback("luaotfload.patch_font", + set_sscale_diments, + "unicodemath.set_sscale_diments") -- vim:tw=71:sw=2:ts=2:expandtab diff --git a/luaotfload.lua b/luaotfload.lua index 1bfca79..0a3b398 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -29,7 +29,14 @@ luaotfload.module = { license = "CC0" } -luaotfload.old_font_definer = false --- toggle wrapper for font loader +--[[doc-- +No final decision has been made on how to handle font definition. +At the moment, there are three candidates: The \textsf{generic} +callback as hard-coded in the font loader, the \textsf{old} wrapper, +and a simplified version of the latter (\textsf{patch}) that does +nothing besides applying font patches. +--doc]]-- +luaotfload.font_definer = "patch" --- | “generic” | “old” local fl_prefix = "otfl" -- “luatex” for luatex-plain -- cgit v1.2.3 From 4645b2624f853cd795140d18964e3e1eb73ea55f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 12:55:28 +0200 Subject: update font-otc --- otfl-font-otc.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/otfl-font-otc.lua b/otfl-font-otc.lua index ae463e7..a87dcad 100644 --- a/otfl-font-otc.lua +++ b/otfl-font-otc.lua @@ -17,8 +17,7 @@ local report_otf = logs.reporter("fonts","otf loading") local fonts = fonts local otf = fonts.handlers.otf -local otffeatures = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register +local registerotffeature = otf.features.register local setmetatableindex = table.setmetatableindex -- In the userdata interface we can not longer tweak the loaded font as @@ -166,7 +165,7 @@ local function addfeature(data,feature,specifications) end end if trace_loading then - report_otf("enhance: registering feature '%s', %s glyphs affected, %s glyphs skipped",feature,done,skip) + report_otf("registering feature %a, affected glyphs %a, skipped glyphs %a",feature,done,skip) end end end @@ -312,14 +311,14 @@ end local anum_specification = { { type = "substitution", - features = { arab = { URD = true, dflt = true } }, + features = { arab = { urd = true, dflt = true } }, data = anum_arabic, flags = noflags, -- { }, valid = valid, }, { type = "substitution", - features = { arab = { URD = true } }, + features = { arab = { urd = true } }, data = anum_persian, flags = noflags, -- { }, valid = valid, -- cgit v1.2.3 From 3b03067cbc7811eb2c537dff7f40fcfa2e2222a2 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 14:52:40 +0200 Subject: load font-otc; tlig seems to work --- luaotfload-deferred.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/luaotfload-deferred.lua b/luaotfload-deferred.lua index 93e8bb2..efa0298 100644 --- a/luaotfload-deferred.lua +++ b/luaotfload-deferred.lua @@ -29,6 +29,8 @@ add_to_callback("hpack_filter", "luaotfload.node_processor", 1) +luaotfload.loadmodule("font-otc.lua") + loadmodule("lib-dir.lua") -- required by font-nms; will change with lualibs update loadmodule("font-nms.lua") loadmodule("font-clr.lua") @@ -95,6 +97,7 @@ local patch_defined_font = function (...) if type(tfmdata) == "table" and tfmdata.shared then call_callback("luaotfload.patch_font", tfmdata) end + --inspect(tfmdata.shared.features) return tfmdata end @@ -128,8 +131,6 @@ elseif luaotfload.font_definer == "patch" then 1) end ---luaotfload.loadmodule("font-otc.lua") -- broken - --local register_base_sub = fonts.otf.features.register_base_substitution --local gsubs = { --"ss01", "ss02", "ss03", "ss04", "ss05", -- cgit v1.2.3 From 3918e104cc75385e87b23707b2669c32eb112bea Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 14:53:57 +0200 Subject: choose less generic identifiers in font-ltx --- otfl-font-ltx.lua | 86 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/otfl-font-ltx.lua b/otfl-font-ltx.lua index 2025db5..4a86593 100644 --- a/otfl-font-ltx.lua +++ b/otfl-font-ltx.lua @@ -5,6 +5,7 @@ if not modules then modules = { } end modules ['font-ltx'] = { copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } +--- where have all the comments gone? local fonts = fonts @@ -21,23 +22,24 @@ end -- the generic name parser (different from context!) -local list = { } +local feature_list = { } +--- ugh TODO use lpeg instead local function isstyle(s) local style = string.lower(s):split("/") for _,v in next, style do if v == "b" then - list.style = "bold" + feature_list.style = "bold" elseif v == "i" then - list.style = "italic" + feature_list.style = "italic" elseif v == "bi" or v == "ib" then - list.style = "bolditalic" + feature_list.style = "bolditalic" elseif v:find("^s=") then - list.optsize = v:split("=")[2] + feature_list.optsize = v:split("=")[2] elseif v == "aat" or v == "icu" or v == "gr" then logs.report("load font", "unsupported font option: %s", v) elseif not v:is_empty() then - list.style = v:gsub("[^%a%d]", "") + feature_list.style = v:gsub("[^%a%d]", "") end end end @@ -103,20 +105,20 @@ local function set_default_features(script) features = defaults["dflt"] end for _,v in next, features do - if list[v] ~= false then - list[v] = true + if feature_list[v] ~= false then + feature_list[v] = true end end end -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 istrue (s) list[s] = true end -local function isfalse(s) list[s] = false end -local function iskey (k,v) list[k] = v end +local function issome () feature_list.lookup = 'name' end +local function isfile () feature_list.lookup = 'file' end +local function isname () feature_list.lookup = 'name' end +local function thename(s) feature_list.name = s end +local function issub (v) feature_list.sub = v end +local function istrue (s) feature_list[s] = true end +local function isfalse(s) feature_list[s] = false end +local function iskey (k,v) feature_list[k] = v end local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C @@ -137,42 +139,42 @@ local options = P(":") * spaces * (P(";")^0 * option)^0 local pattern = (filename + fontname) * subvalue^0 * stylespec^0 * options^0 local function colonized(specification) -- xetex mode - list = { } + feature_list = { } lpeg.match(pattern,specification.specification) - set_default_features(list.script) - if list.style then - specification.style = list.style - list.style = nil + set_default_features(feature_list.script) + if feature_list.style then + specification.style = feature_list.style + feature_list.style = nil end - if list.optsize then - specification.optsize = list.optsize - list.optsize = nil + if feature_list.optsize then + specification.optsize = feature_list.optsize + feature_list.optsize = nil end - if list.name then - if resolvers.findfile(list.name, "tfm") then - list.lookup = "file" - list.name = file.addsuffix(list.name, "tfm") - elseif resolvers.findfile(list.name, "ofm") then - list.lookup = "file" - list.name = file.addsuffix(list.name, "ofm") + if feature_list.name then + if resolvers.findfile(feature_list.name, "tfm") then + feature_list.lookup = "file" + feature_list.name = file.addsuffix(feature_list.name, "tfm") + elseif resolvers.findfile(feature_list.name, "ofm") then + feature_list.lookup = "file" + feature_list.name = file.addsuffix(feature_list.name, "ofm") end - specification.name = list.name - list.name = nil + specification.name = feature_list.name + feature_list.name = nil end - if list.lookup then - specification.lookup = list.lookup - list.lookup = nil + if feature_list.lookup then + specification.lookup = feature_list.lookup + feature_list.lookup = nil end - if list.sub then - specification.sub = list.sub - list.sub = nil + if feature_list.sub then + specification.sub = feature_list.sub + feature_list.sub = nil end - if not list.mode then + if not feature_list.mode then -- if no mode is set, use our default - list.mode = fonts.mode + feature_list.mode = fonts.mode end - specification.features.normal = fonts.handlers.otf.features.normalize(list) + specification.features.normal = fonts.handlers.otf.features.normalize(feature_list) return specification end -- cgit v1.2.3 From b1db13ab10112f6d81522a3f0ab44a67754cf551 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 16:20:41 +0200 Subject: circumvent the callback trap from luatexbase --- luaotfload.lua | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index 0a3b398..dd650fd 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -65,24 +65,35 @@ local loadmodule = function (name) end luaotfload.loadmodule = loadmodule --- required in deferred code +--[[doc-- +The imported font loader will call \verb|callback.register| once +(during \verb|font-def.lua|). +This is unavoidable but harmless, so we make it call a dummy instead. +--doc]]-- +local trapped_register = callback.register +local dummy_function = function () end +callback.register = dummy_function + --[[-- keep --]] --- from Hans (all merged): ---- file name modified include name ---- × basics-gen.lua t luat-basics-gen ---- × font-def -> fonts-def t luatex-font-def (there’s also the normal font-def!) ---- × fonts-enc f luatex-font-enc ---- × fonts-ext t luatex-fonts-ext ---- × fonts-lua f luatex-fonts-lua ---- fonts-tfm f luatex-fonts-tfm ---- × fonts-cbk f luatex-fonts-lua +--- file name modified include name +--- × basics-gen.lua t luat-basics-gen +--- × font-def -> fonts-def t luatex-font-def (there’s also the normal font-def!) +--- × fonts-enc f luatex-font-enc +--- × fonts-ext t luatex-fonts-ext +--- × fonts-lua f luatex-fonts-lua +--- fonts-tfm f luatex-fonts-tfm +--- × fonts-cbk f luatex-fonts-lua + +--- from Hans (unmerged): +--- font-otc.lua -> otfl-font-otc.lua --- from luaotfload: --- otfl-luat-ovr.lua -- override some luat-dum functions --- otfl-font-clr.lua --- otfl-font-ltx.lua --- otfl-font-nms.lua ---- otfl-font-otc.lua --- otfl-font-pfb.lua -- ? --[[-- new --]] @@ -154,6 +165,13 @@ _G.non_generic_context = { luatex_fonts = { loadmodule("fonts.lua") +--[[doc-- +After the fontloader is ready we can restore the callback trap from +\textsf{luatexbase}. +--doc]]-- + +callback.register = trapped_register + --- then continue in luaotfload-deferred.lua -- vim:tw=71:sw=2:ts=2:expandtab -- cgit v1.2.3 From 45527a77a2250494c7beecfb687d530b62281ba6 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 17:01:54 +0200 Subject: merge deferred functionality back into luaotfload.lua --- luaotfload-deferred.lua | 172 ------------------------------------ luaotfload.lua | 228 +++++++++++++++++++++++++++++++++++++++++++++--- luaotfload.sty | 3 +- 3 files changed, 217 insertions(+), 186 deletions(-) delete mode 100644 luaotfload-deferred.lua diff --git a/luaotfload-deferred.lua b/luaotfload-deferred.lua deleted file mode 100644 index efa0298..0000000 --- a/luaotfload-deferred.lua +++ /dev/null @@ -1,172 +0,0 @@ ---- TODO integrate into luaotfload.dtx - -local type, next = type, next - -local stringfind = string.find ---- this part is loaded after luatexbase -local loadmodule = luaotfload.loadmodule -local luatexbase = luatexbase -local generic_context = generic_context --- from fontloader - -local add_to_callback, create_callback = - luatexbase.add_to_callback, luatexbase.create_callback -local reset_callback, call_callback = - luatexbase.reset_callback, luatexbase.call_callback - ---[[doc-- -We do our own callback handling with the means provided by luatexbase. - -Note: \verb|pre_linebreak_filter| and \verb|hpack_filter| are coupled -in \CONTEXT\ in the concept of \emph{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) - -luaotfload.loadmodule("font-otc.lua") - -loadmodule("lib-dir.lua") -- required by font-nms; will change with lualibs update -loadmodule("font-nms.lua") -loadmodule("font-clr.lua") ---loadmodule("font-ovr.lua") -loadmodule("font-ltx.lua") - -local dummy_function = function ( ) end --- upvalue more efficient than lambda -create_callback("luaotfload.patch_font", "simple", dummy_function) - ---[[doc-- -This is a wrapper for the imported font loader. -As of 2013, everything it does appears to be redundand, so we won’t use -it. -Nevertheless, it has been adapted to work with the current structure of -font data objects and will stay here for reference / until somebody -reports breakage. - -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-- -We provide a simplified version of the original font definition -callback. ---doc]]-- -local patch_defined_font = function (...) - local tfmdata = fonts.definers.read(...) - if type(tfmdata) == "table" and tfmdata.shared then - call_callback("luaotfload.patch_font", tfmdata) - end - --inspect(tfmdata.shared.features) - return tfmdata -end - -fonts.mode = "node" - -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 - -reset_callback("define_font") - -if luaotfload.font_definer == "old" then - add_to_callback("define_font", - old_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 - ---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 ---add_to_callback("find_vf_file", - --fonts.vf.find, - --"luaotfload.find_vf_file") - -local set_sscale_diments = function (tfmdata) - local mathconstants = tfmdata.MathConstants - if mathconstants then - local tfmparameters = tfmdata.parameters - if mathconstants.ScriptPercentScaleDown then - tfmparameters[10] = mathconstants.ScriptPercentScaleDown - else -- resort to plain TeX default - tfmparameters[10] = 70 - end - if mathconstants.ScriptScriptPercentScaleDown then - tfmparameters[11] = mathconstants.ScriptScriptPercentScaleDown - else -- resort to plain TeX default - tfmparameters[11] = 50 - end - end -end - -add_to_callback("luaotfload.patch_font", - set_sscale_diments, - "unicodemath.set_sscale_diments") - --- vim:tw=71:sw=2:ts=2:expandtab - --- End of File `luaotfload.lua'. diff --git a/luaotfload.lua b/luaotfload.lua index dd650fd..7266e47 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -29,6 +29,11 @@ luaotfload.module = { license = "CC0" } +local luatexbase = luatexbase + +local type, next = type, next +local stringfind = string.find + --[[doc-- No final decision has been made on how to handle font definition. At the moment, there are three candidates: The \textsf{generic} @@ -63,16 +68,6 @@ local loadmodule = function (name) error("file %s not found.", tofind) end end -luaotfload.loadmodule = loadmodule --- required in deferred code - ---[[doc-- -The imported font loader will call \verb|callback.register| once -(during \verb|font-def.lua|). -This is unavoidable but harmless, so we make it call a dummy instead. ---doc]]-- -local trapped_register = callback.register -local dummy_function = function () end -callback.register = dummy_function --[[-- keep --]] --- from Hans (all merged): @@ -155,6 +150,9 @@ In combination with the option \verb|no_callbacks_yet| in 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 = { @@ -163,7 +161,19 @@ _G.non_generic_context = { luatex_fonts = { skip_loading = true, }} -loadmodule("fonts.lua") +--[[doc-- +The imported font loader will call \verb|callback.register| once +(during \verb|font-def.lua|). +This is unavoidable but harmless, so we make it call a dummy instead. +--doc]]-- +local trapped_register = callback.register +local dummy_function = function () end +callback.register = dummy_function + +--[[doc-- +Now that things are sorted out we can load the fontloader. +--doc]]-- +loadmodule"fonts.lua" --[[doc-- After the fontloader is ready we can restore the callback trap from @@ -172,7 +182,201 @@ After the fontloader is ready we can restore the callback trap from callback.register = trapped_register ---- then continue in luaotfload-deferred.lua +local add_to_callback, create_callback = + luatexbase.add_to_callback, luatexbase.create_callback +local reset_callback, call_callback = + luatexbase.reset_callback, luatexbase.call_callback + +--[[doc-- +We do our own callback handling with the means provided by luatexbase. + +Note: \verb|pre_linebreak_filter| and \verb|hpack_filter| are coupled +in \CONTEXT\ in the concept of \emph{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) + +loadmodule"font-otc.lua" + +loadmodule"lib-dir.lua" -- required by font-nms; will change with lualibs update +loadmodule"font-nms.lua" +loadmodule"font-clr.lua" +--loadmodule"font-ovr.lua" +loadmodule"font-ltx.lua" + +local dummy_function = function ( ) end --- upvalue more efficient than lambda +create_callback("luaotfload.patch_font", "simple", dummy_function) + +--[[doc-- +This is a wrapper for the imported font loader. +As of 2013, everything it does appears to be redundand, so we won’t use +it. +Nevertheless, it has been adapted to work with the current structure of +font data objects and will stay here for reference / until somebody +reports breakage. + +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-- +We provide a simplified version of the original font definition +callback. +--doc]]-- +local patch_defined_font = function (...) + local tfmdata = fonts.definers.read(...) + if type(tfmdata) == "table" and tfmdata.shared then + call_callback("luaotfload.patch_font", tfmdata) + end + --inspect(tfmdata.shared.features) + return tfmdata +end + +fonts.mode = "node" + +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 + +reset_callback("define_font") + +if luaotfload.font_definer == "old" then + add_to_callback("define_font", + old_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 + +--[[doc-- +These vanished in 2011. +\url{http://repo.or.cz/w/context.git/commitdiff/1455dd60b68c9140db1b9977c9e5ce372b772ec8} + +The “ss” stuff is in tables.features in context (see font-ott.lua), but +commented. +I’ll get back at restoring it as soon as someone can tell me what those do. +/phg + +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]]-- + +---TODO check for conflicts with lualibs +-- imported from "util-sto.lua" +--table.setmetatablenewindex = function (t,f) +-- if type(t) ~= "table" then +-- f, t = t, { } +-- end +-- local m = getmetatable(t) +-- if m then +-- if f == "ignore" then +-- m.__newindex = f_ignore +-- else +-- m.__newindex = f +-- end +-- else +-- if f == "ignore" then +-- setmetatable(t, t_ignore) +-- else +-- setmetatable(t,{ __newindex = f }) +-- end +-- end +-- return t +--end +---- this overloads fonts.handlers.features.normalize() +---- breakage ahead. +--loadmodule"font-ott.lua" + +--add_to_callback("find_vf_file", +-- fonts.vf.find, +-- "luaotfload.find_vf_file") + +local set_sscale_diments = function (tfmdata) + local mathconstants = tfmdata.MathConstants + if mathconstants then + local tfmparameters = tfmdata.parameters + if mathconstants.ScriptPercentScaleDown then + tfmparameters[10] = mathconstants.ScriptPercentScaleDown + else -- resort to plain TeX default + tfmparameters[10] = 70 + end + if mathconstants.ScriptScriptPercentScaleDown then + tfmparameters[11] = mathconstants.ScriptScriptPercentScaleDown + else -- resort to plain TeX default + tfmparameters[11] = 50 + end + end +end + +add_to_callback("luaotfload.patch_font", + set_sscale_diments, + "unicodemath.set_sscale_diments") -- vim:tw=71:sw=2:ts=2:expandtab diff --git a/luaotfload.sty b/luaotfload.sty index a7b3b52..b2b574b 100644 --- a/luaotfload.sty +++ b/luaotfload.sty @@ -22,7 +22,6 @@ \bgroup\expandafter\expandafter\expandafter\egroup %\RequireLuaModule{lualibs} %\RequireLuaModule{luaotfload} -\directlua{require"luaotfload"} \expandafter\ifx\csname ProvidesPackage\endcsname\relax \input luatexbase.sty \else @@ -31,7 +30,7 @@ [2011/10/06 v2.0 OpenType layout system] \RequirePackage{luatexbase} \fi -\directlua{require"luaotfload-deferred"} +\directlua{require"luaotfload"} \endinput %% %% End of file `luaotfload.sty'. -- cgit v1.2.3 From a9b52bd969d9285b9d74a80399a133b0c3f92739 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 17:36:40 +0200 Subject: integrate changes from master; add otfl-features.lua --- luaotfload.lua | 69 +++++++++------------------------------------------------- 1 file changed, 10 insertions(+), 59 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index 7266e47..0fba16e 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -31,7 +31,7 @@ luaotfload.module = { local luatexbase = luatexbase -local type, next = type, next +local type, next, dofile = type, next, dofile local stringfind = string.find --[[doc-- @@ -45,12 +45,9 @@ luaotfload.font_definer = "patch" --- | “generic” | “old” local fl_prefix = "otfl" -- “luatex” for luatex-plain ---- these will be provided by luatexbase some time -local error = function(...) print("err", string.format(...)) end -local log = function(...) print("log", string.format(...)) end +local error, warning, info, log = luatexbase.provides_module(luaotfload.module) -kpse.init_prog("", 600, "/") -local luatex_version = 60 +local luatex_version = 75 if tex.luatexversion < luatex_version then warning("LuaTeX v%.2f is old, v%.2f is recommended.", @@ -203,7 +200,7 @@ add_to_callback("hpack_filter", "luaotfload.node_processor", 1) -loadmodule"font-otc.lua" +loadmodule"font-otc.lua" -- TODO check what we can drop from otfl-features loadmodule"lib-dir.lua" -- required by font-nms; will change with lualibs update loadmodule"font-nms.lua" @@ -268,7 +265,7 @@ callback. --doc]]-- local patch_defined_font = function (...) local tfmdata = fonts.definers.read(...) - if type(tfmdata) == "table" and tfmdata.shared then + if type(tfmdata) == "table" then call_callback("luaotfload.patch_font", tfmdata) end --inspect(tfmdata.shared.features) @@ -276,6 +273,7 @@ local patch_defined_font = function (...) end fonts.mode = "node" +caches.compilemethod = "both" function attributes.private(name) local attr = "otfl@" .. name @@ -305,58 +303,10 @@ elseif luaotfload.font_definer == "patch" then 1) end ---[[doc-- -These vanished in 2011. -\url{http://repo.or.cz/w/context.git/commitdiff/1455dd60b68c9140db1b9977c9e5ce372b772ec8} - -The “ss” stuff is in tables.features in context (see font-ott.lua), but -commented. -I’ll get back at restoring it as soon as someone can tell me what those do. -/phg - -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]]-- - ----TODO check for conflicts with lualibs --- imported from "util-sto.lua" ---table.setmetatablenewindex = function (t,f) --- if type(t) ~= "table" then --- f, t = t, { } --- end --- local m = getmetatable(t) --- if m then --- if f == "ignore" then --- m.__newindex = f_ignore --- else --- m.__newindex = f --- end --- else --- if f == "ignore" then --- setmetatable(t, t_ignore) --- else --- setmetatable(t,{ __newindex = f }) --- end --- end --- return t ---end ----- this overloads fonts.handlers.features.normalize() ----- breakage ahead. ---loadmodule"font-ott.lua" - ---add_to_callback("find_vf_file", --- fonts.vf.find, --- "luaotfload.find_vf_file") +loadmodule"features.lua" +--[==[ +---- is this still necessary? local set_sscale_diments = function (tfmdata) local mathconstants = tfmdata.MathConstants if mathconstants then @@ -377,6 +327,7 @@ end add_to_callback("luaotfload.patch_font", set_sscale_diments, "unicodemath.set_sscale_diments") +]==] -- vim:tw=71:sw=2:ts=2:expandtab -- cgit v1.2.3 From cf25e2f185676223b565ab5e099fcc1b83fabfa8 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 19:10:22 +0200 Subject: fallback for earlier lualibs; move locals up in luaotfload.lua --- luaotfload.lua | 14 +++++++------- mkluatexfontdb.lua | 8 +++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index 0fba16e..c90109d 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -34,6 +34,13 @@ local luatexbase = luatexbase local type, next, dofile = type, next, dofile local stringfind = string.find +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 + --[[doc-- No final decision has been made on how to handle font definition. At the moment, there are three candidates: The \textsf{generic} @@ -164,7 +171,6 @@ The imported font loader will call \verb|callback.register| once This is unavoidable but harmless, so we make it call a dummy instead. --doc]]-- local trapped_register = callback.register -local dummy_function = function () end callback.register = dummy_function --[[doc-- @@ -179,11 +185,6 @@ After the fontloader is ready we can restore the callback trap from callback.register = trapped_register -local add_to_callback, create_callback = - luatexbase.add_to_callback, luatexbase.create_callback -local reset_callback, call_callback = - luatexbase.reset_callback, luatexbase.call_callback - --[[doc-- We do our own callback handling with the means provided by luatexbase. @@ -208,7 +209,6 @@ loadmodule"font-clr.lua" --loadmodule"font-ovr.lua" loadmodule"font-ltx.lua" -local dummy_function = function ( ) end --- upvalue more efficient than lambda create_callback("luaotfload.patch_font", "simple", dummy_function) --[[doc-- diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 38b9daa..ca955cb 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -13,13 +13,15 @@ function string.quoted(s) return string.format("%q",s) end -- XXX -- First we need to be able to load module (code copied from -- luatexbase-loader.sty): -local file = "luatexbase.loader.lua" -local path = assert(kpse.find_file(file, 'tex'), - "File '"..file.."' not found") +local loader_file = "luatexbase.loader.lua" +local path = assert(kpse.find_file(loader_file, 'tex'), + "File '"..loader_file.."' not found") texio.write_nl("("..path..")") dofile(path) require("lualibs") +--- TODO we seriously need recent lualibs +file.splitpath, file.collapsepath = file.split_path, file.collapse_path require("otfl-basics-gen.lua") require("otfl-font-nms") require("alt_getopt") -- cgit v1.2.3 From 284c5ae14e8f7f74155b97dc8bae97f8fc84f8b5 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 21:33:12 +0200 Subject: cleanup font db script --- mkluatexfontdb.lua | 56 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index ca955cb..11ef8ba 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -7,32 +7,33 @@ This file is a wrapper for the luaotfload's font names module. It is part of the luaotfload bundle, please see the luaotfload documentation for more info. --]] -kpse.set_program_name("luatex") +kpse.set_program_name"luatex" -function string.quoted(s) return string.format("%q",s) end -- XXX +local stringformat = string.format +local texiowrite_nl = texio.write_nl -- First we need to be able to load module (code copied from -- luatexbase-loader.sty): local loader_file = "luatexbase.loader.lua" -local path = assert(kpse.find_file(loader_file, 'tex'), - "File '"..loader_file.."' not found") -texio.write_nl("("..path..")") -dofile(path) - -require("lualibs") ---- TODO we seriously need recent lualibs -file.splitpath, file.collapsepath = file.split_path, file.collapse_path -require("otfl-basics-gen.lua") -require("otfl-font-nms") -require("alt_getopt") - -local name = 'mkluatexfontdb' -local version = '1.07' -- same version number as luaotfload - +local loader_path = assert(kpse.find_file(loader_file, 'tex'), + "File '"..loader_file.."' not found") +--texiowrite_nl("("..path..")") +dofile(loader_path) -- FIXME this pollutes stdout with filenames + +require"lualibs" +require"otfl-basics-gen.lua" +require"otfl-font-nms" +require"alt_getopt" + +local name = 'mkluatexfontdb' +local version = '2.1' -- same version number as luaotfload local names = fonts.names +local db_src_out = names.path.dir.."/"..names.path.basename +local db_bin_out = file.replacesuffix(db_src_out, "luc") local function help_msg() - texio.write(string.format([[ + texiowrite_nl(stringformat([[ + Usage: %s [OPTION]... Rebuild the LuaTeX font database. @@ -46,14 +47,15 @@ Valid options: -V --version print version and exit -h --help print this message -The output database file is named otfl-fonts.lua and is placed under: +The font database will be saved to + %s + %s - %s" -]], name, names.path.dir)) +]], name, db_src_out, db_bin_out)) end local function version_msg() - texio.write(string.format( + texiowrite_nl(stringformat( "%s version %s, database version %s.\n", name, version, names.version)) end @@ -62,7 +64,7 @@ Command-line processing. Here we fill cmdargs with the good values, and then analyze it. --]] -local long_opts = { +local long_options = { force = "f", quiet = "q", help = "h", @@ -70,14 +72,14 @@ local long_opts = { version = "V", } -local short_opts = "fqpvVh" +local short_options = "fqpvVh" local force_reload = nil local function process_cmdline() - local opts, optind, optarg = alt_getopt.get_ordered_opts (arg, short_opts, long_opts) + local options, _, _ = alt_getopt.get_ordered_opts (arg, short_options, long_options) local log_level = 1 - for i,v in next, opts do + for i,v in next, options do if v == "q" then log_level = 0 elseif v == "v" then @@ -104,7 +106,7 @@ local function generate(force) fontnames = names.update(fontnames, force) logs.report("fonts in the database", "%i", #fontnames.mappings) saved = names.save(fontnames) - texio.write_nl("") + texiowrite_nl("") end process_cmdline() -- cgit v1.2.3 From 6fbac590374db31f6dd03cafc363657efc6f4a44 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 10 Apr 2013 22:57:21 +0200 Subject: =?UTF-8?q?make=20loglevel=20=E2=80=9C-q=E2=80=9D=20effective?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mkluatexfontdb.lua | 3 ++- otfl-font-nms.lua | 37 ++++++++++++++++++++----------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 11ef8ba..0a2ddd3 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -79,7 +79,7 @@ local force_reload = nil local function process_cmdline() local options, _, _ = alt_getopt.get_ordered_opts (arg, short_options, long_options) local log_level = 1 - for i,v in next, options do + for _,v in next, options do if v == "q" then log_level = 0 elseif v == "v" then @@ -111,3 +111,4 @@ end process_cmdline() generate(force_reload) +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 3934919..a0d81ae 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -39,9 +39,10 @@ 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 suppress_output = false +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 @@ -244,7 +245,9 @@ end names.resolvespec = names.resolve function names.set_log_level(level) - if level == 2 then + if level == 0 then + suppress_output = true + elseif level == 2 then trace_loading = true elseif level >= 3 then trace_loading = true @@ -252,30 +255,29 @@ function names.set_log_level(level) end end -local lastislog = 0 - -local function log(category, fmt, ...) - lastislog = 1 - if fmt then - texio.write_nl(format("luaotfload | %s: %s", category, format(fmt, ...))) - elseif category then - texio.write_nl(format("luaotfload | %s", category)) - else - texio.write_nl(format("luaotfload |")) +local function log (category, fmt, ...) + if not suppress_output then + if fmt then + texio.write_nl(format("luaotfload | %s: %s", category, format(fmt, ...))) + elseif category then + texio.write_nl(format("luaotfload | %s", category)) + else + texio.write_nl(format("luaotfload |")) + end + io.flush() end - io.flush() end logs = logs or { } logs.report = logs.report or log -logs.info = logs.info or log +logs.info = logs.info or log local function font_fullinfo(filename, subfont, texmf) local t = { } local f = fontloader.open(filename, subfont) if not f then if trace_loading then - logs.report("error", "failed to open %s", filename) + logs.report("error", "failed to open %s", filename) end return end @@ -769,3 +771,4 @@ names.save = save_names function fonts.names.getfilename(askedname,suffix) -- only supported in mkiv return "" end +-- vim:tw=71:sw=4:ts=4:expandtab -- cgit v1.2.3 From b85a3646ea13a26b2d8506531d2bdf072db81837 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 11 Apr 2013 00:18:13 +0200 Subject: enable overrides for Context loggers --- luaotfload.lua | 2 +- mkluatexfontdb.lua | 1 + otfl-font-nms.lua | 4 ---- otfl-luat-ovr.lua | 25 +++++++++++++------------ 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index c90109d..10e3373 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -204,9 +204,9 @@ add_to_callback("hpack_filter", loadmodule"font-otc.lua" -- TODO check what we can drop from otfl-features loadmodule"lib-dir.lua" -- required by font-nms; will change with lualibs update +loadmodule"luat-ovr.lua" loadmodule"font-nms.lua" loadmodule"font-clr.lua" ---loadmodule"font-ovr.lua" loadmodule"font-ltx.lua" create_callback("luaotfload.patch_font", "simple", dummy_function) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 0a2ddd3..69f22a0 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -22,6 +22,7 @@ dofile(loader_path) -- FIXME this pollutes stdout with filenames require"lualibs" require"otfl-basics-gen.lua" +require"otfl-luat-ovr.lua" require"otfl-font-nms" require"alt_getopt" diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index a0d81ae..856046c 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -268,10 +268,6 @@ local function log (category, fmt, ...) end end -logs = logs or { } -logs.report = logs.report or log -logs.info = logs.info or log - local function font_fullinfo(filename, subfont, texmf) local t = { } local f = fontloader.open(filename, subfont) diff --git a/otfl-luat-ovr.lua b/otfl-luat-ovr.lua index 63a9e19..984dbf5 100644 --- a/otfl-luat-ovr.lua +++ b/otfl-luat-ovr.lua @@ -7,30 +7,31 @@ if not modules then modules = { } end modules ['luat-ovr'] = { } -local write_nl, format, name = texio.write_nl, string.format, "luaotfload" -local dummyfunction = function() end +local module_name = "luaotfload" -callbacks = { - register = dummyfunction, -} +local texiowrite_nl = texio.write_nl +local stringformat = string.format +local ioflush = io.flush +local dummyfunction = function() end function logs.report(category,fmt,...) if fmt then - write_nl('log', format("%s | %s: %s",name,category,format(fmt,...))) + texiowrite_nl('log', stringformat("%s | %s: %s",module_name,category,stringformat(fmt,...))) elseif category then - write_nl('log', format("%s | %s",name,category)) + texiowrite_nl('log', stringformat("%s | %s",module_name,category)) else - write_nl('log', format("%s |",name)) + texiowrite_nl('log', stringformat("%s |",module_name)) end end function logs.info(category,fmt,...) if fmt then - write_nl(format("%s | %s: %s",name,category,format(fmt,...))) + texiowrite_nl(stringformat("%s | %s: %s",module_name,category,stringformat(fmt,...))) elseif category then - write_nl(format("%s | %s",name,category)) + texiowrite_nl(stringformat("%s | %s",module_name,category)) else - write_nl(format("%s |",name)) + texiowrite_nl(stringformat("%s |",module_name)) end - io.flush() + ioflush() end + -- cgit v1.2.3 From b1b150226416c08ead993357c6ae744431d5f274 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 11 Apr 2013 22:35:19 +0200 Subject: update gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 33bb4d9..a366009 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ luaotfload.zip # Temporary files in the tests directory tests/*.log tests/*.pdf +tests/*.lua +tests/*.otf -- cgit v1.2.3 From 83af7240d34e2206e3032f5d424037525ad94121 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 11 Apr 2013 22:38:50 +0200 Subject: update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a366009..11e429a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ tests/*.log tests/*.pdf tests/*.lua tests/*.otf +tests/*.aux +tests/phg-*.tex -- cgit v1.2.3 From 23a527d0d24140f04fc003a5c7f1a9cbdfe94e3e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 11 Apr 2013 22:40:01 +0200 Subject: track otfl-features --- otfl-features.lua | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 otfl-features.lua diff --git a/otfl-features.lua b/otfl-features.lua new file mode 100644 index 0000000..aae0515 --- /dev/null +++ b/otfl-features.lua @@ -0,0 +1,110 @@ +--[[doc-- +Taken from the most recent branch of luaotfload. +--doc]]-- +local addotffeature = fonts.handlers.otf.addfeature +local registerotffeature = fonts.handlers.otf.features.register + +local everywhere = { ["*"] = { ["*"] = true } } + +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 + [0x201E] = {0x002C, 0x002C}, -- quotedblbase + [0x00A1] = {0x0021, 0x2018}, -- exclamdown + [0x00BF] = {0x003F, 0x2018}, -- questiondown + }, + flags = { }, + }, + { + type = "ligature", + features = everywhere, + data = { + [0x201C] = {0x0060, 0x0060}, -- quotedblleft + [0x201D] = {0x0027, 0x0027}, -- quotedblright + [0x00A1] = {0x0021, 0x0060}, -- exclamdown + [0x00BF] = {0x003F, 0x0060}, -- questiondown + }, + flags = { }, + }, +} + +addotffeature("tlig", tlig) +addotffeature("trep", { }) -- empty, all in tlig now +local anum_arabic = { + [0x0030] = 0x0660, + [0x0031] = 0x0661, + [0x0032] = 0x0662, + [0x0033] = 0x0663, + [0x0034] = 0x0664, + [0x0035] = 0x0665, + [0x0036] = 0x0666, + [0x0037] = 0x0667, + [0x0038] = 0x0668, + [0x0039] = 0x0669, +} + +local anum_persian = { + [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, + }, +} + +addotffeature("anum",anum_specification) + +registerotffeature { + name = 'anum', + description = 'arabic digits', +} -- cgit v1.2.3 From 1b17f5aef71290d25d57cca6b0b2a762d22fd098 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 12 Apr 2013 21:19:03 +0200 Subject: [dtx] fix typo --- luaotfload.dtx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 3538075..954e40c 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -63,7 +63,7 @@ and the derived files \generate{% - \usedir{tex/luatex/luaodfload}% + \usedir{tex/luatex/luaotfload}% \file{luaotfload.sty}{\from{luaotfload.dtx}{package}}% } -- cgit v1.2.3 From 0266ebff82d4778f53c39787db23e9fa31837b32 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 13 Apr 2013 19:02:19 +0200 Subject: =?UTF-8?q?attempt=20at=20restoring=20KH=E2=80=99l=20OFM=20hack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaotfload.lua | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/luaotfload.lua b/luaotfload.lua index 10e3373..cfe29a6 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -203,8 +203,21 @@ add_to_callback("hpack_filter", loadmodule"font-otc.lua" -- TODO check what we can drop from otfl-features -loadmodule"lib-dir.lua" -- required by font-nms; will change with lualibs update +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 + -------------------------------------------------------------------- +end loadmodule"font-nms.lua" loadmodule"font-clr.lua" loadmodule"font-ltx.lua" -- cgit v1.2.3 From 83fc1197e06aeb208da7d88e8f9183abbd585b6f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 13 Apr 2013 19:03:26 +0200 Subject: avoid loading of extended lualibs --- mkluatexfontdb.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 69f22a0..034de57 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -17,9 +17,14 @@ local texiowrite_nl = texio.write_nl local loader_file = "luatexbase.loader.lua" local loader_path = assert(kpse.find_file(loader_file, 'tex'), "File '"..loader_file.."' not found") ---texiowrite_nl("("..path..")") +--texiowrite_nl("("..loader_path..")") dofile(loader_path) -- FIXME this pollutes stdout with filenames +local config = config or { } +config.lualibs = config.lualibs or { } +config.lualibs.prefer_merged = false +config.lualibs.load_extended = true + require"lualibs" require"otfl-basics-gen.lua" require"otfl-luat-ovr.lua" -- cgit v1.2.3 From a9107ee964f128f6575813dd8d1b574d0006c8b9 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 13 Apr 2013 19:23:19 +0200 Subject: =?UTF-8?q?move=20=E2=80=9Cconfig=E2=80=9D=20to=20=5FG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mkluatexfontdb.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 034de57..ec8396f 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -20,7 +20,8 @@ local loader_path = assert(kpse.find_file(loader_file, 'tex'), --texiowrite_nl("("..loader_path..")") dofile(loader_path) -- FIXME this pollutes stdout with filenames -local config = config or { } +_G.config = _G.config or { } +local config = _G.config config.lualibs = config.lualibs or { } config.lualibs.prefer_merged = false config.lualibs.load_extended = true -- cgit v1.2.3 From 9909991f8c403ef1119c44902de213695ae46318 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 14 Apr 2013 20:26:01 +0200 Subject: add virtual font file lookup --- luaotfload.lua | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/luaotfload.lua b/luaotfload.lua index cfe29a6..6e951dd 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -33,6 +33,7 @@ local luatexbase = luatexbase local type, next, dofile = type, next, dofile local stringfind = string.find +local find_file = kpse.find_file local add_to_callback, create_callback = luatexbase.add_to_callback, luatexbase.create_callback @@ -63,7 +64,7 @@ if tex.luatexversion < luatex_version then end local loadmodule = function (name) local tofind = fl_prefix .."-"..name - local found = kpse.find_file(tofind,"tex") + local found = find_file(tofind,"tex") if found then log("loading file %s.", found) dofile(found) @@ -73,6 +74,23 @@ local loadmodule = function (name) end end +--[[doc-- +Virtual fonts are resolved via a callback. +\verb|find_vf_file| derives the name of the virtual font file from the +filename. +(NB: \CONTEXT\ handles this likewise in \textsf{font-vf.lua}.) +--doc]]-- +local find_vf_file = function (name) + local fullname = find_file(name, "ovf") + if not fullname then + fullname = find_file(file.removesuffix(file.basename(name)), "ovf") + end + if fullname then + log("loading virtual font file %s.", fullname) + end + return fullname +end + --[[-- keep --]] --- from Hans (all merged): @@ -200,6 +218,8 @@ 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 -- cgit v1.2.3 From 7b6553218a35e5f74ad2013a1a16f54b8aa4f2ca Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 14 Apr 2013 23:13:26 +0200 Subject: import from upstream --- otfl-fonts-merged.lua | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/otfl-fonts-merged.lua b/otfl-fonts-merged.lua index 2cb036d..96f4c79 100644 --- a/otfl-fonts-merged.lua +++ b/otfl-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 04/07/13 14:05:24 +-- merge date : 04/14/13 16:51:15 do -- begin closure to overcome local limits and interference @@ -193,7 +193,7 @@ local function loadedbypath(name,rawname,paths,islib,what) end local function notloaded(name) if helpers.trace then - helpers.report("? unable to locate library '%s'",name) + helpers.report("unable to locate library '%s'",name) end end helpers.loadedaslib=loadedaslib @@ -224,6 +224,16 @@ function helpers.loaded(name) end return notloaded(name) end +function helpers.unload(name) + if helpers.trace then + if package.loaded[name] then + helpers.report("unloading library '%s', %s",name,"done") + else + helpers.report("unloading library '%s', %s",name,"not loaded") + end + end + package.loaded[name]=nil +end end -- closure -- cgit v1.2.3 From 25444ac8cd48cd5ad7ed32bfcc9a0a1314e2223a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 00:14:23 +0200 Subject: rewrite filename logger --- mkluatexfontdb.lua | 10 +++--- otfl-font-nms.lua | 95 +++++++++++++++++------------------------------------- otfl-luat-ovr.lua | 34 +++++++++++++++++++ 3 files changed, 69 insertions(+), 70 deletions(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index ec8396f..956fa18 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -24,11 +24,11 @@ _G.config = _G.config or { } local config = _G.config config.lualibs = config.lualibs or { } config.lualibs.prefer_merged = false -config.lualibs.load_extended = true +config.lualibs.load_extended = false require"lualibs" require"otfl-basics-gen.lua" -require"otfl-luat-ovr.lua" +require"otfl-luat-ovr.lua" --- this populates the logs.* namespace require"otfl-font-nms" require"alt_getopt" @@ -84,7 +84,9 @@ local short_options = "fqpvVh" local force_reload = nil local function process_cmdline() - local options, _, _ = alt_getopt.get_ordered_opts (arg, short_options, long_options) + local options, _, _ = alt_getopt.get_ordered_opts (arg, + short_options, + long_options) local log_level = 1 for _,v in next, options do if v == "q" then @@ -105,7 +107,7 @@ local function process_cmdline() force_reload = 1 end end - names.set_log_level(log_level) + logs.set_loglevel(log_level) end local function generate(force) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 856046c..b2b5390 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -42,7 +42,6 @@ local utfgsub = unicode.utf8.gsub local suppress_output = false 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 @@ -244,37 +243,11 @@ end names.resolvespec = names.resolve -function names.set_log_level(level) - if level == 0 then - suppress_output = true - elseif level == 2 then - trace_loading = true - elseif level >= 3 then - trace_loading = true - trace_search = true - end -end - -local function log (category, fmt, ...) - if not suppress_output then - if fmt then - texio.write_nl(format("luaotfload | %s: %s", category, format(fmt, ...))) - elseif category then - texio.write_nl(format("luaotfload | %s", category)) - else - texio.write_nl(format("luaotfload |")) - end - io.flush() - end -end - local function font_fullinfo(filename, subfont, texmf) local t = { } local f = fontloader.open(filename, subfont) if not f then - if trace_loading then - logs.report("error", "failed to open %s", filename) - end + logs.names_loading("error", "failed to open %s", filename) return end local m = fontloader.to_table(f) @@ -304,9 +277,7 @@ local function font_fullinfo(filename, subfont, texmf) end else -- no names table, propably a broken font - if trace_loading then - logs.report("broken font rejected", "%s", basefile) - end + logs.names_loading("broken font rejected", "%s", basefile) return end t.fontname = m.fontname @@ -359,9 +330,7 @@ local function load_font(filename, fontnames, newfontnames, texmf) newmappings[#newmappings+1] = mappings[v] newstatus[basefile].index[index+1] = #newmappings end - if trace_loading then - logs.report("font already indexed", "%s", basefile) - end + logs.names_loading("font already indexed", "%s", basefile) return end local info = fontloader.info(filename) @@ -396,9 +365,7 @@ local function load_font(filename, fontnames, newfontnames, texmf) newstatus[basefile].index[1] = index end else - if trace_loading then - logs.report("failed to load", "%s", basefile) - end + logs.names_loading("failed to load", "%s", basefile) end end end @@ -479,33 +446,31 @@ end local installed_fonts_scanned = false local function scan_installed_fonts(fontnames, newfontnames) - -- Try to query and add font list from operating system. - -- This uses the lualatex-platform module. - logs.info("Scanning fonts known to operating system...") - local fonts = get_installed_fonts() - if fonts and #fonts > 0 then - installed_fonts_scanned = true - if trace_search then - logs.report("operating system fonts found", "%d", #fonts) - end - for key, value in next, fonts do - local file = value.path - if file then - local ext = extname(file) - if ext and font_extensions_set[ext] then - file = path_normalize(file) - if trace_loading then - logs.report("loading font", "%s", file) - end - load_font(file, fontnames, newfontnames, false) + -- Try to query and add font list from operating system. + -- This uses the lualatex-platform module. + logs.info("Scanning fonts known to operating system...") + local fonts = get_installed_fonts() + if fonts and #fonts > 0 then + installed_fonts_scanned = true + if trace_search then + logs.report("operating system fonts found", "%d", #fonts) + end + for key, value in next, fonts do + local file = value.path + if file then + local ext = extname(file) + if ext and font_extensions_set[ext] then + file = path_normalize(file) + logs.names_loading("loading font", "%s", file) + load_font(file, fontnames, newfontnames, false) + end end - end - end - else - if trace_search then - logs.report("Could not retrieve list of installed fonts") - end - end + end + else + if trace_search then + logs.report("Could not retrieve list of installed fonts") + end + end end local function scan_dir(dirname, fontnames, newfontnames, texmf) @@ -539,9 +504,7 @@ local function scan_dir(dirname, fontnames, newfontnames, texmf) for _,file in next, list do file = path_normalize(file) - if trace_loading then - logs.report("loading font", "%s", file) - end + logs.names_loading("loading font", "%s", file) load_font(file, fontnames, newfontnames, texmf) end end diff --git a/otfl-luat-ovr.lua b/otfl-luat-ovr.lua index 984dbf5..63ad6a7 100644 --- a/otfl-luat-ovr.lua +++ b/otfl-luat-ovr.lua @@ -11,9 +11,32 @@ local module_name = "luaotfload" local texiowrite_nl = texio.write_nl local stringformat = string.format +local tableconcat = table.concat local ioflush = io.flush local dummyfunction = function() end +--[[doc-- +We recreate the verbosity levels previously implemented in font-nms: + + ========================================================== + lvl arg trace_loading trace_search suppress_output + ---------------------------------------------------------- + (0) -> -q ⊥ ⊥ ⊤ + (1) -> ∅ ⊥ ⊥ ⊥ + (2) -> -v ⊤ ⊥ ⊥ + (>2) -> -vv ⊤ ⊤ ⊥ + ========================================================== + +--doc]]-- +local loglevel = 1 --- default + +local set_loglevel = function (n) + if type(n) == "number" then + loglevel = n + end +end +logs.set_loglevel = set_loglevel + function logs.report(category,fmt,...) if fmt then texiowrite_nl('log', stringformat("%s | %s: %s",module_name,category,stringformat(fmt,...))) @@ -35,3 +58,14 @@ function logs.info(category,fmt,...) ioflush() end +logs.names_loading = function (category, fmt, ...) + if loglevel > 1 then + local res = { module_name, " |" } + if category then res[#res+1] = " " .. category end + if fmt then res[#res+1] = ": " .. stringformat(fmt, ...) end + texiowrite_nl(tableconcat(res)) + ioflush() + end +end + +-- vim:tw=71:sw=4:ts=4:expandtab -- cgit v1.2.3 From 5a8c4657de6b599e1897d8ac4e685264e0ac0587 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 00:57:10 +0200 Subject: unify logging --- mkluatexfontdb.lua | 3 +- otfl-font-nms.lua | 93 ++++++++++++++++++++++-------------------------------- otfl-luat-ovr.lua | 52 ++++++++++++++++++++---------- 3 files changed, 75 insertions(+), 73 deletions(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 956fa18..0f0df9f 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -113,7 +113,8 @@ end local function generate(force) local fontnames, saved fontnames = names.update(fontnames, force) - logs.report("fonts in the database", "%i", #fontnames.mappings) + logs.names_report("log", 0, "fonts in the database", + "%i", #fontnames.mappings) saved = names.save(fontnames) texiowrite_nl("") end diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index b2b5390..0942eda 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -39,9 +39,7 @@ 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 suppress_output = false -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 report = logs.names_report local function sanitize(str) if str then @@ -76,9 +74,10 @@ local function load_names() foundname = luaname end if data then - logs.info("Font names database loaded", "%s", foundname) + report("info", 0, "Font names database loaded", "%s", foundname) else - logs.info([[Font names database not found, generating new one. + report("info", 0, + [[Font names database not found, generating new one. This can take several minutes; please be patient.]]) data = names.update(fontnames_init()) names.save(data) @@ -187,9 +186,10 @@ function names.resolve(_,_,specification) -- the 1st two parameters are used by 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]) + report("log", 0, "load font", + "font family='%s', subfamily='%s' found: %s", + name, style, found[1].filename[1] + ) return found[1].filename[1], found[1].filename[2] end elseif #found > 1 then @@ -207,9 +207,10 @@ function names.resolve(_,_,specification) -- the 1st two parameters are used by 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]) + report("log", 0, "load font", + "font family='%s', subfamily='%s' found: %s", + name, style, closest.filename[1] + ) return closest.filename[1], closest.filename[2] end elseif found.fallback then @@ -247,7 +248,7 @@ local function font_fullinfo(filename, subfont, texmf) local t = { } local f = fontloader.open(filename, subfont) if not f then - logs.names_loading("error", "failed to open %s", filename) + report("log", 1, "error", "failed to open %s", filename) return end local m = fontloader.to_table(f) @@ -277,7 +278,7 @@ local function font_fullinfo(filename, subfont, texmf) end else -- no names table, propably a broken font - logs.names_loading("broken font rejected", "%s", basefile) + report("log", 1, "broken font rejected", "%s", basefile) return end t.fontname = m.fontname @@ -305,9 +306,7 @@ local function load_font(filename, fontnames, newfontnames, texmf) if filename then if names.blacklist[filename] or names.blacklist[basename(filename)] then - if trace_search then - logs.report("ignoring font", "%s", filename) - end + report("log", 2, "ignoring font", "%s", filename) return end local timestamp, db_timestamp @@ -330,7 +329,7 @@ local function load_font(filename, fontnames, newfontnames, texmf) newmappings[#newmappings+1] = mappings[v] newstatus[basefile].index[index+1] = #newmappings end - logs.names_loading("font already indexed", "%s", basefile) + report("log", 1, "font already indexed", "%s", basefile) return end local info = fontloader.info(filename) @@ -365,7 +364,7 @@ local function load_font(filename, fontnames, newfontnames, texmf) newstatus[basefile].index[1] = index end else - logs.names_loading("failed to load", "%s", basefile) + report("log", 1, "failed to load", "%s", basefile) end end end @@ -423,10 +422,8 @@ local function read_blacklist() if string.sub(line,1,1) == "-" then whitelist[string.sub(line,2,-1)] = true else - if trace_search then - logs.report("blacklisted file", "%s", line) - end - blacklist[line] = true + report("log", 2, "blacklisted file", "%s", line) + blacklist[line] = true end end end @@ -448,28 +445,24 @@ local installed_fonts_scanned = false local function scan_installed_fonts(fontnames, newfontnames) -- Try to query and add font list from operating system. -- This uses the lualatex-platform module. - logs.info("Scanning fonts known to operating system...") + report("info", 0, "Scanning fonts known to operating system...") local fonts = get_installed_fonts() if fonts and #fonts > 0 then installed_fonts_scanned = true - if trace_search then - logs.report("operating system fonts found", "%d", #fonts) - end + report("log", 2, "operating system fonts found", "%d", #fonts) for key, value in next, fonts do local file = value.path if file then local ext = extname(file) if ext and font_extensions_set[ext] then file = path_normalize(file) - logs.names_loading("loading font", "%s", file) + report("log", 1, "loading font", "%s", file) load_font(file, fontnames, newfontnames, false) end end end else - if trace_search then - logs.report("Could not retrieve list of installed fonts") - end + report("log", 2, "Could not retrieve list of installed fonts") end end @@ -483,28 +476,22 @@ local function scan_dir(dirname, fontnames, newfontnames, texmf) ]] local list, found = { }, { } local nbfound = 0 - if trace_search then - logs.report("scanning", "%s", dirname) - end + report("log", 2, "scanning", "%s", dirname) for _,i in next, font_extensions do for _,ext in next, { i, upper(i) } do found = glob(format("%s/**.%s$", dirname, ext)) -- note that glob fails silently on broken symlinks, which happens -- sometimes in TeX Live. - if trace_search then - logs.report("fonts found", "%s '%s' fonts found", #found, ext) - end + report("log", 2, "fonts found", "%s '%s' fonts found", #found, ext) nbfound = nbfound + #found table.append(list, found) end end - if trace_search then - logs.report("fonts found", "%d fonts found in '%s'", nbfound, dirname) - end + report("log", 2, "fonts found", "%d fonts found in '%s'", nbfound, dirname) for _,file in next, list do file = path_normalize(file) - logs.names_loading("loading font", "%s", file) + report("log", 1, "loading font", "%s", file) load_font(file, fontnames, newfontnames, texmf) end end @@ -515,9 +502,9 @@ local function scan_texmf_fonts(fontnames, newfontnames) variables OPENTYPEFONTS and TTFONTS of texmf.cnf ]] if expandpath("$OSFONTDIR"):is_empty() then - logs.info("Scanning TEXMF fonts...") + report("info", 0, "Scanning TEXMF fonts...") else - logs.info("Scanning TEXMF and OS fonts...") + report("info", 0, "Scanning TEXMF and OS fonts...") end local fontdirs = expandpath("$OPENTYPEFONTS"):gsub("^%.", "") fontdirs = fontdirs .. expandpath("$TTFONTS"):gsub("^%.", "") @@ -548,9 +535,7 @@ local function read_fonts_conf(path, results) ]] local f = io.open(path) if not f then - if trace_search then - logs.report("cannot open file", "%s", path) - end + report("log", 2, "cannot open file", "%s", path) return results end local incomments = false @@ -648,10 +633,8 @@ local function scan_os_fonts(fontnames, newfontnames) - fontcache for Unix (reads the fonts.conf file and scans the directories) - a static set of directories for Windows and MacOSX ]] - logs.info("Scanning OS fonts...") - if trace_search then - logs.info("Searching in static system directories...") - end + report("info", 0, "Scanning OS fonts...") + report("info", 2, "Searching in static system directories...") for _,d in next, get_os_dirs() do scan_dir(d, fontnames, newfontnames, false) end @@ -663,7 +646,7 @@ local function update_names(fontnames, force) - fontnames is the final table to return - force is whether we rebuild it from scratch or not ]] - logs.info("Updating the font names database") + report("info", 0, "Updating the font names database") if force then fontnames = fontnames_init() @@ -673,10 +656,8 @@ local function update_names(fontnames, force) end if fontnames.version ~= names.version then fontnames = fontnames_init() - if trace_search then - logs.report("No font names database or old one found; " - .."generating new one") - end + report("log", 0, "No font names database or old one found; " + .."generating new one") end end local newfontnames = fontnames_init() @@ -700,10 +681,10 @@ local function save_names(fontnames) local luaname, lucname = make_name(path) table.tofile(luaname, fontnames, true) caches.compile(fontnames,luaname,lucname) - logs.info("Font names database saved") + report("info", 0, "Font names database saved") return path else - logs.info("Failed to save names database") + report("info", 0, "Failed to save names database") return nil end end diff --git a/otfl-luat-ovr.lua b/otfl-luat-ovr.lua index 63ad6a7..0657bbe 100644 --- a/otfl-luat-ovr.lua +++ b/otfl-luat-ovr.lua @@ -12,8 +12,8 @@ local module_name = "luaotfload" local texiowrite_nl = texio.write_nl local stringformat = string.format local tableconcat = table.concat -local ioflush = io.flush local dummyfunction = function() end +local type = type --[[doc-- We recreate the verbosity levels previously implemented in font-nms: @@ -47,24 +47,44 @@ function logs.report(category,fmt,...) end end -function logs.info(category,fmt,...) - if fmt then - texiowrite_nl(stringformat("%s | %s: %s",module_name,category,stringformat(fmt,...))) - elseif category then - texiowrite_nl(stringformat("%s | %s",module_name,category)) - else - texiowrite_nl(stringformat("%s |",module_name)) - end - ioflush() -end - -logs.names_loading = function (category, fmt, ...) - if loglevel > 1 then +logs.names_search = function (category, fmt, ...) + if loglevel > 2 then local res = { module_name, " |" } if category then res[#res+1] = " " .. category end if fmt then res[#res+1] = ": " .. stringformat(fmt, ...) end - texiowrite_nl(tableconcat(res)) - ioflush() + texiowrite_nl("log", tableconcat(res)) + end +end + + +local log = function (category, fmt, ...) + local res = { module_name, " |" } + if category then res[#res+1] = " " .. category end + if fmt then res[#res+1] = ": " .. stringformat(fmt, ...) end + texiowrite_nl("log", tableconcat(res)) +end + +local stdout = function (category, fmt, ...) + local res = { module_name, " |" } + if category then res[#res+1] = " " .. category end + if fmt then res[#res+1] = ": " .. stringformat(fmt, ...) end + texiowrite_nl(tableconcat(res)) +end + +local level_ids = { common = 0, loading = 1, search = 2 } + +logs.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 (...) + else + stdout (...) + end end end -- cgit v1.2.3 From 283761cb68bff34c7d69a96b563382aa5e5a5142 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 01:01:25 +0200 Subject: finish move to new loggers --- otfl-font-ltx.lua | 10 ++++++++-- otfl-luat-ovr.lua | 20 -------------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/otfl-font-ltx.lua b/otfl-font-ltx.lua index 4a86593..d1799d7 100644 --- a/otfl-font-ltx.lua +++ b/otfl-font-ltx.lua @@ -24,6 +24,8 @@ end local feature_list = { } +local report = logs.names_report + --- ugh TODO use lpeg instead local function isstyle(s) local style = string.lower(s):split("/") @@ -37,7 +39,8 @@ local function isstyle(s) elseif v:find("^s=") then feature_list.optsize = v:split("=")[2] elseif v == "aat" or v == "icu" or v == "gr" then - logs.report("load font", "unsupported font option: %s", v) + report("log", 0, + "load font", "unsupported font option: %s", v) elseif not v:is_empty() then feature_list.style = v:gsub("[^%a%d]", "") end @@ -98,7 +101,9 @@ defaults.lao = defaults.thai local function set_default_features(script) local features local script = script or "dflt" - logs.report("load font", "auto-selecting default features for script: %s", script) + report("log", 0, "load font", + "auto-selecting default features for script: %s", + script) if defaults[script] then features = defaults[script] else @@ -195,3 +200,4 @@ function fonts.definers.applypostprocessors(tfmdata) end return tfmdata end +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/otfl-luat-ovr.lua b/otfl-luat-ovr.lua index 0657bbe..c57c68e 100644 --- a/otfl-luat-ovr.lua +++ b/otfl-luat-ovr.lua @@ -37,26 +37,6 @@ local set_loglevel = function (n) end logs.set_loglevel = set_loglevel -function logs.report(category,fmt,...) - if fmt then - texiowrite_nl('log', stringformat("%s | %s: %s",module_name,category,stringformat(fmt,...))) - elseif category then - texiowrite_nl('log', stringformat("%s | %s",module_name,category)) - else - texiowrite_nl('log', stringformat("%s |",module_name)) - end -end - -logs.names_search = function (category, fmt, ...) - if loglevel > 2 then - local res = { module_name, " |" } - if category then res[#res+1] = " " .. category end - if fmt then res[#res+1] = ": " .. stringformat(fmt, ...) end - texiowrite_nl("log", tableconcat(res)) - end -end - - local log = function (category, fmt, ...) local res = { module_name, " |" } if category then res[#res+1] = " " .. category end -- cgit v1.2.3 From 6db1231adfffc18f1d015a4b916a7ad51d9a5aa4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 01:10:34 +0200 Subject: cosmetic changes to luat-ovr --- otfl-luat-ovr.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/otfl-luat-ovr.lua b/otfl-luat-ovr.lua index c57c68e..2a8af9e 100644 --- a/otfl-luat-ovr.lua +++ b/otfl-luat-ovr.lua @@ -12,7 +12,6 @@ local module_name = "luaotfload" local texiowrite_nl = texio.write_nl local stringformat = string.format local tableconcat = table.concat -local dummyfunction = function() end local type = type --[[doc-- @@ -31,9 +30,9 @@ We recreate the verbosity levels previously implemented in font-nms: local loglevel = 1 --- default local set_loglevel = function (n) - if type(n) == "number" then - loglevel = n - end + if type(n) == "number" then + loglevel = n + end end logs.set_loglevel = set_loglevel -- cgit v1.2.3 From 17893ca1a58ea8bcea492df192209bc96432fcd0 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 01:49:06 +0200 Subject: =?UTF-8?q?make=20the=20=E2=80=9Cverbose=E2=80=9D=20switch=20effec?= =?UTF-8?q?tive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mkluatexfontdb.lua | 25 +++++++++++++++++++------ otfl-luat-ovr.lua | 12 +++++++++++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 0f0df9f..3e1caf3 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -53,6 +53,7 @@ Valid options: -vvv print all steps of directory searching -V --version print version and exit -h --help print this message + --log=stdout redirect log output to stdout The font database will be saved to %s @@ -73,22 +74,24 @@ Here we fill cmdargs with the good values, and then analyze it. local long_options = { force = "f", - quiet = "q", help = "h", + log = 1, + quiet = "q", verbose = 1 , version = "V", } -local short_options = "fqpvVh" +local short_options = "fqvVh" local force_reload = nil local function process_cmdline() - local options, _, _ = alt_getopt.get_ordered_opts (arg, - short_options, - long_options) + local options, _, optarg = + alt_getopt.get_ordered_opts (arg, short_options, long_options) local log_level = 1 - for _,v in next, options do + local nopts = #options + for n=1, nopts do + local v = options[n] if v == "q" then log_level = 0 elseif v == "v" then @@ -105,6 +108,16 @@ local function process_cmdline() os.exit(0) elseif v == "f" then force_reload = 1 + elseif v == "verbose" then + local lvl = optarg[n] + if lvl then + log_level = tonumber(lvl) + end + elseif v == "log" then + local str = optarg[n] + if str then + logs.set_logout(str) + end end end logs.set_loglevel(log_level) diff --git a/otfl-luat-ovr.lua b/otfl-luat-ovr.lua index 2a8af9e..bd04eeb 100644 --- a/otfl-luat-ovr.lua +++ b/otfl-luat-ovr.lua @@ -28,6 +28,7 @@ We recreate the verbosity levels previously implemented in font-nms: --doc]]-- local loglevel = 1 --- default +local logout = "log" local set_loglevel = function (n) if type(n) == "number" then @@ -36,11 +37,20 @@ local set_loglevel = function (n) end logs.set_loglevel = set_loglevel +local set_logout = function (s) + if s == "stdout" then + logout = "term" + --else --- remains “log” + end +end + +logs.set_logout = set_logout + local log = function (category, fmt, ...) local res = { module_name, " |" } if category then res[#res+1] = " " .. category end if fmt then res[#res+1] = ": " .. stringformat(fmt, ...) end - texiowrite_nl("log", tableconcat(res)) + texiowrite_nl(logout, tableconcat(res)) end local stdout = function (category, fmt, ...) -- cgit v1.2.3 From ba7ad3c0cbede3e91fac9101b6f57f1849726725 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 03:46:31 +0200 Subject: us TFM reader for OFM (needs testing) --- luaotfload.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index 6e951dd..1ca1fdc 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -83,10 +83,10 @@ filename. local find_vf_file = function (name) local fullname = find_file(name, "ovf") if not fullname then - fullname = find_file(file.removesuffix(file.basename(name)), "ovf") + fullname = find_file(file.removesuffix(file.basename(name)), "ovf") end if fullname then - log("loading virtual font file %s.", fullname) + log("loading virtual font file %s.", fullname) end return fullname end @@ -178,9 +178,9 @@ 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, + load_before = "otfl-fonts-merged.lua", + -- load_after = nil, --- TODO, this is meant for callbacks + skip_loading = true, }} --[[doc-- @@ -235,7 +235,9 @@ if fonts and fonts.readers.tfm then --- (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.readers.ofm = fonts.readers.tfm + fonts.handlers.ofm = fonts.handlers.tfm + fonts.formats.ofm = fonts.formats.tfm -------------------------------------------------------------------- end loadmodule"font-nms.lua" @@ -362,6 +364,6 @@ add_to_callback("luaotfload.patch_font", "unicodemath.set_sscale_diments") ]==] --- vim:tw=71:sw=2:ts=2:expandtab +-- vim:tw=71:sw=4:ts=4:expandtab -- End of File `luaotfload.lua'. -- cgit v1.2.3 From 690cd37becccf9e1f63c7c09054f82e54a50a13d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 13:13:18 +0200 Subject: avoid two calls to the file library --- luaotfload.lua | 9 ++++++++- luaotfload.sty | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index 1ca1fdc..290bba7 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -80,10 +80,17 @@ Virtual fonts are resolved via a callback. filename. (NB: \CONTEXT\ handles this likewise in \textsf{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(file.basename(name)), "ovf") + --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) diff --git a/luaotfload.sty b/luaotfload.sty index b2b574b..a6714d9 100644 --- a/luaotfload.sty +++ b/luaotfload.sty @@ -20,8 +20,6 @@ \csname ifluaotfloadloaded\endcsname \let\ifluaotfloadloaded\endinput \bgroup\expandafter\expandafter\expandafter\egroup -%\RequireLuaModule{lualibs} -%\RequireLuaModule{luaotfload} \expandafter\ifx\csname ProvidesPackage\endcsname\relax \input luatexbase.sty \else @@ -30,6 +28,18 @@ [2011/10/06 v2.0 OpenType layout system] \RequirePackage{luatexbase} \fi +%% -------------------------------------------------------------------- +%% from luatex-basics.tex +% As soon as we feel the need this file will file will contain an extension +% to the standard plain register allocation. For the moment we stick to a +% rather dumb attribute allocator. We start at 256 because we don't want +% any interference with the attributes used in the font handler. +%\newcount \lastallocatedattribute \lastallocatedattribute=255 + +%\def\newattribute#1% + %{\global\advance\lastallocatedattribute 1 + %\attributedef#1\lastallocatedattribute} +%% -------------------------------------------------------------------- \directlua{require"luaotfload"} \endinput %% -- cgit v1.2.3 From 9e882b845064f01cd3ba2cf06a2536ec9f43f80c Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 13:21:38 +0200 Subject: load Lua code the *right* way# --- luaotfload.sty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaotfload.sty b/luaotfload.sty index a6714d9..2200736 100644 --- a/luaotfload.sty +++ b/luaotfload.sty @@ -40,7 +40,7 @@ %{\global\advance\lastallocatedattribute 1 %\attributedef#1\lastallocatedattribute} %% -------------------------------------------------------------------- -\directlua{require"luaotfload"} +\RequireLuaModule{luaotfload} \endinput %% %% End of file `luaotfload.sty'. -- cgit v1.2.3 From f3d01d6407f3a8bd178a876a3a6228102618b986 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 14:12:40 +0200 Subject: clean font-pfb --- luaotfload.lua | 6 ++++-- otfl-font-pfb.lua | 28 ++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index 290bba7..34e5bdf 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -243,10 +243,12 @@ if fonts and fonts.readers.tfm then --- 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 - fonts.formats.ofm = fonts.formats.tfm + fonts.handlers.ofm = fonts.handlers.tfm --- empty anyways + fonts.formats.ofm = fonts.formats.tfm --- “type1” -------------------------------------------------------------------- end +loadmodule"font-pfb.lua" + loadmodule"font-nms.lua" loadmodule"font-clr.lua" loadmodule"font-ltx.lua" diff --git a/otfl-font-pfb.lua b/otfl-font-pfb.lua index 66abf23..8ab6b29 100644 --- a/otfl-font-pfb.lua +++ b/otfl-font-pfb.lua @@ -1,8 +1,24 @@ -local fonts = fonts -local readers = fonts.readers +local fonts = fonts -fonts.formats.pfb = "pfb" -fonts.formats.pfa = "pfa" +--- +--- opentype reader (from font-otf.lua): +--- (spec : table) -> (suffix : string) -> (format : string) -> (font : table) +--- -function readers.pfb(specification) return readers.opentype(specification,"pfb","type1") end -function readers.pfa(specification) return readers.opentype(specification,"pfa","type1") end +local pfb_reader = function (specification) + return readers.opentype(specification,"pfb","type1") +end + +local pfa_reader = function (specification) + return readers.opentype(specification,"pfa","type1") +end + +fonts.formats.pfb = "type1" +fonts.readers.pfb = pfb_reader +fonts.handlers.pfb = { } --- empty, as with tfm + +fonts.formats.pfa = "type1" +fonts.readers.pfa = pfa_reader +fonts.handlers.pfa = { } + +-- vim:tw=71:sw=2:ts=2:expandtab -- cgit v1.2.3 From 05d201f0870567a0b4f86cf80e0445be2f987dc3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 14:26:15 +0200 Subject: prefer locals in font-clr --- otfl-font-clr.lua | 64 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/otfl-font-clr.lua b/otfl-font-clr.lua index 9fe2916..439fd7c 100644 --- a/otfl-font-clr.lua +++ b/otfl-font-clr.lua @@ -6,7 +6,15 @@ if not modules then modules = { } end modules ['font-clr'] = { license = "GPL" } -local format = string.format +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 otffeatures = fonts.constructors.newfeatures("otf") local ids = fonts.hashes.identifiers @@ -21,9 +29,9 @@ local function setcolor(tfmdata,value) if #value == 6 or #value == 8 then sanitized = value elseif #value == 7 then - _, _, sanitized = value:find("(......)") + _, _, sanitized = stringfind(value, "(......)") elseif #value > 8 then - _, _, sanitized = value:find("(........)") + _, _, sanitized = stringfind(value, "(........)") else -- broken color code ignored, issue a warning? end @@ -46,9 +54,9 @@ registerotffeature { local function hex2dec(hex,one) if one then - return format("%.1g", tonumber(hex, 16)/255) + return stringformat("%.1g", tonumber(hex, 16)/255) else - return format("%.3g", tonumber(hex, 16)/255) + return stringformat("%.3g", tonumber(hex, 16)/255) end end @@ -59,17 +67,17 @@ local function pageresources(a) if not res then res = "/TransGs1<>" end - res2 = format("/TransGs%s<>", a, a, a) - res = format("%s%s", res, res:find(res2) and "" or res2) + res2 = stringformat("/TransGs%s<>", a, a, a) + res = stringformat("%s%s", res, stringfind(res, res2) and "" or res2) end local function hex_to_rgba(hex) local r, g, b, a, push, pop, res3 if hex then if #hex == 6 then - _, _, r, g, b = hex:find('(..)(..)(..)') + _, _, r, g, b = stringfind(hex, '(..)(..)(..)') elseif #hex == 8 then - _, _, r, g, b, a = hex:find('(..)(..)(..)(..)') + _, _, r, g, b, a = stringfind(hex, '(..)(..)(..)(..)') a = hex2dec(a,true) pageresources(a) end @@ -80,24 +88,24 @@ local function hex_to_rgba(hex) g = hex2dec(g) b = hex2dec(b) if a then - push = format('/TransGs%g gs %s %s %s rg', a, r, g, b) + push = stringformat('/TransGs%g gs %s %s %s rg', a, r, g, b) pop = '0 g /TransGs1 gs' else - push = format('%s %s %s rg', r, g, b) + push = stringformat('%s %s %s rg', r, g, b) pop = '0 g' end return push, pop end -local glyph = node.id('glyph') -local hlist = node.id('hlist') -local vlist = node.id('vlist') -local whatsit = node.id('whatsit') -local pgi = node.id('page_insert') -local sbox = node.id('sub_box') +local glyph = nodetype('glyph') +local hlist = nodetype('hlist') +local vlist = nodetype('vlist') +local whatsit = nodetype('whatsit') +local pgi = nodetype('page_insert') +local sbox = nodetype('sub_box') local function lookup_next_color(head) - for n in node.traverse(head) do + for n in traverse_nodes(head) do if n.id == glyph then if ids[n.font] and ids[n.font].properties and ids[n.font].properties.color then return ids[n.font].properties.color @@ -119,7 +127,7 @@ local function lookup_next_color(head) end local function node_colorize(head, current_color, next_color) - for n in node.traverse(head) do + for n in traverse_nodes(head) do if n.id == hlist or n.id == vlist or n.id == sbox then local next_color_in = lookup_next_color(n.next) or next_color n.list, current_color = node_colorize(n.list, current_color, next_color_in) @@ -128,19 +136,19 @@ local function node_colorize(head, current_color, next_color) if tfmdata and tfmdata.properties and tfmdata.properties.color then if tfmdata.properties.color ~= current_color then local pushcolor = hex_to_rgba(tfmdata.properties.color) - local push = node.new(whatsit, 8) + local push = newnode(whatsit, 8) push.mode = 1 push.data = pushcolor - head = node.insert_before(head, n, push) + head = insert_node_before(head, n, push) current_color = tfmdata.properties.color end local next_color_in = lookup_next_color (n.next) or next_color if next_color_in ~= tfmdata.properties.color then local _, popcolor = hex_to_rgba(tfmdata.properties.color) - local pop = node.new(whatsit, 8) + local pop = newnode(whatsit, 8) pop.mode = 1 pop.data = popcolor - head = node.insert_after(head, n, pop) + head = insert_node_after(head, n, pop) current_color = nil end end @@ -154,11 +162,11 @@ local function font_colorize(head) -- and remove it to avoid duplicating it later if res then local r = "/ExtGState<<"..res..">>" - tex.pdfpageresources = tex.pdfpageresources:gsub(r, "") + tex.pdfpageresources = stringgsub(tex.pdfpageresources, r, "") end local h = node_colorize(head, nil, nil) -- now append our page resources - if res and res:find("%S") then -- test for non-empty string + if res and stringfind(res, "%S") then -- test for non-empty string local r = "/ExtGState<<"..res..">>" tex.pdfpageresources = tex.pdfpageresources..r end @@ -169,7 +177,11 @@ local color_callback_activated = 0 function add_color_callback() if color_callback_activated == 0 then - luatexbase.add_to_callback("pre_output_filter", font_colorize, "loaotfload.colorize") + luatexbase.add_to_callback( + "pre_output_filter", font_colorize, "luaotfload.colorize") color_callback_activated = 1 end end + +-- vim:tw=71:sw=4:ts=4:expandtab + -- cgit v1.2.3 From 279700215fb4992a1ea545bea8654fe8dceb67db Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 14:56:00 +0200 Subject: remove node-inj (contaied in merge) --- otfl-node-inj.lua | 498 ------------------------------------------------------ 1 file changed, 498 deletions(-) delete mode 100644 otfl-node-inj.lua diff --git a/otfl-node-inj.lua b/otfl-node-inj.lua deleted file mode 100644 index 246aaad..0000000 --- a/otfl-node-inj.lua +++ /dev/null @@ -1,498 +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. - -local next = next - -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 nodepool = nodes.pool -local newkern = nodepool.kern - -local traverse_id = node.traverse_id -local unset_attribute = node.unset_attribute -local has_attribute = node.has_attribute -local set_attribute = node.set_attribute -local copy_node = node.copy -local insert_node_before = node.insert_before -local insert_node_after = node.insert_after - -local markbase = attributes.private('markbase') -local markmark = attributes.private('markmark') -local markdone = attributes.private('markdone') -local cursbase = attributes.private('cursbase') -local curscurs = attributes.private('curscurs') -local cursdone = attributes.private('cursdone') -local kernpair = attributes.private('kernpair') -local ligacomp = attributes.private('ligacomp') -local fontkern = attributes.private('fontkern') - -if context then - - local kern = nodes.pool.register(newkern()) - - set_attribute(kern,fontkern,1) -- we can have several, attributes are shared - - newkern = function(k) - local c = copy_node(kern) - c.kern = k - return c - end - -end - --- 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. - -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 - set_attribute(start,cursbase,bound) - set_attribute(nxt,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 = has_attribute(current,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 - set_attribute(current,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 - set_attribute(current,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) -- 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 = has_attribute(base,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 } - set_attribute(start,markmark,bound) - set_attribute(start,markdone,index) - return dx, dy, bound - else - report_injections("possible problem, U+%05X is base mark without data (id: %s)",base.char,bound) - end - end --- index = index or 1 - index = index or 1 - bound = #marks + 1 - set_attribute(base,markbase,bound) - set_attribute(start,markmark,bound) - set_attribute(start,markdone,index) - marks[bound] = { [index] = { dx, dy, rlmode } } - 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 = has_attribute(n,kernpair) - local mb = has_attribute(n,markbase) - local mm = has_attribute(n,markmark) - local md = has_attribute(n,markdone) - local cb = has_attribute(n,cursbase) - local cc = has_attribute(n,curscurs) - report_injections("char U+%05X, font=%s",n.char,n.font) - if kp then - local k = kerns[kp] - if k[3] then - report_injections(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?") - else - report_injections(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?") - end - end - if mb then - report_injections(" markbase: bound=%s",mb) - end - if mm then - local m = marks[mm] - if mb then - local m = m[mb] - if m then - report_injections(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?") - else - report_injections(" markmark: bound=%s, missing index",mm) - end - else - m = m[1] - report_injections(" markmark: bound=%s, dx=%s, dy=%s",mm,m and m[1] or "?",m and m[2] or "?") - end - end - if cb then - report_injections(" cursbase: bound=%s",cb) - end - if cc then - local c = cursives[cc] - report_injections(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?") - 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. - -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 = has_attribute(n,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 = has_attribute(n,cursbase) - if p_cursbase then - local n_curscurs = has_attribute(n,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 = has_attribute(p,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 = has_attribute(n,markmark) - if p_markbase == n_markmark then - local index = has_attribute(n,markdone) or 1 - local d = mrks[index] - if d then - local rlmode = d[3] - if rlmode and rlmode >= 0 then - -- new per 2010-10-06, width adapted per 2010-02-03 - -- we used to negate the width of marks because in tfm - -- that makes sense but we no longer do that so as a - -- consequence the sign of p.width was changed (we need - -- to keep an eye on it as we don't have that many fonts - -- that enter this branch .. I'm still not sure if this - -- one is right - local k = wx[p] - if k then - n.xoffset = p.xoffset + p.width + d[1] - k[2] - else - -- n.xoffset = p.xoffset + p.width + d[1] - -- lucida U\char"032F (default+mark) - n.xoffset = p.xoffset - p.width + d[1] -- 01-05-2011 - end - else - local k = wx[p] - if k then - n.xoffset = p.xoffset - d[1] - k[2] - else - n.xoffset = p.xoffset - d[1] - 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, w = k[2] or 0, 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)) - 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 - -- 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)) - 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 = has_attribute(n,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 - return head, true - else - -- no tracing needed - end - return head, false -end -- cgit v1.2.3 From d4b6b18b1035688a270fa7fff292f38a48e2b0d4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 14:56:46 +0200 Subject: update .gitignore --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 11e429a..5c508a2 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,10 @@ tests/*.lua tests/*.otf tests/*.aux tests/phg-*.tex +tests/*.pfb +tests/*.afm +tests/*.tfm +tests/*.dvi +tests/*.ofm +tests/*.ovp +tests/*.ovf -- cgit v1.2.3 From f48b1743185bb89337a9c8224ba05628c106128e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 20:27:36 +0200 Subject: raw import luaotfload.lua into luaotfload.dtx --- luaotfload.dtx | 512 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 310 insertions(+), 202 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 954e40c..7f1cbdd 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -67,7 +67,7 @@ and the derived files \file{luaotfload.sty}{\from{luaotfload.dtx}{package}}% } -% The following hacks are to generate a lua file with lua comments starting by +% The following hacks are to generate a lua file with lua comments starting with % -- instead of %% \def\MetaPrefix{-- } @@ -468,269 +468,377 @@ and the derived files % \iffalse %<*lua> % \fi -% -% \section{Initializations} -% % \begin{macrocode} module("luaotfload", package.seeall) -% \end{macrocode} -% -% \begin{macrocode} + luaotfload.module = { name = "luaotfload", - version = 2.0, - date = "2011/10/06", + 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, dofile = type, next, dofile +local stringfind = string.find +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 + % \end{macrocode} % -% \begin{macrocode} -local error, warning, info, log = luatexbase.provides_module(luaotfload.module) -% \end{macrocode} -% -% The minimal required \luatex version. -% -% \begin{macrocode} -local luatex_version = 70 -% \end{macrocode} +% No final decision has been made on how to handle font definition. +% At the moment, there are three candidates: The \textsf{generic} +% callback as hard-coded in the font loader, the \textsf{old} wrapper, +% and a simplified version of the latter (\textsf{patch}) that does +% nothing besides applying font patches. % % \begin{macrocode} +luaotfload.font_definer = "patch" --- | “generic” | “old” + +local fl_prefix = "otfl" -- “luatex” for luatex-plain + +local error, warning, info, log = luatexbase.provides_module(luaotfload.module) + +local luatex_version = 75 + if tex.luatexversion < luatex_version then warning("LuaTeX v%.2f is old, v%.2f is recommended.", tex.luatexversion/100, luatex_version /100) end -% \end{macrocode} -% -% Some required functions missing from \textsf{lualibs} package. -% -% \begin{macrocode} -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 +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) + error("file %s not found.", tofind) end end + % \end{macrocode} % -% \begin{macrocode} -function table.derive(parent) - local child = { } - if parent then - setmetatable(child,{ __index = parent }) - end - return child -end -% \end{macrocode} +% Virtual fonts are resolved via a callback. +% \verb|find_vf_file| derives the name of the virtual font file from the +% filename. +% (NB: \CONTEXT\ handles this likewise in \textsf{font-vf.lua}.) % % \begin{macrocode} -function string.quoted(str) - return string.format("%q",str) +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 + +--[[-- keep --]] +--- from Hans (all merged): + +--- file name modified include name +--- × basics-gen.lua t luat-basics-gen +--- × font-def -> fonts-def t luatex-font-def (there’s also the normal font-def!) +--- × fonts-enc f luatex-font-enc +--- × fonts-ext t luatex-fonts-ext +--- × fonts-lua f luatex-fonts-lua +--- fonts-tfm f luatex-fonts-tfm +--- × fonts-cbk f luatex-fonts-lua + +--- from Hans (unmerged): +--- font-otc.lua -> otfl-font-otc.lua + +--- from luaotfload: +--- otfl-luat-ovr.lua -- override some luat-dum functions +--- otfl-font-clr.lua +--- otfl-font-ltx.lua +--- otfl-font-nms.lua +--- otfl-font-pfb.lua -- ? + +--[[-- new --]] +--- basics-nod (merged as fonts-nod !) +--- fonts-demo-vf-1.lua +--- fonts-syn (merged) + +--[[-- merged, to be dropped --]] +--- otfl-data-con.lua +--- otfl-font-cid.lua +--- otfl-font-con.lua +--- otfl-font-ini.lua +--- otfl-font-ota.lua +--- otfl-font-otb.lua +--- otfl-font-otf.lua +--- otfl-font-oti.lua +--- otfl-font-otn.lua + % \end{macrocode} % -% \section{Module loading} +% We treat the fontloader as a black box so behavior is consistent +% between formats. +% The wrapper file is |otfl-fonts.lua| which we imported from +% \LUATEX-Plain. +% It has roughly two purposes: +% (\textit{1}) insert the functionality required for fontloader, and +% (\textit{2}) put it in place via the respective callbacks. +% How the first step is executed depends on the presence on the +% \emph{merged font loader code}. +% In \textsf{luaotfload} this is contained in the file +% |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{tabular}{ll} +% \texttt{generic\textunderscore context} & +% encapsulation mechanism, callback functions +% \\ +% \texttt{non\textunderscore generic\textunderscore context} & +% customized code insertion +% \\ +% \end{tabular} +% With \verb|non_generic_context| we can tailor the font loader insertion +% to our file naming habits (key \verb|load_before|). +% Additionally, \verb|skip_loading| can be unset to force loading of +% the original libraries as though the merged code was absent. +% Another key, \verb|load_after| is called at the time when the font +% loader is actually inserted. +% In combination with the option \verb|no_callbacks_yet| in +% \verb|generic_context|, we can insert our own, +% \textsf{luatexbase}-style callback handling here. % % \begin{macrocode} -require('otfl-basics-gen.lua') -require('otfl-luat-ovr.lua') -- overrides some otfl-basics-gen.lua functions -require('otfl-data-con.lua') -require('otfl-basics-nod.lua') +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, +}} + % \end{macrocode} % -% 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. +% The imported font loader will call \verb|callback.register| once +% (during \verb|font-def.lua|). +% This is unavoidable but harmless, so we make it call a dummy instead. % % \begin{macrocode} -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 -% \end{macrocode} -% -% \begin{macrocode} -require('otfl-font-ini.lua') -require('otfl-font-con.lua') -require('otfl-fonts-enc.lua') -require('otfl-font-cid.lua') -require('otfl-font-map.lua') -require('otfl-font-nms.lua') -require('otfl-fonts-tfm.lua') -require('otfl-font-oti.lua') -require('otfl-font-otf.lua') -require('otfl-font-pfb.lua') -require('otfl-font-otb.lua') -require('otfl-node-inj.lua') -require('otfl-font-otn.lua') -require('otfl-font-ota.lua') -require('otfl-font-otc.lua') -require('otfl-fonts-lua.lua') -require('otfl-font-def.lua') -require('otfl-font-ltx.lua') -require('otfl-fonts-ext.lua') -require('otfl-fonts-cbk.lua') -require('otfl-font-clr.lua') +local trapped_register = callback.register +callback.register = dummy_function + % \end{macrocode} % -% Here we override some defaults set in \context code. +% Now that things are sorted out we can load the fontloader. % % \begin{macrocode} -fonts.mode = "node" -caches.compilemethod = "both" +loadmodule"fonts.lua" + % \end{macrocode} % -% Now overriding the \context's definition of |tlig| and |trep| features, -% using code points instead of glyph names to make it font independent. +% After the fontloader is ready we can restore the callback trap from +% \textsf{luatexbase}. % % \begin{macrocode} -local everywhere = { ["*"] = { ["*"] = true } } - -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 - [0x201E] = {0x002C, 0x002C}, -- quotedblbase - [0x00A1] = {0x0021, 0x2018}, -- exclamdown - [0x00BF] = {0x003F, 0x2018}, -- questiondown - }, - flags = { }, - }, - { - type = "ligature", - features = everywhere, - data = { - [0x201C] = {0x0060, 0x0060}, -- quotedblleft - [0x201D] = {0x0027, 0x0027}, -- quotedblright - [0x00A1] = {0x0021, 0x0060}, -- exclamdown - [0x00BF] = {0x003F, 0x0060}, -- questiondown - }, - flags = { }, - }, -} -fonts.handlers.otf.addfeature("tlig", tlig) -fonts.handlers.otf.addfeature("trep", { }) -- empty, all in tlig now +callback.register = trapped_register + % \end{macrocode} % -% And overriding the \context's definition of |anum|. +% We do our own callback handling with the means provided by luatexbase. +% +% Note: \verb|pre_linebreak_filter| and \verb|hpack_filter| are coupled +% in \CONTEXT\ in the concept of \emph{node processor}. % % \begin{macrocode} -local anum_arabic = { - [0x0030] = 0x0660, - [0x0031] = 0x0661, - [0x0032] = 0x0662, - [0x0033] = 0x0663, - [0x0034] = 0x0664, - [0x0035] = 0x0665, - [0x0036] = 0x0666, - [0x0037] = 0x0667, - [0x0038] = 0x0668, - [0x0039] = 0x0669, -} -local anum_persian = { - [0x0030] = 0x06F0, - [0x0031] = 0x06F1, - [0x0032] = 0x06F2, - [0x0033] = 0x06F3, - [0x0034] = 0x06F4, - [0x0035] = 0x06F5, - [0x0036] = 0x06F6, - [0x0037] = 0x06F7, - [0x0038] = 0x06F8, - [0x0039] = 0x06F9, -} +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") -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 +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” + -------------------------------------------------------------------- end +loadmodule"font-pfb.lua" -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, - }, -} +loadmodule"font-nms.lua" +loadmodule"font-clr.lua" +loadmodule"font-ltx.lua" + +create_callback("luaotfload.patch_font", "simple", dummy_function) -fonts.handlers.otf.addfeature("anum",anum_specification) % \end{macrocode} % -% we provide a callback for patching fonts on the fly, to be used by other -% packages. +% This is a wrapper for the imported font loader. +% As of 2013, everything it does appears to be redundand, so we won’t use +% it. +% Nevertheless, it has been adapted to work with the current structure of +% font data objects and will stay here for reference / until somebody +% reports breakage. % -% \begin{macrocode} -luatexbase.create_callback("luaotfload.patch_font", "simple", function() end) -% \end{macrocode} +% 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. % % \begin{macrocode} -local function deffont(...) - local fontdata = fonts.definers.read(...) - if type(fontdata) == "table" then - luatexbase.call_callback("luaotfload.patch_font", fontdata) +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 fontdata + return tfmdata end + % \end{macrocode} % -% Finally we register the callbacks +% We provide a simplified version of the original font definition +% callback. % % \begin{macrocode} -local handler = nodes.simple_font_handler -luatexbase.reset_callback("define_font") -luatexbase.add_to_callback("pre_linebreak_filter", handler, "luaotfload") -luatexbase.add_to_callback("hpack_filter", handler, "luaotfload") -luatexbase.add_to_callback("define_font", deffont, "luaotfload") +local patch_defined_font = function (...) + local tfmdata = fonts.definers.read(...) + if type(tfmdata) == "table" then + call_callback("luaotfload.patch_font", tfmdata) + end + --inspect(tfmdata.shared.features) + return tfmdata +end + +fonts.mode = "node" +caches.compilemethod = "both" + +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 + +reset_callback("define_font") + +if luaotfload.font_definer == "old" then + add_to_callback("define_font", + old_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 + +loadmodule"features.lua" + +--[==[ +---- is this still necessary? +local set_sscale_diments = function (tfmdata) + local mathconstants = tfmdata.MathConstants + if mathconstants then + local tfmparameters = tfmdata.parameters + if mathconstants.ScriptPercentScaleDown then + tfmparameters[10] = mathconstants.ScriptPercentScaleDown + else -- resort to plain TeX default + tfmparameters[10] = 70 + end + if mathconstants.ScriptScriptPercentScaleDown then + tfmparameters[11] = mathconstants.ScriptScriptPercentScaleDown + else -- resort to plain TeX default + tfmparameters[11] = 50 + end + end +end + +add_to_callback("luaotfload.patch_font", + set_sscale_diments, + "unicodemath.set_sscale_diments") +]==] + +-- vim:tw=71:sw=4:ts=4:expandtab + % \end{macrocode} % % \iffalse -- cgit v1.2.3 From 3b5b837c46a39f20b90c978e34767c0abd5d1e75 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 20:32:28 +0200 Subject: bump version; keep luaotfload well-formed for dtxtool --- luaotfload.lua | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index 34e5bdf..4373cfc 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -1,28 +1,9 @@ --- --- This is file `luaotfload.lua', --- generated with the docstrip utility. --- --- The original source files were: --- --- luaotfload.dtx (with options: `lua') --- This is a generated file. --- --- Copyright (C) 2009-2010 by by Elie Roux --- and Khaled Hosny --- (Support: .) --- --- This work is under the CC0 license. --- --- This work consists of the main source file luaotfload.dtx --- and the derived files --- luaotfload.sty, luaotfload.lua --- module("luaotfload", package.seeall) luaotfload.module = { name = "luaotfload", - version = 1.27, - date = "2012/05/28", + version = 2.2, + date = "2013/04/15", description = "OpenType layout system.", author = "Elie Roux & Hans Hagen", copyright = "Elie Roux", @@ -136,12 +117,6 @@ end --- otfl-font-oti.lua --- otfl-font-otn.lua ---[[-- - it all boils down to this: we load otfl-fonts.lua - which takes care of loading the merged file. - that’s it, go thank Hans! ---]]-- - --[[doc-- We treat the fontloader as a black box so behavior is consistent between formats. @@ -374,5 +349,3 @@ add_to_callback("luaotfload.patch_font", ]==] -- vim:tw=71:sw=4:ts=4:expandtab - --- End of File `luaotfload.lua'. -- cgit v1.2.3 From ed31c97dc564515396dc9a8db7a0e863e8a31a1f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 15 Apr 2013 20:35:50 +0200 Subject: restore section in doc --- luaotfload.dtx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/luaotfload.dtx b/luaotfload.dtx index 7f1cbdd..2d4d022 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -468,6 +468,9 @@ and the derived files % \iffalse %<*lua> % \fi +% +% \section{Initializations} +% % \begin{macrocode} module("luaotfload", package.seeall) -- cgit v1.2.3 From cf7d6eceb827fc8d7c0c0afc2255155552f030af Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 16 Apr 2013 12:13:46 +0200 Subject: =?UTF-8?q?add=20fixes=20courtesy=20of=20=C3=89lie=20Roux=20to=20t?= =?UTF-8?q?he=20database=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otfl-font-nms.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 0942eda..8869bef 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -88,9 +88,16 @@ end local synonyms = { regular = { "normal", "roman", "plain", "book", "medium" }, - bold = { "boldregular", "demi", "demibold" }, + --- TODO note from Élie Roux + --- 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" }, + bolditalic = { + "boldoblique", "boldslanted", "demiitalic", "demioblique", + "demislanted", "demibolditalic", "semibolditalic" + }, } local loaded = false @@ -585,7 +592,7 @@ local function read_fonts_conf(path, results) elseif not lfs.isfile(include) and not lfs.isdir(include) then include = file.join(file.dirname(path), include) end - if lfs.isfile(include) then + if lfs.isfile(include) and kpse.readable_file(include) then -- maybe we should prevent loops here? -- we exclude path with texmf in them, as they should -- be found otherwise -- cgit v1.2.3 From ad7e8482bf485c870e37792a8167d8f414e021a3 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 16 Apr 2013 15:38:44 +0200 Subject: integrate luaotfload.lua into luaotfload.dtx --- luaotfload.dtx | 347 +++++++++++++++++++++++++++++++++++---------------------- luaotfload.lua | 272 +++++++++++++++++++++++++++++--------------- 2 files changed, 401 insertions(+), 218 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 2d4d022..9954a32 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -% Copyright (C) 2009-2011 by Elie Roux +% Copyright (C) 2009-2013 by Elie Roux % and Khaled Hosny % (Support: .) % @@ -47,7 +47,7 @@ \preamble This is a generated file. -Copyright (C) 2009-2011 by by Elie Roux +Copyright (C) 2009-2013 by by Elie Roux and Khaled Hosny (Support: .) @@ -104,35 +104,59 @@ and the derived files %<*driver> \NeedsTeXFormat{LaTeX2e} \ProvidesFile{luaotfload.drv}% - [2011/10/06 v2.0 OpenType layout system]% + [2013/04/16 v2.2 OpenType layout system]% \documentclass{ltxdoc} -\usepackage{metalogo,multicol,mdwlist,fancyvrb,xcolor,xspace} +\usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace} +\usepackage[x11names]{xcolor} +% +\def\primarycolor{DodgerBlue4} +\def\secondarycolor{Goldenrod4} +% \usepackage[ - bookmarks=true, - colorlinks=true, - linkcolor=niceblue, -% urlcolor=niceblue, - citecolor=niceblue, - pdftitle={The luaotfload package}, - pdfsubject={OpenType layout system for Plain TeX and LaTeX}, - pdfauthor={Elie Roux & Khaled Hosny}, + 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}, pdfkeywords={luatex, lualatex, unicode, opentype} - ]{hyperref} - +]{hyperref} \usepackage{fontspec} -\usepackage{unicode-math} -\setmainfont[Ligatures=TeX]{Linux Libertine O} -\setsansfont[Ligatures=TeX]{Linux Biolinum O} -\setmathfont{XITS Math} - -\definecolor{niceblue}{rgb}{0.4,0.6,1.000} - -\newcommand\tex {\TeX\xspace} -\newcommand\pdftex {PDF\TeX\xspace} -\newcommand\luatex {Lua\TeX\xspace} -\newcommand\xetex {\XeTeX\xspace} -\newcommand\latex {\LaTeX\xspace} -\newcommand\context{Con\TeX t\xspace} +%usepackage{unicode-math}%% broken +\setmainfont[Numbers=OldStyle,Ligatures=TeX]{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} + +%%\definecolor{niceblue}{rgb}{0.4,0.6,1.000} + +\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\CONTEXT{Con\TeX t\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}} @@ -163,16 +187,16 @@ and the derived files % % \GetFileInfo{luaotfload.drv} % -% \title{The \textsf{luaotfload} package} -% \date{2011/10/06 v2.0} +% \title{The \identifier{luaotfload} package} +% \date{2013/04/16 v2.2} % \author{Elie Roux and Khaled Hosny\\ % Support: \email{lualatex-dev@tug.org}} % % \maketitle % % \begin{abstract} -% This package is an adaptation of the \context font loading system, providing -% the ability to load \textsf{OpenType} fonts with extended font loading syntax +% This package is an adaptation of the \CONTEXT font loading system, providing +% the ability to load \identifier{OpenType} fonts with extended font loading syntax % supporting a large selection of OpenType font features. % \end{abstract} % @@ -180,31 +204,36 @@ and the derived files % % \section{Introduction} % -% Font management and installation has always been painful with \tex. A lot of -% files are needed for one font (tfm, pfb, map, fd, vf), and as \tex is 8-bit -% each font is limited to 256 characters. But the font world has evolved since -% \tex, and new font technologies have appeared, most notably the so called -% \emph{smart font} technologies like \textsf{OpenType} fonts. These fonts can +% 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 as \TEX is 8-bit each font is limited to 256 +% characters. +% But the font world has evolved since +% \TEX, and new font technologies have appeared, most notably the so called +% \emphasis{smart font} technologies like \identifier{OpenType} fonts. These fonts can % contain a lot of characters, and additional functionalities like ligatures, % old-style numbers, small capitals, etc., and support more complex writing -% systems like Arabic and Indic\footnote{Unfortunately, \textsf{luaotfload} -% doesn't support Indic scripts right now.} scripts. They are widely deployed +% systems like Arabic and Indic\footnote{% +% Unfortunately, \identifier{luaotfload} doesn't support Indic scripts right +% now. +% } scripts. +% They are widely deployed % and available for all modern operating systems and are becoming the de facto % standard fonts for advanced text layout. Until now the only way to use them -% directly in the \tex world was by using them with \xetex. +% directly in the \TEX world was by using them with \XETEX. % -% Unlike \xetex, \luatex does not provide direct support for using these fonts -% by default, but it provides a way to hook Lua code in some points of the \tex +% Unlike \XETEX, \LUATEX does not provide direct support for using these fonts +% by default, but it provides a way to hook \LUA code in some points of the \TEX % processing; for instance, we can improve the font loading system, and text % procession, which what this package is about. % % \section{Loading fonts} % -% \textsf{luaotfload} supports an extended font loading syntax which looks +% \identifier{luaotfload} supports an extended font loading syntax which looks % like: % % \begin{center} -% |\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} % \end{center} % % \noindent @@ -217,11 +246,11 @@ and the derived files % use a select the font from its filename or font name, respectively. If no % prefix is specified |name:| is assumed. % -% For compatibility with \xetex, surrounding the \meta{font name} with square +% For compatibility with \XETEX, surrounding the \meta{font name} with square % brackets is synonymous to using the |file:| prefix. % % Accessing fonts by fontname allows loading system installed fonts as well as -% \textsc{texmf} ones, and requires a font names database; see +% \fileent{texmf} ones, and requires a font names database; see % Section~\ref{sec:fontdb} for more information. % % \paragraph{Font name} @@ -231,8 +260,8 @@ and the derived files % % Fonts loaded by filename may either include their absolute path in the % filesystem or consist of just the filename with a path. If no path is -% specified then \textsf{kpathsea} is used to locate the font (which will -% typically be in the \textsc{texmf} tree or the current directory). +% specified then \identifier{kpathsea} is used to locate the font (which will +% typically be in the \fileent{texmf} tree or the current directory). % % For example, % \begin{quote} @@ -247,7 +276,9 @@ and the derived files % \paragraph{Font features} % % \meta{font features} is semicolon-separated list of feature -% tags\footnote{\url{http://www.microsoft.com/typography/otspec/featurelist.htm}} +% tags\footnote{% +% Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm} +% } % and font options. Font features are prepended with a |+| to turn them on and % a |-| to turn them off, alternatively you can pass |true| or |false| value to % the feature: @@ -267,27 +298,32 @@ and the derived files % \noindent Known font options include: % % \begin{description} -% \item [mode] \hfill \\ -% \textsf{luaotfload} has two OpenType processing modes; |base| and |node|. -% |base| mode works by mapping OpenType features to traditional \tex ligature -% and kerning mechanisms, thus supporting only non-contextual substitutions and -% kerning pairs, but is slightly faster. |node| works by direct processing of -% the node list at Lua end and have more wide support of OpenType features but -% can be slow especially with complex fonts and can't be used in math mode. -% -% By default |node| mode is used, and you have to manually force |base| mode -% when needed e.g. for math fonts. -% -% \item [script] \hfill \\ -% OpenType script -% string,\footnote{\url{http://www.microsoft.com/typography/otspec/scripttags.htm}} -% default value is |dflt|. Some fonts don't assign features to the |dflt| +% \item [mode] +% \identifier{luaotfload} has two OpenType processing modes; \identifier{base} +% and \identifier{node}. +% \identifier{base} mode works by mapping OpenType features to traditional \TEX +% ligature and kerning mechanisms, thus supporting only non-contextual +% substitutions and kerning pairs, but is slightly faster. \identifier{node} +% works by direct processing of the node list at \LUA end and have more wide +% support of OpenType features but can be slow especially with complex fonts +% and can't be used in math mode. +% +% By default \identifier{node} mode is used, and you have to manually force +% \identifier{base} mode when needed, e.~g. for math fonts. +% +% \item [script] +% OpenType script string,\footnote{% +% Cf. \url{http://www.microsoft.com/typography/otspec/scripttags.htm}. +% } +% default value is \identifier{dlft}. +% Some fonts don't assign features to the |dflt| % script, in which case the script need to be set explicitly. % % \item [language] \hfill \\ -% OpenType language -% string,\footnote{\url{http://www.microsoft.com/typography/otspec/languagetags.htm}} -% default value is |latn|. +% OpenType language string,\footnote{% +% Cf. \url{http://www.microsoft.com/typography/otspec/languagetags.htm}. +% } +% default value is \identifier{latn}. % % \item [featurefile] \hfill \\ % a comma-separated list of feature files to be applied to the font. Feature @@ -295,7 +331,9 @@ and the derived files % OpenType features of the font on fly. Features defined in a feature file, % after being applied to the font, can be enabled/disabled like any other % feature. The syntax is documented in Adobe's OpenType Feature File -% Specification.\footnote{\url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}} +% Specification.\footnote{% +% Cf. \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html} +% } % % For example, to set a |tkrn| feature from |mykern.fea| file: % @@ -312,27 +350,43 @@ and the derived files % % \item [protrusion \& expansion] \hfill \\ % Both keys control microtypographic features of the font, namely glyph -% protrusion and expansion. The value of the key is the name of predefined Lua -% tables of protrusion and expansion values; see the end of |otfl-fonts-ext.lua| -% file for an example of such tables. The only predefined value is |default|. -% -% For example, to enable default protrusion:\footnote{You also need to set -% |\pdfprotrudechars2 \pdfadjustspacing2| to activate protrusion and expansion, -% respectively. See \pdftex manual for details.} +% protrusion and expansion. The value of the key is the name of predefined \LUA +% tables of protrusion and expansion values; see the end of +% \fileent{otfl-fonts-ext.lua} +% file for an example of such tables. The only predefined value is +% \identifier{default}. +% +% For example, to enable default protrusion\footnote{% +% You also need to set +% \texmacro{pdfprotrudechars}|=2| +% \texmacro{pdfadjustspacing}|=2| +% to activate protrusion and expansion, respectively. +% See +% \href{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}% +% {\PDFTEX manual} +% for details. +% }: % % |\font\test=Latin Modern Roman:protrusion=default| % \end{description} % % \subparagraph{Non-standard font features} -% \textsf{luaotfload} defines some additional font feature not defined in +% \identifier{luaotfload} defines some additional font feature not defined in % OpenType, currently three features are defined: % % \begin{itemize*} -% \item |anum|: replaces European numbers with eastern Arabic numbers or -% Persian numbers, depending on the value of |language|. -% \item |tlig|: applies legacy \tex ligatures: |``|, |''|, |`|, |'|, |"|, |--|, -% |---|, |!`| and |?`|.\footnote{For \xetex users: this is the equivalent of -% writing |mapping=text-tex| using \xetex's input remapping feature.} +% +% \item \identifier{anum}: +% replaces European numbers with eastern Arabic numbers or Persian +% numbers, depending on the value of \identifier{language}. +% \item \identifier{tlig}: +% applies legacy \TEX ligatures: +% |``|, |''|, |`|, |'|, |"|, |--|, |---|, |!`| and |?`|.% +% \footnote{% +% For \XETEX users: this is the equivalent of the assignment +% \verb|mapping=text-tex| using \XETEX's input remapping feature. +% } +% % \end{itemize*} % % @@ -340,11 +394,11 @@ and the derived files % \section{Font names database} % \label{sec:fontdb} % -% As introduced in the previous section, \textsf{luaotfload} uses a database to -% keep track of fonts available to \luatex. Using this database, fonts can be +% As introduced in the previous section, \identifier{luaotfload} uses a database to +% keep track of fonts available to \LUATEX. Using this database, fonts can be % loaded by font name as well as filename. % -% When \textsf{luaotfload} is asked to load a font by font name, it will check +% When \identifier{luaotfload} is asked to load a font by font name, it will check % if font names database exists and load it, or generate a new database if non % exists. This is all done automatically without user intervention. When the % asked font is missing from the database, it will attempt to update the @@ -352,18 +406,20 @@ and the derived files % fonts without worrying about manually updating the database. % % However, it is sometimes desirable to update the database manually, so -% \textsf{luaotfload} provides a |mkluatexfontdb| utility to manually update +% \identifier{luaotfload} provides a |mkluatexfontdb| utility to manually update % the database. |mkluatexfontdb| is a lua script that can be either run -% directly or as an argument to |texlua|, depending on your system.\footnote{On -% MS Windows it can be run either by calling the wrapper application -% |mkluatexfontdb.exe| or with |texlua.exe mkluatexfontdb.lua|.} +% directly or as an argument to |texlua|, depending on your system.\footnote{% +% On \abbrev{MS} \identifier{Windows} it can be run either by calling the +% wrapper application |mkluatexfontdb.exe| or with +% |texlua.exe mkluatexfontdb.lua|. +% } % % The first time the database is generated may take quite some time to process % every font on your computer. This is particularly noticeable if it occurs % during a typesetting run. Subsequent runs to update the database will be % quite fast, however. % -% \textsf{luaotfload} will parse standard places for fonts in your system to +% \identifier{luaotfload} will parse standard places for fonts in your system to % build the font database. On Linux, it will read |fontconfig| configuration % files to find the font locations; on Windows and Mac~OS~X, it will search in % the standard font locations, |%WINDIR%\Fonts| in Windows and @@ -381,16 +437,16 @@ and the derived files % % \subsection{Blacklisting fonts} % -% Some fonts are problematic in \luatex, if you found that your document takes +% Some fonts are problematic in \LUATEX, if you found that your document takes % too long to compile, or eats all the free memory, you can find the culprit % file by running |mkluatexfontdb| utility with |-v| option to see which font -% file it is stuck with. You can then instruct \textsf{luaotfload} to ignore +% file it is stuck with. You can then instruct \identifier{luaotfload} to ignore % this font by adding it to the blacklist configuration file. % % Simply, create a file named |otfl-blacklist.cnf| and added the to be -% blacklisted files, one per line. Then put the file some where \textsf{kpse} +% blacklisted files, one per line. Then put the file some where \identifier{kpse} % can find. You can either use the base name or the full path. Any thing after -% a |%| sign is ignored. \textsf{luaotfload} reads all files named named +% a |%| sign is ignored. \identifier{luaotfload} reads all files named named % |otfl-blacklist.cnf|, so you can add your own fonts to the global blacklist % by creating a local file |otfl-blacklist.cnf| with the entries you need. You % can also remove a font from this blacklist by prepending the name with a dash @@ -403,15 +459,15 @@ and the derived files % % is blacklisted somewhere else % \end{verbatim} % -% \section{Used \context files} +% \section{Used \CONTEXT files} % -% This package is a wrapper for several files taken from the \context macro -% package. The philosophy is to let \context do all the implementation and +% This package is a wrapper for several files taken from the \CONTEXT macro +% package. The philosophy is to let \CONTEXT do all the implementation and % update these files from time to time. So we try not to modify the files taken -% from \context as far as possible, but we changed their names to prevent name +% from \CONTEXT as far as possible, but we changed their names to prevent name % clashes. % -% The \context files are renamed by adding the prefix |otfl-| to them (|otfl| +% The \CONTEXT files are renamed by adding the prefix |otfl-| to them (|otfl| % as |OTF L|oad). The files are: % % \begin{multicols}{3} @@ -436,8 +492,9 @@ and the derived files % \item |luatex-fonts-tfm.lua| % \item |luatex-basics-gen.lua| % \item |luatex-basics-nod.lua| -% \item |font-age.lua|\footnote{Not renamed as it is loaded directly from -% |fonts-enc.lua|.} +% \item |font-age.lua|\footnote{% +% Not renamed as it is loaded directly from % |fonts-enc.lua|. +% } % \end{itemize*} % \end{multicols} % @@ -446,8 +503,9 @@ and the derived files % \item |otfl-font-clr.lua| % \item |otfl-font-nms.lua| % \item |otfl-luat-ovr.lua| -% \item |otfl-font-ltx.lua|\footnote{A heavily modified version of -% |luatex-fonts-def.lua|.} +% \item |otfl-font-ltx.lua|\footnote{% +% A heavily modified version of |luatex-fonts-def.lua|. +% } % \end{itemize*} % % \section{Troubleshooting} @@ -463,7 +521,7 @@ and the derived files % % |\font\test=file:MyFont.otf:script=latn;+liga;| % -% \part{\texttt{luaotfload.lua}} +% \part{\fileent{luaotfload.lua}} % % \iffalse %<*lua> @@ -500,9 +558,9 @@ local dummy_function = function () end % \end{macrocode} % % No final decision has been made on how to handle font definition. -% At the moment, there are three candidates: The \textsf{generic} -% callback as hard-coded in the font loader, the \textsf{old} wrapper, -% and a simplified version of the latter (\textsf{patch}) that does +% 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. % % \begin{macrocode} @@ -536,7 +594,7 @@ end % Virtual fonts are resolved via a callback. % \verb|find_vf_file| derives the name of the virtual font file from the % filename. -% (NB: \CONTEXT\ handles this likewise in \textsf{font-vf.lua}.) +% (NB: \CONTEXT handles this likewise in \fileent{font-vf.lua}.) % % \begin{macrocode} local Cs, P, lpegmatch = lpeg.Cs, lpeg.P, lpeg.match @@ -602,24 +660,34 @@ end % The wrapper file is |otfl-fonts.lua| which we imported from % \LUATEX-Plain. % It has roughly two purposes: -% (\textit{1}) insert the functionality required for fontloader, and -% (\textit{2}) put it in place via the respective callbacks. +% +% \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 -% \emph{merged font loader code}. -% In \textsf{luaotfload} this is contained in the file +% \emphasis{merged font loader code}. +% In \identifier{luaotfload} this is contained in the file % |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{tabular}{ll} -% \texttt{generic\textunderscore context} & -% encapsulation mechanism, callback functions -% \\ -% \texttt{non\textunderscore generic\textunderscore context} & -% customized code insertion -% \\ -% \end{tabular} +% +% \begin{itemize} +% +% \item \luafunction{generic_context}: +% encapsulation mechanism, callback functions +% +% \item \luafunction{non generic_context}: +% customized code insertion +% +% \end{itemize} +% % With \verb|non_generic_context| we can tailor the font loader insertion % to our file naming habits (key \verb|load_before|). % Additionally, \verb|skip_loading| can be unset to force loading of @@ -628,7 +696,7 @@ end % loader is actually inserted. % In combination with the option \verb|no_callbacks_yet| in % \verb|generic_context|, we can insert our own, -% \textsf{luatexbase}-style callback handling here. +% \identifier{luatexbase}-style callback handling here. % % \begin{macrocode} if not _G. generic_context then _G. generic_context = { } end @@ -665,7 +733,7 @@ loadmodule"fonts.lua" % \end{macrocode} % % After the fontloader is ready we can restore the callback trap from -% \textsf{luatexbase}. +% \identifier{luatexbase}. % % \begin{macrocode} @@ -676,7 +744,7 @@ callback.register = trapped_register % We do our own callback handling with the means provided by luatexbase. % % Note: \verb|pre_linebreak_filter| and \verb|hpack_filter| are coupled -% in \CONTEXT\ in the concept of \emph{node processor}. +% in \CONTEXT\ in the concept of \emphasis{node processor}. % % \begin{macrocode} @@ -848,37 +916,54 @@ add_to_callback("luaotfload.patch_font", % % \fi % -% \part{\texttt{luaotfload.sty}} +% \part{\fileent{luaotfload.sty}} % % \iffalse %<*package> % \fi % -% Classical Plain+\latex package initialization. +% Classical Plain+\LATEX package initialization. % % \begin{macrocode} \csname ifluaotfloadloaded\endcsname \let\ifluaotfloadloaded\endinput -% \end{macrocode} -% -% \begin{macrocode} \bgroup\expandafter\expandafter\expandafter\egroup \expandafter\ifx\csname ProvidesPackage\endcsname\relax \input luatexbase.sty \else \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luaotfload}% - [2011/10/06 v2.0 OpenType layout system] + [2013/04/16 v2.2 OpenType layout system] \RequirePackage{luatexbase} \fi -% \end{macrocode} -% -% \begin{macrocode} \RequireLuaModule{lualibs} +\RequireLuaModule{luaotfload} + +\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}% + [2013/04/16 v2.2 OpenType layout system] + \RequirePackage{luatexbase} +\fi % \end{macrocode} % -% \begin{macrocode} +% %% As soon as we feel the need this file will file will contain an extension +% %% to the standard plain register allocation. For the moment we stick to a +% %% rather dumb attribute allocator. We start at 256 because we don't want +% %% any interference with the attributes used in the font handler. +% %%\newcount \lastallocatedattribute \lastallocatedattribute=255 +% %%\def\newattribute#1% +% %% {\global\advance\lastallocatedattribute 1 +% %% \attributedef#1\lastallocatedattribute} +% +% \begin{macrocode} \RequireLuaModule{luaotfload} +\endinput % \end{macrocode} % \iffalse % diff --git a/luaotfload.lua b/luaotfload.lua index 4373cfc..bdbae0e 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -21,28 +21,55 @@ 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 --[[doc-- -No final decision has been made on how to handle font definition. -At the moment, there are three candidates: The \textsf{generic} -callback as hard-coded in the font loader, the \textsf{old} wrapper, -and a simplified version of the latter (\textsf{patch}) that does -nothing besides applying font patches. +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 fl_prefix = "otfl" -- “luatex” for luatex-plain +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, "/") -local error, warning, info, log = luatexbase.provides_module(luaotfload.module) +--[[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 = 75 +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") @@ -50,16 +77,15 @@ local loadmodule = function (name) log("loading file %s.", found) dofile(found) else - --error("file %s not found.", tofind) error("file %s not found.", tofind) end end --[[doc-- Virtual fonts are resolved via a callback. -\verb|find_vf_file| derives the name of the virtual font file from the -filename. -(NB: \CONTEXT\ handles this likewise in \textsf{font-vf.lua}.) +\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 @@ -118,38 +144,48 @@ end --- otfl-font-otn.lua --[[doc-- + +\subsection{Preparing the Font Loader} We treat the fontloader as a black box so behavior is consistent between formats. -The wrapper file is |otfl-fonts.lua| which we imported from -\LUATEX-Plain. +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: -(\textit{1}) insert the functionality required for fontloader, and -(\textit{2}) put it in place via the respective callbacks. + +\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 \emph{merged font loader code}. -In \textsf{luaotfload} this is contained in the file -|otfl-fonts-merged.lua|. +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{tabular}{ll} - \texttt{generic\textunderscore context} & - encapsulation mechanism, callback functions - \\ - \texttt{non\textunderscore generic\textunderscore context} & - customized code insertion - \\ -\end{tabular} -With \verb|non_generic_context| we can tailor the font loader insertion -to our file naming habits (key \verb|load_before|). -Additionally, \verb|skip_loading| can be unset to force loading of -the original libraries as though the merged code was absent. -Another key, \verb|load_after| is called at the time when the font -loader is actually inserted. -In combination with the option \verb|no_callbacks_yet| in -\verb|generic_context|, we can insert our own, -\textsf{luatexbase}-style callback handling here. + + \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 @@ -166,21 +202,91 @@ _G.non_generic_context = { luatex_fonts = { }} --[[doc-- -The imported font loader will call \verb|callback.register| once -(during \verb|font-def.lua|). -This is unavoidable but harmless, so we make it call a dummy 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. --doc]]-- + local trapped_register = callback.register callback.register = dummy_function --[[doc-- -Now that things are sorted out we can load the fontloader. +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 \emph{private +attributes} for internal use. +These must be kept consistent with the attribute handling methods as +provided by \identifier{luatexbase}. +Previously, when \identifier{luaotfload} imported individual files from +\CONTEXT, the strategy was to override the function that allocates new +attributes at the appropriate time during initialization, making it a +wrapper around \luafunction{luatexbase.new_attribute}. + +\begin{verbatim} +attributes.private = function (name) + local attr = "otfl@" .. name + local number = luatexbase.attributes[attr] + if not number then + number = luatexbase.new_attribute(attr) + end + return number +end +\end{verbatim} + +Now that the fontloader comes as a package, this hack is no longer +applicable. +The attribute handler installed by \identifier{luatex-fonts} (see the +file \fileent{otfl-basics-nod.lua}) cannot be intercepted before the +first call to it takes place. +While it is not feasible to prevent insertion of attributes at the +wrong places, we can still retrieve them from the closure surrounding +the allocation function \luafunction{attributes.private} +using \LUA’s introspection features. + +The recovered attribute identifiers are prefixed “\fileent{otfl@}” to +avoid name clashes. +--doc]]-- + +do + local debug_getupvalue = debug.getupvalue + + local nups = debug.getinfo(attributes.private, "u").nups + local nup, numbers = 0 + while nup <= nups do + nup = nup + 1 + local upname, upvalue = debug_getupvalue(attributes.private, nup) + if upname == "numbers" then + numbers = upvalue + break + end + end + if numbers then + local luatexbase_attributes = luatexbase.attributes + local prefix = "otfl@" + --- re-register attributes from “numbers” + --- ... pull request for luatexbase pending + for name, num in next, numbers do + name = prefix .. name + luatexbase_attributes[name] = num + end + end + --- The definitions used by the fontloader are never + --- called again so it is safe to nil them, I suppose. + debug.setupvalue(attributes.private, nup, { }) + _G.attributes = nil --- needed for initialization only +end + +--[[doc-- + +\subsection{Callbacks} + After the fontloader is ready we can restore the callback trap from -\textsf{luatexbase}. +\identifier{luatexbase}. --doc]]-- callback.register = trapped_register @@ -188,8 +294,8 @@ callback.register = trapped_register --[[doc-- We do our own callback handling with the means provided by luatexbase. -Note: \verb|pre_linebreak_filter| and \verb|hpack_filter| are coupled -in \CONTEXT\ in the concept of \emph{node processor}. +Note: \luafunction{pre_linebreak_filter} and \luafunction{hpack_filter} +are coupled in \CONTEXT in the concept of \emph{node processor}. --doc]]-- add_to_callback("pre_linebreak_filter", @@ -220,29 +326,46 @@ if fonts and fonts.readers.tfm then 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 -loadmodule"font-pfb.lua" +--[[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" +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 appears to be redundand, so we won’t use -it. +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 somebody -reports breakage. +font data objects and will stay here for reference / until breakage is +reported. -TODO +\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 @@ -279,35 +402,34 @@ local define_font_wrapper = function (...) 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 = fonts.definers.read(...) + local tfmdata = read_font_file(...)-- spec -> size -> id -> tmfdata if type(tfmdata) == "table" then call_callback("luaotfload.patch_font", tfmdata) end - --inspect(tfmdata.shared.features) return tfmdata end -fonts.mode = "node" caches.compilemethod = "both" -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 - reset_callback("define_font") +--[[doc-- +Finally we register the callbacks +--doc]]-- + if luaotfload.font_definer == "old" then add_to_callback("define_font", - old_define_font_wrapper, + define_font_wrapper, "luaotfload.define_font", 1) elseif luaotfload.font_definer == "generic" then @@ -324,28 +446,4 @@ end loadmodule"features.lua" ---[==[ ----- is this still necessary? -local set_sscale_diments = function (tfmdata) - local mathconstants = tfmdata.MathConstants - if mathconstants then - local tfmparameters = tfmdata.parameters - if mathconstants.ScriptPercentScaleDown then - tfmparameters[10] = mathconstants.ScriptPercentScaleDown - else -- resort to plain TeX default - tfmparameters[10] = 70 - end - if mathconstants.ScriptScriptPercentScaleDown then - tfmparameters[11] = mathconstants.ScriptScriptPercentScaleDown - else -- resort to plain TeX default - tfmparameters[11] = 50 - end - end -end - -add_to_callback("luaotfload.patch_font", - set_sscale_diments, - "unicodemath.set_sscale_diments") -]==] - -- vim:tw=71:sw=4:ts=4:expandtab -- cgit v1.2.3 From c909ab99d258e6a9132cdd2d14671fe5ea6b15ab Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 16 Apr 2013 18:27:04 +0200 Subject: make mkluatexfontdb script more modular --- mkluatexfontdb.lua | 175 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 132 insertions(+), 43 deletions(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 3e1caf3..5f27d92 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -15,16 +15,18 @@ local texiowrite_nl = texio.write_nl -- First we need to be able to load module (code copied from -- luatexbase-loader.sty): local loader_file = "luatexbase.loader.lua" -local loader_path = assert(kpse.find_file(loader_file, 'tex'), +local loader_path = assert(kpse.find_file(loader_file, "lua"), "File '"..loader_file.."' not found") + --texiowrite_nl("("..loader_path..")") dofile(loader_path) -- FIXME this pollutes stdout with filenames _G.config = _G.config or { } local config = _G.config -config.lualibs = config.lualibs or { } -config.lualibs.prefer_merged = false -config.lualibs.load_extended = false + +config.lualibs = config.lualibs or { } +config.lualibs.prefer_merged = false +config.lualibs.load_extended = false require"lualibs" require"otfl-basics-gen.lua" @@ -32,8 +34,8 @@ require"otfl-luat-ovr.lua" --- this populates the logs.* namespace require"otfl-font-nms" require"alt_getopt" -local name = 'mkluatexfontdb' -local version = '2.1' -- same version number as luaotfload +local name = "mkluatexfontdb" +local version = "2.2" -- same version number as luaotfload local names = fonts.names local db_src_out = names.path.dir.."/"..names.path.basename @@ -64,74 +66,161 @@ end local function version_msg() texiowrite_nl(stringformat( - "%s version %s, database version %s.\n", name, version, names.version)) + "%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. ---]] +--[[-- +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", "generate", "query" } +local action_pending = table.tohash(action_sequence, false) -local long_options = { - force = "f", - help = "h", - log = 1, - quiet = "q", - verbose = 1 , - version = "V", -} +action_pending.loglevel = true --- always set the loglevel +action_pending.generate = true --- this is the default action -local short_options = "fqvVh" +local actions = { } --- (jobspec -> (bool * bool)) list -local force_reload = nil +actions.loglevel = function (job) + logs.set_loglevel(job.log_level) + logs.names_report("log", 2, + "setting log level", "%d", job.log_level) + return true, true +end + +actions.version = function (job) + version_msg() + return true, false +end + +actions.help = function (job) + help_msg() + return true, false +end + +actions.generate = function (job) + local fontnames, savedname + fontnames = names.update(fontnames, job.force_reload) + logs.names_report("log", 0, "fonts in the database", + "%i", #fontnames.mappings) + savedname = names.save(fontnames) + texiowrite_nl"" + if savedname then --- FIXME have names.save return bool + return true, true + end + return false, false +end + +actions.query = function (job) + return true, true +end + +--[[-- +Command-line processing. +mkluatexfontdb.lua 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, + query = "", + log_level = 1, + } + + local long_options = { + force = "f", + help = "h", + log = 1, + quiet = "q", + verbose = 1 , + version = "V", + find = 1, + } + + local short_options = "fqvVh" -local function process_cmdline() local options, _, optarg = alt_getopt.get_ordered_opts (arg, short_options, long_options) - local log_level = 1 + local nopts = #options for n=1, nopts do local v = options[n] if v == "q" then - log_level = 0 + result.log_level = 0 elseif v == "v" then - if log_level > 0 then - log_level = log_level + 1 + if result.log_level > 0 then + result.log_level = result.log_level + 1 else - log_level = 2 + result.log_level = 2 end elseif v == "V" then - version_msg() - os.exit(0) + action_pending["version"] = true elseif v == "h" then - help_msg() - os.exit(0) + action_pending["help"] = true elseif v == "f" then - force_reload = 1 + result.force_reload = 1 elseif v == "verbose" then local lvl = optarg[n] if lvl then - log_level = tonumber(lvl) + result.log_level = tonumber(lvl) end elseif v == "log" then local str = optarg[n] if str then logs.set_logout(str) end + elseif v == "find" then + action_pending["query"] = true + result.query = optarg[n] end end - logs.set_loglevel(log_level) + return result end -local function generate(force) - local fontnames, saved - fontnames = names.update(fontnames, force) - logs.names_report("log", 0, "fonts in the database", - "%i", #fontnames.mappings) - saved = names.save(fontnames) - texiowrite_nl("") +local main = function ( ) -- unit -> int + local retval = 0 + local job = process_cmdline() + +-- inspect(action_pending) + + for i=1, #action_sequence do + local actionname = action_sequence[i] + local exit = false + if action_pending[actionname] then + logs.names_report("log", 3, "preparing for task", + "%s", actionname) + + local action = actions[actionname] + local success, continue = action(job) + + if not success then + logs.names_report(false, 0, "could not finish task", + "%s", actionname) + retval = -1 + exit = true + elseif not continue then + logs.names_report(false, 3, "task completed, exiting", + "%s", actionname) + exit = true + else + logs.names_report(false, 3, "task completed successfully", + "%s", actionname) + end + end + if exit then break end + end + + return retval end -process_cmdline() -generate(force_reload) +return main() + -- vim:tw=71:sw=4:ts=4:expandtab -- cgit v1.2.3 From 0909225c254332ac9cfa587f3d7212bd4141c83e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 16 Apr 2013 20:27:43 +0200 Subject: make fonts.names.resolve() a local --- otfl-font-nms.lua | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 8869bef..095ecbd 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -11,7 +11,7 @@ fonts.names = fonts.names or { } local names = fonts.names local names_dir = "luatex-cache/generic/names" -names.version = 2.010 -- not the same as in context +names.version = 2.2 -- not the same as in context names.data = nil names.path = { basename = "otfl-names.lua", @@ -103,7 +103,18 @@ local synonyms = { local loaded = false local reloaded = false -function names.resolve(_,_,specification) -- the 1st two parameters are used by ConTeXt +--[[doc-- + +Luatex-fonts, the font-loader package luaotfload imports, comes with +basic file location facilities (see luatex-fonts-syn.lua). +However, the builtin functionality is too limited to be of more than +basic use, which is why we supply our own resolver that accesses the +font database created by the mkluatexfontdb script. + +--doc]]-- + +local resolve resolve = function (_,_,specification) -- the 1st two parameters are used by ConTeXt + inspect(specification) local name = sanitize(specification.name) local style = sanitize(specification.style) or "regular" @@ -229,7 +240,7 @@ function names.resolve(_,_,specification) -- the 1st two parameters are used by names.data = names.update(names.data) names.save(names.data) reloaded = true - return names.resolve(_,_,specification) + return resolve(_,_,specification) else -- else, fallback to filename -- XXX: specification.name is empty with absolute paths, looks @@ -242,14 +253,15 @@ function names.resolve(_,_,specification) -- the 1st two parameters are used by names.data = names.update() names.save(names.data) reloaded = true - return names.resolve(_,_,specification) + return resolve(_,_,specification) else return specification.name, false end end end -names.resolvespec = names.resolve +names.resolve = resolve --- replace the resolver from luatex-fonts +names.resolvespec = resolve local function font_fullinfo(filename, subfont, texmf) local t = { } -- cgit v1.2.3 From 5a8500f312740eeca9dd7bc8a507d6e88d7653f7 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Tue, 16 Apr 2013 18:47:44 +0200 Subject: Preventing loop-references in fontconfig files I just had this case on a recent Ubuntu... Conflicts: otfl-font-nms.lua --- otfl-font-nms.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 095ecbd..8e89ea5 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -38,6 +38,7 @@ 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 tableinsert = table.insert local report = logs.names_report @@ -547,12 +548,13 @@ end in OSFONTDIR. ]] -local function read_fonts_conf(path, results) +local read_fonts_conf read_fonts_conf = function (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) + tableinsert(passed_paths, path) if not f then report("log", 2, "cannot open file", "%s", path) return results @@ -604,14 +606,14 @@ local function read_fonts_conf(path, results) elseif not lfs.isfile(include) and not lfs.isdir(include) then include = file.join(file.dirname(path), include) end - if lfs.isfile(include) and kpse.readable_file(include) then + if lfs.isfile(include) and kpse.readable_file(include) and not table.contains(passed_paths, include) then -- maybe we should prevent loops here? -- we exclude path with texmf in them, as they should -- be found otherwise - read_fonts_conf(include, results) + read_fonts_conf(include, results, passed_paths) elseif lfs.isdir(include) then for _,f in next, glob(file.join(include, "*.conf")) do - read_fonts_conf(f, results) + read_fonts_conf(f, results, passed_paths) end end end @@ -639,7 +641,7 @@ local function get_os_dirs() else for _,p in next, {"/usr/local/etc/fonts/fonts.conf", "/etc/fonts/fonts.conf"} do if lfs.isfile(p) then - return read_fonts_conf("/etc/fonts/fonts.conf", {}) + return read_fonts_conf("/etc/fonts/fonts.conf", {}, {}) end end end -- cgit v1.2.3 From 71923457b85908947e9909589989cca873d9a860 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 16 Apr 2013 21:32:18 +0200 Subject: apply hotfix by Hans for bad clig substitution reference: http://www.ntg.nl/pipermail/ntg-context/2013/072536.html --- otfl-fonts-merged.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/otfl-fonts-merged.lua b/otfl-fonts-merged.lua index 96f4c79..926037f 100644 --- a/otfl-fonts-merged.lua +++ b/otfl-fonts-merged.lua @@ -6148,7 +6148,19 @@ local function t_hashed(t,cache) return nil end end -local s_hashed=t_hashed +--local s_hashed=t_hashed +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 -- cgit v1.2.3 From 6a552fe231117c66188afc45503a884507e13e97 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 16 Apr 2013 22:18:21 +0200 Subject: organize font-nms --- otfl-font-nms.lua | 307 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 195 insertions(+), 112 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 8e89ea5..13921a2 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -6,6 +6,43 @@ if not modules then modules = { } end modules ['font-nms'] = { license = "GNU GPL v2" } +--- Luatex builtins +local dofile = dofile +local load = load +local next = next +local pcall = pcall +local require = require +local tonumber = tonumber + +local iolines = io.lines +local ioopen = io.open +local kpseexpand_path = kpse.expand_path +local mathabs = math.abs +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 tableinsert = table.insert +local texiowrite_nl = texio.write_nl +local utf8gsub = unicode.utf8.gsub +local utf8lower = unicode.utf8.lower + +--- these come from Lualibs/Context +local dirglob = dir.glob +local filebasename = file.basename +local filecollapsepath = file.collapsepath +local fileextname = file.extname +local filejoin = file.join +local filereplacesuffix = file.replacesuffix +local filesplitpath = file.splitpath +local stringis_empty = string.is_empty +local stringsplit = string.split +local stringstrip = string.strip + +--- the font loader namespace is “fonts”, same as in Context fonts = fonts or { } fonts.names = fonts.names or { } @@ -15,12 +52,13 @@ names.version = 2.2 -- not the same as in context names.data = nil names.path = { basename = "otfl-names.lua", - dir = file.join(kpse.expand_var("$TEXMFVAR"), names_dir), + dir = filejoin(kpse.expand_var("$TEXMFVAR"), names_dir), } local success = pcall(require, "luatexbase.modutils") if success then - success = pcall(luatexbase.require_module, "lualatex-platform", "2011/03/30") + success = pcall(luatexbase.require_module, + "lualatex-platform", "2011/03/30") end local get_installed_fonts if success then @@ -30,27 +68,21 @@ else end end -local splitpath, collapsepath = file.splitpath, file.collapsepath -local expandpath = kpse.expand_path -local glob, basename = dir.glob, file.basename -local extname = file.extname -local upper, lower, format = string.upper, string.lower, string.format -local gsub, match, rpadd = string.gsub, string.match, string.rpadd -local gmatch, sub, find = string.gmatch, string.sub, string.find -local utfgsub = unicode.utf8.gsub -local tableinsert = table.insert +--[[doc-- +Auxiliary functions +--doc]]-- + local report = logs.names_report -local function sanitize(str) - if str then - return utfgsub(lower(str), "[^%a%d]", "") - else - return str -- nil +local sanitize_string = function (str) + if str ~= nil then + return utf8gsub(utf8lower(str), "[^%a%d]", "") end + return nil end -local function fontnames_init() +local fontnames_init = function ( ) return { mappings = { }, status = { }, @@ -58,22 +90,61 @@ local function fontnames_init() } end -local function make_name(path) - return file.replacesuffix(path, "lua"), file.replacesuffix(path, "luc") +local make_name = function (path) + return filereplacesuffix(path, "lua"), filereplacesuffix(path, "luc") end -local function load_names() - local path = file.join(names.path.dir, names.path.basename) - local luaname, lucname = make_name(path) - local foundname - local data - if file.isreadable(lucname) then - data = dofile(lucname) - foundname = lucname - elseif file.isreadable(luaname) then - data = dofile(luaname) - foundname = luaname +--- 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. +--- Furthermore, we memoize loaded files along the way to avoid +--- duplication. + +local code_cache = { } + +--- string -> (string * table) +local load_lua_file = function (path) + local code = code_cache[path] + if code then return path, code() end + + local foundname = filereplacesuffix(path, "luc") + + 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 return nil, nil end + + code_cache[path] = code --- insert into memo + return foundname, code() +end + +--- define locals in scope +local load_names +local save_names +local scan_external_dir +local update_names +local read_fonts_conf +local resolve + + +load_names = function ( ) + local path = filejoin(names.path.dir, names.path.basename) + local foundname, data = load_lua_file(path) + if data then report("info", 0, "Font names database loaded", "%s", foundname) else @@ -83,22 +154,27 @@ local function load_names() data = names.update(fontnames_init()) names.save(data) end - texio.write_nl("") + texiowrite_nl("") return data end local synonyms = { - regular = { "normal", "roman", "plain", "book", "medium" }, + regular = { "normal", "roman", + "plain", "book", + "medium" }, --- TODO note from Élie Roux --- 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" }, + bold = { "demi", "demibold", + "semibold", "boldregular", }, + italic = { "regularitalic", "normalitalic", + "oblique", "slanted", }, bolditalic = { - "boldoblique", "boldslanted", "demiitalic", "demioblique", - "demislanted", "demibolditalic", "semibolditalic" - }, + "boldoblique", "boldslanted", + "demiitalic", "demioblique", + "demislanted", "demibolditalic", + "semibolditalic", }, } local loaded = false @@ -114,10 +190,9 @@ font database created by the mkluatexfontdb script. --doc]]-- -local resolve resolve = function (_,_,specification) -- the 1st two parameters are used by ConTeXt - inspect(specification) - local name = sanitize(specification.name) - local style = sanitize(specification.style) or "regular" +resolve = function (_,_,specification) -- the 1st two parameters are used by ConTeXt + local name = sanitize_string(specification.name) + local style = sanitize_string(specification.style) or "regular" local size if specification.optsize then @@ -128,7 +203,7 @@ local resolve resolve = function (_,_,specification) -- the 1st two parameters a if not loaded then - names.data = names.load() + names.data = load_names() loaded = true end @@ -137,12 +212,12 @@ local resolve resolve = function (_,_,specification) -- the 1st two parameters a if data.mappings then local found = { } for _,face in next, data.mappings do - local family = sanitize(face.names and face.names.family) - local subfamily = sanitize(face.names and face.names.subfamily) - local fullname = sanitize(face.names and face.names.fullname) - local psname = sanitize(face.names and face.names.psname) - local fontname = sanitize(face.fontname) - local pfullname = sanitize(face.fullname) + local family = sanitize_string(face.names and face.names.family) + local subfamily = sanitize_string(face.names and face.names.subfamily) + local fullname = sanitize_string(face.names and face.names.fullname) + local psname = sanitize_string(face.names and face.names.psname) + local fontname = sanitize_string(face.fontname) + local pfullname = sanitize_string(face.fullname) local optsize, dsnsize, maxsize, minsize if #face.size > 0 then optsize = face.size @@ -219,7 +294,7 @@ local resolve resolve = function (_,_,specification) -- the 1st two parameters a 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) + local difference = mathabs(dsnsize-size) if difference < least then closest = face least = difference @@ -261,9 +336,6 @@ local resolve resolve = function (_,_,specification) -- the 1st two parameters a end end -names.resolve = resolve --- replace the resolver from luatex-fonts -names.resolvespec = resolve - local function font_fullinfo(filename, subfont, texmf) local t = { } local f = fontloader.open(filename, subfont) @@ -304,7 +376,7 @@ local function font_fullinfo(filename, subfont, texmf) t.fontname = m.fontname t.fullname = m.fullname t.familyname = m.familyname - t.filename = { texmf and basename(filename) or filename, subfont } + t.filename = { texmf and filebasename(filename) or filename, subfont } t.weight = m.pfminfo.weight t.width = m.pfminfo.width t.slant = m.italicangle @@ -322,10 +394,11 @@ local function load_font(filename, fontnames, newfontnames, texmf) local newstatus = newfontnames.status local mappings = fontnames.mappings local status = fontnames.status - local basefile = texmf and basename(filename) or filename + local basename = filebasename(filename) + local basefile = texmf and basename or filename if filename then if names.blacklist[filename] or - names.blacklist[basename(filename)] then + names.blacklist[basename] then report("log", 2, "ignoring font", "%s", filename) return end @@ -333,7 +406,7 @@ local function load_font(filename, fontnames, newfontnames, texmf) 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)]) + local index_status = newstatus[basefile] or (not texmf and newstatus[basename]) if index_status and index_status.timestamp == timestamp then -- already indexed this run return @@ -399,23 +472,23 @@ local function path_normalize(path) - using kpse.readable_file on Win32 ]] if os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then - path = path:gsub('\\', '/') - path = path:lower() - path = path:gsub('^/cygdrive/(%a)/', '%1:/') + path = stringgsub(path, '\\', '/') + path = stringlower(path) + path = stringgsub(path, '^/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) + elseif kpse.readable_file(filejoin(file.dirname(path), dest)) then + path = filejoin(file.dirname(path), dest) else -- broken symlink? end end end - path = collapsepath(path) + path = filecollapsepath(path) return path end @@ -432,15 +505,16 @@ local function read_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 + for line in iolines(v) do + line = stringstrip(line) -- to get rid of lines like " % foo" + local first_chr = stringsub(line, 1, 1) --- faster than find + if first_chr == "%" or stringis_empty(line) then -- comment or empty line else - line = line:split("%")[1] - line = line:strip() - if string.sub(line,1,1) == "-" then - whitelist[string.sub(line,2,-1)] = true + line = stringsplit(line, "%")[1] + line = stringstrip(line) + if stringsub(line, 1, 1) == "-" then + whitelist[stringsub(line, 2, -1)] = true else report("log", 2, "blacklisted file", "%s", line) blacklist[line] = true @@ -473,7 +547,7 @@ local function scan_installed_fonts(fontnames, newfontnames) for key, value in next, fonts do local file = value.path if file then - local ext = extname(file) + local ext = fileextname(file) if ext and font_extensions_set[ext] then file = path_normalize(file) report("log", 1, "loading font", "%s", file) @@ -498,8 +572,8 @@ local function scan_dir(dirname, fontnames, newfontnames, texmf) local nbfound = 0 report("log", 2, "scanning", "%s", dirname) for _,i in next, font_extensions do - for _,ext in next, { i, upper(i) } do - found = glob(format("%s/**.%s$", dirname, ext)) + for _,ext in next, { i, stringupper(i) } do + found = dirglob(stringformat("%s/**.%s$", dirname, ext)) -- note that glob fails silently on broken symlinks, which happens -- sometimes in TeX Live. report("log", 2, "fonts found", "%s '%s' fonts found", #found, ext) @@ -521,15 +595,15 @@ 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 + if stringis_empty(kpseexpand_path("$OSFONTDIR")) then report("info", 0, "Scanning TEXMF fonts...") else report("info", 0, "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 + local fontdirs = stringgsub(kpseexpand_path("$OPENTYPEFONTS"), "^%.", "") + fontdirs = fontdirs .. stringgsub(kpseexpand_path("$TTFONTS"), "^%.", "") + if not stringis_empty(fontdirs) then + for _,d in next, filesplitpath(fontdirs) do scan_dir(d, fontnames, newfontnames, true) end end @@ -548,71 +622,76 @@ end in OSFONTDIR. ]] -local read_fonts_conf read_fonts_conf = function (path, results, passed_paths) +--- (string -> tab -> tab -> tab) +read_fonts_conf = function (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. + 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) + local fh = ioopen(path) tableinsert(passed_paths, path) - if not f then + if not fh then report("log", 2, "cannot open file", "%s", path) return results end local incomments = false - for line in f:lines() do + for line in fh:lines() do while line and line ~= "" do -- spaghetti code... hmmm... if incomments then - local tmp = find(line, '-->') + local tmp = stringfind(line, '-->') --- wtf? if tmp then incomments = false - line = sub(line, tmp+3) + line = stringsub(line, tmp+3) else line = nil end else - local tmp = find(line, ' 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 @@ -997,32 +1325,6 @@ loadmodule"features.lua" [2013/04/16 v2.2 OpenType layout system] \RequirePackage{luatexbase} \fi -\RequireLuaModule{lualibs} -\RequireLuaModule{luaotfload} - -\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}% - [2013/04/16 v2.2 OpenType layout system] - \RequirePackage{luatexbase} -\fi -% \end{macrocode} -% -% %% As soon as we feel the need this file will file will contain an extension -% %% to the standard plain register allocation. For the moment we stick to a -% %% rather dumb attribute allocator. We start at 256 because we don't want -% %% any interference with the attributes used in the font handler. -% %%\newcount \lastallocatedattribute \lastallocatedattribute=255 -% %%\def\newattribute#1% -% %% {\global\advance\lastallocatedattribute 1 -% %% \attributedef#1\lastallocatedattribute} -% -% \begin{macrocode} \RequireLuaModule{luaotfload} \endinput % \end{macrocode} diff --git a/luaotfload.lua b/luaotfload.lua index b815c19..3c35150 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -12,9 +12,11 @@ luaotfload.module = { local luatexbase = luatexbase -local type, next, dofile = type, next, dofile -local stringfind = string.find -local find_file = kpse.find_file +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 @@ -277,7 +279,6 @@ 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" @@ -411,6 +412,38 @@ elseif luaotfload.font_definer == "patch" then 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 diff --git a/luaotfload.sty b/luaotfload.sty index 2200736..4d566af 100644 --- a/luaotfload.sty +++ b/luaotfload.sty @@ -28,18 +28,6 @@ [2011/10/06 v2.0 OpenType layout system] \RequirePackage{luatexbase} \fi -%% -------------------------------------------------------------------- -%% from luatex-basics.tex -% As soon as we feel the need this file will file will contain an extension -% to the standard plain register allocation. For the moment we stick to a -% rather dumb attribute allocator. We start at 256 because we don't want -% any interference with the attributes used in the font handler. -%\newcount \lastallocatedattribute \lastallocatedattribute=255 - -%\def\newattribute#1% - %{\global\advance\lastallocatedattribute 1 - %\attributedef#1\lastallocatedattribute} -%% -------------------------------------------------------------------- \RequireLuaModule{luaotfload} \endinput %% diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 2bbaa36..c987c3a 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -697,6 +697,7 @@ local function read_blacklist() if first_chr == "%" or stringis_empty(line) then -- comment or empty line else + --- this is highly inefficient line = stringsplit(line, "%")[1] line = stringstrip(line) if stringsub(line, 1, 1) == "-" then @@ -817,6 +818,11 @@ read_fonts_conf = function (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. + + TODO fonts.conf are some kind of XML so in theory the following + is totally inappropriate. Maybe a future version of the + lualibs will include the lxml-* files from Context so we + can write something presentable instead. ]] local fh = ioopen(path) passed_paths[#passed_paths+1] = path @@ -896,6 +902,7 @@ end -- for testing purpose names.read_fonts_conf = read_fonts_conf +--- TODO stuff those paths into some writable table local function get_os_dirs() if os.name == 'macosx' then return { @@ -907,7 +914,7 @@ local function get_os_dirs() elseif os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then local windir = os.getenv("WINDIR") return { filejoin(windir, 'Fonts') } - else + else --- TODO what about ~/config/fontconfig/fonts.conf etc? for _,p in next, {"/usr/local/etc/fonts/fonts.conf", "/etc/fonts/fonts.conf"} do if lfs.isfile(p) then return read_fonts_conf("/etc/fonts/fonts.conf", {}, {}) -- cgit v1.2.3 From c4013acea469eac4712ff1bae3eea3aa6642b56f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 18 Apr 2013 15:31:16 +0200 Subject: import luatex-fonts as of 2013-04-18 --- otfl-fonts-merged.lua | 170 +++++--------------------------------------------- 1 file changed, 15 insertions(+), 155 deletions(-) diff --git a/otfl-fonts-merged.lua b/otfl-fonts-merged.lua index 8633b8e..44f42ed 100644 --- a/otfl-fonts-merged.lua +++ b/otfl-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 04/16/13 18:49:45 +-- merge date : 04/17/13 18:36:10 do -- begin closure to overcome local limits and interference @@ -82,158 +82,6 @@ function optionalrequire(...) return result end end -local type=type -local gsub,format=string.gsub,string.format -local package=package -local searchers=package.searchers or package.loaders -local libpaths=nil -local clibpaths=nil -local libhash={} -local clibhash={} -local libextras={} -local clibextras={} -local filejoin=file and file.join or function(path,name) return path.."/"..name end -local isreadable=file and file.is_readable or function(name) local f=io.open(name) if f then f:close() return true end end -local addsuffix=file and file.addsuffix or function(name,suffix) return name.."."..suffix end -local function cleanpath(path) - return path -end -local helpers=package.helpers or { - libpaths=function() return {} end, - clibpaths=function() return {} end, - cleanpath=cleanpath, - trace=false, - report=function(...) print(format(...)) end, -} -package.helpers=helpers -local function getlibpaths() - return libpaths or helpers.libpaths(libhash) -end -local function getclibpaths() - return clibpaths or helpers.clibpaths(clibhash) -end -package.libpaths=getlibpaths -package.clibpaths=getclibpaths -local function addpath(what,paths,extras,hash,...) - local pathlist={... } - local cleanpath=helpers.cleanpath - local trace=helpers.trace - local report=helpers.report - local function add(path) - local path=cleanpath(path) - if not hash[path] then - if trace then - report("extra %s path: %s",what,path) - end - paths [#paths+1]=path - extras[#extras+1]=path - end - end - for p=1,#pathlist do - local path=pathlist[p] - if type(path)=="table" then - for i=1,#path do - add(path[i]) - end - else - add(path) - end - end - return paths,extras -end -function package.extralibpath(...) - libpaths,libextras=addpath("lua",getlibpaths(),libextras,libhash,...) -end -function package.extraclibpath(...) - clibpaths,clibextras=addpath("lib",getclibpaths(),clibextras,clibhash,...) -end -if not searchers[-2] then - searchers[-2]=searchers[2] -end -searchers[2]=function(name) - return helpers.loaded(name) -end -searchers[3]=nil -local function loadedaslib(resolved,rawname) - local init="luaopen_"..gsub(rawname,"%.","_") - if helpers.trace then - helpers.report("calling loadlib with '%s' with init '%s'",resolved,init) - end - return package.loadlib(resolved,init) -end -local function loadedbylua(name) - if helpers.trace then - helpers.report("locating '%s' using normal loader",name) - end - return true,searchers[-2](name) -end -local function loadedbypath(name,rawname,paths,islib,what) - local trace=helpers.trace - local report=helpers.report - if trace then - report("locating '%s' as '%s' on '%s' paths",rawname,name,what) - end - for p=1,#paths do - local path=paths[p] - local resolved=filejoin(path,name) - if trace then - report("checking for '%s' using '%s' path '%s'",name,what,path) - end - if isreadable(resolved) then - if trace then - report("lib '%s' located on '%s'",name,resolved) - end - if islib then - return true,loadedaslib(resolved,rawname) - else - return true,loadfile(resolved) - end - end - end -end -local function notloaded(name) - if helpers.trace then - helpers.report("unable to locate library '%s'",name) - end -end -helpers.loadedaslib=loadedaslib -helpers.loadedbylua=loadedbylua -helpers.loadedbypath=loadedbypath -helpers.notloaded=notloaded -function helpers.loaded(name) - local thename=gsub(name,"%.","/") - local luaname=addsuffix(thename,"lua") - local libname=addsuffix(thename,os.libsuffix or "so") - local libpaths=getlibpaths() - local clibpaths=getclibpaths() - local done,result=loadedbypath(luaname,name,libpaths,false,"lua") - if done then - return result - end - local done,result=loadedbypath(luaname,name,clibpaths,false,"lua") - if done then - return result - end - local done,result=loadedbypath(libname,name,clibpaths,true,"lib") - if done then - return result - end - local done,result=loadedbylua(name) - if done then - return result - end - return notloaded(name) -end -function helpers.unload(name) - if helpers.trace then - if package.loaded[name] then - helpers.report("unloading library '%s', %s",name,"done") - else - helpers.report("unloading library '%s', %s",name,"not loaded") - end - end - package.loaded[name]=nil -end end -- closure @@ -5172,6 +5020,7 @@ local lpegmatch=lpeg.match local reversed,concat,remove=table.reversed,table.concat,table.remove 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 @@ -5190,7 +5039,7 @@ local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf otf.glists={ "gsub","gpos" } -otf.version=2.741 +otf.version=2.742 otf.cache=containers.define("fonts","otf",otf.version,true) local fontdata=fonts.hashes.identifiers local chardata=characters and characters.data @@ -6153,7 +6002,18 @@ local function t_hashed(t,cache) return nil end end -local s_hashed=t_hashed +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 -- cgit v1.2.3 From b0c22678d1f776f991ffef67694451b8bb5f9e20 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 18 Apr 2013 16:21:33 +0200 Subject: remove comments on dependencies (in dtx now) --- filegraph.dot | 2 +- luaotfload.lua | 38 -------------------------------------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/filegraph.dot b/filegraph.dot index b86568b..f3ab2f2 100644 --- a/filegraph.dot +++ b/filegraph.dot @@ -196,4 +196,4 @@ strict digraph luaotfload_files { //looks weird with circo ... ] } -/* vim:ft=dot:sw=4:ts=4:expandtab */ +// vim:ft=dot:sw=4:ts=4:expandtab diff --git a/luaotfload.lua b/luaotfload.lua index 3c35150..256c792 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -107,44 +107,6 @@ local find_vf_file = function (name) return fullname end ---[[-- keep --]] ---- from Hans (all merged): - ---- file name modified include name ---- × basics-gen.lua t luat-basics-gen ---- × font-def -> fonts-def t luatex-font-def (there’s also the normal font-def!) ---- × fonts-enc f luatex-font-enc ---- × fonts-ext t luatex-fonts-ext ---- × fonts-lua f luatex-fonts-lua ---- fonts-tfm f luatex-fonts-tfm ---- × fonts-cbk f luatex-fonts-lua - ---- from Hans (unmerged): ---- font-otc.lua -> otfl-font-otc.lua - ---- from luaotfload: ---- otfl-luat-ovr.lua -- override some luat-dum functions ---- otfl-font-clr.lua ---- otfl-font-ltx.lua ---- otfl-font-nms.lua ---- otfl-font-pfb.lua -- ? - ---[[-- new --]] ---- basics-nod (merged as fonts-nod !) ---- fonts-demo-vf-1.lua ---- fonts-syn (merged) - ---[[-- merged, to be dropped --]] ---- otfl-data-con.lua ---- otfl-font-cid.lua ---- otfl-font-con.lua ---- otfl-font-ini.lua ---- otfl-font-ota.lua ---- otfl-font-otb.lua ---- otfl-font-otf.lua ---- otfl-font-oti.lua ---- otfl-font-otn.lua - --[[doc-- \subsection{Preparing the Font Loader} -- cgit v1.2.3 From 79fd3d94527ecaad80cb4352a5650788a9e971df Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Thu, 18 Apr 2013 23:41:25 +0200 Subject: fix font syntax parser --- otfl-font-ltx.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/otfl-font-ltx.lua b/otfl-font-ltx.lua index d1799d7..afc8621 100644 --- a/otfl-font-ltx.lua +++ b/otfl-font-ltx.lua @@ -128,7 +128,9 @@ local function iskey (k,v) feature_list[k] = v 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 namespec = (1-S("/:("))^0 -- was: (1-S("/: ("))^0 +--[[phg-- this prevents matching of absolute paths as file names --]]-- +local namespec = (1-S("/:("))^1 local filespec = (R("az", "AZ") * P(":"))^-1 * (1-S(":("))^1 local stylespec = spaces * P("/") * (((1-P(":"))^0)/isstyle) * spaces local filename = (P("file:")/isfile * (filespec/thename)) + (P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]")) -- cgit v1.2.3 From f10aeb6e760a349986570489b838ae6384291107 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 10:31:44 +0200 Subject: Finishing to fix reference loop --- otfl-font-nms.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index c987c3a..ee9fa33 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -888,7 +888,9 @@ read_fonts_conf = function (path, results, passed_paths) read_fonts_conf(include, results, passed_paths) elseif lfs.isdir(include) then for _,f in next, dirglob(filejoin(include, "*.conf")) do - read_fonts_conf(f, results, passed_paths) + if not passed_paths_set[f] then + read_fonts_conf(f, results, passed_paths) + end end end end -- cgit v1.2.3 From 089dcf6f5742452ceb007f6d712cc925b7178f70 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 11:33:35 +0200 Subject: First try of a NEWS file for the new version --- NEWS | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/NEWS b/NEWS index 42e6e14..754cbcd 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,23 @@ Change History -------------- +2013/04/xx, luaotfload v2.2: + * Synchronisation with ConTeXt from TeXLive 2013, inducing + backward-incompatible changes in the font structure (fontspec and + unicode-math must be updated) + * Synchronisation with ConTeXt is now easier and can be done by just + updating otfl-fonts-merged.lua (available in ConTeXt) + * Improve documentation + * renaming mkluatexfontdb into fontdbutil, with more search functionalities + +2013/04/11, luaotfload v1.28: + * Adapting to LuaTeX 0.75, keeping backward-compatibility + * Fix small documentation issues in mkluatexfontdb + * Fix possibility of infite loop with fontconfig config files references + * Adding semibold synonym for bold + * file:xxx syntax now uses the same search function as name: which + make more fonts recognized + 2011/04/21, luaotfload v1.25: * Fix bug loading *.dfont fonts * Misc. documentation fixes -- cgit v1.2.3 From bd7ed57ad9f8cfc9e7a7dae148d567833f463278 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 18:16:04 +0200 Subject: Not versioning the generated files (a bit confusing) --- luaotfload.lua | 411 --------------------------------------------------------- luaotfload.sty | 34 ----- 2 files changed, 445 deletions(-) delete mode 100644 luaotfload.lua delete mode 100644 luaotfload.sty diff --git a/luaotfload.lua b/luaotfload.lua deleted file mode 100644 index 256c792..0000000 --- a/luaotfload.lua +++ /dev/null @@ -1,411 +0,0 @@ -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 - ---[[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 - 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 diff --git a/luaotfload.sty b/luaotfload.sty deleted file mode 100644 index 4d566af..0000000 --- a/luaotfload.sty +++ /dev/null @@ -1,34 +0,0 @@ -%% -%% This is file `luaotfload.sty', -%% generated with the docstrip utility. -%% -%% The original source files were: -%% -%% luaotfload.dtx (with options: `package') -%% This is a generated file. -%% -%% Copyright (C) 2009-2011 by by Elie Roux -%% and Khaled Hosny -%% (Support: .) -%% -%% This work is under the CC0 license. -%% -%% This work consists of the main source file luaotfload.dtx -%% and the derived files -%% luaotfload.sty, luaotfload.lua -%% -\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}% - [2011/10/06 v2.0 OpenType layout system] - \RequirePackage{luatexbase} -\fi -\RequireLuaModule{luaotfload} -\endinput -%% -%% End of file `luaotfload.sty'. -- cgit v1.2.3 From e8fe5e1e830658776413eb5e8af450ff4ae93ec7 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 18:17:28 +0200 Subject: A more simple loadmodule function Simply using the luatexbase way... May change in the future, but stick with it for now. --- luaotfload.dtx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 818dbd9..8ffb8f9 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -905,14 +905,7 @@ end 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 + require(fl_prefix .."-"..name) end % \end{macrocode} -- cgit v1.2.3 From 4a13ae53c8441c1fba6d059f44f364cc7cdaa673 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 18:29:23 +0200 Subject: Documenting and simplifying ofm font bug handling --- luaotfload.dtx | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 8ffb8f9..03b63c1 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -910,10 +910,12 @@ end % \end{macrocode} % -% 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}.) +% 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 @@ -1120,22 +1122,19 @@ 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 - +% \end{macrocode} +% +% +% \CONTEXT does not support ofm, these lines were added in order to make it +% work. However they do not seem necessary so they are commented for now. +% +% \begin{macrocode} +-- if fonts and fonts.readers.tfm then +-- 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 % \end{macrocode} % % -- cgit v1.2.3 From 3a59b8b6b226ed22ccd16aec2dc2e272f015cc74 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 18:38:10 +0200 Subject: Less verbose output - first step Is it possible to adapt something like this in the ConTeXt files? --- otfl-fonts.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/otfl-fonts.lua b/otfl-fonts.lua index 75bd079..7e32465 100644 --- a/otfl-fonts.lua +++ b/otfl-fonts.lua @@ -37,10 +37,12 @@ if not generic_context then generic_context = { } end +local printinfo = function(s) texio.write_nl("log", s) end + if not generic_context.push_namespaces then function generic_context.push_namespaces() - texio.write(" ") + printinfo(" ") local normalglobal = { } for k, v in next, _G do normalglobal[k] = v @@ -50,7 +52,7 @@ if not generic_context.push_namespaces then function generic_context.pop_namespaces(normalglobal,isolate) if normalglobal then - texio.write(" ") + printinfo(" ") for k, v in next, _G do if not normalglobal[k] then generic_context[k] = v @@ -110,7 +112,7 @@ local function loadmodule(name,continue) end else if verbose then - texio.write(string.format(" <%s>",foundname)) -- no file.basename yet + printinfo(string.format(" <%s>",foundname)) -- no file.basename yet end dofile(foundname) end @@ -252,6 +254,6 @@ end -- We're done. -texio.write(string.format(" ", os.gettimeofday()-starttime)) +--texio.write(string.format(" ", os.gettimeofday()-starttime)) generic_context.pop_namespaces(whatever) -- cgit v1.2.3 From c80fad6066b1ab0ad40ef13d50945c2d3578694a Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 18:55:33 +0200 Subject: basics-gen seems to be already present in the merged file --- otfl-basics-gen.lua | 296 ---------------------------------------------------- 1 file changed, 296 deletions(-) delete mode 100644 otfl-basics-gen.lua diff --git a/otfl-basics-gen.lua b/otfl-basics-gen.lua deleted file mode 100644 index 727086e..0000000 --- a/otfl-basics-gen.lua +++ /dev/null @@ -1,296 +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 ------ dummyreporter = function(c) return function(...) texio.write_nl(c .. " : " .. string.format(...)) end end -local dummyreporter = function(c) return function(...) 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! -} - -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.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, readables = nil, { } - -if not caches.namespace or caches.namespace == "" or caches.namespace == "context" then - caches.namespace = 'generic' -end - -do - - local cachepaths = kpse.expand_path('$TEXMFCACHE') or "" - - if cachepaths == "" then - cachepaths = kpse.expand_path('$TEXMFVAR') - end - - if cachepaths == "" then - cachepaths = kpse.expand_path('$VARTEXMF') - end - - if cachepaths == "" then - cachepaths = "." - end - - cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") - - for i=1,#cachepaths do - if file.is_writable(cachepaths[i]) then - writable = file.join(cachepaths[i],"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 - name = "temp-" .. name -- clash prevention - return file.addsuffix(file.join(path,name),"lua"), file.addsuffix(file.join(path,name),"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,{ reduce = 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. - -caches.compilemethod = "both" - -function caches.compile(data,luaname,lucname) - local done = false - if caches.compilemethod == "luac" or caches.compilemethod == "both" then - done = os.spawn("texluac -o " .. string.quoted(lucname) .. " -s " .. string.quoted(luaname)) == 0 - end - if not done and (caches.compilemethod == "dump" or caches.compilemethod == "both") then - 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 - if _G["loadstring"] then s=loadstring(d) else s=load(d) end - f:write(string.dump(s)) - f:close() - end - end - end -end - --- - -function table.setmetatableindex(t,f) - setmetatable(t,{ __index = f }) -end -- cgit v1.2.3 From 80e5042a10620dbb2c456cf5d3a472c66fe6a90f Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 19:04:49 +0200 Subject: Verbosity reduction step 2 This implies a modification in otfl-fonts-merged.lua that should be asked to Hans... --- luaotfload.dtx | 17 +++++++++++++++++ otfl-fonts-merged.lua | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 03b63c1..e36bb36 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1045,6 +1045,23 @@ _G.non_generic_context = { luatex_fonts = { local trapped_register = callback.register callback.register = dummy_function +% \end{macrocode} +% +% In order to have an output with normal verbosity, we need to pre-override +% some \CONTEXT log function: +% +% \begin{macrocode} + +local dummylogfunction=function() end +local dummylogreporter=function(c) return function(...) log(string.formatters(...)) end end + +_G.logs={ + new=dummylogreporter, + reporter=dummylogreporter, + messenger=dummylogreporter, + report=dummylogfunction, +} + % \end{macrocode} % % Now that things are sorted out we can finally load the fontloader. diff --git a/otfl-fonts-merged.lua b/otfl-fonts-merged.lua index 44f42ed..19c8ce1 100644 --- a/otfl-fonts-merged.lua +++ b/otfl-fonts-merged.lua @@ -2926,7 +2926,7 @@ storage={ register=dummyfunction, shared={}, } -logs={ +logs=logs or { new=dummyreporter, reporter=dummyreporter, messenger=dummyreporter, -- cgit v1.2.3 From 89aaa92df44af21be14c32fa7425b1cfa0674820 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 20:59:22 +0200 Subject: Stop using module() in luaotfload.lua These _G are a bit ugly... --- luaotfload.dtx | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index e36bb36..22afb45 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -822,7 +822,7 @@ and the derived files %<*lua> % \fi % \begin{macrocode} -module("luaotfload", package.seeall) +luaotfload = luaotfload or {} luaotfload.module = { name = "luaotfload", @@ -1019,15 +1019,9 @@ end % \identifier{luatexbase}-style callback handling here. % % \begin{macrocode} -if not _G. generic_context then _G. generic_context = { } end -if not _G.non_generic_context then _G.non_generic_context = { } end +generic_context = {no_callbacks_yet = true} -local generic_context = generic_context -local non_generic_context =non_generic_context - -generic_context.no_callbacks_yet = true - -_G.non_generic_context = { luatex_fonts = { +non_generic_context = { luatex_fonts = { load_before = "otfl-fonts-merged.lua", -- load_after = nil, --- TODO, this is meant for callbacks skip_loading = true, @@ -1055,7 +1049,7 @@ callback.register = dummy_function local dummylogfunction=function() end local dummylogreporter=function(c) return function(...) log(string.formatters(...)) end end -_G.logs={ +logs={ new=dummylogreporter, reporter=dummylogreporter, messenger=dummylogreporter, @@ -1091,9 +1085,9 @@ do local new_attribute = luatexbase.new_attribute local the_attributes = luatexbase.attributes - _G.attributes = _G.attributes or { } + attributes = attributes or { } - _G.attributes.private = function (name) + attributes.private = function (name) local attr = "otfl@" .. name local number = the_attributes[attr] if not number then -- cgit v1.2.3 From 8d1a301641504e6d7ff4194e01ab89bafba06486 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 21:03:49 +0200 Subject: Making test files independent from ConTeXt files --- tests/color.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/color.tex b/tests/color.tex index 1be8896..188889c 100644 --- a/tests/color.tex +++ b/tests/color.tex @@ -4,7 +4,7 @@ \font\testb=file:lmroman10-regular.otf:color=FFFF0099;+trep at 10pt \font\testc=file:lmroman10-regular.otf:color=559922;+trep at 12pt -\testa \input knuth \par -\testb \input knuth \par -\testc \input knuth \par +\testa FF0000BB \par +\testb FFFF0099 \par +\testc 559922 \par \bye -- cgit v1.2.3 From 5c273b8ac66cacbd8e68a272b0317bac442e1958 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Fri, 19 Apr 2013 21:06:48 +0200 Subject: More tests independent from ConTeXt files --- tests/microtypography.tex | 31 +++++++++++++++++++++++++++++-- tests/opbd.tex | 30 ++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/tests/microtypography.tex b/tests/microtypography.tex index 7d032e3..f2d461d 100644 --- a/tests/microtypography.tex +++ b/tests/microtypography.tex @@ -4,6 +4,33 @@ \font\testa=file:texgyretermes-regular:script=latn at 12pt \font\testb=file:texgyretermes-regular:script=latn;protrusion=default at 12pt -\testa \input tufte \par -\testb \input tufte \par + +\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.tex b/tests/opbd.tex index 50c4dfd..3d26662 100644 --- a/tests/opbd.tex +++ b/tests/opbd.tex @@ -4,6 +4,32 @@ \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 \input tufte \par -\testb \input tufte \par + +\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 -- cgit v1.2.3 From f37183f9d32aa1cd616f5560396e4d548eab892a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 19 Apr 2013 23:05:27 +0200 Subject: fix typo in font-nms --- otfl-font-nms.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index c987c3a..f90058b 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -35,7 +35,7 @@ local utf8lower = unicode.utf8.lower --- these come from Lualibs/Context local dirglob = dir.glob -local dirmkdirs = dir.mkdirds +local dirmkdirs = dir.mkdirs local filebasename = file.basename local filecollapsepath = file.collapsepath local fileextname = file.extname -- cgit v1.2.3 From 54fb044ffc2cbace5951e1c92058c2e0362d4ceb Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 20 Apr 2013 01:36:04 +0200 Subject: revise doc --- luaotfload.dtx | 228 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 162 insertions(+), 66 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 818dbd9..3121512 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -130,13 +130,14 @@ and the derived files \setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium} %setmathfont{XITS Math} -\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\CONTEXT{Con\TeX t\xspace} +\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\CONTEXT {Con\TeX t\xspace} +\newcommand\OpenType{\identifier{Open\kern-.25ex Type}\xspace} \def\definehighlight[#1][#2]% {\ifcsname #1\endcsname\else @@ -192,9 +193,9 @@ and the derived files % \maketitle % % \begin{abstract} -% This package is an adaptation of the \CONTEXT font loading system, providing -% the ability to load \identifier{OpenType} fonts with extended font loading syntax -% supporting a large selection of \identifier{OpenType} 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. % \end{abstract} % % \tableofcontents @@ -203,13 +204,13 @@ and the derived files % % 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 as \TEX is 8-bit each font is limited to 256 -% characters. +% \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 \identifier{OpenType} -% (\abbrev{otf}) fonts. -% These fonts can contain a lot of characters and additional +% 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{% @@ -219,7 +220,7 @@ and the derived files % appreciated. % } % scripts. -% \identifier{OpenType} fonts are widely deployed and available for all +% \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. @@ -227,10 +228,15 @@ and the derived files % world was with the \XETEX engine. % % Unlike \XETEX, \LUATEX has no built-in support for -% \identifier{OpenType} or other technologies. -% Instead, it provides hooks for executing Lua during the \TEX run +% \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{Loading fonts} % @@ -252,7 +258,7 @@ and the derived files % \paragraph{Prefix} % % The \meta{prefix} is either |file:| or |name:|. -% It determines whether font loader should interpret the request as a +% It determines whether the font loader should interpret the request as a % file name or font name, respectively, which again influences how it % will attempt to locate the font. % The prefix can be omitted, in which case |name:| is assumed. @@ -266,30 +272,89 @@ and the derived files %% For compatibility with \XETEX, surrounding the \meta{font name} with %% square brackets is synonymous to using the |file:| prefix. % -% Accessing fonts by fontname allows loading system installed fonts as -% well as \fileent{texmf} ones, and requires a font names database; see -% Section~\ref{sec:fontdb} for more information. +% 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. % % \paragraph{Font name} % % The \meta{font name} can be either a font filename or actual font % name based on the \meta{prefix} as mentioned above. % -% Fonts loaded by filename may either include their absolute path in -% the filesystem or consist of just the filename without a path. If no -% path is specified, then \identifier{kpathsea} is used to locate the +% A filename request may optionally include the absolute path to the font file, +% allowing for fonts outside the standard locations to be loaded as well. +% If no path is specified, then \identifier{kpathsea} is used to locate the % font (which will typically be in the \fileent{texmf} tree or the % current directory). % -% For example, +% \subparagraph{Examples for 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\1={file:ec-lmr10} at 10pt -% \font\2={/Users/Shared/Fonts/aldus.otf} at 11pt -% \font\3={name:TeX Gyre Pagella} at 9pt -% \end{verbatim} +% \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} (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} +% +% \subparagraph{Examples for 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} % +% 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} % % \paragraph{Font features} % @@ -298,10 +363,14 @@ and the derived files % Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm}. % } % and font options. -% Prepending a font feature with a |+|-sign enables it, while -% a |-| disables it. For instance, the request +% Prepending a font feature with a |+| (plus sign) enables it, whereas +% a |-| (minus) disables it. For instance, the request % -% |\font\test=Latin Modern Roman:+clig;-kern| +% \begin{quote} +% \begin{verbatim} +% \font\test=LatinModernRoman:+clig;-kern +% \end{verbatim} +% \end{quote} % % \noindent activates contextual ligatures (|clig|) and disables % kerning (|kern|). @@ -309,35 +378,45 @@ and the derived files % the feature in a key/value expression. % The following request has the same meaning as the last one: % -% |\font\test=Latin Modern Roman:clig=true;kern=false| +% \begin{quote} +% \begin{verbatim} +% \font\test=LatinModernRoman:clig=true;kern=false +% \end{verbatim} +% \end{quote} % % \noindent -% Furthermore, this second syntax is required if a font feature -% accepts options besides its activation state. -% For example, \emphasis{stylistic alternates} (|salt|) provide a set -% of variants to given glyphs. -% These can be selected either explicitly by supplying the variant -% index (starting from 1), or randomly by setting the value to, -% obviously, |random|: -% -% |\font\test=Latin Modern Roman:salt=1| +% 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 feature!\false +% \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 \identifier{OpenType} processing +% \identifier{luaotfload} has two \OpenType processing % \emphasis{modes}: % \identifier{base} and \identifier{node}. % -% \identifier{base} mode works by mapping \identifier{OpenType} -% features to traditional \TEX ligature and kerning mechanisms, -% thus supporting only non-contextual substitutions and kerning -% pairs, but is the slightly faster variant. -% \identifier{node} mode works by processing \TEX’s internal +% \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 \identifier{OpenType} features. +% 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. @@ -346,19 +425,20 @@ and the derived files % mode, and \identifier{base} mode has to be requested where needed, % e.~g. for math fonts. % -% \item [script] \ref{script-tag} \hfill \\ -% An \identifier{OpenType} script tag;\footnote{% +% \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 do not assign features to the |dflt| script, in +% 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 \identifier{OpenType} language system identifier,\footnote{% +% An \OpenType language system identifier,\footnote{% % Cf. \url{http://www.microsoft.com/typography/otspec/languagetags.htm}. % } % defaulting to |dflt|. @@ -367,13 +447,13 @@ and the derived files % A comma-separated list of feature files to be applied to the % font. % Feature files contain a textual representation of -% \identifier{OpenType} tables and extend the features of a font +% \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 -% \identifier{OpenType} Feature File Specification.\footnote{% +% \OpenType Feature File Specification.\footnote{% % Cf. \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. % } % @@ -391,13 +471,17 @@ and the derived files % % For example, in order to set text in semitransparent red: % -% |\font\test=Latin Modern Roman:color=FF0000BB| +% \begin{quote} +% \begin{verbatim} +% \font\test={Latin Modern Roman}:color=FF0000BB +% \end{verbatim} +% \end{verbatim} % % \item [protrusion \& expansion] \hfill \\ -% These keys both control microtypographic features of the font, +% These keys control microtypographic features of the font, % namely \emphasis{character protrusion} and \emphasis{font % expansion}. -% They accept names of predefined \LUA tables that contain +% 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{otfl-fonts-ext.lua} where the @@ -423,12 +507,16 @@ and the derived files % for details. % }: % -% |\font\test=Latin Modern Roman:protrusion=default| +% \begin{quote} +% \begin{verbatim} +% \font\test=LatinModernRoman:protrusion=default +% \end{verbatim} +% \end{verbatim} % \end{description} % % \paragraph{Non-standard font features} -% \identifier{luaotfload} add a number of features that are not defined -% in the original \identifier{OpenType} specification, most of them +% \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: % @@ -441,7 +529,15 @@ and the derived files % % \item [tlig] % Applies legacy \TEX ligatures: -% |``|, |''|, |`|, |'|, |"|, |--|, |---|, |!`| and |?`|.% +% +% \begin{tabular}{rl|rl} +% `` & \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}. @@ -768,7 +864,7 @@ and the derived files % by \fileent{otfl-font-nms.lua}. % \ouritem {otfl-luat-ovr.lua} overrides for the \CONTEXT logging % functionality. -% \ouritem {otfl-font-pfb.lua} registers the \identifier{OpenType} +% \ouritem {otfl-font-pfb.lua} registers the \OpenType % font reader as handler for % Postscript fonts. % \ouritem {otfl-font-nms.lua} font database. @@ -803,7 +899,7 @@ and the derived files % being (see above, page \pageref{font-blacklist}). % % A common problem is the lack of features for some -% \identifier{OpenType} fonts even when specified. +% \OpenType fonts even when specified. % This can be related to the fact that some fonts do not provide % features for the |dflt| script (see above on page % \pageref{script-tag}), -- cgit v1.2.3 From 7f2b679825e404f7f574931bf7a00eccb190a63d Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sat, 20 Apr 2013 11:05:15 +0200 Subject: revise docs --- luaotfload.dtx | 103 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 3121512..45436a8 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -200,6 +200,8 @@ and the derived files % % \tableofcontents % +% \part{Package Description} +% % \section{Introduction} % % Font management and installation has always been painful with \TEX. A lot of @@ -238,7 +240,7 @@ and the derived files % Additionally, it provides means for accessing fonts known to the operating % system conveniently by indexing the metadata. % -% \section{Loading fonts} +% \section{Loading Fonts} % % \identifier{luaotfload} supports an extended font loading syntax: % @@ -537,7 +539,7 @@ and the derived files % --- & \verb|---| & !` & \verb|!`| \\ % ?` & \verb|?`| & & \\ % \end{tabular} - +% % \footnote{% % These contain the feature set \verb|trep| of earlier % versions of \identifier{luaotfload}. @@ -559,16 +561,16 @@ and the derived files % % As mentioned above, \identifier{luaotfload} keeps track of which % fonts are available to \LUATEX by means of a \emphasis{database}. -% This allows loading not only by explicit filenames but also by the -% proper names contained in the metadata which is often more accessible -% to humans.\footnote{% +% 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|-o| +% 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 font name, it -% will check if the database exists and load it or else generate a +% 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 @@ -578,14 +580,19 @@ and the derived files % 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[mkluatexfontdb.lua]% % {\fileent{mkluatexfontdb.lua}\footnote{% % The script may be named just \fileent{mkluatexfontdb} in your % distribution. % }} % -% However, it can be desirable at times to do some of these steps -% manually. +% 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{mkluatexfontdb} that offers an interface to the database % functionality. @@ -595,7 +602,7 @@ and the derived files % Tests by the maintainer show only marginal performance gain by % running with Luigi Scarso’s % \href{https://foundry.supelec.fr/projects/luajittex/}% -% {\LUA jit\TEX}, +% {\itentifier{Luajit\kern-.25ex\TEX}}, % which is probably due to the fact that most of the time is spent % on file system operations. % @@ -608,14 +615,11 @@ and the derived files % Invoke it from the command line with the \verb|--force| switch to % initiate a complete rebuild of the database. % -% \begin{verbatim} -% mkluatexfontdb --force -% \end{verbatim} -% -% 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. +% \begin{quote} +% \begin{verbatim} +% mkluatexfontdb --force +% \end{verbatim} +% \end{quote} % % \subsection{Search Paths} % @@ -644,9 +648,9 @@ and the derived files % Windows & \verb|%WINDIR%\Fonts| % \\ % Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break -% \fileent{/etc/fonts/fonts.conf"} +% \fileent{/etc/fonts/fonts.conf} % \\ -% Mac & \fileent{~/Library/Fonts},\break +% Mac & \fileent{\texttilde/Library/Fonts},\break % \fileent{/Library/Fonts},\break % \fileent{/System/Library/Fonts}, and\hfill\break % \fileent{/Network/Library/Fonts} @@ -657,18 +661,22 @@ and the derived files % \hrule % \end{table} % -% \subsection{Querying from outside} +% \subsection{Querying from Outside} % % \fileent{mkluatexfontdb.lua} also provides rudimentary means of -% accessing the font database. -% If the option \verb|--find=name| is given, the script will try and search -% the fonts indexed by \identifier{luaotfload} for a matching name. +% 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{verbatim} -% mkluatexfontdb.lua --find="Iwona Regular" -% \end{verbatim} +% \begin{quote} +% \begin{verbatim} +% mkluatexfontdb.lua --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. % @@ -678,16 +686,19 @@ and the derived files % \identifier{Iwona} you are looking for was “Bright” or “Light”. % The query % -% \begin{verbatim} -% mkluatexfontdb.lua -F --find="Iwona Bright" -% \end{verbatim} +% \begin{quote} +% \begin{verbatim} +% mkluatexfontdb.lua -F --find="Iwona Bright" +% \end{verbatim} +% \end{quote} % +% \noindent % will tell you that indeed the latter name is correct. % % \verb|mkluatexfontdb.lua --help| will list the available command line -% switches, including some that will not be discussed in detail here. +% switches, including some not discussed in detail here. % -% \subsection{Blacklisting fonts} +% \subsection{Blacklisting Fonts} % \label{font-blacklist} % % Some fonts are problematic in general, or just in \LUATEX. @@ -699,7 +710,7 @@ and the derived files % \fileent{otfl-blacklist.cnf}. % % A blacklist file is a list of font filenames, one per line. -% Specifying the full path where the file is located is optional, the +% 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 @@ -912,7 +923,27 @@ and the derived files % \font\test=file:MyFont.otf:script=latn;+liga; % \end{verbatim} % -% \part{\fileent{luaotfload.lua}} +% \part{Implementation} +% +% \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> @@ -1401,7 +1432,7 @@ loadmodule"features.lua" % % \fi % -% \part{\fileent{luaotfload.sty}} +% \section{\fileent{luaotfload.sty}} % % \iffalse %<*package> -- cgit v1.2.3 From 267997e49570d3d08b4de3eb6c9c5394ec1d2f97 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 14:28:25 +0200 Subject: Making the file method resolve fonts with the database --- luaotfload.dtx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/luaotfload.dtx b/luaotfload.dtx index 22afb45..595a9fa 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -1157,6 +1157,20 @@ loadmodule"font-nms.lua" loadmodule"font-clr.lua" loadmodule"font-ltx.lua" --- new in 2.0, added 2011 +% \end{macrocode} +% +% +% This hack makes fonts called with file method found by fonts.names.resove +% instead of just trying to find them with kpse. It is necessary in case +% of fonts that are not accessible by kpse but present in the database, a +% quite common case under Linux. +% +% \begin{macrocode} + +fonts.definers.resolvers.file = function (specification) + specification.name = fonts.names.resolve('', '', specification) +end + % \end{macrocode} % % -- cgit v1.2.3 From 488d7fd86850691244709064777bc22113262701 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 14:31:15 +0200 Subject: Adding a little TODO for file lookup --- otfl-font-nms.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index ee9fa33..b62f5ec 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -243,6 +243,9 @@ font database created by the mkluatexfontdb script. --- successful lookup as this cannot be inferred from the other --- values. --- +--- TODO: this function is used also with the file lookup, it could be optimized +--- a lot for this case. +--- resolve = function (_,_,specification) -- the 1st two parameters are used by ConTeXt local name = sanitize_string(specification.name) local style = sanitize_string(specification.style) or "regular" -- cgit v1.2.3 From 0d3168d8b69d8eed835ff137cb24b6f83c2a28f6 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 16:19:46 +0200 Subject: As a matter of fact, there was nothing to optimize... --- otfl-font-nms.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index b62f5ec..e8ad626 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -243,8 +243,6 @@ font database created by the mkluatexfontdb script. --- successful lookup as this cannot be inferred from the other --- values. --- ---- TODO: this function is used also with the file lookup, it could be optimized ---- a lot for this case. --- resolve = function (_,_,specification) -- the 1st two parameters are used by ConTeXt local name = sanitize_string(specification.name) -- cgit v1.2.3 From b1e675cbd171400ccc7a41a092a668215a8d8343 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 16:29:53 +0200 Subject: Making make check work on my system I cannot actually understand what was all the ENV thing for... --- Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 6606861..0a8770d 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,6 @@ TESTDIR = tests TESTFILES = $(wildcard $(TESTDIR)/*.tex) TESTFILES_SYS = $(TESTDIR)/systemfonts.tex $(TESTDIR)/fontconfig_conf_reading.tex TESTFILES_TL = $(filter-out $(TESTFILES_SYS), $(TESTFILES)) -TESTENV = env TEXINPUTS='.;..;$$TEXMF/tex/{luatex,plain,generic,}//' TEXMFVAR='../var' # Files grouped by installation location SCRIPTFILES = $(SCRIPT) @@ -95,9 +94,9 @@ install: $(ALL_FILES) check: $(RUNFILES) $(TESTFILES_TL) @rm -rf var - @cd $(TESTDIR); for f in $(TESTFILES_TL); do \ + @for f in $(TESTFILES_TL); do \ echo "check: luatex $$f"; \ - $(TESTENV) luatex --interaction=batchmode ../$$f \ + luatex --interaction=batchmode $$f \ > /dev/null || exit $$?; \ done -- cgit v1.2.3 From 0736fb6f4a24ed1f645e2c197170ce211c562a84 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 17:42:51 +0200 Subject: New version of marks test, nothing new --- tests/marks.tex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/marks.tex b/tests/marks.tex index d33c82a..3af264e 100644 --- a/tests/marks.tex +++ b/tests/marks.tex @@ -1,6 +1,9 @@ \input luaotfload.sty \font\test={file:GenBasR.ttf:script=latn} +\test ä\quad Ä + \test a\char"0308 \quad A\char"0308 \quad j\char"0323 + \bye -- cgit v1.2.3 From 11634a490e4a39e7ae066a6701fd367bddd874d7 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 17:43:26 +0200 Subject: Adding fontspec test file --- tests/fontspec_lookup.ltx | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/fontspec_lookup.ltx diff --git a/tests/fontspec_lookup.ltx b/tests/fontspec_lookup.ltx new file mode 100644 index 0000000..6645427 --- /dev/null +++ b/tests/fontspec_lookup.ltx @@ -0,0 +1,41 @@ +\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} -- cgit v1.2.3 From c07fee9600a52d1990868b6e900a1397a1aebafe Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 17:44:50 +0200 Subject: Fixing outdated math test --- tests/math.tex | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/math.tex b/tests/math.tex index 55bb2aa..a2615f1 100644 --- a/tests/math.tex +++ b/tests/math.tex @@ -35,7 +35,6 @@ $$ $$ \Umathaccent "0 "4 "23DE {a+b} -+ \Umathbotaccent "0 "4 "23DF {a+b} = C $$ $$ -- cgit v1.2.3 From ea1bf4512a22082e3e6c712c4f0722be31452cf1 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 17:48:57 +0200 Subject: Oops --- tests/microtypography.tex | 4 ++-- tests/opbd.tex | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/microtypography.tex b/tests/microtypography.tex index f2d461d..99deb5f 100644 --- a/tests/microtypography.tex +++ b/tests/microtypography.tex @@ -5,7 +5,7 @@ \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 +\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, @@ -19,7 +19,7 @@ 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 +\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, diff --git a/tests/opbd.tex b/tests/opbd.tex index 3d26662..1a838cd 100644 --- a/tests/opbd.tex +++ b/tests/opbd.tex @@ -5,7 +5,7 @@ \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 +\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, @@ -19,7 +19,7 @@ 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 +\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, -- cgit v1.2.3 From 77576a1a919a9e97ca51493ba2f37060d1358af1 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 17:50:41 +0200 Subject: Adding the .ltx files in the tests --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0a8770d..ae850ff 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ SOURCE = $(DTX) $(OTFL) README Makefile NEWS $(SCRIPT) # test files TESTDIR = tests -TESTFILES = $(wildcard $(TESTDIR)/*.tex) +TESTFILES = $(wildcard $(TESTDIR)/*.tex $(TESTDIR)/*.ltx) TESTFILES_SYS = $(TESTDIR)/systemfonts.tex $(TESTDIR)/fontconfig_conf_reading.tex TESTFILES_TL = $(filter-out $(TESTFILES_SYS), $(TESTFILES)) -- cgit v1.2.3 From 90cbb697883ead85063e26fc33784e0f366a262f Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 17:51:14 +0200 Subject: Renaming microtypography (.tex -> .ltx) --- tests/microtypography.ltx | 36 ++++++++++++++++++++++++++++++++++++ tests/microtypography.tex | 36 ------------------------------------ 2 files changed, 36 insertions(+), 36 deletions(-) create mode 100644 tests/microtypography.ltx delete mode 100644 tests/microtypography.tex diff --git a/tests/microtypography.ltx b/tests/microtypography.ltx new file mode 100644 index 0000000..99deb5f --- /dev/null +++ b/tests/microtypography.ltx @@ -0,0 +1,36 @@ +\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/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 -- cgit v1.2.3 From e49d16d82efc3327faa3143ce31a7bc2e9d09a93 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 17:52:57 +0200 Subject: Hmmm... --- tests/microtypography.ltx | 36 ------------------------------------ tests/microtypography.tex | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 36 deletions(-) delete mode 100644 tests/microtypography.ltx create mode 100644 tests/microtypography.tex diff --git a/tests/microtypography.ltx b/tests/microtypography.ltx deleted file mode 100644 index 99deb5f..0000000 --- a/tests/microtypography.ltx +++ /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/microtypography.tex b/tests/microtypography.tex new file mode 100644 index 0000000..99deb5f --- /dev/null +++ b/tests/microtypography.tex @@ -0,0 +1,36 @@ +\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 -- cgit v1.2.3 From 1828aabd4a79d6d31dfc93bb77a500c648b6ba3f Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sat, 20 Apr 2013 18:16:18 +0200 Subject: Adding a test for quite uncommon otf features This is a font I actually use... --- tests/zero_width_marks_lig.tex | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/zero_width_marks_lig.tex diff --git a/tests/zero_width_marks_lig.tex b/tests/zero_width_marks_lig.tex new file mode 100644 index 0000000..9dbaac0 --- /dev/null +++ b/tests/zero_width_marks_lig.tex @@ -0,0 +1,13 @@ +\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 + +\bye -- cgit v1.2.3 From d58de069818ff073ef22c73f085fc7453516d9d4 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 00:19:52 +0200 Subject: decrease verbosity of normal logging --- otfl-font-nms.lua | 63 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index f90058b..6f236b6 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -16,6 +16,9 @@ local tonumber = tonumber local iolines = io.lines local ioopen = io.open local kpseexpand_path = kpse.expand_path +local kpseexpand_var = kpse.expand_var +local kpselookup = kpse.lookup +local kpsereadable_file = kpse.readable_file local mathabs = math.abs local mathmin = math.min local stringfind = string.find @@ -39,6 +42,7 @@ local dirmkdirs = dir.mkdirs local filebasename = file.basename local filecollapsepath = file.collapsepath local fileextname = file.extname +local fileiswritable = file.iswritable local filejoin = file.join local filereplacesuffix = file.replacesuffix local filesplitpath = file.splitpath @@ -58,7 +62,7 @@ names.version = 2.2 names.data = nil names.path = { basename = "otfl-names.lua", - dir = filejoin(kpse.expand_var("$TEXMFVAR"), names_dir), + dir = filejoin(kpseexpand_var("$TEXMFVAR"), names_dir), } @@ -165,15 +169,15 @@ load_names = function ( ) local foundname, data = load_lua_file(path) if data then - report("info", 0, "Font names database loaded", "%s", foundname) + report("info", 1, "db", + "Font names database loaded", "%s", foundname) else - report("info", 0, + report("info", 0, "db", [[Font names database not found, generating new one. This can take several minutes; please be patient.]]) data = update_names(fontnames_init()) save_names(data) end - texiowrite_nl"" return data end @@ -342,8 +346,8 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con end end if #found == 1 then - if kpse.lookup(found[1].filename[1]) then - report("log", 0, "load font", + if kpselookup(found[1].filename[1]) then + report("log", 0, "resolve", "font family='%s', subfamily='%s' found: %s", name, style, found[1].filename[1] ) @@ -363,8 +367,8 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con least = difference end end - if kpse.lookup(closest.filename[1]) then - report("log", 0, "load font", + if kpselookup(closest.filename[1]) then + report("log", 0, "resolve", "font family='%s', subfamily='%s' found: %s", name, style, closest.filename[1] ) @@ -665,9 +669,9 @@ local function path_normalize(path) if os.type ~= "windows" and os.type ~= "msdos" then local dest = lfs.readlink(path) if dest then - if kpse.readable_file(dest) then + if kpsereadable_file(dest) then path = dest - elseif kpse.readable_file(filejoin(file.dirname(path), dest)) then + elseif kpsereadable_file(filejoin(file.dirname(path), dest)) then path = filejoin(file.dirname(path), dest) else -- broken symlink? @@ -684,7 +688,7 @@ names.blacklist = { } local function read_blacklist() local files = { - kpse.lookup("otfl-blacklist.cnf", {all=true, format="tex"}) + kpselookup("otfl-blacklist.cnf", {all=true, format="tex"}) } local blacklist = names.blacklist local whitelist = { } @@ -703,7 +707,7 @@ local function read_blacklist() if stringsub(line, 1, 1) == "-" then whitelist[stringsub(line, 2, -1)] = true else - report("log", 2, "blacklisted file", "%s", line) + report("log", 2, "db", "blacklisted file", "%s", line) blacklist[line] = true end end @@ -760,22 +764,25 @@ local function scan_dir(dirname, fontnames, newfontnames, texmf) ]] local list, found = { }, { } local nbfound = 0 - report("log", 2, "scanning", "%s", dirname) + report("log", 2, "db", "scanning", "%s", dirname) for _,i in next, font_extensions do for _,ext in next, { i, stringupper(i) } do found = dirglob(stringformat("%s/**.%s$", dirname, ext)) -- note that glob fails silently on broken symlinks, which happens -- sometimes in TeX Live. - report("log", 2, "fonts found", "%s '%s' fonts found", #found, ext) + report("log", 2, "db", + "fonts found", "%s '%s' fonts found", #found, ext) nbfound = nbfound + #found tableappend(list, found) end end - report("log", 2, "fonts found", "%d fonts found in '%s'", nbfound, dirname) + report("log", 2, "db", + "fonts found", "%d fonts found in '%s'", nbfound, dirname) for _,file in next, list do file = path_normalize(file) - report("log", 1, "loading font", "%s", file) + report("log", 1, "db", + "loading font", "%s", file) load_font(file, fontnames, newfontnames, texmf) end end @@ -786,9 +793,9 @@ local function scan_texmf_fonts(fontnames, newfontnames) variables OPENTYPEFONTS and TTFONTS of texmf.cnf ]] if stringis_empty(kpseexpand_path("$OSFONTDIR")) then - report("info", 0, "Scanning TEXMF fonts...") + report("info", 1, "db", "Scanning TEXMF fonts...") else - report("info", 0, "Scanning TEXMF and OS fonts...") + report("info", 1, "db", "Scanning TEXMF and OS fonts...") end local fontdirs = stringgsub(kpseexpand_path("$OPENTYPEFONTS"), "^%.", "") fontdirs = fontdirs .. stringgsub(kpseexpand_path("$TTFONTS"), "^%.", "") @@ -828,7 +835,7 @@ read_fonts_conf = function (path, results, passed_paths) passed_paths[#passed_paths+1] = path passed_paths_set = tabletohash(passed_paths, true) if not fh then - report("log", 2, "cannot open file", "%s", path) + report("log", 2, "db", "cannot open file", "%s", path) return results end local incomments = false @@ -879,7 +886,7 @@ read_fonts_conf = function (path, results, passed_paths) include = filejoin(file.dirname(path), include) end if lfs.isfile(include) - and kpse.readable_file(include) + and kpsereadable_file(include) and not passed_paths_set[include] then -- maybe we should prevent loops here? @@ -930,8 +937,8 @@ local function scan_os_fonts(fontnames, newfontnames) - fontcache for Unix (reads the fonts.conf file and scans the directories) - a static set of directories for Windows and MacOSX ]] - report("info", 0, "Scanning OS fonts...") - report("info", 2, "Searching in static system directories...") + report("info", 1, "db", "Scanning OS fonts...") + report("info", 2, "db", "Searching in static system directories...") for _,d in next, get_os_dirs() do scan_dir(d, fontnames, newfontnames, false) end @@ -943,7 +950,7 @@ update_names = function (fontnames, force) - fontnames is the final table to return - force is whether we rebuild it from scratch or not ]] - report("info", 0, "Updating the font names database") + report("info", 1, "db", "Updating the font names database") if force then fontnames = fontnames_init() @@ -953,8 +960,8 @@ update_names = function (fontnames, force) end if fontnames.version ~= names.version then fontnames = fontnames_init() - report("log", 0, "No font names database or old one found; " - .."generating new one") + report("log", 1, "db", "No font names database or old " + .. "one found; generating new one") end end local newfontnames = fontnames_init() @@ -977,14 +984,14 @@ save_names = function (fontnames) dirmkdirs(path) end path = filejoin(path, names.path.basename) - if file.iswritable(path) then + if fileiswritable(path) then local luaname, lucname = make_name(path) tabletofile(luaname, fontnames, true) caches.compile(fontnames,luaname,lucname) - report("info", 0, "Font names database saved") + report("info", 1, "db", "Font names database saved") return path else - report("info", 0, "Failed to save names database") + report("info", 1, "db", "Failed to save names database") return nil end end -- cgit v1.2.3 From b5b0f282e7db1365a95e12401f9239cbe50b21f7 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 00:58:34 +0200 Subject: make mkluatexfontdb.lua behave differently if called as *fontdbutil* --- mkluatexfontdb.lua | 137 +++++++++++++++++++++++++++++++++++++++++++---------- otfl-font-nms.lua | 12 ++--- 2 files changed, 117 insertions(+), 32 deletions(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 80c0951..5ff6a5b 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -11,6 +11,8 @@ kpse.set_program_name"luatex" local stringformat = string.format local texiowrite_nl = texio.write_nl +local stringfind = string.find +local stringlower = string.lower -- First we need to be able to load module (code copied from -- luatexbase-loader.sty): @@ -21,11 +23,49 @@ local loader_path = assert(kpse.find_file(loader_file, "lua"), --texiowrite_nl("("..loader_path..")") dofile(loader_path) -- FIXME this pollutes stdout with filenames -_G.config = _G.config or { } -local config = _G.config +--[[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{fontdbutil} -- 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 +config.luaotfload = config.luaotfload or { } + +do -- we don’t have file.basename and the likes yet, so inline parser ftw + local C, P = lpeg.C, lpeg.P + local lpegmatch = lpeg.match + 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) + + -- if stringfind(stringlower(arg[0]), "^fontdbutil") then + local self = lpegmatch(p_basename, stringlower(arg[0])) + if self == "fontdbutil" then + config.luaotfload.self = "fontdbutil" + else + config.luaotfload.self = "mkluatexfontdb" + end +end config.lualibs = config.lualibs or { } -config.lualibs.prefer_merged = false +config.lualibs.verbose = false +config.lualibs.prefer_merged = true config.lualibs.load_extended = false require"lualibs" @@ -34,21 +74,24 @@ require"otfl-luat-ovr.lua" --- this populates the logs.* namespace require"otfl-font-nms" require"alt_getopt" -local name = "mkluatexfontdb" local version = "2.2" -- same version number as luaotfload local names = fonts.names local db_src_out = names.path.dir.."/"..names.path.basename local db_bin_out = file.replacesuffix(db_src_out, "luc") -local function help_msg() - texiowrite_nl(stringformat([[ + +local help_messages = { + fontdbutil = [[ Usage: %s [OPTION]... -Rebuild the LuaTeX font database. +Operations on the LuaTeX font database. + +This tool is part of the luaotfload package. Valid options are: + +------------------------------------------------------------------------------- + VERBOSITY AND LOGGING -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 @@ -56,6 +99,12 @@ Valid options: -V --version print version and exit -h --help print this message +------------------------------------------------------------------------------- + DATABASE + + -u --update update the database + -f --force force re-indexing all fonts + --find="font name" query the database for a font name -F --fuzzy look for approximate matches if --find fails @@ -65,13 +114,39 @@ The font database will be saved to %s %s -]], name, db_src_out, db_bin_out)) +]], + mkluatexfontdb = [[ + +Usage: %s [OPTION]... + +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 font database will be saved to + %s + %s + +]], +} + +local help_msg = function ( ) + local template = help_messages[config.luaotfload.self] + or help.messages.fontdbutil + texiowrite_nl(stringformat(template, config.luaotfload.self, db_src_out, db_bin_out)) end -local function version_msg() +local version_msg = function ( ) texiowrite_nl(stringformat( "%s version %s, database version %s.\n", - name, version, names.version)) + config.luaotfload.self, version, names.version)) end --[[-- @@ -80,17 +155,19 @@ executed in the correct order. To avoid duplication we track them in a set. --]]-- -local action_sequence = { "loglevel", "help", "version", "generate", "query" } +local action_sequence = { + "loglevel", "help", "version", "generate", "query" +} local action_pending = table.tohash(action_sequence, false) -action_pending.loglevel = true --- always set the loglevel -action_pending.generate = true --- this is the default action +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) logs.set_loglevel(job.log_level) - logs.names_report("log", 2, + logs.names_report("log", 2, "util", "setting log level", "%d", job.log_level) return true, true end @@ -108,8 +185,8 @@ end actions.generate = function (job) local fontnames, savedname fontnames = names.update(fontnames, job.force_reload) - logs.names_report("log", 0, "fonts in the database", - "%i", #fontnames.mappings) + logs.names_report("log", 0, "db", + "fonts in the database", "%i", #fontnames.mappings) savedname = names.save(fontnames) if savedname then --- FIXME have names.save return bool return true, true @@ -164,6 +241,10 @@ local process_cmdline = function ( ) -- unit -> jobspec log_level = 1, } + if config.luaotfload.self == "mkluatexfontdb" then + action_pending["generate"] = true + end + local long_options = { force = "f", help = "h", @@ -174,9 +255,10 @@ local process_cmdline = function ( ) -- unit -> jobspec find = 1, fuzzy = "F", limit = 1, + update = "u", } - local short_options = "fFqvVh" + local short_options = "fFquvVh" local options, _, optarg = alt_getopt.get_ordered_opts (arg, short_options, long_options) @@ -186,6 +268,8 @@ local process_cmdline = function ( ) -- unit -> jobspec 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 @@ -197,6 +281,7 @@ local process_cmdline = function ( ) -- unit -> jobspec 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] @@ -234,24 +319,24 @@ local main = function ( ) -- unit -> int local actionname = action_sequence[i] local exit = false if action_pending[actionname] then - logs.names_report("log", 3, "preparing for task", + logs.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, "could not finish task", - "%s", actionname) + logs.names_report(false, 0, "util", + "could not finish task", "%s", actionname) retval = -1 exit = true elseif not continue then - logs.names_report(false, 3, "task completed, exiting", - "%s", actionname) + logs.names_report(false, 3, "util", + "task completed, exiting", "%s", actionname) exit = true else - logs.names_report(false, 3, "task completed successfully", - "%s", actionname) + logs.names_report(false, 3, "util", + "task completed successfully", "%s", actionname) end end if exit then break end diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 6f236b6..048a90a 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -557,7 +557,7 @@ font_fullinfo = function (filename, subfont, texmf) end else -- no names table, propably a broken font - report("log", 1, "broken font rejected", "%s", basefile) + report("log", 1, "db", "broken font rejected", "%s", basefile) return end tfmdata.fontname = metadata.fontname @@ -589,7 +589,7 @@ local load_font = function (filename, fontnames, newfontnames, texmf) if filename then if names.blacklist[filename] or names.blacklist[basename] then - report("log", 2, "ignoring font", "%s", filename) + report("log", 2, "db", "ignoring font", "%s", filename) return end local timestamp, db_timestamp @@ -612,7 +612,7 @@ local load_font = function (filename, fontnames, newfontnames, texmf) newmappings[#newmappings+1] = mappings[v] newstatus[basefile].index[index+1] = #newmappings end - report("log", 1, "font already indexed", "%s", basefile) + report("log", 1, "db", "font already indexed", "%s", basefile) return end local info = fontloader.info(filename) @@ -647,7 +647,7 @@ local load_font = function (filename, fontnames, newfontnames, texmf) newstatus[basefile].index[1] = index end else - report("log", 1, "failed to load", "%s", basefile) + report("log", 1, "db", "failed to load", "%s", basefile) end end end @@ -988,10 +988,10 @@ save_names = function (fontnames) local luaname, lucname = make_name(path) tabletofile(luaname, fontnames, true) caches.compile(fontnames,luaname,lucname) - report("info", 1, "db", "Font names database saved") + report("info", 0, "db", "Font names database saved") return path else - report("info", 1, "db", "Failed to save names database") + report("info", 0, "db", "Failed to save names database") return nil end end -- cgit v1.2.3 From 56188a1cc038c94defb8102bfd4926482b25ffb7 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 01:12:08 +0200 Subject: document behavior modes of fontdbutil --- luaotfload.dtx | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 45436a8..06541f6 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -585,8 +585,9 @@ and the derived files % This is particularly noticeable if it occurs during a typesetting run. % In any case, subsequent updates to the database will be quite fast. % -% \subsection[mkluatexfontdb.lua]% -% {\fileent{mkluatexfontdb.lua}\footnote{% +% \subsection[fontdbutil / mkluatexfontdb.lua]% +% {\fileent{fontdbutil} / +% \fileent{mkluatexfontdb.lua}\footnote{% % The script may be named just \fileent{mkluatexfontdb} in your % distribution. % }} @@ -594,7 +595,7 @@ and the derived files % 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{mkluatexfontdb} that offers an interface to the database +% \fileent{fontdbutil} 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 @@ -609,18 +610,33 @@ and the derived files % \emphasis{Note}: % On \abbrev{MS} \identifier{Windows} systems, the script can be run % either by calling the wrapper application -% \fileent{mkluatexfontdb.exe} or as -% \verb|texlua.exe mkluatexfontdb.lua|. +% \fileent{fontdbutil.exe} or as +% \verb|texlua.exe fontdbutil|. % } -% Invoke it from the command line with the \verb|--force| switch to -% initiate a complete rebuild of the database. +% Invoked with the argument \verb|--update| it will perform a database +% update, scanning for fonts not indexed. % % \begin{quote} % \begin{verbatim} -% mkluatexfontdb --force +% fontdbutil --update % \end{verbatim} % \end{quote} % +% Adding the \verb|--force| switch will initiate a complete +% rebuild of the database. +% +% \begin{quote} +% \begin{verbatim} +% fontdbutil --update --force +% \end{verbatim} +% \end{quote} +% +% For sake of backwards compatibility, \fileent{fontdbutil} 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 @@ -663,7 +679,7 @@ and the derived files % % \subsection{Querying from Outside} % -% \fileent{mkluatexfontdb.lua} also provides rudimentary means of +% \fileent{fontdbutil} 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 @@ -672,7 +688,7 @@ and the derived files % % \begin{quote} % \begin{verbatim} -% mkluatexfontdb.lua --find="Iwona Regular" +% fontdbutil --find="Iwona Regular" % \end{verbatim} % \end{quote} % @@ -688,14 +704,14 @@ and the derived files % % \begin{quote} % \begin{verbatim} -% mkluatexfontdb.lua -F --find="Iwona Bright" +% fontdbutil -F --find="Iwona Bright" % \end{verbatim} % \end{quote} % % \noindent % will tell you that indeed the latter name is correct. % -% \verb|mkluatexfontdb.lua --help| will list the available command line +% \verb|fontdbutil --help| will list the available command line % switches, including some not discussed in detail here. % % \subsection{Blacklisting Fonts} @@ -704,7 +720,7 @@ and the derived files % 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|mkluatexfontdb -v| to increase verbosity. +% running \verb|fontdbutil -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{otfl-blacklist.cnf}. @@ -901,7 +917,7 @@ and the derived files % verbosity levels and redirecting log output to \fileent{stdout}: % % \begin{verbatim} -% mkluatexfontdb.lua -F -vvv --log=stdout +% fontdbutil -F -vvv --log=stdout % \end{verbatim} % % If this fails, the font last printed to the terminal is likely to be -- cgit v1.2.3 From 8f8e806fa7f86f5e0fa25da9b992304a354b88cf Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 10:49:22 +0200 Subject: revise doc --- luaotfload.dtx | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 06541f6..0205898 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -757,13 +757,13 @@ and the derived files % % \section{Files from \CONTEXT and \LUATEX-Fonts} % -% This package relies on code originally written by Hans +% \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. -% \identifier{luaotfload} integrates the font loader as distributed in +% 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. @@ -776,7 +776,7 @@ and the derived files % 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 the files to prevent name +% \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 @@ -837,10 +837,10 @@ and the derived files % \end{itemize} % \end{multicols} % -% \normalitem The \emphasis{Font Loader} itself. +% \normalitem The \emphasis{font loader} itself. % These files have been written for -% \LUATEX-Fonts and are distributed along with -% \identifier{luaotfload}. +% \LUATEX-Fonts and they are distributed along +% with \identifier{luaotfload}. % \begin{multicols}{2} % \begin{itemize} % \incitem{luatex-basics-gen.lua} @@ -871,6 +871,16 @@ and the derived files % \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 they are hard-coded in +% \fileent{otfl-fonts.lua}. +% Thus if you prefer running bleeding edge code from the +% \CONTEXT beta, all you have to do is remove +% \fileent{otfl-fonts-merged.lua} from the search path. +% % \end{itemize} % % In addition to these, \identifier{luaotfload} requires a number of @@ -889,7 +899,7 @@ and the derived files % \ouritem {otfl-lib-dir.lua} \fileent{l-dir} from \CONTEXT; % contains functionality required % by \fileent{otfl-font-nms.lua}. -% \ouritem {otfl-luat-ovr.lua} overrides for the \CONTEXT logging +% \ouritem {otfl-luat-ovr.lua} overrides the \CONTEXT logging % functionality. % \ouritem {otfl-font-pfb.lua} registers the \OpenType % font reader as handler for @@ -913,11 +923,12 @@ and the derived files % version of this package before reporting a bug, as % \identifier{luaotfload} is under active development and still a % moving target. +% % Errors during database generation can be traced by increasing % verbosity levels and redirecting log output to \fileent{stdout}: % % \begin{verbatim} -% fontdbutil -F -vvv --log=stdout +% fontdbutil -fuvvv --log=stdout % \end{verbatim} % % If this fails, the font last printed to the terminal is likely to be @@ -928,12 +939,12 @@ and the derived files % 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 |dflt| script (see above on page +% 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 script when the font is defined should +% If this happens, assigning a noth script when the font is defined should % fix it. -% For example with the |latn| script: +% For example with \verb|latn|: % % \begin{verbatim} % \font\test=file:MyFont.otf:script=latn;+liga; -- cgit v1.2.3 From e7b982403c2c77e22a263e87b4d40bb226d61107 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sun, 21 Apr 2013 12:10:25 +0200 Subject: A possible bug in otfl-fonts-merged.lua? Really nothing at all but who knows... this should be reported to Hans. --- otfl-fonts-merged.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otfl-fonts-merged.lua b/otfl-fonts-merged.lua index 19c8ce1..e095153 100644 --- a/otfl-fonts-merged.lua +++ b/otfl-fonts-merged.lua @@ -2988,7 +2988,7 @@ end do local cachepaths=kpse.expand_path('$TEXMFCACHE') or "" if cachepaths=="" then - cachepaths=kpse.expand_path('$TEXMFVAR') + cachepaths=kpse.expand_path('$TEXMFVAR') or "" end if cachepaths=="" then cachepaths=kpse.expand_path('$VARTEXMF') -- cgit v1.2.3 From cdd05ad4815f088f368c4ee7f4bfdea2bcacbf5f Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 12:14:20 +0200 Subject: add --info arg to fontdbutil --- mkluatexfontdb.lua | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 5ff6a5b..776fbb3 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -107,6 +107,9 @@ This tool is part of the luaotfload package. Valid options are: --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 font metadata --log=stdout redirect log output to stdout @@ -149,6 +152,37 @@ local version_msg = function ( ) config.luaotfload.self, version, names.version)) end +local show_info_items = function (fontinfo) + local items = table.sortedkeys(fontinfo) + for n = 1, #items do + local item = items[n] + texiowrite_nl(stringformat( + [[ %11s: %s]], item, fontinfo[item])) + end +end + +local show_font_info = function (filename) + local fullname = resolvers.findfile(filename) + if fullname then + local fontinfo = fontloader.info(fullname) + local nfonts = #fontinfo + if nfonts > 0 then -- true type collection + logs.names_report(true, 0, "resolve", + [[%s is a font collection]], filename) + for n = 1, nfonts do + logs.names_report(true, 0, "resolve", + [[showing info for font no. %d]], n) + show_info_items(fontinfo[n]) + end + else + show_info_items(fontinfo) + end + else + logs.names_report(true, 0, "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 @@ -212,6 +246,9 @@ actions.query = function (job) "resolve", "Font “%s” found!", query) logs.names_report(false, 0, "resolve", "Resolved file name “%s”:", foundname) + if job.show_info then + show_font_info(foundname) + end else logs.names_report(false, 0, "resolve", "Cannot find “%s”.", query) @@ -246,19 +283,20 @@ local process_cmdline = function ( ) -- unit -> jobspec end local long_options = { + find = 1, force = "f", + fuzzy = "F", help = "h", + info = "i", + limit = 1, log = 1, quiet = "q", + update = "u", verbose = 1 , version = "V", - find = 1, - fuzzy = "F", - limit = 1, - update = "u", } - local short_options = "fFquvVh" + local short_options = "fFiquvVh" local options, _, optarg = alt_getopt.get_ordered_opts (arg, short_options, long_options) @@ -303,6 +341,8 @@ local process_cmdline = function ( ) -- unit -> jobspec if lim then result.fuzzy_limit = tonumber(lim) end + elseif v == "i" then + result.show_info = true end end return result -- cgit v1.2.3 From f5cc8a1784969e5dd93909d6a9455370e475f4fb Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 12:21:46 +0200 Subject: document --info option --- luaotfload.dtx | 18 ++++++++++++++++-- luaotfload.lua | 5 +++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 0205898..8b468bb 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -696,8 +696,9 @@ and the derived files % 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 you can add the -% \verb|-F| switch to the command line to enable approximate matching. +% 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 @@ -711,6 +712,19 @@ and the derived files % \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} +% fontdbutil -F --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}. +% } +% % \verb|fontdbutil --help| will list the available command line % switches, including some not discussed in detail here. % diff --git a/luaotfload.lua b/luaotfload.lua index 256c792..c85041b 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -25,6 +25,9 @@ local reset_callback, 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} @@ -32,6 +35,7 @@ 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 = @@ -346,6 +350,7 @@ local patch_defined_font = function (...) if type(tfmdata) == "table" then call_callback("luaotfload.patch_font", tfmdata) end + -- inspect(table.keys(tfmdata)) return tfmdata end -- cgit v1.2.3 From 14b3ba09e583caaa7e31ab7bfabc4871cbb206fd Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sun, 21 Apr 2013 15:17:58 +0200 Subject: Using caches.* functions from ConTeXt (fixes Issue #5) --- otfl-font-nms.lua | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index e8ad626..3ee6ee4 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -53,14 +53,25 @@ fonts = fonts or { } fonts.names = fonts.names or { } local names = fonts.names -local names_dir = "luatex-cache/generic/names" + names.version = 2.2 names.data = nil names.path = { basename = "otfl-names.lua", - dir = filejoin(kpse.expand_var("$TEXMFVAR"), names_dir), + dir = "", + path = "", } +-- We use the cache.* of ConTeXt (see luat-basics-gen), we can +-- use it safely (all checks and directory creations are already done). It +-- uses TEXMFCACHE or TEXMFVAR as starting points. +local writable_path = caches.getwritablepath("names","") +if not writable_path then + error("Impossible to find a suitable writeable cache...") +end +names.path.dir = writable_path +names.path.path = filejoin(writable_path, names.path.basename) + ---- --- @@ -161,8 +172,7 @@ local scan_external_dir local update_names load_names = function ( ) - local path = filejoin(names.path.dir, names.path.basename) - local foundname, data = load_lua_file(path) + local foundname, data = load_lua_file(names.path.path) if data then report("info", 0, "Font names database loaded", "%s", foundname) -- cgit v1.2.3 From 397c25951b2b1fd8e67e2ddea651b0ee953b0f36 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sun, 21 Apr 2013 15:29:44 +0200 Subject: A wonderful bug fix --- otfl-font-nms.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index 3ee6ee4..c9c700c 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -927,12 +927,17 @@ local function get_os_dirs() elseif os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then local windir = os.getenv("WINDIR") return { filejoin(windir, 'Fonts') } - else --- TODO what about ~/config/fontconfig/fonts.conf etc? + else + local passed_paths = {} + local os_dirs = {} + -- what about ~/config/fontconfig/fonts.conf etc? + -- Answer: they should be included by the others, please report if it's not for _,p in next, {"/usr/local/etc/fonts/fonts.conf", "/etc/fonts/fonts.conf"} do if lfs.isfile(p) then - return read_fonts_conf("/etc/fonts/fonts.conf", {}, {}) + read_fonts_conf(p, os_dirs, passed_paths) end end + return os_dirs end return {} end -- cgit v1.2.3 From 908c644982184f1119339a6abeb6fb8b548d5710 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sun, 21 Apr 2013 15:42:55 +0200 Subject: Adding a little TODO and commenting a XXX --- otfl-font-nms.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index c9c700c..2e54028 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -6,6 +6,11 @@ if not modules then modules = { } end modules ['font-nms'] = { license = "GNU GPL v2" } +--- TODO: if the specification is an absolute filename with a font not in the +--- database, add the font to the database and load it. There is a small +--- difficulty with the filenames of the TEXMF tree that are referenced as +--- relative paths... + --- Luatex builtins local load = load local next = next @@ -246,7 +251,7 @@ font database created by the mkluatexfontdb script. --- --- the return value of “resolve” is the file name of the requested --- font - +--- --- 'a -> 'a -> table -> (string * string | bool * bool) --- --- note by phg: I added a third return value that indicates a @@ -390,12 +395,12 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con return reload_db(resolve, nil, nil, specification) else --- else, fallback to requested name - --- XXX: specification.name is empty with absolute paths, looks - --- like a bug in the specification parser + --- specification.name is empty with absolute paths, looks + --- like a bug in the specification parser << is it still + --- relevant? looks not... return specification.name, false, false end end - else --- no db or outdated; reload names and retry if not fonts_reloaded then return reload_db(resolve, nil, nil, specification) -- cgit v1.2.3 From 7f0665e076a4a80079b103335e9cd02bd88e4c37 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sun, 21 Apr 2013 15:43:18 +0200 Subject: Adding a test with absolute path in file: specification --- tests/fullname.tex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/fullname.tex b/tests/fullname.tex index 0209c98..78cf4d0 100644 --- a/tests/fullname.tex +++ b/tests/fullname.tex @@ -3,7 +3,13 @@ \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 -- cgit v1.2.3 From 20e92df3bcad34b2951df17182cdb4c8ffef210a Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Sun, 21 Apr 2013 15:52:35 +0200 Subject: Adding more technical details in the failing test --- tests/zero_width_marks_lig.tex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/zero_width_marks_lig.tex b/tests/zero_width_marks_lig.tex index 9dbaac0..2c6dba9 100644 --- a/tests/zero_width_marks_lig.tex +++ b/tests/zero_width_marks_lig.tex @@ -10,4 +10,7 @@ % 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 3d9ffcf967635f7bd4c70ff8e41bc7603c461d42 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 21:20:01 +0200 Subject: fix docs --- luaotfload.dtx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/luaotfload.dtx b/luaotfload.dtx index 8b468bb..d59e18a 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -231,7 +231,7 @@ and the derived files % % 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 +% 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: @@ -396,7 +396,7 @@ and the derived files % obviously, |random|. % % \iffalse TODO verify that this actually works with a font that supports -% the salt feature!\false +% the salt/random feature!\fi % \begin{quote} % \begin{verbatim} % \font\librmsaltfirst=LatinModernRoman:salt=1 @@ -477,7 +477,7 @@ and the derived files % \begin{verbatim} % \font\test={Latin Modern Roman}:color=FF0000BB % \end{verbatim} -% \end{verbatim} +% \end{quote} % % \item [protrusion \& expansion] \hfill \\ % These keys control microtypographic features of the font, @@ -513,7 +513,7 @@ and the derived files % \begin{verbatim} % \font\test=LatinModernRoman:protrusion=default % \end{verbatim} -% \end{verbatim} +% \end{quote} % \end{description} % % \paragraph{Non-standard font features} @@ -532,12 +532,12 @@ and the derived files % \item [tlig] % Applies legacy \TEX ligatures: % -% \begin{tabular}{rl|rl} -% `` & \verb|``| & '' & \verb|''| \\ -% ` & \verb|`| & ' & \verb|'| \\ -% " & \verb|"| & -- & \verb|--| \\ -% --- & \verb|---| & !` & \verb|!`| \\ -% ?` & \verb|?`| & & \\ +% \begin{tabular}{rlrl} +% `` & \verb|``| & '' & \verb|''| \\ +% ` & \verb|`| & ' & \verb|'| \\ +% " & \verb|"| & -- & \verb|--| \\ +% --- & \verb|---| & !` & \verb|!`| \\ +% ?` & \verb|?`| & & \\ % \end{tabular} % % \footnote{% @@ -603,7 +603,7 @@ and the derived files % Tests by the maintainer show only marginal performance gain by % running with Luigi Scarso’s % \href{https://foundry.supelec.fr/projects/luajittex/}% -% {\itentifier{Luajit\kern-.25ex\TEX}}, +% {\identifier{Luajit\kern-.25ex\TEX}}, % which is probably due to the fact that most of the time is spent % on file system operations. % @@ -666,7 +666,7 @@ and the derived files % Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break % \fileent{/etc/fonts/fonts.conf} % \\ -% Mac & \fileent{\texttilde/Library/Fonts},\break +% Mac & \fileent{\textasciitilde/Library/Fonts},\break % \fileent{/Library/Fonts},\break % \fileent{/System/Library/Fonts}, and\hfill\break % \fileent{/Network/Library/Fonts} -- cgit v1.2.3 From 4386c093ef7e40d1734604fb893065819b4f1737 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 22:49:46 +0200 Subject: sync with Context as of 2013-04-21 --- otfl-fonts-merged.lua | 38 +++++++++++++------------------------- otfl-fonts.lua | 24 +++++++++--------------- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/otfl-fonts-merged.lua b/otfl-fonts-merged.lua index e095153..314305a 100644 --- a/otfl-fonts-merged.lua +++ b/otfl-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 04/17/13 18:36:10 +-- merge date : 04/20/13 13:33:53 do -- begin closure to overcome local limits and interference @@ -1401,7 +1401,7 @@ function table.tofile(filename,root,name,specification) io.flush() end end -local function flattened(t,f,depth) +local function flattened(t,f,depth) if f==nil then f={} depth=0xFFFF @@ -1416,19 +1416,16 @@ local function flattened(t,f,depth) if depth>0 and type(v)=="table" then flattened(v,f,depth-1) else - f[k]=v + f[#f+1]=v end end end - local n=#f for k=1,#t do local v=t[k] if depth>0 and type(v)=="table" then flattened(v,f,depth-1) - n=#f else - n=n+1 - f[n]=v + f[#f+1]=v end end return f @@ -2926,7 +2923,7 @@ storage={ register=dummyfunction, shared={}, } -logs=logs or { +logs={ new=dummyreporter, reporter=dummyreporter, messenger=dummyreporter, @@ -2988,7 +2985,7 @@ end do local cachepaths=kpse.expand_path('$TEXMFCACHE') or "" if cachepaths=="" then - cachepaths=kpse.expand_path('$TEXMFVAR') or "" + cachepaths=kpse.expand_path('$TEXMFVAR') end if cachepaths=="" then cachepaths=kpse.expand_path('$VARTEXMF') @@ -10379,6 +10376,7 @@ 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 @@ -10579,18 +10577,7 @@ function definers.loadfont(specification) end return tfmdata end -local function checkvirtual(tfmdata) - local fonts=tfmdata.fonts - local selfid=font.nextid() - if fonts and #fonts>0 then - for i=1,#fonts do - if fonts[i][2]==0 then - fonts[i][2]=selfid - end - end - else - tfmdata.fonts={ "id",selfid } - end +function constructors.checkvirtualids() end function constructors.readanddefine(name,size) local specification=definers.analyze(name,size) @@ -10604,7 +10591,8 @@ function constructors.readanddefine(name,size) if not id then local tfmdata=definers.loadfont(specification) if tfmdata then - checkvirtual(tfmdata) + tfmdata.properties.hash=hash + constructors.checkvirtualids(tfmdata) id=font.define(tfmdata) definers.register(tfmdata,id) else @@ -10613,8 +10601,6 @@ function constructors.readanddefine(name,size) end return fontdata[id],id end -local lastdefined=nil -local internalized={} function definers.current() return lastdefined end @@ -10625,7 +10611,9 @@ end function definers.register(tfmdata,id) if tfmdata and id then local hash=tfmdata.properties.hash - if not internalized[hash] then + 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) diff --git a/otfl-fonts.lua b/otfl-fonts.lua index 7e32465..96f3501 100644 --- a/otfl-fonts.lua +++ b/otfl-fonts.lua @@ -16,6 +16,10 @@ if not modules then modules = { } end modules ['luatex-fonts'] = { -- places where in context other code is plugged in, but this does not affect the core code. Users -- can (given that their macro package provides this option) access the font data (characters, -- descriptions, properties, parameters, etc) of this main table. +-- +-- Future versions will probably have some more specific context code removed, like tracing and +-- obscure hooks, so that we have a more efficient version (and less files too). So, don't depend +-- too much on low level code that is meant for context as it can change without notice. utf = utf or unicode.utf8 @@ -37,12 +41,10 @@ if not generic_context then generic_context = { } end -local printinfo = function(s) texio.write_nl("log", s) end - if not generic_context.push_namespaces then function generic_context.push_namespaces() - printinfo(" ") + texio.write(" ") local normalglobal = { } for k, v in next, _G do normalglobal[k] = v @@ -52,7 +54,7 @@ if not generic_context.push_namespaces then function generic_context.pop_namespaces(normalglobal,isolate) if normalglobal then - printinfo(" ") + texio.write(" ") for k, v in next, _G do if not normalglobal[k] then generic_context[k] = v @@ -112,7 +114,7 @@ local function loadmodule(name,continue) end else if verbose then - printinfo(string.format(" <%s>",foundname)) -- no file.basename yet + texio.write(string.format(" <%s>",foundname)) -- no file.basename yet end dofile(foundname) end @@ -158,19 +160,11 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then loadmodule("l-string.lua") loadmodule("l-table.lua") loadmodule("l-io.lua") - ----------("l-number.lua") - ----------("l-set.lua") - ----------("l-os.lua") loadmodule("l-file.lua") - ----------("l-md5.lua") - ----------("l-url.lua") - ----------("l-dir.lua") loadmodule("l-boolean.lua") - ----------("l-unicode.lua") loadmodule("l-math.lua") loadmodule("util-str.lua") - -- The following modules contain code that is either not used at all outside context or will fail -- when enabled due to lack of other modules. @@ -192,7 +186,7 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then -- with context. The mtx-fonts script can be used to genate this file (using the --names option). -- in 2013/14 we will merge/move some generic files into luatex-fonts-* files (copies) so that - -- intermediate updates of context not interfere + -- intermediate updates of context not interfere; we can then also use the general merger loadmodule('font-ini.lua') loadmodule('font-con.lua') @@ -254,6 +248,6 @@ end -- We're done. ---texio.write(string.format(" ", os.gettimeofday()-starttime)) +texio.write(string.format(" ", os.gettimeofday()-starttime)) generic_context.pop_namespaces(whatever) -- cgit v1.2.3 From 608b4d7816a854663c7c2bb1696127c81ce5de09 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 23:47:27 +0200 Subject: reduce verbosity of the font loader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this is my version of: https://github.com/phi-gamma/luaotfload/commit/3a59b8b6b226ed22ccd16aec2dc2e272f015cc74 I undid the original because our policy is not to change imported files except for applying hot fixes in advance that Hans will add eventually. However, we’ll see if we really need otfl-fonts.lua anyways as its functionality might fit better in to luaotfload.lua. --- luaotfload.lua | 41 +++++++++++++++++++++++++++++++++++++++++ tests/marks.tex | 7 ++++--- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/luaotfload.lua b/luaotfload.lua index c85041b..f11218e 100644 --- a/luaotfload.lua +++ b/luaotfload.lua @@ -16,6 +16,7 @@ local type, next = type, next local stringfind = string.find local stringsub = string.sub local stringmatch = string.match +local stringformat = string.format local find_file = kpse.find_file local add_to_callback, create_callback = @@ -169,6 +170,41 @@ _G.non_generic_context = { luatex_fonts = { skip_loading = true, }} +--[[doc-- +In its raw form, the font loader will write to the terminal quite +liberally, not using the proper channels (loggers) even of \CONTEXT. +To make it behave we temporarily replace two functions from the +\luafunction{texio} library with wrappers that redirect output to the +log. +Just in case Hans decides to call \luafunction{texio.write*} with the +optional target parameter (which he doesn’t at the moment), we catch the +first argument and skip it where appropriate. +The originals are backed up and restored after loading +\fileent{otfl-fonts.lua}. + +Should we decide to do our own packaging (we’re capable of that +anyways), this will most likely become unnecessary. +--doc]]-- + +local normal_write, normal_write_nl = texio.write, texio.write_nl + +local log_template = "luaotfload: %s" +local fake_write = function (first, rest) + if first == "log" or first == "term" then -- ignore + normal_write("log", stringformat(log_template, rest)) + else + normal_write("log", stringformat(log_template, first)) + end +end +local fake_write_nl = function (first, rest) + if first == "log" or first == "term" then -- ignore + normal_write_nl("log", stringformat(log_template, rest)) + else + normal_write_nl("log", stringformat(log_template, first, rest)) + end +end +texio.write, texio.write_nl = fake_write, fake_write_nl + --[[doc-- The imported font loader will call \luafunction{callback.register} once while reading \fileent{font-def.lua}. @@ -185,6 +221,11 @@ Now that things are sorted out we can finally load the fontloader. loadmodule"fonts.lua" +--[[doc-- +Here we restore the original \luafunction{texio} functions. +--doc]]-- +texio.write, texio.write_nl = normal_write, normal_write_nl + --[[doc-- By default, the fontloader requires a number of \emphasis{private attributes} for internal use. diff --git a/tests/marks.tex b/tests/marks.tex index 3af264e..9dcf460 100644 --- a/tests/marks.tex +++ b/tests/marks.tex @@ -1,9 +1,10 @@ \input luaotfload.sty \font\test={file:GenBasR.ttf:script=latn} +%font\test={name:AntykwaTorunska:script=latn} \test ä\quad Ä -\test a\char"0308 -\quad A\char"0308 -\quad j\char"0323 +\test a\char"0308 %% -> combining dihaeresis +\quad A\char"0308 %% -> combining dihaeresis +\quad j\char"0323 %% -> combining dot below \bye -- cgit v1.2.3 From 17488f095c0c1f7007fee89da8a337763605280e Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Sun, 21 Apr 2013 23:51:40 +0200 Subject: restore otfl-basics-gen.lua (needed in fontdbutil) --- otfl-basics-gen.lua | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 otfl-basics-gen.lua diff --git a/otfl-basics-gen.lua b/otfl-basics-gen.lua new file mode 100644 index 0000000..727086e --- /dev/null +++ b/otfl-basics-gen.lua @@ -0,0 +1,296 @@ +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 +----- dummyreporter = function(c) return function(...) texio.write_nl(c .. " : " .. string.format(...)) end end +local dummyreporter = function(c) return function(...) 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! +} + +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.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, readables = nil, { } + +if not caches.namespace or caches.namespace == "" or caches.namespace == "context" then + caches.namespace = 'generic' +end + +do + + local cachepaths = kpse.expand_path('$TEXMFCACHE') or "" + + if cachepaths == "" then + cachepaths = kpse.expand_path('$TEXMFVAR') + end + + if cachepaths == "" then + cachepaths = kpse.expand_path('$VARTEXMF') + end + + if cachepaths == "" then + cachepaths = "." + end + + cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") + + for i=1,#cachepaths do + if file.is_writable(cachepaths[i]) then + writable = file.join(cachepaths[i],"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 + name = "temp-" .. name -- clash prevention + return file.addsuffix(file.join(path,name),"lua"), file.addsuffix(file.join(path,name),"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,{ reduce = 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. + +caches.compilemethod = "both" + +function caches.compile(data,luaname,lucname) + local done = false + if caches.compilemethod == "luac" or caches.compilemethod == "both" then + done = os.spawn("texluac -o " .. string.quoted(lucname) .. " -s " .. string.quoted(luaname)) == 0 + end + if not done and (caches.compilemethod == "dump" or caches.compilemethod == "both") then + 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 + if _G["loadstring"] then s=loadstring(d) else s=load(d) end + f:write(string.dump(s)) + f:close() + end + end + end +end + +-- + +function table.setmetatableindex(t,f) + setmetatable(t,{ __index = f }) +end -- cgit v1.2.3 From c170cba2b369564a94b10e992822cbe8601b0f99 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Mon, 22 Apr 2013 10:05:24 +0200 Subject: Fixing backward compatibility with current released lualibs --- mkluatexfontdb.lua | 4 ++++ otfl-font-nms.lua | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mkluatexfontdb.lua b/mkluatexfontdb.lua index 776fbb3..96dcf60 100755 --- a/mkluatexfontdb.lua +++ b/mkluatexfontdb.lua @@ -20,6 +20,10 @@ local loader_file = "luatexbase.loader.lua" local loader_path = assert(kpse.find_file(loader_file, "lua"), "File '"..loader_file.."' not found") +string.quoted = string.quoted or function (str) + return string.format("%q",str) +end + --texiowrite_nl("("..loader_path..")") dofile(loader_path) -- FIXME this pollutes stdout with filenames diff --git a/otfl-font-nms.lua b/otfl-font-nms.lua index cec2a54..35611a6 100644 --- a/otfl-font-nms.lua +++ b/otfl-font-nms.lua @@ -45,12 +45,12 @@ local utf8lower = unicode.utf8.lower local dirglob = dir.glob local dirmkdirs = dir.mkdirs local filebasename = file.basename -local filecollapsepath = file.collapsepath +local filecollapsepath = file.collapsepath or file.collapse_path local fileextname = file.extname local fileiswritable = file.iswritable local filejoin = file.join local filereplacesuffix = file.replacesuffix -local filesplitpath = file.splitpath +local filesplitpath = file.splitpath or file.split_path local stringis_empty = string.is_empty local stringsplit = string.split local stringstrip = string.strip -- cgit v1.2.3 From 655ab8e887944e82c48d4d5c1745e00c771dbae0 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 22 Apr 2013 15:03:17 +0200 Subject: remove luaotfload.lua from repo the version in luaotfload.dtx is authoritative as of now; I apologize in advance for every time I forget to merge back changes into the dtx ;-) --- luaotfload.lua | 457 --------------------------------------------------------- 1 file changed, 457 deletions(-) delete mode 100644 luaotfload.lua diff --git a/luaotfload.lua b/luaotfload.lua deleted file mode 100644 index f11218e..0000000 --- a/luaotfload.lua +++ /dev/null @@ -1,457 +0,0 @@ -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 stringformat = string.format -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-- -In its raw form, the font loader will write to the terminal quite -liberally, not using the proper channels (loggers) even of \CONTEXT. -To make it behave we temporarily replace two functions from the -\luafunction{texio} library with wrappers that redirect output to the -log. -Just in case Hans decides to call \luafunction{texio.write*} with the -optional target parameter (which he doesn’t at the moment), we catch the -first argument and skip it where appropriate. -The originals are backed up and restored after loading -\fileent{otfl-fonts.lua}. - -Should we decide to do our own packaging (we’re capable of that -anyways), this will most likely become unnecessary. ---doc]]-- - -local normal_write, normal_write_nl = texio.write, texio.write_nl - -local log_template = "luaotfload: %s" -local fake_write = function (first, rest) - if first == "log" or first == "term" then -- ignore - normal_write("log", stringformat(log_template, rest)) - else - normal_write("log", stringformat(log_template, first)) - end -end -local fake_write_nl = function (first, rest) - if first == "log" or first == "term" then -- ignore - normal_write_nl("log", stringformat(log_template, rest)) - else - normal_write_nl("log", stringformat(log_template, first, rest)) - end -end -texio.write, texio.write_nl = fake_write, fake_write_nl - ---[[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-- -Here we restore the original \luafunction{texio} functions. ---doc]]-- -texio.write, texio.write_nl = normal_write, normal_write_nl - ---[[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 -- cgit v1.2.3 From 4f0774100b6000784cf29bcc8677e63a3169f21a Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Mon, 22 Apr 2013 15:15:22 +0200 Subject: =?UTF-8?q?migrate=20files=20from=20=E2=80=98otfl=E2=80=99=20to=20?= =?UTF-8?q?=E2=80=98luaotfload=E2=80=99=20prefix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaotfload-basics-gen.lua | 296 ++ luaotfload-basics-nod.lua | 95 + luaotfload-blacklist.cnf | 23 + luaotfload-features.lua | 110 + luaotfload-font-clr.lua | 187 + luaotfload-font-ltx.lua | 205 + luaotfload-font-nms.lua | 1049 +++++ luaotfload-font-otc.lua | 333 ++ luaotfload-font-pfb.lua | 24 + luaotfload-fonts-cbk.lua | 68 + luaotfload-fonts-def.lua | 97 + luaotfload-fonts-enc.lua | 28 + luaotfload-fonts-ext.lua | 272 ++ luaotfload-fonts-lua.lua | 33 + luaotfload-fonts-tfm.lua | 38 + luaotfload-fonts.lua | 253 + luaotfload-lib-dir.lua | 449 ++ luaotfload-luat-ovr.lua | 81 + luaotfload-merged.lua | 11041 ++++++++++++++++++++++++++++++++++++++++++++ otfl-basics-gen.lua | 296 -- otfl-basics-nod.lua | 95 - otfl-blacklist.cnf | 23 - otfl-features.lua | 110 - otfl-font-clr.lua | 187 - otfl-font-ltx.lua | 205 - otfl-font-nms.lua | 1049 ----- otfl-font-otc.lua | 333 -- otfl-font-pfb.lua | 24 - otfl-fonts-cbk.lua | 68 - otfl-fonts-def.lua | 97 - otfl-fonts-enc.lua | 28 - otfl-fonts-ext.lua | 272 -- otfl-fonts-lua.lua | 33 - otfl-fonts-merged.lua | 11041 -------------------------------------------- otfl-fonts-tfm.lua | 38 - otfl-fonts.lua | 253 - otfl-lib-dir.lua | 449 -- otfl-luat-ovr.lua | 80 - 38 files changed, 14682 insertions(+), 14681 deletions(-) create mode 100644 luaotfload-basics-gen.lua create mode 100644 luaotfload-basics-nod.lua create mode 100644 luaotfload-blacklist.cnf create mode 100644 luaotfload-features.lua create mode 100644 luaotfload-font-clr.lua create mode 100644 luaotfload-font-ltx.lua create mode 100644 luaotfload-font-nms.lua create mode 100644 luaotfload-font-otc.lua create mode 100644 luaotfload-font-pfb.lua create mode 100644 luaotfload-fonts-cbk.lua create mode 100644 luaotfload-fonts-def.lua create mode 100644 luaotfload-fonts-enc.lua create mode 100644 luaotfload-fonts-ext.lua create mode 100644 luaotfload-fonts-lua.lua create mode 100644 luaotfload-fonts-tfm.lua create mode 100644 luaotfload-fonts.lua create mode 100644 luaotfload-lib-dir.lua create mode 100644 luaotfload-luat-ovr.lua create mode 100644 luaotfload-merged.lua delete mode 100644 otfl-basics-gen.lua delete mode 100644 otfl-basics-nod.lua delete mode 100644 otfl-blacklist.cnf delete mode 100644 otfl-features.lua delete mode 100644 otfl-font-clr.lua delete mode 100644 otfl-font-ltx.lua delete mode 100644 otfl-font-nms.lua delete mode 100644 otfl-font-otc.lua delete mode 100644 otfl-font-pfb.lua delete mode 100644 otfl-fonts-cbk.lua delete mode 100644 otfl-fonts-def.lua delete mode 100644 otfl-fonts-enc.lua delete mode 100644 otfl-fonts-ext.lua delete mode 100644 otfl-fonts-lua.lua delete mode 100644 otfl-fonts-merged.lua delete mode 100644 otfl-fonts-tfm.lua delete mode 100644 otfl-fonts.lua delete mode 100644 otfl-lib-dir.lua delete mode 100644 otfl-luat-ovr.lua diff --git a/luaotfload-basics-gen.lua b/luaotfload-basics-gen.lua new file mode 100644 index 0000000..727086e --- /dev/null +++ b/luaotfload-basics-gen.lua @@ -0,0 +1,296 @@ +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 +----- dummyreporter = function(c) return function(...) texio.write_nl(c .. " : " .. string.format(...)) end end +local dummyreporter = function(c) return function(...) 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! +} + +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.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, readables = nil, { } + +if not caches.namespace or caches.namespace == "" or caches.namespace == "context" then + caches.namespace = 'generic' +end + +do + + local cachepaths = kpse.expand_path('$TEXMFCACHE') or "" + + if cachepaths == "" then + cachepaths = kpse.expand_path('$TEXMFVAR') + end + + if cachepaths == "" then + cachepaths = kpse.expand_path('$VARTEXMF') + end + + if cachepaths == "" then + cachepaths = "." + end + + cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") + + for i=1,#cachepaths do + if file.is_writable(cachepaths[i]) then + writable = file.join(cachepaths[i],"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 + name = "temp-" .. name -- clash prevention + return file.addsuffix(file.join(path,name),"lua"), file.addsuffix(file.join(path,name),"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,{ reduce = 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. + +caches.compilemethod = "both" + +function caches.compile(data,luaname,lucname) + local done = false + if caches.compilemethod == "luac" or caches.compilemethod == "both" then + done = os.spawn("texluac -o " .. string.quoted(lucname) .. " -s " .. string.quoted(luaname)) == 0 + end + if not done and (caches.compilemethod == "dump" or caches.compilemethod == "both") then + 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 + if _G["loadstring"] then s=loadstring(d) else s=load(d) end + f:write(string.dump(s)) + f:close() + end + 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 new file mode 100644 index 0000000..151d98a --- /dev/null +++ b/luaotfload-basics-nod.lua @@ -0,0 +1,95 @@ +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.unsetvalue = -0x7FFFFFFF + +local numbers, last = { }, 127 + +function attributes.private(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 + +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 + +nodes.before = node.insert_before +nodes.after = node.insert_after + +function nodes.pool.kern(k) + local n = new_node("kern",1) + n.kern = k + return n +end diff --git a/luaotfload-blacklist.cnf b/luaotfload-blacklist.cnf new file mode 100644 index 0000000..771649b --- /dev/null +++ b/luaotfload-blacklist.cnf @@ -0,0 +1,23 @@ +% Takes ages to load +LastResort.ttf % a MacOSX font, but also available for free from unicode.org + +% Mac OS X TTC fonts, this list need to be filtered out +% luatex bug fixed? +% /Library/Fonts/AmericanTypewriter.ttc +% /Library/Fonts/Baskerville.ttc +% /Library/Fonts/Chalkboard.ttc +% /Library/Fonts/Cochin.ttc +% /Library/Fonts/Copperplate.ttc +% /Library/Fonts/Didot.ttc +% /Library/Fonts/Futura.ttc +% /Library/Fonts/GillSans.ttc +% /Library/Fonts/Hoefler Text.ttc +% /Library/Fonts/MarkerFelt.ttc +% /Library/Fonts/Optima.ttc +% /Library/Fonts/Papyrus.ttc +% /Library/Fonts/STHeiti Medium.ttc +% /System/Library/Fonts/AquaKana.ttc +% /System/Library/Fonts/HelveticaNeue.ttc +% /System/Library/Fonts/LucidaGrande.ttc +% /System/Library/Fonts/Menlo.ttc +% /System/Library/Fonts/STHeiti Light.ttc diff --git a/luaotfload-features.lua b/luaotfload-features.lua new file mode 100644 index 0000000..aae0515 --- /dev/null +++ b/luaotfload-features.lua @@ -0,0 +1,110 @@ +--[[doc-- +Taken from the most recent branch of luaotfload. +--doc]]-- +local addotffeature = fonts.handlers.otf.addfeature +local registerotffeature = fonts.handlers.otf.features.register + +local everywhere = { ["*"] = { ["*"] = true } } + +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 + [0x201E] = {0x002C, 0x002C}, -- quotedblbase + [0x00A1] = {0x0021, 0x2018}, -- exclamdown + [0x00BF] = {0x003F, 0x2018}, -- questiondown + }, + flags = { }, + }, + { + type = "ligature", + features = everywhere, + data = { + [0x201C] = {0x0060, 0x0060}, -- quotedblleft + [0x201D] = {0x0027, 0x0027}, -- quotedblright + [0x00A1] = {0x0021, 0x0060}, -- exclamdown + [0x00BF] = {0x003F, 0x0060}, -- questiondown + }, + flags = { }, + }, +} + +addotffeature("tlig", tlig) +addotffeature("trep", { }) -- empty, all in tlig now +local anum_arabic = { + [0x0030] = 0x0660, + [0x0031] = 0x0661, + [0x0032] = 0x0662, + [0x0033] = 0x0663, + [0x0034] = 0x0664, + [0x0035] = 0x0665, + [0x0036] = 0x0666, + [0x0037] = 0x0667, + [0x0038] = 0x0668, + [0x0039] = 0x0669, +} + +local anum_persian = { + [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, + }, +} + +addotffeature("anum",anum_specification) + +registerotffeature { + name = 'anum', + description = 'arabic digits', +} diff --git a/luaotfload-font-clr.lua b/luaotfload-font-clr.lua new file mode 100644 index 0000000..439fd7c --- /dev/null +++ b/luaotfload-font-clr.lua @@ -0,0 +1,187 @@ +if not modules then modules = { } end modules ['font-clr'] = { + version = 1.001, + comment = "companion to font-otf.lua (font color)", + author = "Khaled Hosny and Elie Roux", + copyright = "Luaotfload Development Team", + license = "GPL" +} + +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 otffeatures = fonts.constructors.newfeatures("otf") +local ids = fonts.hashes.identifiers +local registerotffeature = otffeatures.register + +local function setcolor(tfmdata,value) + local sanitized + local properties = tfmdata.properties + + if value then + value = tostring(value) + if #value == 6 or #value == 8 then + sanitized = value + elseif #value == 7 then + _, _, sanitized = stringfind(value, "(......)") + elseif #value > 8 then + _, _, sanitized = stringfind(value, "(........)") + else + -- broken color code ignored, issue a warning? + end + end + + if sanitized then + tfmdata.properties.color = sanitized + add_color_callback() + end +end + +registerotffeature { + name = "color", + description = "color", + initializers = { + base = setcolor, + node = setcolor, + } +} + +local function hex2dec(hex,one) + if one then + return stringformat("%.1g", tonumber(hex, 16)/255) + else + return stringformat("%.3g", tonumber(hex, 16)/255) + end +end + +local res + +local function pageresources(a) + local res2 + if not res then + res = "/TransGs1<>" + end + res2 = stringformat("/TransGs%s<>", a, a, a) + res = stringformat("%s%s", res, stringfind(res, res2) and "" or res2) +end + +local function hex_to_rgba(hex) + local r, g, b, a, push, pop, res3 + if hex then + if #hex == 6 then + _, _, r, g, b = stringfind(hex, '(..)(..)(..)') + elseif #hex == 8 then + _, _, r, g, b, a = stringfind(hex, '(..)(..)(..)(..)') + a = hex2dec(a,true) + pageresources(a) + end + else + return nil + end + r = hex2dec(r) + g = hex2dec(g) + b = hex2dec(b) + if a then + push = stringformat('/TransGs%g gs %s %s %s rg', a, r, g, b) + pop = '0 g /TransGs1 gs' + else + push = stringformat('%s %s %s rg', r, g, b) + pop = '0 g' + end + return push, pop +end + +local glyph = nodetype('glyph') +local hlist = nodetype('hlist') +local vlist = nodetype('vlist') +local whatsit = nodetype('whatsit') +local pgi = nodetype('page_insert') +local sbox = nodetype('sub_box') + +local function lookup_next_color(head) + for n in traverse_nodes(head) do + if n.id == glyph then + if ids[n.font] and ids[n.font].properties and ids[n.font].properties.color then + return ids[n.font].properties.color + else + return -1 + end + elseif n.id == vlist or n.id == hlist or n.id == sbox then + local r = lookup_next_color(n.list) + if r == -1 then + return -1 + elseif r then + return r + end + elseif n.id == whatsit or n.id == pgi then + return -1 + end + end + return nil +end + +local function node_colorize(head, current_color, next_color) + for n in traverse_nodes(head) do + if n.id == hlist or n.id == vlist or n.id == sbox then + local next_color_in = lookup_next_color(n.next) or next_color + n.list, current_color = node_colorize(n.list, current_color, next_color_in) + elseif n.id == glyph then + local tfmdata = ids[n.font] + if tfmdata and tfmdata.properties and tfmdata.properties.color then + if tfmdata.properties.color ~= current_color then + local pushcolor = hex_to_rgba(tfmdata.properties.color) + local push = newnode(whatsit, 8) + push.mode = 1 + push.data = pushcolor + head = insert_node_before(head, n, push) + current_color = tfmdata.properties.color + end + local next_color_in = lookup_next_color (n.next) or next_color + if next_color_in ~= tfmdata.properties.color then + local _, popcolor = hex_to_rgba(tfmdata.properties.color) + local pop = newnode(whatsit, 8) + pop.mode = 1 + pop.data = popcolor + head = insert_node_after(head, n, pop) + current_color = nil + end + end + end + end + return head, current_color +end + +local function font_colorize(head) + -- check if our page resources existed in the previous run + -- and remove it to avoid duplicating it later + if res then + local r = "/ExtGState<<"..res..">>" + tex.pdfpageresources = stringgsub(tex.pdfpageresources, r, "") + end + local h = node_colorize(head, nil, nil) + -- now append our page resources + if res and stringfind(res, "%S") then -- test for non-empty string + local r = "/ExtGState<<"..res..">>" + tex.pdfpageresources = tex.pdfpageresources..r + end + return h +end + +local color_callback_activated = 0 + +function add_color_callback() + if color_callback_activated == 0 then + luatexbase.add_to_callback( + "pre_output_filter", font_colorize, "luaotfload.colorize") + color_callback_activated = 1 + end +end + +-- vim:tw=71:sw=4:ts=4:expandtab + diff --git a/luaotfload-font-ltx.lua b/luaotfload-font-ltx.lua new file mode 100644 index 0000000..afc8621 --- /dev/null +++ b/luaotfload-font-ltx.lua @@ -0,0 +1,205 @@ +if not modules then modules = { } end modules ['font-ltx'] = { + 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" +} +--- where have all the comments gone? + +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 feature_list = { } + +local report = logs.names_report + +--- ugh TODO use lpeg instead +local function isstyle(s) + local style = string.lower(s):split("/") + for _,v in next, style do + if v == "b" then + feature_list.style = "bold" + elseif v == "i" then + feature_list.style = "italic" + elseif v == "bi" or v == "ib" then + feature_list.style = "bolditalic" + elseif v:find("^s=") then + feature_list.optsize = v:split("=")[2] + elseif v == "aat" or v == "icu" or v == "gr" then + report("log", 0, + "load font", "unsupported font option: %s", v) + elseif not v:is_empty() then + feature_list.style = v:gsub("[^%a%d]", "") + end + end +end + +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", + }, +} + +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 + +local function set_default_features(script) + local features + local script = script or "dflt" + report("log", 0, "load font", + "auto-selecting default features for script: %s", + script) + if defaults[script] then + features = defaults[script] + else + features = defaults["dflt"] + end + for _,v in next, features do + if feature_list[v] ~= false then + feature_list[v] = true + end + end +end + +local function issome () feature_list.lookup = 'name' end +local function isfile () feature_list.lookup = 'file' end +local function isname () feature_list.lookup = 'name' end +local function thename(s) feature_list.name = s end +local function issub (v) feature_list.sub = v end +local function istrue (s) feature_list[s] = true end +local function isfalse(s) feature_list[s] = false end +local function iskey (k,v) feature_list[k] = v 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 +--[[phg-- this prevents matching of absolute paths as file names --]]-- +local namespec = (1-S("/:("))^1 +local filespec = (R("az", "AZ") * P(":"))^-1 * (1-S(":("))^1 +local stylespec = spaces * P("/") * (((1-P(":"))^0)/isstyle) * spaces +local filename = (P("file:")/isfile * (filespec/thename)) + (P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]")) +local fontname = (P("name:")/isname * (namespec/thename)) + 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 = P("+") + (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 + fontname) * subvalue^0 * stylespec^0 * options^0 + +local function colonized(specification) -- xetex mode + feature_list = { } + lpeg.match(pattern,specification.specification) + set_default_features(feature_list.script) + if feature_list.style then + specification.style = feature_list.style + feature_list.style = nil + end + if feature_list.optsize then + specification.optsize = feature_list.optsize + feature_list.optsize = nil + end + if feature_list.name then + if resolvers.findfile(feature_list.name, "tfm") then + feature_list.lookup = "file" + feature_list.name = file.addsuffix(feature_list.name, "tfm") + elseif resolvers.findfile(feature_list.name, "ofm") then + feature_list.lookup = "file" + feature_list.name = file.addsuffix(feature_list.name, "ofm") + end + + specification.name = feature_list.name + feature_list.name = nil + end + if feature_list.lookup then + specification.lookup = feature_list.lookup + feature_list.lookup = nil + end + if feature_list.sub then + specification.sub = feature_list.sub + feature_list.sub = nil + end + if not feature_list.mode then + -- if no mode is set, use our default + feature_list.mode = fonts.mode + end + specification.features.normal = fonts.handlers.otf.features.normalize(feature_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 +-- vim:tw=71:sw=4:ts=4:expandtab diff --git a/luaotfload-font-nms.lua b/luaotfload-font-nms.lua new file mode 100644 index 0000000..cec2a54 --- /dev/null +++ b/luaotfload-font-nms.lua @@ -0,0 +1,1049 @@ +if not modules then modules = { } end modules ['font-nms'] = { + version = 2.2, + comment = "companion to luaotfload.lua", + author = "Khaled Hosny and Elie Roux", + copyright = "Luaotfload Development Team", + license = "GNU GPL v2" +} + +--- TODO: if the specification is an absolute filename with a font not in the +--- database, add the font to the database and load it. There is a small +--- difficulty with the filenames of the TEXMF tree that are referenced as +--- relative paths... + +--- Luatex builtins +local load = load +local next = next +local pcall = pcall +local require = require +local tonumber = tonumber + +local iolines = io.lines +local ioopen = io.open +local kpseexpand_path = kpse.expand_path +local kpseexpand_var = kpse.expand_var +local kpselookup = kpse.lookup +local kpsereadable_file = kpse.readable_file +local mathabs = math.abs +local mathmin = math.min +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 tablecopy = table.copy +local tablesort = table.sort +local tabletofile = table.tofile +local texiowrite_nl = texio.write_nl +local utf8gsub = unicode.utf8.gsub +local utf8lower = unicode.utf8.lower + +--- these come from Lualibs/Context +local dirglob = dir.glob +local dirmkdirs = dir.mkdirs +local filebasename = file.basename +local filecollapsepath = file.collapsepath +local fileextname = file.extname +local fileiswritable = file.iswritable +local filejoin = file.join +local filereplacesuffix = file.replacesuffix +local filesplitpath = file.splitpath +local stringis_empty = string.is_empty +local stringsplit = string.split +local stringstrip = string.strip +local tableappend = table.append +local tabletohash = table.tohash + +--- the font loader namespace is “fonts”, same as in Context +fonts = fonts or { } +fonts.names = fonts.names or { } + +local names = fonts.names + +names.version = 2.2 +names.data = nil +names.path = { + basename = "otfl-names.lua", + dir = "", + path = "", +} + +-- We use the cache.* of ConTeXt (see luat-basics-gen), we can +-- use it safely (all checks and directory creations are already done). It +-- uses TEXMFCACHE or TEXMFVAR as starting points. +local writable_path = caches.getwritablepath("names","") +if not writable_path then + error("Impossible to find a suitable writeable cache...") +end +names.path.dir = writable_path +names.path.path = filejoin(writable_path, names.path.basename) + + +---- +--- +--- these lines load some binary module called “lualatex-platform” +--- that doesn’t appear to build with Lua 5.2. I’m going ahead and +--- disable it for the time being until someone clarifies what it +--- is supposed to do and whether we should care to fix it. +--- +--local success = pcall(require, "luatexbase.modutils") +--if success then +-- success = pcall(luatexbase.require_module, +-- "lualatex-platform", "2011/03/30") +-- print(success) +--end + +--local get_installed_fonts +--if success then +-- get_installed_fonts = lualatex.platform.get_installed_fonts +--else +-- function get_installed_fonts() +-- end +--end +---- + +local get_installed_fonts = nil + +--[[doc-- +Auxiliary functions +--doc]]-- + + +local report = logs.names_report + +local sanitize_string = function (str) + if str ~= nil then + return utf8gsub(utf8lower(str), "[^%a%d]", "") + end + return nil +end + +local fontnames_init = function ( ) + return { + mappings = { }, + status = { }, + version = names.version, + } +end + +local make_name = function (path) + return filereplacesuffix(path, "lua"), filereplacesuffix(path, "luc") +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. + +local code_cache = { } + +--- string -> (string * table) +local load_lua_file = function (path) + local code = code_cache[path] + if code then return path, code() end + + local foundname = filereplacesuffix(path, "luc") + + 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 return nil, nil end + + code_cache[path] = code --- insert into memo + return foundname, code() +end + +--- define locals in scope +local find_closest +local font_fullinfo +local load_names +local read_fonts_conf +local reload_db +local resolve +local save_names +local scan_external_dir +local update_names + +load_names = function ( ) + local foundname, data = load_lua_file(names.path.path) + + if data then + report("info", 1, "db", + "Font names database loaded", "%s", foundname) + else + report("info", 0, "db", + [[Font names database not found, generating new one. + This can take several minutes; please be patient.]]) + data = update_names(fontnames_init()) + save_names(data) + end + return data +end + +local fuzzy_limit = 1 --- display closest only + +local style_synonyms = { set = { } } +do + style_synonyms.list = { + regular = { "normal", "roman", + "plain", "book", + "medium", }, + --- TODO note from Élie Roux + --- 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", }, + } + + for category, synonyms in next, style_synonyms.list do + style_synonyms.set[category] = tabletohash(synonyms, true) + end +end + +--- state of the database +local fonts_loaded = false +local fonts_reloaded = false + +--[[doc-- + +Luatex-fonts, the font-loader package luaotfload imports, comes with +basic file location facilities (see luatex-fonts-syn.lua). +However, the builtin functionality is too limited to be of more than +basic use, which is why we supply our own resolver that accesses the +font database created by the mkluatexfontdb script. + +--doc]]-- + + +--- +--- the request specification has the fields: +--- +--- · features: table +--- · normal: set of { ccmp clig itlc kern liga locl mark mkmk rlig } +--- · ??? +--- · forced: string +--- · lookup: "name" | "file" +--- · method: string +--- · name: string +--- · resolved: string +--- · size: int +--- · specification: string (== ":" ) +--- · sub: string +--- +--- the return value of “resolve” is the file name of the requested +--- font +--- +--- 'a -> 'a -> table -> (string * string | bool * bool) +--- +--- note by phg: I added a third return value that indicates a +--- successful lookup as this cannot be inferred from the other +--- values. +--- +--- +resolve = function (_,_,specification) -- the 1st two parameters are used by ConTeXt + local name = sanitize_string(specification.name) + local style = sanitize_string(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 fonts_loaded then + names.data = load_names() + fonts_loaded = true + end + + local data = names.data + if type(data) == "table" then + local db_version, nms_version = data.version, names.version + if data.version ~= names.version then + report("log", 0, "db", + [[version mismatch; expected %4.3f, got %4.3f]], + nms_version, db_version + ) + return reload_db(resolve, nil, nil, specification) + end + if data.mappings then + local found = { } + local synonym_set = style_synonyms.set + for _,face in next, data.mappings do + --- TODO we really should store those in dedicated + --- .sanitized field + local family = sanitize_string(face.names and face.names.family) + local subfamily = sanitize_string(face.names and face.names.subfamily) + local fullname = sanitize_string(face.names and face.names.fullname) + local psname = sanitize_string(face.names and face.names.psname) + local fontname = sanitize_string(face.fontname) + local pfullname = sanitize_string(face.fullname) + local optsize, dsnsize, maxsize, minsize + if #face.size > 0 then + optsize = face.size + dsnsize = optsize[1] and optsize[1] / 10 + -- can be nil + maxsize = optsize[2] and optsize[2] / 10 or dsnsize + minsize = optsize[3] and optsize[3] / 10 or dsnsize + end + if name == family then + if subfamily == style then + if optsize then + if dsnsize == size + or (size > minsize and size <= maxsize) then + found[1] = face + break + else + found[#found+1] = face + end + else + found[1] = face + break + end + elseif synonym_set[style] and + synonym_set[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 + synonym_set.regular[subfamily] then + found.fallback = face + end + else + if name == fullname + or name == pfullname + or name == fontname + or name == psname then + if optsize then + if dsnsize == size + or (size > minsize and size <= maxsize) then + found[1] = face + break + else + found[#found+1] = face + end + else + found[1] = face + break + end + end + end + end + if #found == 1 then + if kpselookup(found[1].filename[1]) then + report("log", 0, "resolve", + "font family='%s', subfamily='%s' found: %s", + name, style, found[1].filename[1] + ) + return found[1].filename[1], found[1].filename[2], true + 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 = mathabs(dsnsize-size) + if difference < least then + closest = face + least = difference + end + end + if kpselookup(closest.filename[1]) then + report("log", 0, "resolve", + "font family='%s', subfamily='%s' found: %s", + name, style, closest.filename[1] + ) + return closest.filename[1], closest.filename[2], true + end + elseif found.fallback then + return found.fallback.filename[1], found.fallback.filename[2], true + end + --- no font found so far + if not fonts_reloaded then + --- last straw: try reloading the database + return reload_db(resolve, nil, nil, specification) + else + --- else, fallback to requested name + --- specification.name is empty with absolute paths, looks + --- like a bug in the specification parser 'a) -> 'a list -> 'a +reload_db = function (caller, ...) + report("log", 1, "db", "reload initiated") + names.data = update_names() + save_names(names.data) + fonts_reloaded = true + return caller(...) +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_string(name) + limit = limit or fuzzy_limit + + if not fonts_loaded then + names.data = load_names() + fonts_loaded = true + end + + local data = names.data + + if type(data) == "table" then + local by_distance = { } --- (int, string list) dict + local distances = { } --- int list + local cached = { } --- (string, int) dict + local mappings = data.mappings + local n_fonts = #mappings + + for n = 1, n_fonts do + local current = mappings[n] + local cnames = current.names + --[[ + This is simplistic but surpisingly fast. + Matching is performed against the “family” name + of a db record. We then store its “fullname” at + it edit distance. + We should probably do some weighting over all the + font name categories as well as whatever agrep + does. + --]] + if cnames then + local fullname, family = cnames.fullname, cnames.family + family = sanitize_string(family) + + local dist = cached[family]--- maybe already calculated + if not dist then + dist = iterative_levenshtein(name, family) + cached[family] = 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 + 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 “" .. name .. "”: " .. dist + .. "\n " .. tableconcat(namelst, "\n ") + ) + end + + return true + end + return false + else --- need reload + return reload_db(find_closest, name) + end + return false +end --- find_closest() + +--[[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]]-- +font_fullinfo = function (filename, subfont, texmf) + local tfmdata = { } + local rawfont = fontloader.open(filename, subfont) + if not rawfont then + report("log", 1, "error", "failed to open %s", filename) + return + end + local metadata = fontloader.to_table(rawfont) + fontloader.close(rawfont) + collectgarbage("collect") + -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size + if metadata.fontstyle_name then + for _, name in next, metadata.fontstyle_name do + if name.lang == 1033 then --- I hate magic numbers + tfmdata.fontstyle_name = name.name + end + end + end + if metadata.names then + for _, namedata in next, metadata.names do + if namedata.lang == "English (US)" then + tfmdata.names = { + --- see + --- https://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html + fullname = namedata.names.compatfull + or namedata.names.fullname, + family = namedata.names.preffamilyname + or namedata.names.family, + subfamily= tfmdata.fontstyle_name + or namedata.names.prefmodifiers + or namedata.names.subfamily, + psname = namedata.names.postscriptname + } + end + end + else + -- no names table, propably a broken font + report("log", 1, "db", "broken font rejected", "%s", basefile) + return + end + tfmdata.fontname = metadata.fontname + tfmdata.fullname = metadata.fullname + tfmdata.familyname = metadata.familyname + tfmdata.filename = { + texmf and filebasename(filename) or filename, + subfont + } + tfmdata.weight = metadata.pfminfo.weight + tfmdata.width = metadata.pfminfo.width + tfmdata.slant = metadata.italicangle + -- don't waste the space with zero values + tfmdata.size = { + metadata.design_size ~= 0 and metadata.design_size or nil, + metadata.design_range_top ~= 0 and metadata.design_range_top or nil, + metadata.design_range_bottom ~= 0 and metadata.design_range_bottom or nil, + } + return tfmdata +end + +local load_font = function (filename, fontnames, newfontnames, texmf) + local newmappings = newfontnames.mappings + local newstatus = newfontnames.status + local mappings = fontnames.mappings + local status = fontnames.status + local basename = filebasename(filename) + local basefile = texmf and basename or filename + if filename then + if names.blacklist[filename] or + names.blacklist[basename] then + report("log", 2, "db", "ignoring font", "%s", filename) + 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]) + 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 + report("log", 1, "db", "font already indexed", "%s", basefile) + return + end + local info = fontloader.info(filename) + if info then + if type(info) == "table" and #info > 1 then + for i in next, info do + local fullinfo = font_fullinfo(filename, i-1, texmf) + if not fullinfo then + return + end + local index = newstatus[basefile].index[i] + if newstatus[basefile].index[i] then + index = newstatus[basefile].index[i] + else + index = #newmappings+1 + end + newmappings[index] = fullinfo + newstatus[basefile].index[i] = index + end + else + local fullinfo = font_fullinfo(filename, false, texmf) + if not fullinfo then + return + end + local index + if newstatus[basefile].index[1] then + index = newstatus[basefile].index[1] + else + index = #newmappings+1 + end + newmappings[index] = fullinfo + newstatus[basefile].index[1] = index + end + else + report("log", 1, "db", "failed to load", "%s", basefile) + end + end +end + +local function path_normalize(path) + --[[ + path normalization: + - a\b\c -> a/b/c + - a/../b -> b + - /cygdrive/a/b -> a:/b + - reading symlinks under non-Win32 + - using kpse.readable_file on Win32 + ]] + if os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then + path = stringgsub(path, '\\', '/') + path = stringlower(path) + path = stringgsub(path, '^/cygdrive/(%a)/', '%1:/') + end + if os.type ~= "windows" and os.type ~= "msdos" then + local dest = lfs.readlink(path) + if dest then + if kpsereadable_file(dest) then + path = dest + elseif kpsereadable_file(filejoin(file.dirname(path), dest)) then + path = filejoin(file.dirname(path), dest) + else + -- broken symlink? + end + end + end + path = filecollapsepath(path) + return path +end + +fonts.path_normalize = path_normalize + +names.blacklist = { } + +local function read_blacklist() + local files = { + kpselookup("otfl-blacklist.cnf", {all=true, format="tex"}) + } + local blacklist = names.blacklist + local whitelist = { } + + if files and type(files) == "table" then + for _,v in next, files do + for line in iolines(v) do + line = stringstrip(line) -- to get rid of lines like " % foo" + local first_chr = stringsub(line, 1, 1) --- faster than find + if first_chr == "%" or stringis_empty(line) then + -- comment or empty line + else + --- this is highly inefficient + line = stringsplit(line, "%")[1] + line = stringstrip(line) + if stringsub(line, 1, 1) == "-" then + whitelist[stringsub(line, 2, -1)] = true + else + report("log", 2, "db", "blacklisted file", "%s", line) + blacklist[line] = true + end + end + end + end + end + for _,fontname in next, whitelist do + blacklist[fontname] = nil + end +end + +local font_extensions = { "otf", "ttf", "ttc", "dfont" } +local font_extensions_set = {} +for key, value in next, font_extensions do + font_extensions_set[value] = true +end + +--local installed_fonts_scanned = false --- ugh + +--- we already have scan_os_fonts don’t we? + +--local function scan_installed_fonts(fontnames, newfontnames) +-- --- Try to query and add font list from operating system. +-- --- This uses the lualatex-platform module. +-- --- what for? why can’t we do this in Lua? +-- report("info", 0, "Scanning fonts known to operating system...") +-- local fonts = get_installed_fonts() +-- if fonts and #fonts > 0 then +-- installed_fonts_scanned = true +-- report("log", 2, "operating system fonts found", "%d", #fonts) +-- for key, value in next, fonts do +-- local file = value.path +-- if file then +-- local ext = fileextname(file) +-- if ext and font_extensions_set[ext] then +-- file = path_normalize(file) +-- report("log", 1, "loading font", "%s", file) +-- load_font(file, fontnames, newfontnames, false) +-- end +-- end +-- end +-- else +-- report("log", 2, "Could not retrieve list of installed fonts") +-- end +--end + +local function scan_dir(dirname, fontnames, newfontnames, texmf) + --[[ + This function scans a directory and populates the list of fonts + with all the fonts it finds. + - dirname is the name of the directory to scan + - names is the font database to fill + - texmf is a boolean saying if we are scanning a texmf directory + ]] + local list, found = { }, { } + local nbfound = 0 + report("log", 2, "db", "scanning", "%s", dirname) + for _,i in next, font_extensions do + for _,ext in next, { i, stringupper(i) } do + found = dirglob(stringformat("%s/**.%s$", dirname, ext)) + -- note that glob fails silently on broken symlinks, which happens + -- sometimes in TeX Live. + report("log", 2, "db", + "fonts found", "%s '%s' fonts found", #found, ext) + nbfound = nbfound + #found + tableappend(list, found) + end + end + report("log", 2, "db", + "fonts found", "%d fonts found in '%s'", nbfound, dirname) + + for _,file in next, list do + file = path_normalize(file) + report("log", 1, "db", + "loading font", "%s", file) + 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 stringis_empty(kpseexpand_path("$OSFONTDIR")) then + report("info", 1, "db", "Scanning TEXMF fonts...") + else + report("info", 1, "db", "Scanning TEXMF and OS fonts...") + end + local fontdirs = stringgsub(kpseexpand_path("$OPENTYPEFONTS"), "^%.", "") + fontdirs = fontdirs .. stringgsub(kpseexpand_path("$TTFONTS"), "^%.", "") + if not stringis_empty(fontdirs) then + for _,d in next, filesplitpath(fontdirs) do + scan_dir(d, fontnames, newfontnames, true) + end + end +end + +--[[ + For the OS fonts, there are several options: + - if OSFONTDIR is set (which is the case under windows by default but + not on the other OSs), it scans it at the same time as the texmf tree, + in the scan_texmf_fonts. + - if not: + - under Windows and Mac OSX, we take a look at some hardcoded directories + - under Unix, we read /etc/fonts/fonts.conf and read the directories in it + + This means that if you have fonts in fancy directories, you need to set them + in OSFONTDIR. +]] + +--- (string -> tab -> tab -> tab) +read_fonts_conf = function (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. + + TODO fonts.conf are some kind of XML so in theory the following + is totally inappropriate. Maybe a future version of the + lualibs will include the lxml-* files from Context so we + can write something presentable instead. + ]] + local fh = ioopen(path) + passed_paths[#passed_paths+1] = path + passed_paths_set = tabletohash(passed_paths, true) + if not fh then + report("log", 2, "db", "cannot open file", "%s", path) + return results + end + local incomments = false + for line in fh:lines() do + while line and line ~= "" do + -- spaghetti code... hmmm... + if incomments then + local tmp = stringfind(line, '-->') --- wtf? + if tmp then + incomments = false + line = stringsub(line, tmp+3) + else + line = nil + end + else + local tmp = stringfind(line, '') --- wtf? - if tmp then - incomments = false - line = stringsub(line, tmp+3) - else - line = nil - end - else - local tmp = stringfind(line, '') --- wtf? + if tmp then + incomments = false + line = stringsub(line, tmp+3) + else + line = nil + end + else + local tmp = stringfind(line, '') --- wtf? - if tmp then - incomments = false - line = stringsub(line, tmp+3) - else - line = nil - end - else - local tmp = stringfind(line, '