summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/font-ots.lua
diff options
context:
space:
mode:
authorHans Hagen <pragma@wxs.nl>2017-05-25 13:21:58 +0200
committerContext Git Mirror Bot <phg42.2a@gmail.com>2017-05-25 13:21:58 +0200
commit82aed3e7e8af29f359ebef4f93684d20e98107e6 (patch)
tree2b92c44d14566481aad5635f479b1b106d4e2347 /tex/context/base/mkiv/font-ots.lua
parentaceba29d651766f5621b9812d4c40e28029bc4ea (diff)
downloadcontext-82aed3e7e8af29f359ebef4f93684d20e98107e6.tar.gz
2017-05-25 12:56:00
Diffstat (limited to 'tex/context/base/mkiv/font-ots.lua')
-rw-r--r--tex/context/base/mkiv/font-ots.lua462
1 files changed, 242 insertions, 220 deletions
diff --git a/tex/context/base/mkiv/font-ots.lua b/tex/context/base/mkiv/font-ots.lua
index 16c2ce735..1230475b3 100644
--- a/tex/context/base/mkiv/font-ots.lua
+++ b/tex/context/base/mkiv/font-ots.lua
@@ -155,11 +155,10 @@ local report_process = logs.reporter("fonts","otf process")
local report_warning = logs.reporter("fonts","otf warning")
local report_run = logs.reporter("fonts","otf run")
-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")
+registertracker("otf.substitutions", "otf.singles","otf.multiples","otf.alternatives","otf.ligatures")
+registertracker("otf.positions", "otf.marks","otf.kerns","otf.cursive")
+registertracker("otf.actions", "otf.substitutions","otf.positions")
+registertracker("otf.sample", "otf.steps","otf.substitutions","otf.positions","otf.analyzing")
local nuts = nodes.nuts
local tonode = nuts.tonode
@@ -192,6 +191,7 @@ local getdir = nuts.getdir
local getwidth = nuts.getwidth
local ischar = nuts.is_char
+local usesfont = nuts.uses_font
local insert_node_after = nuts.insert_after
local copy_node = nuts.copy
@@ -932,7 +932,8 @@ end
function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,step,i,injection)
local startchar = getchar(start)
- if step.format == "pair" then
+-- if step.format == "pair" then
+ if step.format == "pair" or type(kerns) == "table" then
local dx, dy, w, h = setpair(start,factor,rlmode,sequence.flags[4],kerns,injection)
if trace_kerns then
logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(dataset,sequence),gref(startchar),dx,dy,w,h)
@@ -3830,132 +3831,213 @@ otf.helpers.pardirstate = pardirstate
-- optimizations the principles of processing the features hasn't changed much since
-- the beginning.
-local function featuresprocessor(head,font,attr,direction)
+do
- local sequences = sequencelists[font] -- temp hack
+ -- experimental speedup (only with hyphenated text and multiple fonts per processing)
+ --
+ -- at some point this might become true by default
- if not sequencelists then
- return head, false
- end
+ local fastdisc = false directives.register("otf.fastdisc",function(v) fastdisc = v end)
- nesting = nesting + 1
+ function otf.featuresprocessor(head,font,attr,direction,n)
- if nesting == 1 then
- currentfont = font
- tfmdata = fontdata[font]
- descriptions = tfmdata.descriptions -- only needed in gref so we could pass node there instead
- characters = tfmdata.characters -- but this branch is not entered that often anyway
- local resources = tfmdata.resources
- marks = resources.marks
- classes = resources.classes
- threshold,
- factor = getthreshold(font)
- checkmarks = tfmdata.properties.checkmarks
+ local sequences = sequencelists[font] -- temp hack
- elseif currentfont ~= font then
+ if not sequencelists then
+ return head, false
+ end
- report_warning("nested call with a different font, level %s, quitting",nesting)
- nesting = nesting - 1
- return head, false
+ nesting = nesting + 1
- end
+ if nesting == 1 then
+ currentfont = font
+ tfmdata = fontdata[font]
+ descriptions = tfmdata.descriptions -- only needed in gref so we could pass node there instead
+ characters = tfmdata.characters -- but this branch is not entered that often anyway
+ local resources = tfmdata.resources
+ marks = resources.marks
+ classes = resources.classes
+ threshold,
+ factor = getthreshold(font)
+ checkmarks = tfmdata.properties.checkmarks
- -- some 10% faster when no dynamics but hardly measureable on real runs .. but: it only
- -- works when we have no other dynamics as otherwise the zero run will be applied to the
- -- whole stream for which we then need to pass another variable which we won't
+ elseif currentfont ~= font then
- -- if attr == 0 then
- -- attr = false
- -- end
+ report_warning("nested call with a different font, level %s, quitting",nesting)
+ nesting = nesting - 1
+ return head, false
- head = tonut(head)
+ end
- if trace_steps then
- checkstep(head)
- end
+ -- some 10% faster when no dynamics but hardly measureable on real runs .. but: it only
+ -- works when we have no other dynamics as otherwise the zero run will be applied to the
+ -- whole stream for which we then need to pass another variable which we won't
- local initialrl = direction == "TRT" and -1 or 0
+ -- if attr == 0 then
+ -- attr = false
+ -- end
- local done = false
- local datasets = otf.dataset(tfmdata,font,attr)
- local dirstack = { } -- could move outside function but we can have local runs
- sweephead = { }
-
- -- Keeping track of the headnode is needed for devanagari. (I generalized it a bit
- -- so that multiple cases are also covered.) We could prepend a temp node.
-
- -- We don't goto the next node when a disc node is created so that we can then treat
- -- the pre, post and replace. It's a bit of a hack but works out ok for most cases.
-
- for s=1,#datasets do
- local dataset = datasets[s]
- ----- featurevalue = dataset[1] -- todo: pass to function instead of using a global
- local attribute = dataset[2]
- local sequence = dataset[3] -- sequences[s] -- also dataset[5]
- local rlparmode = initialrl
- local topstack = 0
- local typ = sequence.type
- local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- store in dataset
- local handler = handlers[typ]
- local steps = sequence.steps
- local nofsteps = sequence.nofsteps
- if not steps then
- -- this permits injection, watch the different arguments
- local h, d, ok = handler(head,head,dataset,sequence,nil,nil,nil,0,font,attr)
- if ok then
- done = true
- if h then
- head = h
- end
- end
- elseif typ == "gsub_reversecontextchain" then
- -- this is a limited case, no special treatments like 'init' etc
- local start = find_node_tail(head)
- local rlmode = 0 -- how important is this .. do we need to check for dir?
- while start do
- local char = ischar(start,font)
- if char then
- local a -- happens often so no assignment is faster
- if attr then
- a = getattr(start,0)
+ head = tonut(head)
+
+ if trace_steps then
+ checkstep(head)
+ end
+
+ local initialrl = direction == "TRT" and -1 or 0
+
+ local done = false
+ local datasets = otf.dataset(tfmdata,font,attr)
+ local dirstack = { } -- could move outside function but we can have local runs
+ sweephead = { }
+
+ -- Keeping track of the headnode is needed for devanagari. (I generalized it a bit
+ -- so that multiple cases are also covered.) We could prepend a temp node.
+
+ -- We don't goto the next node when a disc node is created so that we can then treat
+ -- the pre, post and replace. It's a bit of a hack but works out ok for most cases.
+
+ local discs = fastdisc and n and n > 1 and setmetatableindex(function(t,k)
+ local v = usesfont(k,font)
+ t[k] = v
+ return v
+ end)
+
+ for s=1,#datasets do
+ local dataset = datasets[s]
+ ----- featurevalue = dataset[1] -- todo: pass to function instead of using a global
+ local attribute = dataset[2]
+ local sequence = dataset[3] -- sequences[s] -- also dataset[5]
+ local rlparmode = initialrl
+ local topstack = 0
+ local typ = sequence.type
+ local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- store in dataset
+ local handler = handlers[typ]
+ local steps = sequence.steps
+ local nofsteps = sequence.nofsteps
+ if not steps then
+ -- this permits injection, watch the different arguments
+ local h, d, ok = handler(head,head,dataset,sequence,nil,nil,nil,0,font,attr)
+ if ok then
+ done = true
+ if h then
+ head = h
end
- if not a or (a == attr) then
- for i=1,nofsteps do
- local step = steps[i]
- local lookupcache = step.coverage
- if lookupcache then
- local lookupmatch = lookupcache[char]
- if lookupmatch then
- -- todo: disc?
- local ok
- head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
- if ok then
- done = true
- break
+ end
+ elseif typ == "gsub_reversecontextchain" then
+ -- this is a limited case, no special treatments like 'init' etc
+ local start = find_node_tail(head)
+ local rlmode = 0 -- how important is this .. do we need to check for dir?
+ while start do
+ local char = ischar(start,font)
+ if char then
+ local a -- happens often so no assignment is faster
+ if attr then
+ a = getattr(start,0)
+ end
+ if not a or (a == attr) then
+ for i=1,nofsteps do
+ local step = steps[i]
+ local lookupcache = step.coverage
+ if lookupcache then
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ -- todo: disc?
+ local ok
+ head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
+ if ok then
+ done = true
+ break
+ end
end
+ else
+ report_missing_coverage(dataset,sequence)
end
- else
- report_missing_coverage(dataset,sequence)
end
- end
- if start then
+ if start then
+ start = getprev(start)
+ end
+ else
start = getprev(start)
end
else
start = getprev(start)
end
- else
- start = getprev(start)
end
- end
- else
- local start = head
- local rlmode = initialrl
- if nofsteps == 1 then -- happens often
- local step = steps[1]
- local lookupcache = step.coverage
- if not lookupcache then
- report_missing_coverage(dataset,sequence)
+ else
+ local start = head
+ local rlmode = initialrl
+ if nofsteps == 1 then -- happens often
+ local step = steps[1]
+ local lookupcache = step.coverage
+ if not lookupcache then
+ report_missing_coverage(dataset,sequence)
+ else
+ while start do
+ local char, id = ischar(start,font)
+ if char then
+ -- local a = attr and getattr(start,0)
+ -- if a then
+ -- a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
+ -- else
+ -- a = not attribute or getprop(start,a_state) == attribute
+ -- end
+ local a -- happens often so no assignment is faster
+ if attr then
+ if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then
+ a = true
+ end
+ elseif not attribute or getprop(start,a_state) == attribute then
+ a = true
+ end
+ if a then
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ local ok
+ head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)
+ if ok then
+ done = true
+ end
+ end
+ if start then
+ start = getnext(start)
+ end
+ else
+ start = getnext(start)
+ end
+ elseif char == false then
+ -- whatever glyph
+ start = getnext(start)
+ elseif id == glue_code then
+ -- happens often
+ start = getnext(start)
+ elseif id == disc_code then
+ if not discs or discs[start] == true then
+ local ok
+ if gpossing then
+ start, ok = kernrun(start,k_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
+ elseif typ == "gsub_ligature" then
+ start, ok = testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
+ else
+ start, ok = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
+ end
+ if ok then
+ done = true
+ end
+ else
+ start = getnext(start)
+ end
+ elseif id == math_code then
+ start = getnext(end_of_math(start))
+ elseif id == dir_code then
+ start, topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode)
+ elseif id == localpar_code then
+ start, rlparmode, rlmode = pardirstate(start)
+ else
+ start = getnext(start)
+ end
+ end
+ end
+
else
while start do
local char, id = ischar(start,font)
@@ -3975,37 +4057,54 @@ local function featuresprocessor(head,font,attr,direction)
a = true
end
if a then
- local lookupmatch = lookupcache[char]
- if lookupmatch then
- local ok
- head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)
- if ok then
- done = true
+ for i=1,nofsteps do
+ local step = steps[i]
+ local lookupcache = step.coverage
+ if lookupcache then
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ -- we could move all code inline but that makes things even more unreadable
+ local ok
+ head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
+ if ok then
+ done = true
+ break
+ elseif not start then
+ -- don't ask why ... shouldn't happen
+ break
+ end
+ end
+ else
+ report_missing_coverage(dataset,sequence)
end
end
if start then
start = getnext(start)
end
else
- start = getnext(start)
+ start = getnext(start)
end
elseif char == false then
-- whatever glyph
- start = getnext(start)
+ start = getnext(start)
elseif id == glue_code then
-- happens often
- start = getnext(start)
+ start = getnext(start)
elseif id == disc_code then
- local ok
- if gpossing then
- start, ok = kernrun(start,k_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
- elseif typ == "gsub_ligature" then
- start, ok = testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
+ if not discs or discs[start] == true then
+ local ok
+ if gpossing then
+ start, ok = kernrun(start,k_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
+ elseif typ == "gsub_ligature" then
+ start, ok = testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
+ else
+ start, ok = comprun(start,c_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
+ end
+ if ok then
+ done = true
+ end
else
- start, ok = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
- end
- if ok then
- done = true
+ start = getnext(start)
end
elseif id == math_code then
start = getnext(end_of_math(start))
@@ -4018,94 +4117,20 @@ local function featuresprocessor(head,font,attr,direction)
end
end
end
+ end
- else
- while start do
- local char, id = ischar(start,font)
- if char then
- -- local a = attr and getattr(start,0)
- -- if a then
- -- a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
- -- else
- -- a = not attribute or getprop(start,a_state) == attribute
- -- end
- local a -- happens often so no assignment is faster
- if attr then
- if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then
- a = true
- end
- elseif not attribute or getprop(start,a_state) == attribute then
- a = true
- end
- if a then
- for i=1,nofsteps do
- local step = steps[i]
- local lookupcache = step.coverage
- if lookupcache then
- local lookupmatch = lookupcache[char]
- if lookupmatch then
- -- we could move all code inline but that makes things even more unreadable
- local ok
- head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
- if ok then
- done = true
- break
- elseif not start then
- -- don't ask why ... shouldn't happen
- break
- end
- end
- else
- report_missing_coverage(dataset,sequence)
- end
- end
- if start then
- start = getnext(start)
- end
- else
- start = getnext(start)
- end
- elseif char == false then
- -- whatever glyph
- start = getnext(start)
- elseif id == glue_code then
- -- happens often
- start = getnext(start)
- elseif id == disc_code then
- local ok
- if gpossing then
- start, ok = kernrun(start,k_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
- elseif typ == "gsub_ligature" then
- start, ok = testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
- else
- start, ok = comprun(start,c_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
- end
- if ok then
- done = true
- end
- elseif id == math_code then
- start = getnext(end_of_math(start))
- elseif id == dir_code then
- start, topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode)
- elseif id == localpar_code then
- start, rlparmode, rlmode = pardirstate(start)
- else
- start = getnext(start)
- end
- end
+ if trace_steps then -- ?
+ registerstep(head)
end
- end
- if trace_steps then -- ?
- registerstep(head)
end
- end
+ nesting = nesting - 1
+ head = tonode(head)
- nesting = nesting - 1
- head = tonode(head)
+ return head, done
+ end
- return head, done
end
-- so far
@@ -4119,13 +4144,13 @@ function otf.registerplugin(name,f)
end
end
-local function plugininitializer(tfmdata,value)
+function otf.plugininitializer(tfmdata,value)
if type(value) == "string" then
tfmdata.shared.plugin = plugins[value]
end
end
-local function pluginprocessor(head,font)
+function otf.pluginprocessor(head,font)
local s = fontdata[font].shared
local p = s and s.plugin
if p then
@@ -4138,7 +4163,7 @@ local function pluginprocessor(head,font)
end
end
-local function featuresinitializer(tfmdata,value)
+function otf.featuresinitializer(tfmdata,value)
-- nothing done here any more
end
@@ -4148,18 +4173,15 @@ registerotffeature {
default = true,
initializers = {
position = 1,
- node = featuresinitializer,
- plug = plugininitializer,
+ node = otf.featuresinitializer,
+ plug = otf.plugininitializer,
},
processors = {
- node = featuresprocessor,
- plug = pluginprocessor,
+ node = otf.featuresprocessor,
+ plug = otf.pluginprocessor,
}
}
-otf.nodemodeinitializer = featuresinitializer
-otf.featuresprocessor = featuresprocessor
-
-- This can be used for extra handlers, but should be used with care!
otf.handlers = handlers -- used in devanagari
@@ -4289,9 +4311,9 @@ local function spaceinitializer(tfmdata,value) -- attr
if type(kern) ~= "table" then
left[k] = kern
elseif single then
- left[k] = v[3]
+ left[k] = kern[3]
else
- local one = v[1]
+ local one = kern[1]
if one then
left[k] = one[3]
end