summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/luaotfload/luaotfload-notdef.lua
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/generic/luaotfload/luaotfload-notdef.lua')
-rw-r--r--macros/luatex/generic/luaotfload/luaotfload-notdef.lua231
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,
},
}