diff options
Diffstat (limited to 'tex/context/base/mkiv/font-ots.lua')
-rw-r--r-- | tex/context/base/mkiv/font-ots.lua | 462 |
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 |