diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/font-ots.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkiv/font-ots.lua | 1182 |
1 files changed, 644 insertions, 538 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-ots.lua b/Master/texmf-dist/tex/context/base/mkiv/font-ots.lua index 880bcb6d50a..6d7c5fb2568 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/font-ots.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/font-ots.lua @@ -8,6 +8,11 @@ if not modules then modules = { } end modules ['font-ots'] = { -- sequences } --[[ldx-- +<p>I need to check the description at the microsoft site ... it has been improved +so maybe there are some interesting details there. Most below is based on old and +incomplete documentation and involved quite a bit of guesswork (checking with the +abstract uniscribe of those days. But changing things is tricky!</p> + <p>This module is a bit more split up that I'd like but since we also want to test with plain <l n='tex'/> it has to be so. This module is part of <l n='context'/> and discussion about improvements and functionality mostly happens on the @@ -163,13 +168,10 @@ local trace_testruns = false registertracker("otf.testruns", function local forcediscretionaries = false local forcepairadvance = false -- for testing -directives.register("otf.forcediscretionaries",function(v) - forcediscretionaries = v -end) +local repeatablemultiples = context or false -directives.register("otf.forcepairadvance",function(v) - forcepairadvance = v -end) +directives.register("otf.forcediscretionaries", function(v) forcediscretionaries = v end) +directives.register("otf.forcepairadvance", function(v) forcepairadvance = v end) local report_direct = logs.reporter("fonts","otf direct") local report_subchain = logs.reporter("fonts","otf subchain") @@ -195,7 +197,6 @@ local setboth = nuts.setboth local getid = nuts.getid local getstate = nuts.getstate local getsubtype = nuts.getsubtype -local setsubtype = nuts.setsubtype local getchar = nuts.getchar local setchar = nuts.setchar local getdisc = nuts.getdisc @@ -213,39 +214,37 @@ local getglyphdata = nuts.getglyphdata -- their positions because some complex ligatures might need that. For the moment we -- use an x_ prefix because for now generic follows the other approach. -local copy_no_components = nuts.copy_no_components -local copy_only_glyphs = nuts.copy_only_glyphs -local count_components = nuts.count_components -local set_components = nuts.set_components -local get_components = nuts.get_components -local flush_components = nuts.flush_components +local components = nuts.components +local copynocomponents = components.copynocomponents +local copyonlyglyphs = components.copyonlyglyphs +local countcomponents = components.count +local setcomponents = components.set +local getcomponents = components.get +local flushcomponents = components.flush --------------------------------------------------------------------------------------- local ischar = nuts.ischar -local usesfont = nuts.uses_font - -local insert_node_after = nuts.insert_after -local copy_node = nuts.copy -local copy_node_list = nuts.copy_list -local remove_node = nuts.remove -local find_node_tail = nuts.tail -local flush_node_list = nuts.flush_list -local flush_node = nuts.flush_node -local end_of_math = nuts.end_of_math -local start_of_par = nuts.start_of_par +local usesfont = nuts.usesfont + +local insertnodeafter = nuts.insertafter +local copynode = nuts.copy +local copynodelist = nuts.copylist +local removenode = nuts.remove +local findnodetail = nuts.tail +local flushnodelist = nuts.flushlist +local flushnode = nuts.flushnode +local endofmath = nuts.endofmath + +local startofpar = nuts.startofpar local setmetatable = setmetatable local setmetatableindex = table.setmetatableindex local nextnode = nuts.traversers.node ------ zwnj = 0x200C ------ zwj = 0x200D - local nodecodes = nodes.nodecodes local glyphcodes = nodes.glyphcodes -local disccodes = nodes.disccodes local glyph_code = nodecodes.glyph local glue_code = nodecodes.glue @@ -257,8 +256,7 @@ local par_code = nodecodes.par local lefttoright_code = nodes.dirvalues.lefttoright local righttoleft_code = nodes.dirvalues.righttoleft -local discretionarydisc_code = disccodes.discretionary -local ligatureglyph_code = glyphcodes.ligature +local discretionarydisc_code = nodes.disccodes.discretionary local a_noligature = attributes.private("noligature") @@ -317,13 +315,6 @@ local checkstep = (tracers and tracers.steppers.check) or function() local registerstep = (tracers and tracers.steppers.register) or function() end local registermessage = (tracers and tracers.steppers.message) or function() end --- local function checkdisccontent(d) --- local pre, post, replace = getdisc(d) --- if pre then for n in traverse_id(glue_code,pre) do report("pre: %s",nodes.idstostring(pre)) break end end --- if post then for n in traverse_id(glue_code,post) do report("pos: %s",nodes.idstostring(post)) break end end --- if replace then for n in traverse_id(glue_code,replace) do report("rep: %s",nodes.idstostring(replace)) break end end --- end - local function logprocess(...) if trace_steps then registermessage(...) @@ -380,7 +371,7 @@ local function cref(dataset,sequence,index) return "no valid dataset" end local merged = sequence.merged and "merged " or "" - if index then + if index and index > 1 then return formatters["feature %a, type %a, %schain lookup %a, index %a"]( dataset[4],sequence.type,merged,sequence.name,index) else @@ -423,12 +414,12 @@ local function flattendisk(head,disc) local prev, next = getboth(disc) local ishead = head == disc setdisc(disc) - flush_node(disc) + flushnode(disc) if pre then - flush_node_list(pre) + flushnodelist(pre) end if post then - flush_node_list(post) + flushnodelist(post) end if ishead then if replace then @@ -458,7 +449,7 @@ end local function appenddisc(disc,list) local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true) local posthead = list - local replacehead = copy_node_list(list) + local replacehead = copynodelist(list) if post then setlink(posttail,posthead) else @@ -480,16 +471,15 @@ local function markstoligature(head,start,stop,char) local next = getnext(stop) setprev(start) setnext(stop) - local base = copy_no_components(start,copyinjection) + local base = copynocomponents(start,copyinjection) if head == start then head = base end resetinjection(base) setchar(base,char) - setsubtype(base,ligatureglyph_code) - set_components(base,start) + setcomponents(base,start) setlink(prev,base,next) - flush_components(start) + flushcomponents(start) return head, base end end @@ -509,7 +499,7 @@ local no_right_ligature_code = 2 local no_left_kern_code = 4 local no_right_kern_code = 8 -local has_glyph_option = node.direct.has_glyph_option or function(n,c) +local hasglyphoption = function(n,c) if c == no_left_ligature_code or c == no_right_ligature_code then return getattr(n,a_noligature) == 1 else @@ -520,7 +510,7 @@ end -- in lmtx we need to check the components and can be slightly more clever local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfound,hasmarks) -- brr head - if has_glyph_option(start,no_right_ligature_code) then + if hasglyphoption(start,no_right_ligature_code) then return head, start end if start == stop and getchar(start) == char then @@ -533,14 +523,13 @@ local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfou local comp = start setprev(start) setnext(stop) - local base = copy_no_components(start,copyinjection) + local base = copynocomponents(start,copyinjection) if start == head then head = base end resetinjection(base) setchar(base,char) - setsubtype(base,ligatureglyph_code) - set_components(base,comp) + setcomponents(base,comp) setlink(prev,base,next) if not discfound then local deletemarks = not skiphash or hasmarks @@ -554,7 +543,7 @@ local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfou local char = getchar(start) if not marks[char] then baseindex = baseindex + componentindex - componentindex = count_components(start,marks) + componentindex = countcomponents(start,marks) -- we can be more clever here: "not deletemarks or (skiphash and not skiphash[char])" -- and such: elseif not deletemarks then @@ -563,9 +552,9 @@ local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfou if trace_marks then logwarning("%s: keep ligature mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start)) end - local n = copy_node(start) + local n = copynode(start) copyinjection(n,start) -- is this ok ? we position later anyway - head, current = insert_node_after(head,current,n) -- unlikely that mark has components + head, current = insertnodeafter(head,current,n) -- unlikely that mark has components elseif trace_marks then logwarning("%s: delete ligature mark %s",pref(dataset,sequence),gref(char)) end @@ -590,7 +579,7 @@ local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfou break end end - flush_components(components) + flushcomponents(components) else -- discfound ... forget about marks .. probably no scripts that hyphenate and have marks local discprev, discnext = getboth(discfound) @@ -602,8 +591,8 @@ local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfou if not replace then -- looks like we never come here as it's not okay local prev = getprev(base) - -- local comp = get_components(base) -- already set - local copied = copy_only_glyphs(comp) + -- local comp = getcomponents(base) -- already set + local copied = copyonlyglyphs(comp) if pre then setlink(discprev,pre) else @@ -620,7 +609,7 @@ local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfou setlink(prev,discfound,next) setboth(base) -- here components have a pointer so we can't free it! - set_components(base,copied) + setcomponents(base,copied) replace = base if forcediscretionaries then setdisc(discfound,pre,post,replace,discretionarydisc_code) @@ -637,32 +626,69 @@ end local function multiple_glyphs(head,start,multiple,skiphash,what,stop) -- what to do with skiphash matches here local nofmultiples = #multiple if nofmultiples > 0 then + local first = start resetinjection(start) setchar(start,multiple[1]) if nofmultiples > 1 then - local sn = getnext(start) - for k=2,nofmultiples do + -- local sn = getnext(start) + for i=2,nofmultiples do -- untested: -- -- while ignoremarks and marks[getchar(sn)] then -- local sn = getnext(sn) -- end - local n = copy_node(start) -- ignore components + local n = copynode(start) -- ignore components resetinjection(n) - setchar(n,multiple[k]) - insert_node_after(head,start,n) + setchar(n,multiple[i]) + insertnodeafter(head,start,n) start = n end - if what == true then - -- we're ok - elseif what > 1 then - local m = multiple[nofmultiples] - for i=2,what do - local n = copy_node(start) -- ignore components - resetinjection(n) - setchar(n,m) - insert_node_after(head,start,n) - start = n + end + if what ~= true and repeatablemultiples then + -- This is just some experimental code; we might introduce gsub_extensible + -- some day instead. Beware: when we have a feature that mixes alternates and + -- multiples we need to make sure we don't handle the alternate string values + -- here. + local kind = type(what) + local m, f, l + if kind == "string" then + local what, n = string.match(what,"^repeat(.-)[:=](%d+)$") + if what == "middle" then + m = tonumber(n) + elseif what == "first" then + f = tonumber(n) + elseif what == "last" then + l = tonumber(n) + end + elseif kind == "table" then + -- won't happen because currently we don't split these values + m = what.middle + f = what.first + l = what.last + end + if f or m or l then + if m and m > 1 and nofmultiples == 3 then + local middle = getnext(first) + for i=2,m do + local n = copynode(middle) -- ignore components + resetinjection(n) + insertnodeafter(head,first,n) + end + end + if f and f > 1 then + for i=2,f do + local n = copynode(first) -- ignore components + resetinjection(n) + insertnodeafter(head,first,n) + end + end + if l and l > 1 then + for i=2,l do + local n = copynode(start) -- ignore components + resetinjection(n) + insertnodeafter(head,start,n) + start = n + end end end end @@ -768,7 +794,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip while current do local char = ischar(current,currentfont) if char then - local lg = ligature[char] + local lg = not tonumber(ligature) and ligature[char] if lg then stop = current ligature = lg @@ -781,14 +807,14 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip end end if stop then - local lig = ligature.ligature - if lig then + local ligature = tonumber(ligature) or ligature.ligature + if ligature then if trace_ligatures then local stopchar = getchar(stop) - head, start = markstoligature(head,start,stop,lig) + head, start = markstoligature(head,start,stop,ligature) logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start))) else - head, start = markstoligature(head,start,stop,lig) + head, start = markstoligature(head,start,stop,ligature) end return head, start, true, false else @@ -804,7 +830,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip if skiphash and skiphash[char] then current = getnext(current) else - local lg = ligature[char] + local lg = not tonumber(ligature) and ligature[char] if lg then if marks[char] then hasmarks = true @@ -838,20 +864,20 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip local match if replace then local char = ischar(replace,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match = true end end if not match and pre then local char = ischar(pre,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match = true end end if not match and not pre or not replace then local n = getnext(discfound) local char = ischar(n,currentfont) - if char and ligature[char] then + if char and (not tonumber(ligature) and ligature[char]) then match = true end end @@ -861,9 +887,9 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip local prev = getprev(start) if stop then setnext(stop) - local copy = copy_node_list(start) + local copy = copynodelist(start) local tail = stop -- was: getprev(stop) -- Kai: needs checking on your samples - local liat = find_node_tail(copy) + local liat = findnodetail(copy) if pre then setlink(liat,pre) end @@ -874,7 +900,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip replace = start else setnext(start) - local copy = copy_node(start) + local copy = copynode(start) if pre then setlink(copy,pre) end @@ -895,24 +921,26 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip return head, start, true, true end end - local lig = ligature.ligature - if lig then + local ligature = tonumber(ligature) or ligature.ligature + if ligature then if stop then if trace_ligatures then local stopchar = getchar(stop) - -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) - head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) - logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig)) + -- head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks) + head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) + logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(ligature)) + -- we can have a rare case of multiple disc in a lig but that makes no sense language wise but if really + -- needed we could backtrack if we're in a disc node else - -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) - head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) + -- head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks) + head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) end else -- weird but happens (in some arabic font) resetinjection(start) - setchar(start,lig) + setchar(start,ligature) if trace_ligatures then - logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig)) + logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(ligature)) end end return head, start, true, false @@ -924,7 +952,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip end function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection) - if has_glyph_option(start,no_right_kern_code) then + if hasglyphoption(start,no_right_kern_code) then return head, start, false else local startchar = getchar(start) @@ -945,7 +973,7 @@ function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,skiphash, end function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection) - if has_glyph_option(start,no_right_kern_code) then + if hasglyphoption(start,no_right_kern_code) then return head, start, false else local snext = getnext(start) @@ -1055,7 +1083,7 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode, end return head, start, true elseif trace_bugs then - -- onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + -- onetimemessage(currentfont,basechar,"no base anchors") logwarning("%s: mark %s is not anchored to %s",pref(dataset,sequence),gref(markchar),gref(basechar)) end elseif trace_bugs then @@ -1121,7 +1149,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm end elseif trace_bugs then -- logwarning("%s: char %s is missing in font",pref(dataset,sequence),gref(basechar)) - onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + onetimemessage(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),1) @@ -1286,7 +1314,7 @@ as less as needed but that would also make the code even more messy.</p> -- To be done (example needed): what if > 1 steps --- this is messy: do we need this disc checking also in alternaties? +-- this is messy: do we need this disc checking also in alternates? local function reportzerosteps(dataset,sequence) logwarning("%s: no steps",cref(dataset,sequence)) @@ -1322,7 +1350,7 @@ function chainprocs.gsub_remove(head,start,stop,dataset,sequence,currentlookup,r if trace_chains then logprocess("%s: removing character %s",cref(dataset,sequence,chainindex),gref(getchar(start))) end - head, start = remove_node(head,start,true) + head, start = removenode(head,start,true) return head, getprev(start), true end @@ -1489,7 +1517,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup current = getnext(current) -- end else - local lg = ligatures[schar] + local lg = not tonumber(ligatures) and ligatures[schar] if lg then ligatures = lg last = current @@ -1508,7 +1536,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup end end end - local ligature = ligatures.ligature + local ligature = tonumber(ligatures) or ligatures.ligature if ligature then if chainindex then stop = last @@ -1536,7 +1564,7 @@ end function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) -- we actually should check no_left_kern_code with next - if not has_glyph_option(start,no_right_kern_code) then + if not hasglyphoption(start,no_right_kern_code) then local mapping = currentlookup.mapping if mapping == nil then mapping = getmapping(dataset,sequence,currentlookup) @@ -1566,7 +1594,7 @@ end function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) -- todo: injections ? -- we actually should check no_left_kern_code with next - if not has_glyph_option(start,no_right_kern_code) then + if not hasglyphoption(start,no_right_kern_code) then local mapping = currentlookup.mapping if mapping == nil then mapping = getmapping(dataset,sequence,currentlookup) @@ -1861,7 +1889,7 @@ function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup, end end elseif trace_bugs then - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + onetimemessage(currentfont,startchar,"no entry anchors") end break end @@ -1917,7 +1945,7 @@ do if not userkern then -- generic local setkern = nuts.setkern -- not injections.setkern userkern = function(k) - local n = copy_node(thekern) + local n = copynode(thekern) setkern(n,k) return n end @@ -1934,13 +1962,13 @@ local function checked(head) if next then setlink(kern,next) end - flush_node(current) + flushnode(current) head = kern current = next else local prev, next = getboth(current) setlink(prev,kern,next) - flush_node(current) + flushnode(current) current = next end else @@ -1959,12 +1987,11 @@ end local noflags = { false, false, false, false } -local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) +local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck,where) local size = ck[5] - ck[4] + 1 local chainlookups = ck[6] local done = false - -- current match if chainlookups then -- Lookups can be like { 1, false, 3 } or { false, 2 } or basically anything and @@ -1975,21 +2002,30 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) -- if nofchainlookups > size then -- -- bad rules -- end - local chainlookup = chainlookups[1] - for j=1,#chainlookup do - local chainstep = chainlookup[j] - local chainkind = chainstep.type - local chainproc = chainprocs[chainkind] - if chainproc then - local ok - head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) - if ok then - done = true + if chainlookup then + for j=1,#chainlookup do + local chainstep = chainlookup[j] + if chainstep then + local chainkind = chainstep.type + local chainproc = chainprocs[chainkind] + if chainproc then + local ok + -- HH: chainindex 1 added here (for KAI to check too), there are weird ligatures e.g. + -- char + mark -> char where mark has to disappear + head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,1) + if ok then + done = true + end + else + logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind) + end + else + logprocess("%s: has an issue (1)",cref(dataset,sequence)) end - else - logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind) end + else + -- whatever end else @@ -2030,23 +2066,28 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) if chainlookup then for j=1,#chainlookup do local chainstep = chainlookup[j] - local chainkind = chainstep.type - local chainproc = chainprocs[chainkind] - if chainproc then - local ok, n - head, start, ok, n = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i) - -- messy since last can be changed ! - if ok then - done = true - if n and n > 1 and i + n > nofchainlookups then - -- this is a safeguard, we just ignore the rest of the lookups - i = size -- prevents an advance - break + if chainstep then + local chainkind = chainstep.type + local chainproc = chainprocs[chainkind] + if chainproc then + local ok, n + head, start, ok, n = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i) + -- messy since last can be changed ! + if ok then + done = true + if n and n > 1 and i + n > nofchainlookups then + -- this is a safeguard, we just ignore the rest of the lookups + i = size -- prevents an advance + break + end end + else + -- actually an error + logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind) end else -- actually an error - logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind) + logprocess("%s: has an issue (2)",cref(dataset,sequence)) end end else @@ -2074,7 +2115,7 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) else done = true if trace_contexts then - logprocess("%s: skipping match",cref(dataset,sequence)) + logprocess("%s: skipping match @ %i",cref(dataset,sequence),where) end end end @@ -2104,6 +2145,8 @@ local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) local last = start local prev = getprev(start) local hasglue = false + local useddisc = nil -- new 2022-09-25 + local usedstart = start -- new 2022-09-25 -- fishy: so we can overflow and then go on in the sweep? -- todo : id can also be glue_code as we checked spaces @@ -2174,7 +2217,7 @@ local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) tail = prev setprev(current,sweepnode) else - tail = find_node_tail(head) + tail = findnodetail(head) end setnext(sweepnode,current) setprev(head) @@ -2265,15 +2308,14 @@ local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) end end end + local done = false if lookaheaddisc then - local cf = start local cl = getprev(lookaheaddisc) local cprev = getprev(start) local insertedmarks = 0 - while cprev do local char = ischar(cf,currentfont) if char and marks[char] then @@ -2292,13 +2334,13 @@ local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) head = lookaheaddisc end local pre, post, replace = getdisc(lookaheaddisc) - local new = copy_node_list(cf) -- br, how often does that happen + local new = copynodelist(cf) -- br, how often does that happen local cnew = new if pre then - setlink(find_node_tail(cf),pre) + setlink(findnodetail(cf),pre) end if replace then - local tail = find_node_tail(new) + local tail = findnodetail(new) setlink(tail,replace) end for i=1,insertedmarks do @@ -2312,14 +2354,14 @@ local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) end if not notmatchpre[lookaheaddisc] then local ok = false - cf, start, ok = chainrun(cf,start,cl,dataset,sequence,rlmode,skiphash,ck) + cf, start, ok = chainrun(cf,start,cl,dataset,sequence,rlmode,skiphash,ck,1) if ok then done = true end end if not notmatchreplace[lookaheaddisc] then local ok = false - new, cnew, ok = chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck) + new, cnew, ok = chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck,2) if ok then done = true end @@ -2330,16 +2372,14 @@ local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) setdisc(lookaheaddisc,cf,post,new) end start = getprev(lookaheaddisc) + useddisc = lookaheaddisc -- new 2022-09-25 sweephead[cf] = getnext(clast) or false sweephead[new] = getnext(cl) or false - elseif backtrackdisc then - local cf = getnext(backtrackdisc) local cl = start local cnext = getnext(start) local insertedmarks = 0 - while cnext do local char = ischar(cnext,currentfont) if char and marks[char] then @@ -2354,8 +2394,8 @@ local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) setprev(cf) setnext(cl) local pre, post, replace, pretail, posttail, replacetail = getdisc(backtrackdisc,true) - local new = copy_node_list(cf) - local cnew = find_node_tail(new) + local new = copynodelist(cf) + local cnew = findnodetail(new) for i=1,insertedmarks do cnew = getprev(cnew) end @@ -2365,14 +2405,14 @@ local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) end if not notmatchpost[backtrackdisc] then local ok = false - cf, start, ok = chainrun(cf,start,last,dataset,sequence,rlmode,skiphash,ck) + cf, start, ok = chainrun(cf,start,last,dataset,sequence,rlmode,skiphash,ck,3) if ok then done = true end end if not notmatchreplace[backtrackdisc] then local ok = false - new, cnew, ok = chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck) + new, cnew, ok = chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck,4) if ok then done = true end @@ -2393,33 +2433,57 @@ local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) setdisc(backtrackdisc,pre,post,replace) end start = getprev(backtrackdisc) + useddisc = backtrackdisc -- new 2022-09-25 sweephead[post] = getnext(clast) or false sweephead[replace] = getnext(last) or false - else local ok = false - head, start, ok = chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) + head, start, ok = chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck,5) if ok then done = true end end - - return head, start, done + if useddisc and start ~= usedstart then -- make this option per font -- new 2022-09-25 + start = getnext(start) -- new 2022-09-25 + end -- new 2022-09-25 + return head, start, done, useddisc -- new 2022-09-25 end -local function chaintrac(head,start,dataset,sequence,rlmode,skiphash,ck,match,discseen,sweepnode) - local rule = ck[1] - local lookuptype = ck[8] or ck[2] - local nofseq = #ck[3] - local first = ck[4] - local last = ck[5] - local char = getchar(start) - logwarning("%s: rule %s %s at char %s for (%s,%s,%s) chars, lookuptype %a, %sdisc seen, %ssweeping", - cref(dataset,sequence),rule,match and "matches" or "nomatch", - gref(char),first-1,last-first+1,nofseq-last,lookuptype, - discseen and "" or "no ", sweepnode and "" or "not ") +local chaintrac do + + local level = 0 + local last = { } + + chaintrac = function(head,start,dataset,sequence,rlmode,skiphash,ck,match,discseen,sweepnode) + if dataset then + level = level + 1 + last[level] = start + local rule = ck[1] + local lookuptype = ck[8] or ck[2] + local nofseq = #ck[3] -- ck[3].n + local first = ck[4] + local last = ck[5] + local char = getchar(start) + logwarning("+ %i : %s: rule %s %s at char %s for (%s,%s,%s) chars, lookuptype %a, %sdisc seen, %ssweeping", + level,cref(dataset,sequence),rule,match and "matches" or "nomatch", + gref(char),first-1,last-first+1,nofseq-last,lookuptype, + discseen and "" or "no ", sweepnode and "" or "not ") + else + -- (start,done) + local what = start and "done" or "continue" + local where = head == last[level] and "same" or "different" + local char = getchar(head) + if char then + logwarning("- %i : %s at char %s, %s node",level,what,gref(char),where) + else + logwarning("- %i : %s, %s node",level,what,where) + end + level = level - 1 + end + end + end -- The next one is quite optimized but still somewhat slow, fonts like ebgaramond @@ -2433,6 +2497,9 @@ end -- the previous disc .. such be it (<before><disc><current=fl><after> with only f done) local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,skiphash) + if not contexts then + return head, start, false + end -- optimizing for rlmode gains nothing local sweepnode = sweepnode local sweeptype = sweeptype @@ -2473,160 +2540,291 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s for k=1,nofcontexts do -- does this disc mess work well with n > 1 - local ck = contexts[k] - local seq = ck[3] - local f = ck[4] -- first current + local ck = contexts[k] + local seq = ck[3] + local f = ck[4] -- first current + local last = start if not startchar or not seq[f][startchar] then -- report("no hit in %a at %i of %i contexts",sequence.type,k,nofcontexts) goto next end - local s = seq.n -- or #seq - local l = ck[5] -- last current - local current = start - local last = start - + local s = seq.n -- or #seq if s == 1 then - goto next - end + -- bit weird case: why use a chain, but it is a hit + else + local l = ck[5] -- last current + local current = start + -- local last = start - -- current match + -- current match - if l > f then - -- before/current/after | before/current | current/after - local discfound -- = nil - local n = f + 1 - last = startnext -- the second in current (first already matched) - while n <= l do - if postreplace and not last then - last = getnext(sweepnode) - sweeptype = nil - end - if last then - local char, id = ischar(last,currentfont) - if char then - if skiphash and skiphash[char] then - skipped = true - if trace_skips then - show_skip(dataset,sequence,char,ck,classes[char]) - end - last = getnext(last) - elseif seq[n][char] then - if n < l then + if l > f then + -- before/current/after | before/current | current/after + local discfound -- = nil + local n = f + 1 + last = startnext -- the second in current (first already matched) + while n <= l do + if postreplace and not last then + last = getnext(sweepnode) + sweeptype = nil + end + if last then + local char, id = ischar(last,currentfont) + if char then + if skiphash and skiphash[char] then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end last = getnext(last) - end - n = n + 1 - elseif discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then - goto next + elseif seq[n][char] then + if n < l then + last = getnext(last) + end + n = n + 1 + elseif discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + else + break + end else - break - end - else - goto next - end - elseif char == false then - if discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then goto next + end + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next + else + break + end else - break + goto next end - else - goto next - end - elseif id == disc_code then - -- elseif id == disc_code and (not discs or discs[last]) then - discseen = true - discfound = last - notmatchpre[last] = nil - notmatchpost[last] = true - notmatchreplace[last] = nil - local pre, post, replace = getdisc(last) - if pre then - local n = n - while pre do - if seq[n][getchar(pre)] then - n = n + 1 - if n > l then + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[last]) then + discseen = true + discfound = last + notmatchpre[last] = nil + notmatchpost[last] = true + notmatchreplace[last] = nil + local pre, post, replace = getdisc(last) + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + if n > l then + break + end + pre = getnext(pre) + else + notmatchpre[last] = true break end - pre = getnext(pre) - else + end + if n <= l then notmatchpre[last] = true - break end - end - if n <= l then + else notmatchpre[last] = true end + if replace then + -- so far we never entered this branch + while replace do + if seq[n][getchar(replace)] then + n = n + 1 + if n > l then + break + end + replace = getnext(replace) + else + notmatchreplace[last] = true + if notmatchpre[last] then + goto next + else + break + end + end + end + -- why here again + if notmatchpre[last] then + goto next + end + end + -- maybe only if match + last = getnext(last) else - notmatchpre[last] = true + goto next end - if replace then - -- so far we never entered this branch - while replace do - if seq[n][getchar(replace)] then - n = n + 1 - if n > l then - break + else + goto next + end + end + end + + -- before + + if f > 1 then + -- if startprev then -- new 2022-09-25 + local prev = startprev + if prereplace and prev == checkdisc then + prev = getprev(sweepnode) + end + if prev then + local discfound -- = nil + local n = f - 1 + while n >= 1 do + if prev then + local char, id = ischar(prev,currentfont) + if char then + if skiphash and skiphash[char] then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + prev = getprev(prev) + elseif seq[n][char] then + if n > 1 then + prev = getprev(prev) + end + n = n - 1 + elseif discfound then + notmatchreplace[discfound] = true + if notmatchpost[discfound] then + goto next + else + break + end + else + goto next end - replace = getnext(replace) - else - notmatchreplace[last] = true - if notmatchpre[last] then + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpost[discfound] then + goto next + end + else goto next + end + break + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[prev]) then + -- the special case: f i where i becomes dottless i .. + discseen = true + discfound = prev + notmatchpre[prev] = true + notmatchpost[prev] = nil + notmatchreplace[prev] = nil + local pre, post, replace, pretail, posttail, replacetail = getdisc(prev,true) + -- weird test: needs checking + if pre ~= start and post ~= start and replace ~= start then + if post then + local n = n + while posttail do + if seq[n][getchar(posttail)] then + n = n - 1 + if posttail == post or n < 1 then + break + else + posttail = getprev(posttail) + end + else + notmatchpost[prev] = true + break + end + end + if n >= 1 then + notmatchpost[prev] = true + end + else + notmatchpost[prev] = true + end + if replace then + -- we seldom enter this branch (e.g. on brill efficient) + while replacetail do + if seq[n][getchar(replacetail)] then + n = n - 1 + if replacetail == replace or n < 1 then + break + else + replacetail = getprev(replacetail) + end + else + notmatchreplace[prev] = true + if notmatchpost[prev] then + goto next + else + break + end + end + end + else + -- notmatchreplace[prev] = true -- not according to Kai + end + end + prev = getprev(prev) + -- elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then + -- elseif seq[n][32] and spaces[prev] then + -- n = n - 1 + -- prev = getprev(prev) + elseif id == glue_code then + local sn = seq[n] + if (sn[32] and spaces[prev]) or sn[0xFFFC] then + n = n - 1 + prev = getprev(prev) else - break + goto next end + elseif seq[n][0xFFFC] then + n = n - 1 + prev = getprev(prev) + else + goto next end - end - -- why here again - if notmatchpre[last] then + else goto next end end - -- maybe only if match - last = getnext(last) else goto next end - else - goto next - end + -- else -- new 2022-09-25 + -- goto next -- new 2022-09-25 + -- end -- new 2022-09-25 end - end - -- before + -- after - if f > 1 then - if startprev then - local prev = startprev - if prereplace and prev == checkdisc then - prev = getprev(sweepnode) + if s > l then + local current = last and getnext(last) + if not current and postreplace then + current = getnext(sweepnode) end - if prev then + if current then local discfound -- = nil - local n = f - 1 - while n >= 1 do - if prev then - local char, id = ischar(prev,currentfont) + local n = l + 1 + while n <= s do + if current then + local char, id = ischar(current,currentfont) if char then if skiphash and skiphash[char] then skipped = true if trace_skips then show_skip(dataset,sequence,char,ck,classes[char]) end - prev = getprev(prev) + current = getnext(current) -- was absent elseif seq[n][char] then - if n > 1 then - prev = getprev(prev) + if n < s then -- new test + current = getnext(current) -- was absent end - n = n - 1 + n = n + 1 elseif discfound then notmatchreplace[discfound] = true - if notmatchpost[discfound] then + if notmatchpre[discfound] then goto next else break @@ -2637,237 +2835,108 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s elseif char == false then if discfound then notmatchreplace[discfound] = true - if notmatchpost[discfound] then + if notmatchpre[discfound] then goto next + else + break end else goto next end - break elseif id == disc_code then - -- elseif id == disc_code and (not discs or discs[prev]) then - -- the special case: f i where i becomes dottless i .. - discseen = true - discfound = prev - notmatchpre[prev] = true - notmatchpost[prev] = nil - notmatchreplace[prev] = nil - local pre, post, replace, pretail, posttail, replacetail = getdisc(prev,true) - -- weird test: needs checking - if pre ~= start and post ~= start and replace ~= start then - if post then - local n = n - while posttail do - if seq[n][getchar(posttail)] then - n = n - 1 - if posttail == post or n < 1 then - break - else - posttail = getprev(posttail) - end - else - notmatchpost[prev] = true + -- elseif id == disc_code and (not discs or discs[current]) then + discseen = true + discfound = current + notmatchpre[current] = nil + notmatchpost[current] = true + notmatchreplace[current] = nil + local pre, post, replace = getdisc(current) + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + if n > s then break + else + pre = getnext(pre) end + else + notmatchpre[current] = true + break end - if n >= 1 then - notmatchpost[prev] = true - end - else - notmatchpost[prev] = true end - if replace then - -- we seldom enter this branch (e.g. on brill efficient) - while replacetail do - if seq[n][getchar(replacetail)] then - n = n - 1 - if replacetail == replace or n < 1 then - break - else - replacetail = getprev(replacetail) - end + if n <= s then + notmatchpre[current] = true + end + else + notmatchpre[current] = true + end + if replace then + -- so far we never entered this branch + while replace do + if seq[n][getchar(replace)] then + n = n + 1 + if n > s then + break else - notmatchreplace[prev] = true - if notmatchpost[prev] then - goto next - else - break - end + replace = getnext(replace) + end + else + notmatchreplace[current] = true + if notmatchpre[current] then + goto next + else + break end end - else - -- notmatchreplace[prev] = true -- not according to Kai end + else + -- notmatchreplace[current] = true -- not according to Kai end - prev = getprev(prev) - -- elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then - -- elseif seq[n][32] and spaces[prev] then - -- n = n - 1 - -- prev = getprev(prev) + current = getnext(current) elseif id == glue_code then local sn = seq[n] - if (sn[32] and spaces[prev]) or sn[0xFFFC] then - n = n - 1 - prev = getprev(prev) + if (sn[32] and spaces[current]) or sn[0xFFFC] then + n = n + 1 + current = getnext(current) else goto next end elseif seq[n][0xFFFC] then - n = n - 1 - prev = getprev(prev) - else - goto next - end - else - goto next - end - end - else - goto next - end - else - goto next - end - end - - -- after - - if s > l then - local current = last and getnext(last) - if not current and postreplace then - current = getnext(sweepnode) - end - if current then - local discfound -- = nil - local n = l + 1 - while n <= s do - if current then - local char, id = ischar(current,currentfont) - if char then - if skiphash and skiphash[char] then - skipped = true - if trace_skips then - show_skip(dataset,sequence,char,ck,classes[char]) - end - current = getnext(current) -- was absent - elseif seq[n][char] then - if n < s then -- new test - current = getnext(current) -- was absent - end - n = n + 1 - elseif discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then - goto next - else - break - end - else - goto next - end - elseif char == false then - if discfound then - notmatchreplace[discfound] = true - if notmatchpre[discfound] then - goto next - else - break - end - else - goto next - end - elseif id == disc_code then - -- elseif id == disc_code and (not discs or discs[current]) then - discseen = true - discfound = current - notmatchpre[current] = nil - notmatchpost[current] = true - notmatchreplace[current] = nil - local pre, post, replace = getdisc(current) - if pre then - local n = n - while pre do - if seq[n][getchar(pre)] then - n = n + 1 - if n > s then - break - else - pre = getnext(pre) - end - else - notmatchpre[current] = true - break - end - end - if n <= s then - notmatchpre[current] = true - end - else - notmatchpre[current] = true - end - if replace then - -- so far we never entered this branch - while replace do - if seq[n][getchar(replace)] then - n = n + 1 - if n > s then - break - else - replace = getnext(replace) - end - else - notmatchreplace[current] = true - if notmatchpre[current] then - goto next - else - break - end - end - end - else - -- notmatchreplace[current] = true -- not according to Kai - end - current = getnext(current) - elseif id == glue_code then - local sn = seq[n] - if (sn[32] and spaces[current]) or sn[0xFFFC] then n = n + 1 current = getnext(current) else goto next end - elseif seq[n][0xFFFC] then - n = n + 1 - current = getnext(current) else goto next end - else - goto next end + else + goto next end - else - goto next end end - if trace_contexts then chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,true,discseen,sweepnode) end if discseen or sweepnode then + -- When we process a disc we can collapse and therefore we backtrack one node (start) and + -- reprocess. This is needed because there might be more in the collapsed list. head, start, done = chaindisk(head,start,dataset,sequence,rlmode,skipped and skiphash,ck) else - head, start, done = chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck) + head, start, done = chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck,6) + end + if trace_contexts then + chaintrac(start,done) end if done then break -- else -- next context end - ::next:: - -- if trace_chains then - -- chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,false,discseen,sweepnode) - -- end + ::next:: end if discseen then notmatchpre = { } @@ -2886,21 +2955,46 @@ handlers.gsub_reversecontextchain = handle_contextchain handlers.gpos_contextchain = handle_contextchain handlers.gpos_context = handle_contextchain --- this needs testing +-- local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash) +-- local steps = currentlookup.steps +-- local nofsteps = currentlookup.nofsteps +-- if nofsteps > 1 then +-- reportmoresteps(dataset,sequence) +-- end +-- -- probably wrong +-- local l = steps[1].coverage[getchar(start)] +-- if l then +-- return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash) +-- else +-- return head, start, false +-- end +-- end + +-- new 2022-09-25 local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash) local steps = currentlookup.steps local nofsteps = currentlookup.nofsteps - if nofsteps > 1 then - reportmoresteps(dataset,sequence) - end - -- probably wrong - local l = steps[1].coverage[getchar(start)] - if l then - return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash) + local char = getchar(start) + if nofsteps == 1 then + local s = steps[1] + local l = s.coverage[char] + if l then + return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash) + end else - return head, start, false + for i=1,nofsteps do + local s = steps[i] + local l = s.coverage[char] + if l then + local h, s, d = handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash) + if d then + return h, s, d + end + end + end end + return head, start, false end chainprocs.gsub_context = chained_contextchain @@ -3039,7 +3133,7 @@ do -- overcome local limit end -- Functions like kernrun, comprun etc evolved over time and in the end look rather --- complex. It's a bit of a compromis between extensive copying and creating subruns. +-- complex. It's a bit of a compromise between extensive copying and creating subruns. -- The logic has been improved a lot by Kai and Ivo who use complex fonts which -- really helped to identify border cases on the one hand and get insight in the diverse -- ways fonts implement features (not always that consistent and efficient). At the same @@ -3146,7 +3240,8 @@ local function kernrun(disc,k_run,font,attr,...) if done and trace_testruns then report_disc("done",disc) end - return nextstart, done + -- return nextstart, done + return nextstart end -- fonts like ebgaramond do ligatures this way (less efficient than e.g. dejavu which @@ -3199,12 +3294,19 @@ local function comprun(disc,c_run,...) -- vararg faster than the whole list setdisc(disc,pre,post,replace) end -- - return getnext(disc), renewed + -- return getnext(disc), renewed + return getnext(disc) end -- if we can hyphenate in a lig then unlikely a lig so we -- could have a option here to ignore lig +local test_flatten_start = 2 -- must start at 2 according to Kai + +directives.register("otf.testrun.forceflatten", function(v) + test_flatten_start = v and 1 or 2 +end) + local function testrun(disc,t_run,c_run,...) if trace_testruns then report_disc("test",disc) @@ -3233,7 +3335,7 @@ local function testrun(disc,t_run,c_run,...) local d = d_replace > d_post and d_replace or d_post local head = getnext(disc) -- is: next local tail = head - for i=2,d do -- must start at 2 according to Kai + for i=test_flatten_start,d do local nx = getnext(tail) local id = getid(nx) if id == disc_code then @@ -3248,7 +3350,7 @@ local function testrun(disc,t_run,c_run,...) next = getnext(tail) setnext(tail) setprev(head) - local new = copy_node_list(head) + local new = copynodelist(head) if posttail then setlink(posttail,head) else @@ -3321,7 +3423,8 @@ local function testrun(disc,t_run,c_run,...) end end -- next can have changed (copied list) - return getnext(disc), renewed + -- return getnext(disc), renewed + return getnext(disc) end -- 1{2{\oldstyle\discretionary{3}{4}{5}}6}7\par @@ -3414,7 +3517,7 @@ local function t_run_single(start,stop,font,attr,lookupcache) while s do local char = ischar(s,font) if char then - local lg = lookupmatch[char] + local lg = not tonumber(lookupmatch) and lookupmatch[char] if lg then if sstop then d = 1 @@ -3444,7 +3547,7 @@ local function t_run_single(start,stop,font,attr,lookupcache) break end end - if l and l.ligature then -- so we test for ligature + if l and (tonumber(l) or l.ligature) then -- so we test for ligature lastd = d end -- why not: if not l then break elseif l.ligature then return d end @@ -3585,7 +3688,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) while s do local char = ischar(s) if char then - local lg = lookupmatch[char] + local lg = not tonumber(lookupmatch) and lookupmatch[char] if lg then if sstop then d = 1 @@ -3615,7 +3718,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps) break end end - if l and l.ligature then + if l and (tonumber(l) or l.ligature) then lastd = d end end @@ -3795,7 +3898,7 @@ do local initialrl = 0 - if getid(head) == par_code and start_of_par(head) then + if getid(head) == par_code and startofpar(head) then initialrl = pardirstate(head) elseif direction == righttoleft_code then initialrl = -1 @@ -3844,7 +3947,7 @@ do -- the before and after lists in the loader. But first I need to see a font that uses multiple -- matches. -- - local start = find_node_tail(head) + local start = findnodetail(head) local rlmode = 0 -- how important is this .. do we need to check for dir? local merged = steps.merged while start do @@ -3930,26 +4033,22 @@ do 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,skiphash,handler) + start = kernrun(start,k_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) elseif forcetestrun then - start, ok = testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + start = testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) else - start, ok = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + start = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) end - -- if ok then - -- done = true - -- end else start = getnext(start) end elseif id == math_code then - start = getnext(end_of_math(start)) + start = getnext(endofmath(start)) elseif id == dir_code then topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode) start = getnext(start) - -- elseif id == par_code and start_of_par(start) then + -- elseif id == par_code and startofpar(start) then -- rlparmode, rlmode = pardirstate(start) -- start = getnext(start) else @@ -4014,26 +4113,22 @@ do 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_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + start = kernrun(start,k_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) elseif forcetestrun then - start, ok = testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + start = testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) else - start, ok = comprun(start,c_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + start = comprun(start,c_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) end - -- if ok then - -- done = true - -- end else start = getnext(start) end elseif id == math_code then - start = getnext(end_of_math(start)) + start = getnext(endofmath(start)) elseif id == dir_code then topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode) start = getnext(start) - -- elseif id == par_code and start_of_par(start) then + -- elseif id == par_code and startofpar(start) then -- rlparmode, rlmode = pardirstate(start) -- start = getnext(start) else @@ -4140,13 +4235,13 @@ do -- a different font|state or glue (happens often) start = getnext(start) elseif id == math_code then - start = getnext(end_of_math(start)) + start = getnext(endofmath(start)) elseif id == dir_code then topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode) start = getnext(start) - -- elseif id == par_code and start_of_par(start) then + -- elseif id == par_code and startofpar(start) then -- rlparmode, rlmode = pardirstate(start) - -- start = getnext(start) + -- start = nxt else start = getnext(start) end @@ -4161,49 +4256,60 @@ end -- so far -local plugins = { } -otf.plugins = plugins +do -local report = logs.reporter("fonts") + local plugins = { } + otf.plugins = plugins -function otf.registerplugin(name,f) - if type(name) == "string" and type(f) == "function" then - plugins[name] = { name, f } - report() - report("plugin %a has been loaded, please be aware of possible side effects",name) - report() - if logs.pushtarget then - logs.pushtarget("log") - end - report("Plugins are not officially supported unless stated otherwise. This is because") - report("they bypass the regular font handling and therefore some features in ConTeXt") - report("(especially those related to fonts) might not work as expected or might not work") - report("at all. Some plugins are for testing and development only and might change") - report("whenever we feel the need for it.") - report() - if logs.poptarget then - logs.poptarget() + local report = logs.reporter("fonts") + local warned = false + local okay = { text = true } + + function otf.registerplugin(name,f) + if type(name) == "string" and type(f) == "function" then + plugins[name] = { name, f } + if okay[name] then + -- no warning (e.g. the diagnostic text plugin) + else + report("plugin %a has been loaded, please be aware of possible side effects",name) + if not warned then + if logs.pushtarget then + logs.pushtarget("log") + end + report("Plugins are not officially supported unless stated otherwise. This is because") + report("they bypass the regular font handling and therefore some features in ConTeXt") + report("(especially those related to fonts) might not work as expected or might not work") + report("at all. Some plugins are for testing and development only and might change") + report("whenever we feel the need for it.") + report() + if logs.poptarget then + logs.poptarget() + end + warned = true + end + end end end -end -function otf.plugininitializer(tfmdata,value) - if type(value) == "string" then - tfmdata.shared.plugin = plugins[value] + function otf.plugininitializer(tfmdata,value) + if type(value) == "string" then + tfmdata.shared.plugin = plugins[value] + end end -end -function otf.pluginprocessor(head,font,attr,direction) -- n - local s = fontdata[font].shared - local p = s and s.plugin - if p then - if trace_plugins then - report_process("applying plugin %a",p[1]) + function otf.pluginprocessor(head,font,dynamic,direction) -- n + local s = fontdata[font].shared + local p = s and s.plugin + if p then + if trace_plugins then + report_process("applying plugin %a",p[1]) + end + return p[2](head,font,dynamic,direction) + else + return head, false end - return p[2](head,font,attr,direction) - else - return head, false end + end function otf.featuresinitializer(tfmdata,value) @@ -4225,8 +4331,8 @@ registerotffeature { } } --- Moved here (up) a bit. This doesn't really belong in generic so it will --- move to a context module some day. +-- Moved here (up) a bit. This doesn't really belong in generic so it will move to a +-- context module some day. local function markinitializer(tfmdata,value) local properties = tfmdata.properties |