From c0641b1563ce1b0219724e19c772bbc180e86c2a Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Mon, 14 Jun 2010 14:02:28 +0300 Subject: Sync with ConTeXt beta (beta 2010.06.14) Fixes several bugs, including math bugs. --- luaotfload.dtx | 26 ++-- otfl-data-con.lua | 80 ++++++----- otfl-font-def.lua | 29 ++-- otfl-font-dum.lua | 19 +++ otfl-font-ini.lua | 5 +- otfl-font-ota.lua | 38 ------ otfl-font-otd.lua | 7 +- otfl-font-otf.lua | 391 ++++++++++++++++++++++++++++++------------------------ otfl-font-otn.lua | 116 ++++++++++------ otfl-font-tfm.lua | 5 + otfl-luat-dum.lua | 97 +++++++++++--- otfl-node-dum.lua | 108 ++++++++++++++- otfl-node-fnt.lua | 205 ---------------------------- otfl-node-ini.lua | 244 ---------------------------------- otfl-node-inj.lua | 92 ++++--------- otfl-node-res.lua | 302 ----------------------------------------- 16 files changed, 608 insertions(+), 1156 deletions(-) delete mode 100644 otfl-node-fnt.lua delete mode 100644 otfl-node-ini.lua delete mode 100644 otfl-node-res.lua diff --git a/luaotfload.dtx b/luaotfload.dtx index 0918a4b..f2239b7 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -350,9 +350,7 @@ and the derived files % \begin{itemize*} % \item \texttt{luat-dum.lua} % \item \texttt{data-con.lua} -% \item \texttt{node-ini.lua} % \item \texttt{node-inj.lua} -% \item \texttt{node-fnt.lua} % \item \texttt{node-dum.lua} % \item \texttt{font-ini.lua} % \item \texttt{font-tfm.lua} @@ -490,11 +488,12 @@ luaotfload.loadmodule('data-con.lua') -- maybe some day we don't need this one % \end{macrocode} % -% This one is for node support. +% Node support modules. % % \begin{macrocode} -luaotfload.loadmodule('node-ini.lua') +luaotfload.loadmodule('node-dum.lua') +luaotfload.loadmodule('node-inj.lua') % \end{macrocode} % @@ -517,7 +516,7 @@ end % \end{macrocode} % -% A hack to remove a warning from \texttt{node-fnt.lua} as it is \ConTeXt\ +% A hack to remove a warning from \texttt{node-dum.lua} as it is \ConTeXt\ % specific. % % \begin{macrocode} @@ -526,21 +525,15 @@ tex.attribute[0] = 0 % \end{macrocode} % -% Some more modules. We don't load neither \texttt{font-enc.lua} nor -% \texttt{font-afm.lua} as it will never be used here. +% Font handling modules. % % \begin{macrocode} -luaotfload.loadmodule('node-res.lua') -luaotfload.loadmodule('node-inj.lua') -luaotfload.loadmodule('node-fnt.lua') -luaotfload.loadmodule('node-dum.lua') - luaotfload.loadmodule('font-ini.lua') luaotfload.loadmodule('font-tfm.lua') luaotfload.loadmodule('font-cid.lua') -luaotfload.loadmodule('font-map.lua') luaotfload.loadmodule('font-ott.lua') +luaotfload.loadmodule('font-map.lua') luaotfload.loadmodule('font-otf.lua') luaotfload.loadmodule('font-otd.lua') luaotfload.loadmodule('font-oti.lua') @@ -551,6 +544,13 @@ luaotfload.loadmodule('font-otc.lua') luaotfload.loadmodule('font-def.lua') luaotfload.loadmodule('font-xtx.lua') luaotfload.loadmodule('font-dum.lua') + +% \end{macrocode} +% +% \textsf{luaotfload} specific modules. +% +% \begin{macrocode} + luaotfload.loadmodule('font-nms.lua') luaotfload.loadmodule('font-clr.lua') diff --git a/otfl-data-con.lua b/otfl-data-con.lua index fabe0ba..e7bb8af 100644 --- a/otfl-data-con.lua +++ b/otfl-data-con.lua @@ -1,5 +1,5 @@ if not modules then modules = { } end modules ['data-con'] = { - version = 1.001, + version = 1.100, comment = "companion to luat-lib.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", @@ -37,38 +37,48 @@ end local allocated = { } --- tracing +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 +} function containers.define(category, subcategory, version, enabled) - return function() - 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 1.000, - trace = false, - path = caches and caches.setpath and caches.setpath(category,subcategory), - } - c[subcategory] = s - end - return s - else - return nil + 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.iswritable(container.path, name) + return container.enabled and caches and caches.iswritable(container.writable, name) end function containers.is_valid(container, name) @@ -81,18 +91,20 @@ function containers.is_valid(container, name) end function containers.read(container,name) - if container.enabled and caches and not container.storage[name] and containers.usecache then - container.storage[name] = caches.loaddata(container.path,name) - if containers.is_valid(container,name) then + 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 - container.storage[name] = nil + stored = nil end - end - if container.storage[name] then + storage[name] = stored + elseif stored then report(container,"reusing",name) end - return container.storage[name] + return stored end function containers.write(container, name, data) @@ -101,7 +113,7 @@ function containers.write(container, name, data) if container.enabled and caches then local unique, shared = data.unique, data.shared data.unique, data.shared = nil, nil - caches.savedata(container.path, name, data) + caches.savedata(container.writable, name, data) report(container,"saved",name) data.unique, data.shared = unique, shared end diff --git a/otfl-font-def.lua b/otfl-font-def.lua index 0add703..8e64872 100644 --- a/otfl-font-def.lua +++ b/otfl-font-def.lua @@ -233,18 +233,29 @@ end define.resolvers = resolvers +-- todo: reporter + function define.resolvers.file(specification) - specification.forced = file.extname(specification.name) - specification.name = file.removesuffix(specification.name) + local suffix = file.suffix(specification.name) + if fonts.formats[suffix] then + specification.forced = suffix + specification.name = file.removesuffix(specification.name) + end end function define.resolvers.name(specification) local resolve = fonts.names.resolve if resolve then - specification.resolved, specification.sub = fonts.names.resolve(specification) - if specification.resolved then - specification.forced = file.extname(specification.resolved) - specification.name = file.removesuffix(specification.resolved) + local resolved, sub = fonts.names.resolve(specification) + specification.resolved, specification.sub = resolved, sub + if resolved then + 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 define.resolvers.file(specification) @@ -456,7 +467,7 @@ end local function check_otf(forced,specification,suffix,what) local name = specification.name if forced then - name = file.addsuffix(name,suffix) + name = file.addsuffix(name,suffix,true) end local fullname, tfmtable = resolvers.findbinfile(name,suffix) or "", nil -- one shot if fullname == "" then @@ -578,7 +589,7 @@ function define.read(specification,size,id) -- id can be optional, name can alre specification = define.resolve(specification) local hash = tfm.hash_instance(specification) if cache_them then - local fontdata = containers.read(fonts.cache(),hash) -- for tracing purposes + local fontdata = containers.read(fonts.cache,hash) -- for tracing purposes end local fontdata = define.registered(hash) -- id if not fontdata then @@ -591,7 +602,7 @@ function define.read(specification,size,id) -- id can be optional, name can alre end end if cache_them then - fontdata = containers.write(fonts.cache(),hash,fontdata) -- for tracing purposes + fontdata = containers.write(fonts.cache,hash,fontdata) -- for tracing purposes end if fontdata then fontdata.hash = hash diff --git a/otfl-font-dum.lua b/otfl-font-dum.lua index 2de1ae1..c9ffb63 100644 --- a/otfl-font-dum.lua +++ b/otfl-font-dum.lua @@ -379,3 +379,22 @@ function fonts.otf.char(n) tex.sprint("\\char" .. n) end end + +-- another one: + +fonts.strippables = table.tohash { + 0x000AD, 0x017B4, 0x017B5, 0x0200B, 0x0200C, 0x0200D, 0x0200E, 0x0200F, 0x0202A, 0x0202B, + 0x0202C, 0x0202D, 0x0202E, 0x02060, 0x02061, 0x02062, 0x02063, 0x0206A, 0x0206B, 0x0206C, + 0x0206D, 0x0206E, 0x0206F, 0x0FEFF, 0x1D173, 0x1D174, 0x1D175, 0x1D176, 0x1D177, 0x1D178, + 0x1D179, 0x1D17A, 0xE0001, 0xE0020, 0xE0021, 0xE0022, 0xE0023, 0xE0024, 0xE0025, 0xE0026, + 0xE0027, 0xE0028, 0xE0029, 0xE002A, 0xE002B, 0xE002C, 0xE002D, 0xE002E, 0xE002F, 0xE0030, + 0xE0031, 0xE0032, 0xE0033, 0xE0034, 0xE0035, 0xE0036, 0xE0037, 0xE0038, 0xE0039, 0xE003A, + 0xE003B, 0xE003C, 0xE003D, 0xE003E, 0xE003F, 0xE0040, 0xE0041, 0xE0042, 0xE0043, 0xE0044, + 0xE0045, 0xE0046, 0xE0047, 0xE0048, 0xE0049, 0xE004A, 0xE004B, 0xE004C, 0xE004D, 0xE004E, + 0xE004F, 0xE0050, 0xE0051, 0xE0052, 0xE0053, 0xE0054, 0xE0055, 0xE0056, 0xE0057, 0xE0058, + 0xE0059, 0xE005A, 0xE005B, 0xE005C, 0xE005D, 0xE005E, 0xE005F, 0xE0060, 0xE0061, 0xE0062, + 0xE0063, 0xE0064, 0xE0065, 0xE0066, 0xE0067, 0xE0068, 0xE0069, 0xE006A, 0xE006B, 0xE006C, + 0xE006D, 0xE006E, 0xE006F, 0xE0070, 0xE0071, 0xE0072, 0xE0073, 0xE0074, 0xE0075, 0xE0076, + 0xE0077, 0xE0078, 0xE0079, 0xE007A, 0xE007B, 0xE007C, 0xE007D, 0xE007E, 0xE007F, +} + diff --git a/otfl-font-ini.lua b/otfl-font-ini.lua index e451497..c695ec4 100644 --- a/otfl-font-ini.lua +++ b/otfl-font-ini.lua @@ -13,6 +13,7 @@ if not modules then modules = { } end modules ['font-ini'] = { local utf = unicode.utf8 local format, serialize = string.format, table.serialize local write_nl = texio.write_nl +local lower = string.lower if not fontloader then fontloader = fontforge end @@ -84,12 +85,12 @@ end fonts.formats = { } function fonts.fontformat(filename,default) - local extname = file.extname(filename) + local extname = lower(file.extname(filename)) local format = fonts.formats[extname] if format then return format else - logs.report("fonts define","unable to detemine font format for '%s'",filename) + logs.report("fonts define","unable to determine font format for '%s'",filename) return default end end diff --git a/otfl-font-ota.lua b/otfl-font-ota.lua index 558e2fc..0b61e17 100644 --- a/otfl-font-ota.lua +++ b/otfl-font-ota.lua @@ -35,14 +35,8 @@ local penalty = node.id('penalty') local set_attribute = node.set_attribute local has_attribute = node.has_attribute local traverse_id = node.traverse_id -local delete_node = nodes.delete -local replace_node = nodes.replace -local insert_node_after = node.insert_after -local insert_node_before = node.insert_before local traverse_node_list = node.traverse -local new_glue_node = nodes.glue - local fontdata = fonts.ids local state = attributes.private('state') @@ -56,7 +50,6 @@ local a_to_language = otf.a_to_language -- font related value, but then we also need dynamic features which is -- somewhat slower; and .. we need a chain of them - function fonts.initializers.node.otf.analyze(tfmdata,value,attr) if attr and attr > 0 then script, language = a_to_script[attr], a_to_language[attr] @@ -195,8 +188,6 @@ function fonts.analyzers.methods.nocolor(head,font,attr) return head, true end -otf.remove_joiners = false -- true -- for idris who want it as option - local function finish(first,last) if last then if first == last then @@ -242,22 +233,10 @@ function fonts.analyzers.methods.arab(head,font,attr) -- maybe make a special ve local tfmdata = fontdata[font] local marks = tfmdata.marks local first, last, current, done = nil, nil, head, false - local joiners, nonjoiners - local removejoiners = tfmdata.remove_joiners -- or otf.remove_joiners - if removejoiners then - joiners, nonjoiners = { }, { } - end while current do if current.id == glyph and current.subtype<256 and current.font == font and not has_attribute(current,state) then done = true local char = current.char - if removejoiners then - if char == zwj then - joiners[#joiners+1] = current - elseif char == zwnj then - nonjoiners[#nonjoiners+1] = current - end - end if marks[char] then set_attribute(current,state,5) -- mark if trace_analyzing then fcs(current,"font:mark") end @@ -303,22 +282,5 @@ function fonts.analyzers.methods.arab(head,font,attr) -- maybe make a special ve current = current.next end first, last = finish(first,last) - if removejoiners then - -- is never head - for i=1,#joiners do - delete_node(head,joiners[i]) - end - for i=1,#nonjoiners do - replace_node(head,nonjoiners[i],new_glue_node(0)) -- or maybe a kern - end - end return head, done end - -table.insert(fonts.manipulators,"joiners") - -function fonts.initializers.node.otf.joiners(tfmdata,value) - if value == "strip" then - tfmdata.remove_joiners = true - end -end diff --git a/otfl-font-otd.lua b/otfl-font-otd.lua index 41e8853..14a9727 100644 --- a/otfl-font-otd.lua +++ b/otfl-font-otd.lua @@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['font-otd'] = { license = "see context related readme files" } -local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) +local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) fonts = fonts or { } fonts.otf = fonts.otf or { } @@ -60,9 +60,10 @@ function otf.set_dynamics(font,dynamics,attribute) tfmdata.script = script tfmdata.shared.features = { } -- end of save - dsla = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) + local set = fonts.define.check(features,otf.features.default) + dsla = otf.set_features(tfmdata,set) if trace_dynamics then - logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) + logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s, set: %s",context_numbers[attribute],attribute,script,language,table.sequenced(set)) end -- we need to restore some values tfmdata.script = saved.script diff --git a/otfl-font-otf.lua b/otfl-font-otf.lua index d68137c..b113cf6 100644 --- a/otfl-font-otf.lua +++ b/otfl-font-otf.lua @@ -8,9 +8,11 @@ if not modules then modules = { } end modules ['font-otf'] = { local utf = unicode.utf8 -local concat, getn, utfbyte = table.concat, table.getn, utf.byte +local concat, utfbyte = table.concat, 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 trace_private = false trackers.register("otf.private", function(v) trace_private = v end) @@ -80,7 +82,7 @@ otf.features.default = otf.features.default or { } otf.enhancers = otf.enhancers or { } otf.glists = { "gsub", "gpos" } -otf.version = 2.650 -- beware: also sync font-mis.lua +otf.version = 2.653 -- beware: also sync font-mis.lua otf.pack = true -- beware: also sync font-mis.lua otf.syncspace = true otf.notdef = false @@ -220,6 +222,7 @@ local enhancers = { function otf.load(filename,format,sub,featurefile) local name = file.basename(file.removesuffix(filename)) + local size = lfs.attributes(filename,"size") or 0 if featurefile then name = name .. "@" .. file.removesuffix(file.basename(featurefile)) end @@ -229,8 +232,7 @@ function otf.load(filename,format,sub,featurefile) hash = hash .. "-" .. sub end hash = containers.cleanname(hash) - local data = containers.read(otf.cache(), hash) - local size = lfs.attributes(filename,"size") or 0 + local data = containers.read(otf.cache,hash) if not data or data.verbose ~= fonts.verbose or data.size ~= size then logs.report("load otf","loading: %s (hash: %s)",filename,hash) local ff, messages @@ -267,9 +269,9 @@ function otf.load(filename,format,sub,featurefile) data.size = size data.verbose = fonts.verbose logs.report("load otf","saving in cache: %s",filename) - data = containers.write(otf.cache(), hash, data) + data = containers.write(otf.cache, hash, data) collectgarbage("collect") - data = containers.read(otf.cache(), hash) -- this frees the old table and load the sparse one + data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one collectgarbage("collect") else logs.report("load otf","loading failed (table conversion error)") @@ -788,14 +790,12 @@ otf.enhancers["check math"] = function(data,filename) if hv then math.horiz_variants = hv.variants local p = hv.parts - if p then - if #p>0 then - for i=1,#p do - local pi = p[i] - pi.glyph = unicodes[pi.component] or 0 - end - math.horiz_parts = p + if p and #p > 0 then + for i=1,#p do + local pi = p[i] + pi.glyph = unicodes[pi.component] or 0 end + math.horiz_parts = p end local ic = hv.italic_correction if ic and ic ~= 0 then @@ -807,14 +807,12 @@ otf.enhancers["check math"] = function(data,filename) local uc = unicodes[index] math.vert_variants = vv.variants local p = vv.parts - if p then - if #p>0 then - for i=1,#p do - local pi = p[i] - pi.glyph = unicodes[pi.component] or 0 - end - math.vert_parts = p + if p and #p > 0 then + for i=1,#p do + local pi = p[i] + pi.glyph = unicodes[pi.component] or 0 end + math.vert_parts = p end local ic = vv.italic_correction if ic and ic ~= 0 then @@ -863,10 +861,15 @@ 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). + --~ otf.enhancers["reorganize kerns"] = function(data,filename) --~ local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes --~ local mkdone = false ---~ for index, glyph in next, data.glyphs do +--~ for index, glyph in next, glyphs do --~ if glyph.kerns then --~ local mykerns = { } --~ for k,v in next, glyph.kerns do @@ -915,6 +918,9 @@ end --~ end --~ local dgpos = data.gpos --~ if dgpos then +--~ local separator = lpeg.P(" ") +--~ local other = ((1 - separator)^0) / unicodes +--~ local splitter = lpeg.Ct(other * (separator * other)^0) --~ for gp=1,#dgpos do --~ local gpos = dgpos[gp] --~ local subtables = gpos.subtables @@ -923,56 +929,70 @@ end --~ 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, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular --~ if type(lookups) ~= "table" then --~ lookups = { lookups } --~ end +--~ local maxfirsts, maxseconds = getn(firsts), getn(seconds) +--~ for _, s in next, firsts do +--~ split[s] = split[s] or lpegmatch(splitter,s) +--~ end +--~ for _, s in next, seconds do +--~ split[s] = split[s] or lpegmatch(splitter,s) +--~ end --~ for l=1,#lookups do --~ local lookup = lookups[l] ---~ -- weird, as maxfirst and maxseconds can have holes ---~ local maxfirsts, maxseconds = getn(firsts), getn(seconds) ---~ if trace_loading then ---~ logs.report("load otf", "adding kernclass %s with %s times %s pairs",lookup, maxfirsts, maxseconds) ---~ end ---~ for fk, fv in next, firsts do ---~ for first in gmatch(fv,"[^ ]+") do ---~ local first_unicode = unicodes[first] ---~ if type(first_unicode) == "number" then ---~ first_unicode = { first_unicode } +--~ local function do_it(fk,first_unicode) +--~ local glyph = glyphs[mapmap[first_unicode]] +--~ if glyph then +--~ local mykerns = glyph.mykerns +--~ if not mykerns then +--~ mykerns = { } -- unicode indexed ! +--~ glyph.mykerns = mykerns --~ end ---~ for f=1,#first_unicode do ---~ local glyph = glyphs[mapmap[first_unicode[f]]] ---~ if glyph then ---~ local mykerns = glyph.mykerns ---~ if not mykerns then ---~ mykerns = { } -- unicode indexed ! ---~ glyph.mykerns = mykerns ---~ end ---~ local lookupkerns = mykerns[lookup] ---~ if not lookupkerns then ---~ lookupkerns = { } ---~ mykerns[lookup] = lookupkerns ---~ end ---~ for sk, sv in next, seconds do ---~ local offset = offsets[(fk-1) * maxseconds + sk] ---~ --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] ---~ for second in gmatch(sv,"[^ ]+") do ---~ local second_unicode = unicodes[second] ---~ if type(second_unicode) == "number" then +--~ local lookupkerns = mykerns[lookup] +--~ if not lookupkerns then +--~ lookupkerns = { } +--~ mykerns[lookup] = lookupkerns +--~ end +--~ local baseoffset = (fk-1) * maxseconds +--~ for sk=2,maxseconds do -- we can avoid this loop with a table +--~ local sv = seconds[sk] +--~ local splt = split[sv] +--~ if splt then +--~ local offset = offsets[baseoffset + sk] +--~ --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] +--~ if offset then +--~ for i=1,#splt do +--~ local second_unicode = splt[i] +--~ if tonumber(second_unicode) then --~ lookupkerns[second_unicode] = offset ---~ else ---~ for s=1,#second_unicode do ---~ lookupkerns[second_unicode[s]] = offset ---~ end ---~ end +--~ else for s=1,#second_unicode do +--~ lookupkerns[second_unicode[s]] = offset +--~ end end --~ end --~ end ---~ elseif trace_loading then ---~ logs.report("load otf", "no glyph data for U+%04X", first_unicode[f]) --~ end --~ end +--~ elseif trace_loading then +--~ logs.report("load otf", "no glyph data for U+%04X", first_unicode) +--~ end +--~ end +--~ for fk=1,#firsts do +--~ local fv = firsts[fk] +--~ local splt = split[fv] +--~ if splt then +--~ for i=1,#splt do +--~ local first_unicode = splt[i] +--~ if tonumber(first_unicode) then +--~ do_it(fk,first_unicode) +--~ else for f=1,#first_unicode do +--~ do_it(fk,first_unicode[f]) +--~ end end +--~ end --~ end --~ end --~ end @@ -989,7 +1009,27 @@ end otf.enhancers["reorganize kerns"] = function(data,filename) local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes local mkdone = false - for index, glyph in next, data.glyphs do + local function do_it(lookup,first_unicode,kerns) + local glyph = glyphs[mapmap[first_unicode]] + if glyph then + local mykerns = glyph.mykerns + if not mykerns then + mykerns = { } -- unicode indexed ! + glyph.mykerns = mykerns + end + local lookupkerns = mykerns[lookup] + if not lookupkerns then + lookupkerns = { } + mykerns[lookup] = lookupkerns + end + for second_unicode, kern in next, kerns do + lookupkerns[second_unicode] = kern + end + elseif trace_loading then + logs.report("load otf", "no glyph data for U+%04X", first_unicode) + end + end + for index, glyph in next, glyphs do if glyph.kerns then local mykerns = { } for k,v in next, glyph.kerns do @@ -1049,75 +1089,53 @@ otf.enhancers["reorganize kerns"] = function(data,filename) 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, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular if type(lookups) ~= "table" then lookups = { lookups } end - local split = { } + local maxfirsts, maxseconds = getn(firsts), getn(seconds) + -- here we could convert split into a list of unicodes which is a bit + -- faster but as this is only done when caching it does not save us much + for _, s in next, firsts do + split[s] = split[s] or lpegmatch(splitter,s) + end + for _, s in next, seconds do + split[s] = split[s] or lpegmatch(splitter,s) + end for l=1,#lookups do local lookup = lookups[l] - -- weird, as maxfirst and maxseconds can have holes, first seems to be indexed, seconds starts at 2 - local maxfirsts, maxseconds = getn(firsts), getn(seconds) - for _, s in next, firsts do - split[s] = split[s] or lpegmatch(splitter,s) - end - for _, s in next, seconds do - split[s] = split[s] or lpegmatch(splitter,s) - end - if trace_loading then - logs.report("load otf", "adding kernclass %s with %s times %s pairs",lookup, maxfirsts, maxseconds) - end - local function do_it(fk,first_unicode) - local glyph = glyphs[mapmap[first_unicode]] - if glyph then - local mykerns = glyph.mykerns - if not mykerns then - mykerns = { } -- unicode indexed ! - glyph.mykerns = mykerns - end - local lookupkerns = mykerns[lookup] - if not lookupkerns then - lookupkerns = { } - mykerns[lookup] = lookupkerns - end - local baseoffset = (fk-1) * maxseconds + for fk=1,#firsts do + local fv = firsts[fk] + local splt = split[fv] + if splt then + local kerns, baseoffset = { }, (fk-1) * maxseconds for sk=2,maxseconds do local sv = seconds[sk] - local offset = offsets[baseoffset + sk] - --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] local splt = split[sv] if splt then - for i=1,#splt do - local second_unicode = splt[i] - if tonumber(second_unicode) then - lookupkerns[second_unicode] = offset - else - for s=1,#second_unicode do - lookupkerns[second_unicode[s]] = offset - end + local offset = offsets[baseoffset + sk] + if offset then + for i=1,#splt do + local second_unicode = splt[i] + if tonumber(second_unicode) then + kerns[second_unicode] = offset + else for s=1,#second_unicode do + kerns[second_unicode[s]] = offset + end end end end end end - elseif trace_loading then - logs.report("load otf", "no glyph data for U+%04X", first_unicode) - end - end - for fk=1,#firsts do - local fv = firsts[fk] - local splt = split[fv] - if splt then for i=1,#splt do local first_unicode = splt[i] if tonumber(first_unicode) then - do_it(fk,first_unicode) - else - for f=1,#first_unicode do - do_it(fk,first_unicode[f]) - end - end + do_it(lookup,first_unicode,kerns) + else for f=1,#first_unicode do + do_it(lookup,first_unicode[f],kerns) + end end end end end @@ -1132,6 +1150,14 @@ otf.enhancers["reorganize kerns"] = function(data,filename) end end + + + + + + + + otf.enhancers["strip not needed data"] = function(data,filename) local verbose = fonts.verbose local int_to_uni = data.luatex.unicodes @@ -1356,10 +1382,12 @@ function otf.features.register(name,default) otf.features.default[name] = default end +-- for context this will become a task handler + function otf.set_features(tfmdata,features) local processes = { } if features and next(features) then - local lists = { + local lists = { -- why local fonts.triggers, fonts.processors, fonts.manipulators, @@ -1396,7 +1424,7 @@ function otf.set_features(tfmdata,features) end end end - local fm = fonts.methods[mode] + local fm = fonts.methods[mode] -- todo: zonder node/mode otf/... if fm then local fmotf = fm.otf if fmotf then @@ -1429,7 +1457,7 @@ function otf.otf_to_tfm(specification) local format = specification.format local features = specification.features.normal local cache_id = specification.hash - local tfmdata = containers.read(tfm.cache(),cache_id) + local tfmdata = containers.read(tfm.cache,cache_id) --~ print(cache_id) if not tfmdata then local otfdata = otf.load(filename,format,sub,features and features.featurefile) @@ -1463,7 +1491,7 @@ function otf.otf_to_tfm(specification) shared.processes, shared.features = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) end end - containers.write(tfm.cache(),cache_id,tfmdata) + containers.write(tfm.cache,cache_id,tfmdata) end return tfmdata end @@ -1505,14 +1533,11 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th local unicodes = luatex.unicodes -- names to unicodes local indices = luatex.indices local characters, parameters, math_parameters, descriptions = { }, { }, { }, { } - local tfm = { - characters = characters, - parameters = parameters, - math_parameters = math_parameters, - descriptions = descriptions, - indices = indices, - unicodes = unicodes, - } + local designsize = metadata.designsize or metadata.design_size or 100 + if designsize == 0 then + designsize = 100 + end + local spaceunits = 500 -- indices maps from unicodes to indices for u, i in next, indices do characters[u] = { } -- we need this because for instance we add protruding info and loop over characters @@ -1531,9 +1556,8 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th -- we have them shared because that packs nicer -- we could prepare the variants and keep 'm in descriptions if m then - local variants = m.horiz_variants + local variants, parts, c = m.horiz_variants, m.horiz_parts, char if variants then - local c = char for n in gmatch(variants,"[^ ]+") do local un = unicodes[n] if un and u ~= un then @@ -1541,21 +1565,26 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th c = characters[un] end end - c.horiz_variants = m.horiz_parts - else - local variants = m.vert_variants - if variants then - local c = char - for n in gmatch(variants,"[^ ]+") do - local un = unicodes[n] - if un and u ~= un then - c.next = un - c = characters[un] - end + c.horiz_variants = parts + elseif parts then + c.horiz_variants = parts + end + local variants, parts, c = m.vert_variants, m.vert_parts, char + if variants then + for n in gmatch(variants,"[^ ]+") do + local un = unicodes[n] + if un and u ~= un then + c.next = un + c = characters[un] end - c.vert_variants = m.vert_parts - c.vert_italic_correction = m.vert_italic_correction - end + end -- c is now last in chain + c.vert_variants = parts + elseif parts then + c.vert_variants = parts + end + local italic_correction = m.vert_italic_correction + if italic_correction then + c.vert_italic_correction = italic_correction end local kerns = m.kerns if kerns then @@ -1565,65 +1594,49 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th end end -- end math - local designsize = metadata.designsize or metadata.design_size or 100 - if designsize == 0 then - designsize = 100 - end - local spaceunits = 500 - -- we need a runtime lookup because of running from cdrom or zip, brrr (shouldn't we use the basename then?) - tfm.filename = fonts.tfm.checked_filename(luatex) - tfm.fontname = metadata.fontname - tfm.fullname = metadata.fullname or tfm.fontname - tfm.psname = tfm.fontname or tfm.fullname - tfm.name = tfm.filename or tfm.fullname or tfm.fontname - tfm.units = metadata.units_per_em or 1000 - tfm.encodingbytes = 2 - tfm.format = fonts.fontformat(tfm.filename,"opentype") - tfm.cidinfo = data.cidinfo - tfm.cidinfo.registry = tfm.cidinfo.registry or "" - tfm.type = "real" - tfm.direction = 0 - tfm.boundarychar_label = 0 - tfm.boundarychar = 65536 - tfm.designsize = (designsize/10)*65536 - tfm.spacer = "500 units" - local endash, emdash = 0x20, 0x2014 -- unicodes['space'], unicodes['emdash'] + local endash, emdash, space = 0x20, 0x2014, "space" -- unicodes['space'], unicodes['emdash'] if metadata.isfixedpitch then if descriptions[endash] then - spaceunits, tfm.spacer = descriptions[endash].width, "space" + spaceunits, spacer = descriptions[endash].width, "space" end if not spaceunits and descriptions[emdash] then - spaceunits, tfm.spacer = descriptions[emdash].width, "emdash" + spaceunits, spacer = descriptions[emdash].width, "emdash" end if not spaceunits and metadata.charwidth then - spaceunits, tfm.spacer = metadata.charwidth, "charwidth" + spaceunits, spacer = metadata.charwidth, "charwidth" end else if descriptions[endash] then - spaceunits, tfm.spacer = descriptions[endash].width, "space" + spaceunits, spacer = descriptions[endash].width, "space" end if not spaceunits and descriptions[emdash] then - spaceunits, tfm.spacer = descriptions[emdash].width/2, "emdash/2" + spaceunits, spacer = descriptions[emdash].width/2, "emdash/2" end if not spaceunits and metadata.charwidth then - spaceunits, tfm.spacer = metadata.charwidth, "charwidth" + spaceunits, spacer = metadata.charwidth, "charwidth" end end spaceunits = tonumber(spaceunits) or tfm.units/2 -- 500 -- brrr + -- we need a runtime lookup because of running from cdrom or zip, brrr (shouldn't we use the basename then?) + local filename = fonts.tfm.checked_filename(luatex) + local fontname = metadata.fontname + local fullname = metadata.fullname or fontname + local cidinfo = data.cidinfo + local units = metadata.units_per_em or 1000 + -- + cidinfo.registry = cidinfo and cidinfo.registry or "" -- weird here, fix upstream + -- parameters.slant = 0 - parameters.space = spaceunits -- 3.333 (cmr10) - parameters.space_stretch = tfm.units/2 -- 500 -- 1.666 (cmr10) - parameters.space_shrink = 1*tfm.units/3 -- 333 -- 1.111 (cmr10) - parameters.x_height = 2*tfm.units/5 -- 400 - parameters.quad = tfm.units -- 1000 - if spaceunits < 2*tfm.units/5 then + 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 local italicangle = metadata.italicangle - tfm.ascender = math.abs(metadata.ascent or 0) - tfm.descender = math.abs(metadata.descent or 0) if italicangle then -- maybe also in afm _ - tfm.italicangle = italicangle parameters.slant = parameters.slant - math.round(math.tan(italicangle*math.pi/180)) end if metadata.isfixedpitch then @@ -1645,8 +1658,34 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th end end end - -- [6] - return tfm + -- + return { + characters = characters, + parameters = parameters, + math_parameters = math_parameters, + descriptions = descriptions, + indices = indices, + unicodes = unicodes, + type = "real", + direction = 0, + boundarychar_label = 0, + boundarychar = 65536, + designsize = (designsize/10)*65536, + spacer = "500 units", + encodingbytes = 2, + filename = filename, + fontname = fontname, + fullname = fullname, + psname = fontname or fullname, + name = filename or fullname, + units = units, + format = fonts.fontformat(filename,"opentype"), + cidinfo = cidinfo, + ascender = abs(metadata.ascent or 0), + descender = abs(metadata.descent or 0), + spacer = spacer, + italicangle = italicangle, + } else return nil end diff --git a/otfl-font-otn.lua b/otfl-font-otn.lua index d4f89ad..4402dd6 100644 --- a/otfl-font-otn.lua +++ b/otfl-font-otn.lua @@ -1882,6 +1882,9 @@ end local resolved = { } -- we only resolve a font,script,language pair once -- todo: pass all these 'locals' in a table +-- +-- dynamics will be isolated some day ... for the moment we catch attribute zero +-- not being set function fonts.methods.node.otf.features(head,font,attr) if trace_steps then @@ -1926,8 +1929,7 @@ function fonts.methods.node.otf.features(head,font,attr) local ra = rl [attr] if ra == nil then ra = { } rl [attr] = ra end -- attr can be false -- sequences always > 1 so no need for optimization for s=1,#sequences do - local pardir, txtdir = 0, { } - local success = false + local pardir, txtdir, success = 0, { }, false local sequence = sequences[s] local r = ra[s] -- cache if r == nil then @@ -1994,24 +1996,33 @@ function fonts.methods.node.otf.features(head,font,attr) while start do local id = start.id if id == glyph then - if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) then ---~ if start.subtype<256 and start.font == font and has_attribute(start,0,attr) then - for i=1,#subtables do - local lookupname = subtables[i] - local lookupcache = thecache[lookupname] - if lookupcache then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - start, success = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) - if success then - break + 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 = thecache[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + start, success = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) + if success then + break + end end + else + report_missing_cache(typ,lookupname) end - else - report_missing_cache(typ,lookupname) end + if start then start = start.prev end + else + start = start.prev end - if start then start = start.prev end else start = start.prev end @@ -2034,18 +2045,27 @@ function fonts.methods.node.otf.features(head,font,attr) while start do local id = start.id if id == glyph then ---~ if start.font == font and start.subtype<256 and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then - if start.font == font and start.subtype<256 and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then - local lookupmatch = lookupcache[start.char] - if lookupmatch then - -- sequence kan weg - local ok - start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) - if ok then - success = true + 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,r[4],lookupname,lookupmatch,sequence,featuredata,1) + if ok then + success = true + end end + if start then start = start.next end + else + start = start.next end - if start then start = start.next end else start = start.next end @@ -2109,27 +2129,36 @@ function fonts.methods.node.otf.features(head,font,attr) while start do local id = start.id if id == glyph then - if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then ---~ if start.subtype<256 and start.font == font and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then - for i=1,ns do - local lookupname = subtables[i] - local lookupcache = thecache[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,r[4],lookupname,lookupmatch,sequence,featuredata,i) - if ok then - success = true - break + 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 = thecache[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,r[4],lookupname,lookupmatch,sequence,featuredata,i) + if ok then + success = true + break + end end + else + report_missing_cache(typ,lookupname) end - else - report_missing_cache(typ,lookupname) end + if start then start = start.next end + else + start = start.next end - if start then start = start.next end else start = start.next end @@ -2149,7 +2178,6 @@ function fonts.methods.node.otf.features(head,font,attr) -- start = start.next -- end elseif id == whatsit then - local subtype = start.subtype local subtype = start.subtype if subtype == 7 then local dir = start.dir diff --git a/otfl-font-tfm.lua b/otfl-font-tfm.lua index 31ae2ca..560ba1c 100644 --- a/otfl-font-tfm.lua +++ b/otfl-font-tfm.lua @@ -52,6 +52,8 @@ tfm.fontname_mode = "fullpath" tfm.enhance = tfm.enhance or function() end +fonts.formats.tfm = "type1" -- we need to have at least a value here + function tfm.read_from_tfm(specification) local fname, tfmdata = specification.filename or "", nil if fname ~= "" then @@ -391,6 +393,9 @@ t.colorscheme = tfmtable.colorscheme local vn = v.next if vn then chr.next = vn + --~ if v.vert_variants or v.horiz_variants then + --~ logs.report("glyph 0x%05X has combination of next, vert_variants and horiz_variants",index) + --~ end else local vv = v.vert_variants if vv then diff --git a/otfl-luat-dum.lua b/otfl-luat-dum.lua index 3946b6f..0737762 100644 --- a/otfl-luat-dum.lua +++ b/otfl-luat-dum.lua @@ -1,5 +1,5 @@ if not modules then modules = { } end modules ['luat-dum'] = { - version = 1.001, + version = 1.100, comment = "companion to luatex-*.tex", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", @@ -80,29 +80,80 @@ end -- usage as I don't want any dependency at all. Also, ConTeXt might have -- different needs and tricks added. +--~ containers.usecache = true + caches = { } ---~ containers.usecache = true +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('$VARTEXMF') + end + + if cachepaths == "" then + cachepaths = "." + end + + cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") -function caches.setpath(category,subcategory) --- local root = kpse.var_value("TEXMFCACHE") or "" --- if root == "" then --- root = kpse.var_value("VARTEXMF") or "" --- end - local var = kpse.var_value("TEXMFVAR") - local root = var and (var .. "/luatex/generic/luaotfload/") or "" - if root ~= "" then - root = file.join(root,category) - lfs.mkdir(root) - root = file.join(root,subcategory) - lfs.mkdir(root) - return lfs.isdir(root) and root + for i=1,#cachepaths do + if file.iswritable(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.isreadable(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-" and name -- clash prevention + name = "temp-" .. name -- clash prevention return file.addsuffix(file.join(path,name),"lua") end end @@ -112,17 +163,21 @@ function caches.iswritable(path,name) return fullname and file.iswritable(fullname) end -function caches.loaddata(path,name) - local fullname = makefullname(path,name) - if fullname then - local data = loadfile(fullname) - return data and data() +function caches.loaddata(paths,name) + for i=1,#paths do + local fullname = makefullname(paths[i],name) + if fullname then + texio.write(string.format("(load: %s)",fullname)) + local data = loadfile(fullname) + return data and data() + end end end function caches.savedata(path,name,data) local fullname = makefullname(path,name) if fullname then + texio.write(string.format("(save: %s)",fullname)) table.tofile(fullname,data,'return',false,true,false) end end diff --git a/otfl-node-dum.lua b/otfl-node-dum.lua index f39a087..fbc8264 100644 --- a/otfl-node-dum.lua +++ b/otfl-node-dum.lua @@ -6,7 +6,19 @@ if not modules then modules = { } end modules ['node-dum'] = { license = "see context related readme files" } -nodes = nodes or { } +nodes = nodes or { } +fonts = fonts or { } +attributes = attributes or { } + +local traverse_id = node.traverse_id +local free_node = node.free +local remove_node = node.remove + +local glyph = node.id('glyph') + +-- fonts + +local fontdata = fonts.ids or { } function nodes.simple_font_handler(head) -- lang.hyphenate(head) @@ -17,3 +29,97 @@ function nodes.simple_font_handler(head) head = node.kerning(head) return head 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","! purposed so setting them at the TeX end might break the font handler.") + texio.write_nl("log","!") + + tex.attribute[0] = 0 -- else no features + +end + +nodes.protect_glyphs = node.protect_glyphs +nodes.unprotect_glyphs = node.unprotect_glyphs + +function nodes.process_characters(head) + local usedfonts, done, prevfont = { }, false, nil + for n in traverse_id(glyph,head) do + if font ~= prevfont then + prevfont = font + local used = usedfonts[font] + if not used then + local tfmdata = fontdata[font] + if tfmdata then + local shared = tfmdata.shared -- we need to check shared, only when same features + if shared then + local processors = shared.processes + if processors and #processors > 0 then + usedfonts[font] = processors + done = true + end + end + end + end + end + end + if done then + for font, processors in next, usedfonts do + for i=1,#processors do + local h, d = processors[i](head,font,0) + head, done = h or head, done or d + end + end + end + return head, true +end + +-- helper + +function nodes.kern(k) + local n = new_node("kern",1) + n.kern = k + return n +end + +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 + +-- 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 diff --git a/otfl-node-fnt.lua b/otfl-node-fnt.lua deleted file mode 100644 index f2d8e1d..0000000 --- a/otfl-node-fnt.lua +++ /dev/null @@ -1,205 +0,0 @@ -if not modules then modules = { } end modules ['node-fnt'] = { - 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, type = next, type - -local trace_characters = false trackers.register("nodes.characters", function(v) trace_characters = v end) - -local glyph = node.id('glyph') - -local traverse_id = node.traverse_id -local has_attribute = node.has_attribute - -local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming - -fonts = fonts or { } -fonts.tfm = fonts.tfm or { } -fonts.ids = fonts.ids or { } - -local fontdata = fonts.ids - --- some tests with using an array of dynamics[id] and processes[id] demonstrated --- that there was nothing to gain (unless we also optimize other parts) --- --- maybe getting rid of the intermediate shared can save some time - --- potential speedup: check for subtype < 256 so that we can remove that test --- elsewhere, danger: injected nodes will not be dealt with but that does not --- happen often; we could consider processing sublists but that might need mor --- checking later on; the current approach also permits variants - -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","! purposed so setting them at the TeX end might break the font handler.") - texio.write_nl("log","!") - - tex.attribute[0] = 0 -- else no features - -end - -function nodes.process_characters(head) - -- either next or not, but definitely no already processed list - starttiming(nodes) - local usedfonts, attrfonts, done = { }, { }, false - local a, u, prevfont, prevattr = 0, 0, nil, 0 - for n in traverse_id(glyph,head) do - local font, attr = n.font, has_attribute(n,0) -- zero attribute is reserved for fonts in context - if attr and attr > 0 then - if font ~= prevfont or attr ~= prevattr then - local used = attrfonts[font] - if not used then - used = { } - attrfonts[font] = used - end - if not used[attr] then - -- we do some testing outside the function - local tfmdata = fontdata[font] - local shared = tfmdata.shared - if shared then - local dynamics = shared.dynamics - if dynamics then - local d = shared.set_dynamics(font,dynamics,attr) -- still valid? - if d then - used[attr] = d - a = a + 1 - end - end - end - end - prevfont, prevattr = font, attr - end - elseif font ~= prevfont then - prevfont, prevattr = font, 0 - local used = usedfonts[font] - if not used then - local tfmdata = fontdata[font] - if tfmdata then - local shared = tfmdata.shared -- we need to check shared, only when same features - if shared then - local processors = shared.processes - if processors and #processors > 0 then - usedfonts[font] = processors - u = u + 1 - end - end - else - -- probably nullfont - end - end - else - prevattr = attr - end - end - -- we could combine these and just make the attribute nil - if u == 1 then - local font, processors = next(usedfonts) - local n = #processors - if n > 0 then - local h, d = processors[1](head,font,false) - head, done = h or head, done or d - if n > 1 then - for i=2,n do - local h, d = processors[i](head,font,false) - head, done = h or head, done or d - end - end - end - elseif u > 0 then - for font, processors in next, usedfonts do - local n = #processors - local h, d = processors[1](head,font,false) - head, done = h or head, done or d - if n > 1 then - for i=2,n do - local h, d = processors[i](head,font,false) - head, done = h or head, done or d - end - end - end - end - if a == 1 then - local font, dynamics = next(attrfonts) - for attribute, processors in next, dynamics do -- attr can switch in between - local n = #processors - local h, d = processors[1](head,font,attribute) - head, done = h or head, done or d - if n > 1 then - for i=2,n do - local h, d = processors[i](head,font,attribute) - head, done = h or head, done or d - end - end - end - elseif a > 0 then - for font, dynamics in next, attrfonts do - for attribute, processors in next, dynamics do -- attr can switch in between - local n = #processors - local h, d = processors[1](head,font,attribute) - head, done = h or head, done or d - if n > 1 then - for i=2,n do - local h, d = processors[i](head,font,attribute) - head, done = h or head, done or d - end - end - end - end - end - stoptiming(nodes) - if trace_characters then - nodes.report(head,done) - end - return head, true -end - -if node.protect_glyphs then - - nodes.protect_glyphs = node.protect_glyphs - nodes.unprotect_glyphs = node.unprotect_glyphs - -else do - - -- initial value subtype : X000 0001 = 1 = 0x01 = char - -- - -- expected before linebreak : X000 0000 = 0 = 0x00 = glyph - -- X000 0010 = 2 = 0x02 = ligature - -- X000 0100 = 4 = 0x04 = ghost - -- X000 1010 = 10 = 0x0A = leftboundary lig - -- X001 0010 = 18 = 0x12 = rightboundary lig - -- X001 1010 = 26 = 0x1A = both boundaries lig - -- X000 1100 = 12 = 0x1C = leftghost - -- X001 0100 = 20 = 0x14 = rightghost - - function nodes.protect_glyphs(head) - local done = false - for g in traverse_id(glyph,head) do - local s = g.subtype - if s == 1 then - done, g.subtype = true, 256 - elseif s <= 256 then - done, g.subtype = true, 256 + s - end - end - return done - end - - function nodes.unprotect_glyphs(head) - local done = false - for g in traverse_id(glyph,head) do - local s = g.subtype - if s > 256 then - done, g.subtype = true, s - 256 - end - end - return done - end - -end end diff --git a/otfl-node-ini.lua b/otfl-node-ini.lua deleted file mode 100644 index 36e2402..0000000 --- a/otfl-node-ini.lua +++ /dev/null @@ -1,244 +0,0 @@ -if not modules then modules = { } end modules ['node-ini'] = { - 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" -} - ---[[ldx-- -

Most of the code that had accumulated here is now separated in -modules.

---ldx]]-- - --- this module is being reconstructed - -local utf = unicode.utf8 -local next, type = next, type -local format, concat, match, utfchar = string.format, table.concat, string.match, utf.char - -local chardata = characters and characters.data - ---[[ldx-- -

We start with a registration system for atributes so that we can use the -symbolic names later on.

---ldx]]-- - -attributes = attributes or { } - -attributes.names = attributes.names or { } -attributes.numbers = attributes.numbers or { } -attributes.list = attributes.list or { } -attributes.unsetvalue = -0x7FFFFFFF - -storage.register("attributes/names", attributes.names, "attributes.names") -storage.register("attributes/numbers", attributes.numbers, "attributes.numbers") -storage.register("attributes/list", attributes.list, "attributes.list") - -local names, numbers, list = attributes.names, attributes.numbers, attributes.list - -function attributes.define(name,number) -- at the tex end - if not numbers[name] then - numbers[name], names[number], list[number] = number, name, { } - end -end - ---[[ldx-- -

We can use the attributes in the range 127-255 (outside user space). These -are only used when no attribute is set at the \TEX\ end which normally -happens in .

---ldx]]-- - -storage.shared.attributes_last_private = storage.shared.attributes_last_private or 127 - -function attributes.private(name) -- at the lua end (hidden from user) - local number = numbers[name] - if not number then - local last = storage.shared.attributes_last_private or 127 - if last < 255 then - last = last + 1 - storage.shared.attributes_last_private = last - end - number = last - numbers[name], names[number], list[number] = number, name, { } - end - return number -end - ---[[ldx-- -

Access to nodes is what gives its power. Here we -implement a few helper functions. These functions are rather optimized.

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

When manipulating node lists in , we will remove -nodes and insert new ones. While node access was implemented, we did -quite some experiments in order to find out if manipulating nodes -in was feasible from the perspective of performance.

- -

First of all, we noticed that the bottleneck is more with excessive -callbacks (some gets called very often) and the conversion from and to -'s datastructures. However, at the end, we -found that inserting and deleting nodes in a table could become a -bottleneck.

- -

This resulted in two special situations in passing nodes back to -: a table entry with value false is ignored, -and when instead of a table true is returned, the -original table is used.

- -

Insertion is handled (at least in as follows. When -we need to insert a node at a certain position, we change the node at -that position by a dummy node, tagged inline which itself -has_attribute the original node and one or more new nodes. Before we pass -back the list we collapse the list. Of course collapsing could be built -into the engine, but this is a not so natural extension.

- -

When we collapse (something that we only do when really needed), we -also ignore the empty nodes. [This is obsolete!]

---ldx]]-- - -nodes = nodes or { } - -local hlist = node.id('hlist') -local vlist = node.id('vlist') -local glyph = node.id('glyph') -local glue = node.id('glue') -local penalty = node.id('penalty') -local kern = node.id('kern') -local whatsit = node.id('whatsit') - -local traverse_id = node.traverse_id -local traverse = node.traverse -local free_node = node.free -local remove_node = node.remove -local insert_node_before = node.insert_before -local insert_node_after = node.insert_after - -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 = insert_node_before -nodes.after = insert_node_after - --- we need to test this, as it might be fixed now - -function nodes.before(h,c,n) - if c then - if c == h then - n.next = h - n.prev = nil - h.prev = n - else - local cp = c.prev - n.next = c - n.prev = cp - if cp then - cp.next = n - end - c.prev = n - return h, n - end - end - return n, n -end - -function nodes.after(h,c,n) - if c then - local cn = c.next - if cn then - n.next = cn - cn.prev = n - else - n.next = nil - end - c.next = n - n.prev = c - return h, n - end - return n, n -end - --- local h, c = nodes.replace(head,current,new) --- local c = nodes.replace(false,current,new) --- local c = nodes.replace(current,new) - -function nodes.replace(head,current,new) -- no head returned if false - if not new then - head, current, new = false, head, current - end - local prev, next = current.prev, current.next - if next then - new.next, next.prev = next, new - end - if prev then - new.prev, prev.next = prev, new - end - if head then - if head == current then - head = new - end - free_node(current) - return head, new - else - free_node(current) - return new - end -end - --- will move - -local function count(stack,flat) - local n = 0 - while stack do - local id = stack.id - if not flat and id == hlist or id == vlist then - local list = stack.list - if list then - n = n + 1 + count(list) -- self counts too - else - n = n + 1 - end - else - n = n + 1 - end - stack = stack.next - end - return n -end - -nodes.count = count - --- new, will move - -function attributes.ofnode(n) - local a = n.attr - if a then - local names = attributes.names - a = a.next - while a do - local number, value = a.number, a.value - texio.write_nl(format("%s : attribute %3i, value %4i, name %s",tostring(n),number,value,names[number] or '?')) - a = a.next - end - end -end - -local left, space = lpeg.P("<"), lpeg.P(" ") - -nodes.filterkey = left * (1-left)^0 * left * space^0 * lpeg.C((1-space)^0) diff --git a/otfl-node-inj.lua b/otfl-node-inj.lua index 9c4612a..579a266 100644 --- a/otfl-node-inj.lua +++ b/otfl-node-inj.lua @@ -342,39 +342,21 @@ function nodes.inject_kerns(head,where,keep) -- only w can be nil, can be sped up when w == nil local rl, x, w, r2l = k[1], k[2] or 0, k[4] or 0, k[6] local wx = w - x ---~ if rl < 0 then ---~ if r2l 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 r2l 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 + if r2l 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 + end end end if next(cx) then @@ -413,39 +395,21 @@ function nodes.inject_kerns(head,where,keep) -- copied from above local r2l = kk[6] local wx = w - x ---~ if rl < 0 then ---~ if r2l then ---~ if x ~= 0 then ---~ insert_node_before(head,n,newkern(x)) ---~ end ---~ if wx ~= 0 then ---~ insert_node_after(head,n,newkern(wx)) ---~ end ---~ else ---~ if wx ~= 0 then ---~ insert_node_before(head,n,newkern(wx)) ---~ end ---~ if x ~= 0 then ---~ insert_node_after (head,n,newkern(x)) ---~ end ---~ end ---~ else - if r2l 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 + if r2l 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 ---~ end + if wx ~= 0 then + insert_node_after(head,n,newkern(wx)) + end + end else -- simple (e.g. kernclass kerns) if x ~= 0 then diff --git a/otfl-node-res.lua b/otfl-node-res.lua deleted file mode 100644 index a8ea874..0000000 --- a/otfl-node-res.lua +++ /dev/null @@ -1,302 +0,0 @@ -if not modules then modules = { } end modules ['node-res'] = { - 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 gmatch, format = string.gmatch, string.format -local copy_node, free_node, free_list, new_node, node_type, node_id = node.copy, node.free, node.flush_list, node.new, node.type, node.id -local tonumber, round = tonumber, math.round - -local glyph_node = node_id("glyph") - ---[[ldx-- -

The next function is not that much needed but in we use -for debugging node management.

---ldx]]-- - -nodes = nodes or { } - -nodes.whatsits = { } -- table.swapped(node.whatsits()) - -local reserved = { } -local whatsits = nodes.whatsits - -for k, v in next, node.whatsits() do - whatsits[k], whatsits[v] = v, k -- two way -end - -local function register_node(n) - reserved[#reserved+1] = n - return n -end - -nodes.register = register_node - -function nodes.cleanup_reserved(nofboxes) -- todo - nodes.tracers.steppers.reset() -- todo: make a registration subsystem - local nr, nl = #reserved, 0 - for i=1,nr do - local ri = reserved[i] - -- if not (ri.id == glue_spec and not ri.is_writable) then - free_node(reserved[i]) - -- end - end - if nofboxes then - local tb = tex.box - for i=0,nofboxes do - local l = tb[i] - if l then - free_node(tb[i]) - nl = nl + 1 - end - end - end - reserved = { } - return nr, nl, nofboxes -- can be nil -end - -function nodes.usage() - local t = { } - for n, tag in gmatch(status.node_mem_usage,"(%d+) ([a-z_]+)") do - t[tag] = n - end - return t -end - -local disc = register_node(new_node("disc")) -local kern = register_node(new_node("kern",1)) -local penalty = register_node(new_node("penalty")) -local glue = register_node(new_node("glue")) -- glue.spec = nil -local glue_spec = register_node(new_node("glue_spec")) -local glyph = register_node(new_node("glyph",0)) -local textdir = register_node(new_node("whatsit",whatsits.dir)) -- 7 (6 is local par node) -local rule = register_node(new_node("rule")) -local latelua = register_node(new_node("whatsit",whatsits.late_lua)) -- 35 -local user_n = register_node(new_node("whatsit",whatsits.user_defined)) user_n.type = 100 -- 44 -local user_l = register_node(new_node("whatsit",whatsits.user_defined)) user_l.type = 110 -- 44 -local user_s = register_node(new_node("whatsit",whatsits.user_defined)) user_s.type = 115 -- 44 -local user_t = register_node(new_node("whatsit",whatsits.user_defined)) user_t.type = 116 -- 44 -local left_margin_kern = register_node(new_node("margin_kern",0)) -local right_margin_kern = register_node(new_node("margin_kern",1)) -local lineskip = register_node(new_node("glue",1)) -local baselineskip = register_node(new_node("glue",2)) -local leftskip = register_node(new_node("glue",8)) -local rightskip = register_node(new_node("glue",9)) -local temp = register_node(new_node("temp",0)) - -function nodes.zeroglue(n) - local s = n.spec - return not writable or ( - s.width == 0 - and s.stretch == 0 - and s.shrink == 0 - and s.stretch_order == 0 - and s.shrink_order == 0 - ) -end - -function nodes.glyph(fnt,chr) - local n = copy_node(glyph) - if fnt then n.font = fnt end - if chr then n.char = chr end - return n -end - -function nodes.penalty(p) - local n = copy_node(penalty) - n.penalty = p - return n -end - -function nodes.kern(k) - local n = copy_node(kern) - n.kern = k - return n -end - -function nodes.glue_spec(width,stretch,shrink) - local s = copy_node(glue_spec) - s.width, s.stretch, s.shrink = width, stretch, shrink - return s -end - -local function someskip(skip,width,stretch,shrink) - local n = copy_node(skip) - if not width then - -- no spec - elseif tonumber(width) then - local s = copy_node(glue_spec) - s.width, s.stretch, s.shrink = width, stretch, shrink - n.spec = s - else - -- shared - n.spec = copy_node(width) - end - return n -end - -function nodes.glue(width,stretch,shrink) - return someskip(glue,width,stretch,shrink) -end -function nodes.leftskip(width,stretch,shrink) - return someskip(leftskip,width,stretch,shrink) -end -function nodes.rightskip(width,stretch,shrink) - return someskip(rightskip,width,stretch,shrink) -end -function nodes.lineskip(width,stretch,shrink) - return someskip(lineskip,width,stretch,shrink) -end -function nodes.baselineskip(width,stretch,shrink) - return someskip(baselineskip,width,stretch,shrink) -end - -function nodes.disc() - return copy_node(disc) -end - -function nodes.textdir(dir) - local t = copy_node(textdir) - t.dir = dir - return t -end - -function nodes.rule(width,height,depth,dir) - local n = copy_node(rule) - if width then n.width = width end - if height then n.height = height end - if depth then n.depth = depth end - if dir then n.dir = dir end - return n -end - -function nodes.latelua(code) - local n = copy_node(latelua) - n.data = code - return n -end - -function nodes.leftmarginkern(glyph,width) - local n = copy_node(left_margin_kern) - if not glyph then - logs.fatal("nodes","invalid pointer to left margin glyph node") - elseif glyph.id ~= glyph_node then - logs.fatal("nodes","invalid node type %s for left margin glyph node",node_type(glyph)) - else - n.glyph = glyph - end - if width then - n.width = width - end - return n -end - -function nodes.rightmarginkern(glyph,width) - local n = copy_node(right_margin_kern) - if not glyph then - logs.fatal("nodes","invalid pointer to right margin glyph node") - elseif glyph.id ~= glyph_node then - logs.fatal("nodes","invalid node type %s for right margin glyph node",node_type(p)) - else - n.glyph = glyph - end - if width then - n.width = width - end - return n -end - -function nodes.temp() - return copy_node(temp) -end ---[[ -

At some point we ran into a problem that the glue specification -of the zeropoint dimension was overwritten when adapting a glue spec -node. This is a side effect of glue specs being shared. After a -couple of hours tracing and debugging Taco and I came to the -conclusion that it made no sense to complicate the spec allocator -and settled on a writable flag. This all is a side effect of the -fact that some glues use reserved memory slots (with the zeropoint -glue being a noticeable one). So, next we wrap this into a function -and hide it for the user. And yes, LuaTeX now gives a warning as -well.

-]]-- - -if tex.luatexversion > 51 then - - function nodes.writable_spec(n) - local spec = n.spec - if not spec then - spec = copy_node(glue_spec) - n.spec = spec - elseif not spec.writable then - spec = copy_node(spec) - n.spec = spec - end - return spec - end - -else - - function nodes.writable_spec(n) - local spec = n.spec - if not spec then - spec = copy_node(glue_spec) - else - spec = copy_node(spec) - end - n.spec = spec - return spec - end - -end - -local cache = { } - -function nodes.usernumber(num) - local n = cache[num] - if n then - return copy_node(n) - else - local n = copy_node(user_n) - if num then n.value = num end - return n - end -end - -function nodes.userlist(list) - local n = copy_node(user_l) - if list then n.value = list end - return n -end - -local cache = { } -- we could use the same cache - -function nodes.userstring(str) - local n = cache[str] - if n then - return copy_node(n) - else - local n = copy_node(user_s) - n.type = 115 - if str then n.value = str end - return n - end -end - -function nodes.usertokens(tokens) - local n = copy_node(user_t) - if tokens then n.value = tokens end - return n -end - -statistics.register("cleaned up reserved nodes", function() - return format("%s nodes, %s lists of %s", nodes.cleanup_reserved(tex.count["lastallocatedbox"])) -end) -- \topofboxstack - -statistics.register("node memory usage", function() -- comes after cleanup ! - return status.node_mem_usage -end) -- cgit v1.2.3