diff options
Diffstat (limited to 'macros/luatex/generic/luaotfload/luaotfload-notdef.lua')
-rw-r--r-- | macros/luatex/generic/luaotfload/luaotfload-notdef.lua | 231 |
1 files changed, 205 insertions, 26 deletions
diff --git a/macros/luatex/generic/luaotfload/luaotfload-notdef.lua b/macros/luatex/generic/luaotfload/luaotfload-notdef.lua index 3156882e86..7108e7d7df 100644 --- a/macros/luatex/generic/luaotfload/luaotfload-notdef.lua +++ b/macros/luatex/generic/luaotfload/luaotfload-notdef.lua @@ -5,8 +5,8 @@ local ProvidesLuaModule = { name = "luaotfload-notdef", - version = "3.12", --TAGVERSION - date = "2020-02-02", --TAGDATE + version = "3.13", --TAGVERSION + date = "2020-05-01", --TAGDATE description = "luaotfload submodule / color", license = "GPL v2.0", author = "Marcel Krüger" @@ -16,6 +16,7 @@ if luatexbase and luatexbase.provides_module then luatexbase.provides_module (ProvidesLuaModule) end +local harfbuzz = luaotfload.harfbuzz local flush_node = node.direct.flush_node local getfont = font.getfont local getnext = node.direct.getnext @@ -23,12 +24,21 @@ local getwhd = node.direct.getwhd local insert = table.insert local insert_after = node.direct.insert_after local kern_id = node.id'kern' +local disc_id = node.id'disc' local nodenew = node.direct.new +local nodecopy = node.direct.copy local otfregister = fonts.constructors.features.otf.register local protect_glyph = node.direct.protect_glyph local remove = node.direct.remove local setfont = node.direct.setfont local traverse_char = node.direct.traverse_char +local traverse_id = node.direct.traverse_id +local setchar = node.direct.setchar +local getwidth = node.direct.getwidth +local setkern = node.direct.setkern +local setattributelist = node.direct.setattributelist +local getattributelist = node.direct.getattributelist +local setmove = luaotfload.fontloader.nodes.injections.setmove -- According to DerivedCoreProperties.txt, Default_Ignorable_Code_Point -- is generated from: @@ -154,42 +164,211 @@ otfregister { } } -function fonts.handlers.otf.handlers.gsub_remove(head,char,dataset,sequence,replacement) - local next - head, next = remove(head, char) - flush_node(char) - if not head and not next then -- Avoid a double free if we were alone - head = nodenew(kern_id) - end - return head, next, true, true -end +local font_invisible_replacement = setmetatable({}, {__index = function(t, fid) + local fontdata = font.getfont(fid) + local replacement = fontdata.shared.features.invisible + if replacement == "remove" then + t[fid] = false + return false + end + replacement = tonumber(replacement) or 32 + local char = fontdata.characters[replacement] + if char then + t[fid] = {replacement, -char.width} + return t[fid] + else + t[fid] = false + return false + end +end}) -local sequence = { - features = {invisible = {["*"] = {["*"] = true}}}, +local push, pop do + local function checkprop(n) + local p = node.direct.getproperty(n) + return p and p.zwnj + end + local list = {} + function push(head) + head = node.direct.todirect(head) + local l = {} + list[#list+1] = l + for n, id in node.direct.traverse(head) do + if checkprop(n) then + head = node.direct.remove(head, n) + l[#l+1] = n + elseif id == disc_id then + local pre, post, replace = node.direct.getdisc(n) + for nn in node.direct.traverse(pre) do + if checkprop(nn) then + local after + pre, after = node.direct.remove(pre, nn) + l[#l+1] = {nn, n, 'pre'} + end + end + for nn in node.direct.traverse(post) do + if checkprop(nn) then + post = node.direct.remove(post, nn) + l[#l+1] = {nn, n, 'post'} + end + end + for nn in node.direct.traverse(replace) do + if checkprop(nn) then + replace = node.direct.remove(replace, nn) + l[#l+1] = {nn, n, 'replace'} + end + end + node.direct.setdisc(n, pre, post, replace) + end + end + return head + end + local getfield, setfield = node.direct.getfield, node.direct.setfield + local function pop(head) + head = node.direct.todirect(head) + local l = list[#list] + list[#list] = nil + for i = #l,1,-1 do + local e = l[i] + local n = tonumber(e) + local disc, thishead + if n then + thishead = head + else + disc, n = e[2], e[1] + thishead = getfield(disc, e[3]) + end + local prev, next = node.direct.getboth(n) + if prev or not next then + thishead = node.direct.insert_after(thishead, prev, n) + else + thishead = node.direct.insert_before(thishead, next, n) + end + if disc then + setfield(disc, e[3], thishead) + else + head = thishead + end + end + return head + end + fonts.handlers.otf.handlers.marked_push = push + fonts.handlers.otf.handlers.marked_pop = pop +end +local sequence1 = { + features = {["semiignored-node"] = {["*"] = {["*"] = true}}}, flags = {false, false, false, false}, - name = "invisible", - order = {"invisible"}, - nofsteps = 1, - steps = {{ - coverage = ignorable_codepoints, - index = 1, - }}, - type = "gsub_remove", + name = "semiignored-node", + order = {"semiignored-node"}, + type = "marked_push", } -local function invisibleinitialiser(tfmdata, value) +local sequence2 = { + features = {["semiignored-node"] = {["*"] = {["*"] = true}}}, + flags = {false, false, false, false}, + name = "semiignored-node", + order = {"semiignored-node"}, + type = "marked_pop", +} +local function pushpopinitialiser(tfmdata, value, features) local resources = tfmdata.resources local sequences = resources and resources.sequences + local first_gpos, last_gpos if sequences then - -- Now we get to the interesting part: At which point should our new sequence be inserted? Let's do it at the end, then they are still seen by all features. - insert(sequences, sequence) + local alreadydone + for i=1,#sequences do + local sequence = sequences[i] + if sequence1 == sequence then + return + elseif sequence.type:sub(1,5) == "gpos_" then + if not first_gpos then + first_gpos = i + end + last_gpos = i + end + end + if first_gpos then + insert(sequences, last_gpos+1, sequence2) + insert(sequences, first_gpos, sequence1) + end end end + +otfregister { + name = 'semiignored-node', + description = 'Allow adding nodes which break ligatures but do not affect kerning', + default = true, -- Should basically never be disabled manually + initializers = { + node = pushpopinitialiser, + -- plug = ? -- TODO: Manually handle in luaotfload-harf-plug.lua + }, +} + +ignorable_replacement = {} + +local delayed_remove do + local delayed + function delayed_remove(n) + flush_node(delayed) + delayed = n + end +end + +local function ignorablehandler(head, fid, ...) -- FIXME: The arguments are probably wrong + local fontparam = font_invisible_replacement[fid] + local replacement = fontparam and fontparam[1] + local font_kern = fontparam and fontparam[2] + for n, c, f in traverse_char(head) do if f == fid then + local lookup = ignorable_codepoints[c] + if lookup then + if replacement then + setchar(n, replacement) + if font_kern then + local k = nodenew(kern_id) + setkern(k, font_kern) + setattributelist(k, getattributelist(n)) + head = insert_after(head, n, k) + end + else + local after + head, after = remove(head, n) + delayed_remove(n) + end + end + end end + delayed_remove() + for n in traverse_id(head, disc_id) do + local a, b, c = getdisc(n) + setdisc(ignorablehandler(a, fid), ignorablehandler(b, fid), ignorablehandler(c, fid)) + end + return head +end + +if harfbuzz then + local harf_settings = luaotfload.harf + local preserve_flag = harfbuzz.Buffer.FLAG_PRESERVE_DEFAULT_IGNORABLES or 0 + local remove_flag = harfbuzz.Buffer.FLAG_REMOVE_DEFAULT_IGNORABLES or 0 + local dotted_circle_flag = harfbuzz.Buffer.FLAG_DO_NOT_INSERT_DOTTED_CIRCLE or 0 + harf_settings.default_buf_flags = (harf_settings.default_buf_flags & ~remove_flag) | preserve_flag | dotted_circle_flag + local function dottedcircleinitialize(tfmdata, value) + if not tfmdata.hb then return end + local hb = tfmdata.hb + hb.buf_flags = hb.buf_flags & ~dotted_circle_flag + end + otfregister { + name = 'dottedcircle', + description = 'Insert dotted circle to fix invalid clusters', + default = true, + initializers = { + plug = dottedcircleinitialize, + }, + } +end otfregister { name = 'invisible', description = 'Remove invisible control characters', default = true, - initializers = { - node = invisibleinitialiser, + processors = { + node = ignorablehandler, + plug = ignorablehandler, }, } |