diff options
author | Context Git Mirror Bot <phg42.2a@gmail.com> | 2015-09-01 11:15:08 +0200 |
---|---|---|
committer | Context Git Mirror Bot <phg42.2a@gmail.com> | 2015-09-01 11:15:08 +0200 |
commit | f4508b5b95b4fc2b3ed59aaa28a8dfc1a667360a (patch) | |
tree | 74d5ca2ad226b4b8fa07c512f433c11b7b9acaaf /tex/context/base | |
parent | db76d739a1e8dc1faa0ba194d4d4f2d6081e9ae7 (diff) | |
download | context-f4508b5b95b4fc2b3ed59aaa28a8dfc1a667360a.tar.gz |
2015-09-01 11:12:00
Diffstat (limited to 'tex/context/base')
-rw-r--r-- | tex/context/base/cont-new.mkiv | 2 | ||||
-rw-r--r-- | tex/context/base/context-version.pdf | bin | 4205 -> 4181 bytes | |||
-rw-r--r-- | tex/context/base/context.mkiv | 2 | ||||
-rw-r--r-- | tex/context/base/font-dsp.lua | 6 | ||||
-rw-r--r-- | tex/context/base/font-otd.lua | 2 | ||||
-rw-r--r-- | tex/context/base/font-otn.lua | 1404 | ||||
-rw-r--r-- | tex/context/base/font-ots.lua | 1243 | ||||
-rw-r--r-- | tex/context/base/lang-dis.lua | 152 | ||||
-rw-r--r-- | tex/context/base/m-newotf.mkiv | 2 | ||||
-rw-r--r-- | tex/context/base/node-nut.lua | 103 | ||||
-rw-r--r-- | tex/context/base/status-files.pdf | bin | 24455 -> 24498 bytes | |||
-rw-r--r-- | tex/context/base/status-lua.pdf | bin | 255169 -> 255952 bytes | |||
-rw-r--r-- | tex/context/base/util-fil.lua | 4 |
13 files changed, 2025 insertions, 895 deletions
diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv index 86f9d7473..fb713cf57 100644 --- a/tex/context/base/cont-new.mkiv +++ b/tex/context/base/cont-new.mkiv @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2015.08.30 23:03} +\newcontextversion{2015.09.01 11:10} %D This file is loaded at runtime, thereby providing an excellent place for %D hacks, patches, extensions and new features. diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf Binary files differindex 6625867b0..b2d52084f 100644 --- a/tex/context/base/context-version.pdf +++ b/tex/context/base/context-version.pdf diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv index f013626e6..31e02d6e5 100644 --- a/tex/context/base/context.mkiv +++ b/tex/context/base/context.mkiv @@ -39,7 +39,7 @@ %D up and the dependencies are more consistent. \edef\contextformat {\jobname} -\edef\contextversion{2015.08.30 23:03} +\edef\contextversion{2015.09.01 11:10} \edef\contextkind {beta} %D For those who want to use this: diff --git a/tex/context/base/font-dsp.lua b/tex/context/base/font-dsp.lua index a1a89dd3b..8cdd645c8 100644 --- a/tex/context/base/font-dsp.lua +++ b/tex/context/base/font-dsp.lua @@ -1419,7 +1419,7 @@ do end lookups[lookupid] = { type = lookuptype, - chain = chaindirections[lookuptype] or nil, + -- chain = chaindirections[lookuptype] or nil, flags = lookupflags, name = lookupid, subtables = subtables, @@ -1475,6 +1475,7 @@ do local nofsubtables = #subtables local order = lookup.order local flags = lookup.flags + -- local chain = lookup.chain local markclass = lookup.markclass if nofsubtables > 0 then local steps = { } @@ -1537,6 +1538,7 @@ do type = lookuptype, markclass = markclass or nil, flags = flags, + -- chain = chain, order = order, features = features, } @@ -1553,6 +1555,7 @@ do type = lookuptype, markclass = markclass or nil, flags = flags, + -- chain = chain, } sublookuplist[nofsublookups] = l sublookuphash[lookupid] = nofsublookups @@ -1616,6 +1619,7 @@ do type = d.lookuptype, markclass = d.markclass or nil, flags = d.flags, + -- chain = d.chain, } sublookuplist[nofsublookups] = h sublookuphash[lookupid] = nofsublookups diff --git a/tex/context/base/font-otd.lua b/tex/context/base/font-otd.lua index ff1b471fc..a2354a0cc 100644 --- a/tex/context/base/font-otd.lua +++ b/tex/context/base/font-otd.lua @@ -178,7 +178,7 @@ local function initialize(sequence,script,language,s_enabled,a_enabled,font,attr "font %s, dynamic %a (%a), feature %a, script %a, language %a, lookup %a, value %a", font,attr or 0,dynamic,kind,script,language,sequence.name,valid) end - ra[#ra+1] = { valid, attribute, sequence.chain or 0, kind, sequence } + ra[#ra+1] = { valid, attribute, sequence, kind } end end end diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua index 99e52a3b0..9a85dcf96 100644 --- a/tex/context/base/font-otn.lua +++ b/tex/context/base/font-otn.lua @@ -1,4 +1,4 @@ -if not modules then modules = { } end modules ['font-otn'] = { +if not modules then modules = { } end modules ['font-ots'] = { -- sequences version = 1.001, comment = "companion to font-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", @@ -6,9 +6,6 @@ if not modules then modules = { } end modules ['font-otn'] = { license = "see context related readme files", } --- todo: looks like we have a leak somewhere (probably in ligatures) --- todo: copy attributes to disc - -- this is a context version which can contain experimental code, but when we -- have serious patches we also need to change the other two font-otn files @@ -83,9 +80,12 @@ is currently acceptable. Not all functions are implemented yet, often because I lack the fonts for testing. Many scripts are not yet supported either, but I will look into them as soon as <l n='context'/> users ask for it.</p> -<p>Because there are different interpretations possible, I will extend the code -with more (configureable) variants. I can also add hooks for users so that they can -write their own extensions.</p> +<p>The specification leaves room for interpretation. In case of doubt the microsoft +implementation is the reference as it is the most complete one. As they deal with +lots of scripts and fonts, Kai and Ivo did a lot of testing of the generic code and +their suggestions help improve the code. I'm aware that not all border cases can be +taken care of, unless we accept excessive runtime, and even then the interference +with other mechanisms (like hyphenation) are not trivial.</p> <p>Glyphs are indexed not by unicode but in their own way. This is because there is no relationship with unicode at all, apart from the fact that a font might cover certain @@ -112,12 +112,12 @@ when there's a fix in the <l n='fontforge'/> library or <l n='lua'/> code that results in different tables.</p> --ldx]]-- --- action handler chainproc chainmore comment +-- action handler chainproc -- --- gsub_single ok ok ok --- gsub_multiple ok ok not implemented yet --- gsub_alternate ok ok not implemented yet --- gsub_ligature ok ok ok +-- gsub_single ok ok +-- gsub_multiple ok ok +-- gsub_alternate ok ok +-- gsub_ligature ok ok -- gsub_context ok -- -- gsub_contextchain ok -- -- gsub_reversecontextchain ok -- @@ -182,13 +182,11 @@ local trace_discruns = false registertracker("otf.discruns", function(v local trace_compruns = false registertracker("otf.compruns", function(v) trace_compruns = v end) local quit_on_no_replacement = true -- maybe per font -local check_discretionaries = true -- "trace" local zwnjruns = true registerdirective("otf.zwnjruns", function(v) zwnjruns = v end) registerdirective("otf.chain.quitonnoreplacement",function(value) quit_on_no_replacement = value end) - local report_direct = logs.reporter("fonts","otf direct") local report_subchain = logs.reporter("fonts","otf subchain") local report_chain = logs.reporter("fonts","otf chain") @@ -260,7 +258,7 @@ local localpar_code = whatcodes.localpar local discretionary_code = disccodes.discretionary local regular_code = disccodes.regular -local automatic_code = disccodes.automatic +----- automatic_code = disccodes.automatic local ligature_code = glyphcodes.ligature @@ -313,6 +311,15 @@ local handlers = { } local rlmode = 0 local featurevalue = false +local sweephead = { } +local sweepnode = nil +local sweepprev = nil +local sweepnext = nil + +local notmatchpre = { } +local notmatchpost = { } +local notmatchreplace = { } + -- head is always a whatsit so we can safely assume that head is not changed -- we use this for special testing and documentation @@ -403,50 +410,64 @@ local function copy_glyph(g) -- next and prev are untouched ! end end --- temp here (context) - -local function collapse_disc(start,next) - local replace1 = getfield(start,"replace") - local replace2 = getfield(next,"replace") - if replace1 and replace2 then - local pre2 = getfield(next,"pre") - local post2 = getfield(next,"post") - setfield(replace1,"prev",nil) - if pre2 then - local pre1 = getfield(start,"pre") - if pre1 then - flush_node_list(pre1) +local function flattendisk(head,disc) + local replace = getfield(disc,"replace") + setfield(disc,"replace",nil) + free_node(disc) + if head == disc then + local next = getnext(disc) + if replace then + if next then + local tail = find_node_tail(replace) + setfield(tail,"next",next) + setfield(next,"prev",tail) end - local pre1 = copy_node_list(replace1) - local tail1 = find_node_tail(pre1) - setfield(tail1,"next",pre2) - setfield(pre2,"prev",tail1) - setfield(start,"pre",pre1) - setfield(next,"pre",nil) + return replace, replace + elseif next then + return next, next else - setfield(start,"pre",nil) + return -- maybe warning end - if post2 then - local post1 = getfield(start,"post") - if post1 then - flush_node_list(post1) + else + local next = getnext(disc) + local prev = getprev(disc) + if replace then + local tail = find_node_tail(replace) + if next then + setfield(tail,"next",next) + setfield(next,"prev",tail) end - setfield(start,"post",post2) + setfield(prev,"next",replace) + setfield(replace,"prev",prev) + return head, replace else - setfield(start,"post",nil) - end - local tail1 = find_node_tail(replace1) - setfield(tail1,"next",replace2) - setfield(replace2,"prev",tail1) - setfield(start,"replace",replace1) - setfield(next,"replace",nil) - -- - local nextnext = getnext(next) - setfield(nextnext,"prev",start) - setfield(start,"next",nextnext) - free_node(next) + if next then + setfield(next,"prev",prev) + end + setfield(prev,"next",next) + return head, next + end + end +end + +local function appenddisc(disc,list) + local post = getfield(disc,"post") + local replace = getfield(disc,"replace") + local phead = list + local rhead = copy_node_list(list) + local ptail = find_node_tail(post) + local rtail = find_node_tail(replace) + if post then + setfield(ptail,"next",phead) + setfield(phead,"prev",ptail) else - -- maybe remove it + setfield(disc,"post",phead) + end + if replace then + setfield(rtail,"next",rhead) + setfield(rhead,"prev",rtail) + else + setfield(disc,"replace",rhead) end end @@ -716,13 +737,38 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun return head, base end -function handlers.gsub_single(head,start,kind,lookupname,replacement) - if trace_singles then - logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement)) +local function multiple_glyphs(head,start,multiple,ignoremarks) + local nofmultiples = #multiple + if nofmultiples > 0 then + resetinjection(start) + setfield(start,"char",multiple[1]) + if nofmultiples > 1 then + local sn = getnext(start) + for k=2,nofmultiples do -- todo: use insert_node +-- untested: +-- +-- while ignoremarks and marks[getchar(sn)] then +-- local sn = getnext(sn) +-- end + local n = copy_node(start) -- ignore components + resetinjection(n) + setfield(n,"char",multiple[k]) + setfield(n,"next",sn) + setfield(n,"prev",start) + if sn then + setfield(sn,"prev",n) + end + setfield(start,"next",n) + start = n + end + end + return head, start, true + else + if trace_multiples then + logprocess("no multiple for %s",gref(getchar(start))) + end + return head, start, false end - resetinjection(start) - setfield(start,"char",replacement) - return head, start, true end local function get_alternative_glyph(start,alternatives,value,trace_alternatives) @@ -757,38 +803,15 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives end end -local function multiple_glyphs(head,start,multiple,ignoremarks) - local nofmultiples = #multiple - if nofmultiples > 0 then - resetinjection(start) - setfield(start,"char",multiple[1]) - if nofmultiples > 1 then - local sn = getnext(start) - for k=2,nofmultiples do -- todo: use insert_node --- untested: --- --- while ignoremarks and marks[getchar(sn)] then --- local sn = getnext(sn) --- end - local n = copy_node(start) -- ignore components - resetinjection(n) - setfield(n,"char",multiple[k]) - setfield(n,"next",sn) - setfield(n,"prev",start) - if sn then - setfield(sn,"prev",n) - end - setfield(start,"next",n) - start = n - end - end - return head, start, true - else - if trace_multiples then - logprocess("no multiple for %s",gref(getchar(start))) - end - return head, start, false +-- handlers + +function handlers.gsub_single(head,start,kind,lookupname,replacement) + if trace_singles then + logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement)) end + resetinjection(start) + setfield(start,"char",replacement) + return head, start, true end function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence) @@ -910,19 +933,78 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) return head, start, false, discfound end --- function is_gsub_ligature(start,ligature) -- limited case: in disc nodes, only latin, always glyphs --- local s = getnext(start) --- while s do --- local lg = ligature[getchar(s)] --- if lg then --- ligature = lg --- s = getnext(s) --- else --- return --- end --- end --- return ligature and ligature.ligature --- end +function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence,injection) + local startchar = getchar(start) + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection) -- ,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) + end + return head, start, false +end + +function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection) + -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too + -- todo: kerns in components of ligatures + local snext = getnext(start) + if not snext then + return head, start, false + else + local prev, done = start, false + local factor = tfmdata.parameters.factor + local lookuptype = lookuptypes[lookupname] + while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do + local nextchar = getchar(snext) + local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = getnext(snext) + else + if not krn then + -- skip + elseif type(krn) == "table" then + if lookuptype == "pair" then -- probably not needed + local a, b = krn[2], krn[3] + if a and #a > 0 then + local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,injection) -- characters[startchar]) + if trace_kerns then + local startchar = getchar(start) + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection) -- characters[nextchar]) + if trace_kerns then + local startchar = getchar(start) + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else -- wrong ... position has different entries + report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) + -- local a, b = krn[2], krn[6] + -- if a and a ~= 0 then + -- local k = setkern(snext,factor,rlmode,a) + -- if trace_kerns then + -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) + -- end + -- end + -- if b and b ~= 0 then + -- logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) + -- end + end + done = true + elseif krn ~= 0 then + local k = setkern(snext,factor,rlmode,krn,injection) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) -- prev? + end + done = true + end + break + end + end + return head, start, done + end +end --[[ldx-- <p>We get hits on a mark, but we're not sure if the it has to be applied so @@ -1169,85 +1251,11 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) end end -function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence,injection) - local startchar = getchar(start) - local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection) -- ,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) - end - return head, start, false -end - -function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection) - -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too - -- todo: kerns in components of ligatures - local snext = getnext(start) - if not snext then - return head, start, false - else - local prev, done = start, false - local factor = tfmdata.parameters.factor - local lookuptype = lookuptypes[lookupname] - while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do - local nextchar = getchar(snext) - local krn = kerns[nextchar] - if not krn and marks[nextchar] then - prev = snext - snext = getnext(snext) - else - if not krn then - -- skip - elseif type(krn) == "table" then - if lookuptype == "pair" then -- probably not needed - local a, b = krn[2], krn[3] - if a and #a > 0 then - local startchar = getchar(start) - local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,injection) -- characters[startchar]) - if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - if b and #b > 0 then - local startchar = getchar(start) - local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection) -- characters[nextchar]) - if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - else -- wrong ... position has different entries - report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) - -- local a, b = krn[2], krn[6] - -- if a and a ~= 0 then - -- local k = setkern(snext,factor,rlmode,a) - -- if trace_kerns then - -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) - -- end - -- end - -- if b and b ~= 0 then - -- logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) - -- end - end - done = true - elseif krn ~= 0 then - local k = setkern(snext,factor,rlmode,krn,injection) - if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) - end - done = true - end - break - end - end - return head, start, done - end -end - --[[ldx-- <p>I will implement multiple chain replacements once I run into a font that uses it. It's not that complex to handle.</p> --ldx]]-- -local chainmores = { } local chainprocs = { } local function logprocess(...) @@ -1276,11 +1284,6 @@ function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,looku return head, start, false end -function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) - logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) - return head, start, false -end - -- The reversesub is a special case, which is why we need to store the replacements -- in a bit weird way. There is no lookup and the replacement comes from the lookup -- itself. It is meant mostly for dealing with Urdu. @@ -1377,83 +1380,7 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) end resetinjection(current) - if check_discretionaries then - -- some fonts use a chain lookup to replace e.g. an f in a fi ligature - -- and there can be a disc node in between ... the next code tries to catch - -- this - local next = getnext(current) - local prev = getprev(current) -- todo: just remember it above - local done = false - if next then - if getid(next) == disc_code then - local subtype = getsubtype(next) - if subtype == discretionary_code then - setfield(next,"prev",prev) - setfield(prev,"next",next) - setfield(current,"prev",nil) - setfield(current,"next",nil) - local replace = getfield(next,"replace") - local pre = getfield(next,"pre") - local new = copy_node(current) - setfield(new,"char",replacement) - if replace then - setfield(new,"next",replace) - setfield(replace,"prev",new) - end - if pre then - setfield(current,"next",pre) - setfield(pre,"prev",current) - end - setfield(next,"replace",new) -- also updates tail - setfield(next,"pre",current) -- also updates tail - end - start = next - done = true - local next = getnext(start) - if next and getid(next) == disc_code then - collapse_disc(start,next) - end - end - end - if not done and prev then - if getid(prev) == disc_code then - local subtype = getsubtype(prev) - if subtype == discretionary_code then - setfield(next,"prev",prev) - setfield(prev,"next",next) - setfield(current,"prev",nil) - setfield(current,"next",nil) - local replace = getfield(prev,"replace") - local post = getfield(prev,"post") - local new = copy_node(current) - setfield(new,"char",replacement) - if replace then - local tail = find_node_tail(replace) - setfield(tail,"next",new) - setfield(new,"prev",tail) - else - replace = new - end - if post then - local tail = find_node_tail(post) - setfield(tail,"next",current) - setfield(current,"prev",tail) - else - post = current - end - setfield(prev,"replace",replace) -- also updates tail - setfield(prev,"post",post) -- also updates tail - start = prev - done = true - end - end - end - if not done then - setfield(current,"char",replacement) - end - else - setfield(current,"char",replacement) - end + setfield(current,"char",replacement) end end return head, start, true @@ -1466,8 +1393,6 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo return head, start, false end -chainmores.gsub_single = chainprocs.gsub_single - --[[ldx-- <p>Here we replace start by a sequence of new glyphs.</p> --ldx]]-- @@ -1498,8 +1423,6 @@ function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext, return head, start, false end -chainmores.gsub_multiple = chainprocs.gsub_multiple - --[[ldx-- <p>Here we replace start by new glyph. First we delete the rest of the match.</p> --ldx]]-- @@ -1554,8 +1477,6 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext return head, start, false end -chainmores.gsub_alternate = chainprocs.gsub_alternate - --[[ldx-- <p>When we replace ligatures we use a helper that handles the marks. I might change this function (move code inline and handle the marks by a separate function). We @@ -1643,7 +1564,93 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, return head, start, false, 0, false end -chainmores.gsub_ligature = chainprocs.gsub_ligature +function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + -- untested .. needs checking for the new model + local startchar = getchar(start) + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = lookuphash[lookupname] + if kerns then + kerns = kerns[startchar] -- needed ? + if kerns then + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns) -- ,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) + end + end + end + return head, start, false +end + +function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + local snext = getnext(start) + if snext then + local startchar = getchar(start) + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = lookuphash[lookupname] + if kerns then + kerns = kerns[startchar] + if kerns then + local lookuptype = lookuptypes[lookupname] + local prev, done = start, false + local factor = tfmdata.parameters.factor + while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do + local nextchar = getchar(snext) + local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = getnext(snext) + else + if not krn then + -- skip + elseif type(krn) == "table" then + if lookuptype == "pair" then + local a, b = krn[2], krn[3] + if a and #a > 0 then + local startchar = getchar(start) + local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a) -- ,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local startchar = getchar(start) + local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b) -- ,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else + report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) + -- local a, b = krn[2], krn[6] + -- if a and a ~= 0 then + -- local k = setkern(snext,factor,rlmode,a) + -- if trace_kerns then + -- logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) + -- end + -- end + -- if b and b ~= 0 then + -- logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) + -- end + end + done = true + elseif krn ~= 0 then + local k = setkern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) + end + done = true + end + break + end + end + return head, start, done + end + end + end + return head, start, false +end function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = getchar(start) @@ -1903,129 +1910,345 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l return head, start, false end -function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) - -- untested .. needs checking for the new model - local startchar = getchar(start) - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local kerns = lookuphash[lookupname] - if kerns then - kerns = kerns[startchar] -- needed ? - if kerns then - local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns) -- ,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) +-- what pointer to return, spec says stop +-- to be discussed ... is bidi changer a space? +-- elseif char == zwnj and sequence[n][32] then -- brrr + +-- somehow l or f is global +-- we don't need to pass the currentcontext, saves a bit +-- make a slow variant then can be activated but with more tracing + +local function show_skip(kind,chainname,char,ck,class) + if ck[9] then + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) + else + logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) + end +end + +-- A previous version had disc collapsing code in the (single sub) handler plus some +-- checking in the main loop, but that left the pre/post sequences undone. The best +-- solution is to add some checking there and backtrack when a replace/post matches +-- but it takes a bit of work to figure out an efficient way (this is what the sweep* +-- names refer to). I might look into that variant one day again as it can replace +-- some other code too. In that approach we can have a special version for gub and pos +-- which gains some speed. This method does the test and passes info to the handlers +-- (sweepnode, sweepmode, sweepprev, sweepnext, etc). Here collapsing is handled in the +-- main loop which also makes code elsewhere simpler (i.e. no need for the other special +-- runners and disc code in ligature building). I also experimented with pushing preceding +-- glyphs sequences in the replace/pre fields beforehand which saves checking afterwards +-- but at the cost of duplicate glyphs (memory) but it's too much overhead (runtime). +-- +-- In the meantime Kai had moved the code from the single chain into a more general handler +-- and this one (renamed to chaindisk) is used now. I optimized the code a bit and brought +-- it in sycn with the other code. Hopefully I didn't introduce errors. Note: this somewhat +-- complex approach is meant for fonts that implement (for instance) ligatures by character +-- replacement which to some extend is not that suitable for hyphenation. I also use some +-- helpers. This method passes some states but reparses the list. There is room for a bit of +-- speed up but that will be done in the context version. (In fact a partial rewrite of all +-- code can bring some more efficientry.) +-- +-- I didn't test it with extremes but successive disc nodes still can give issues but in +-- order to handle that we need more complex code which also slows down even more. The main +-- loop variant could deal with that: test, collapse, backtrack. + +local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,chainindex,sequence,chainproc) + + if not start then + return -- safeguard + end + + local startishead = start == head + local seq = ck[3] + local f = ck[4] + local l = ck[5] + local s = #seq + local done = false + local sweepnode = sweepnode + local sweeptype = sweeptype + local sweepoverflow = false + local checkdisc = getprev(head) -- hm bad name head + local keepdisc = not sweepnode + local lookaheaddisc = nil + local backtrackdisc = nil + local current = start + local last = start + local prev = getprev(start) + + -- fishy: so we can overflow and then go on in the sweep? + + local i = f + while i <= l do + local id = getid(current) + if id == glyph_code then + i = i + 1 + last = current + current = getnext(current) + elseif id == disc_code then + if keepdisc then + keepdisc = false + if notmatchpre[current] ~= notmatchreplace[current] then + lookaheaddisc = current + end + local replace = getfield(current,"replace") + while replace and i <= l do + if getid(replace) == glyph_code then + i = i + 1 + end + replace = getnext(replace) + end + last = current + current = getnext(c) + else + head, current = flattendisk(head,current) + end + else + last = current + current = getnext(current) + end + if current then + -- go on + elseif sweepoverflow then + -- we already are folling up on sweepnode + break + elseif sweeptype == "post" or sweeptype == "replace" then + current = getnext(sweepnode) + if current then + sweeptype = nil + sweepoverflow = true + else + break end end end - return head, start, false -end -chainmores.gpos_single = chainprocs.gpos_single -- okay? + if sweepoverflow then + local prev = current and getprev(current) + if not current or prev ~= sweepnode then + local head = getnext(sweepnode) + local tail = nil + if prev then + tail = prev + setfield(current,"prev",sweepnode) + else + tail = find_node_tail(head) + end + setfield(sweepnode,"next",current) + setfield(head,"prev",nil) + setfield(tail,"next",nil) + appenddisc(sweepnode,head) + end + end --- when machines become faster i will make a shared function + if l < s then + local i = l + local t = sweeptype == "post" or sweeptype == "replace" + while current and i < s do + local id = getid(current) + if id == glyph_code then + i = i + 1 + current = getnext(current) + elseif id == disc_code then + if keepdisc then + keepdisc = false + if notmatchpre[current] ~= notmatchreplace[current] then + lookaheaddisc = current + end + local replace = getfield(c,"replace") + while replace and i < s do + if getid(replace) == glyph_code then + i = i + 1 + end + replace = getnext(replace) + end + current = getnext(current) + elseif notmatchpre[current] ~= notmatchreplace[current] then + head, current = flattendisk(head,current) + else + current = getnext(current) -- HH + end + else + current = getnext(current) + end + if not current and t then + current = getnext(sweepnode) + if current then + sweeptype = nil + end + end + end + end -function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) - local snext = getnext(start) - if snext then - local startchar = getchar(start) - local subtables = currentlookup.subtables - local lookupname = subtables[1] - local kerns = lookuphash[lookupname] - if kerns then - kerns = kerns[startchar] - if kerns then - local lookuptype = lookuptypes[lookupname] - local prev, done = start, false - local factor = tfmdata.parameters.factor - while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do - local nextchar = getchar(snext) - local krn = kerns[nextchar] - if not krn and marks[nextchar] then - prev = snext - snext = getnext(snext) - else - if not krn then - -- skip - elseif type(krn) == "table" then - if lookuptype == "pair" then - local a, b = krn[2], krn[3] - if a and #a > 0 then - local startchar = getchar(start) - local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a) -- ,characters[startchar]) - if trace_kerns then - logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - if b and #b > 0 then - local startchar = getchar(start) - local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b) -- ,characters[nextchar]) - if trace_kerns then - logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) - end - end - else - report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) - -- local a, b = krn[2], krn[6] - -- if a and a ~= 0 then - -- local k = setkern(snext,factor,rlmode,a) - -- if trace_kerns then - -- logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) - -- end - -- end - -- if b and b ~= 0 then - -- logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) - -- end - end - done = true - elseif krn ~= 0 then - local k = setkern(snext,factor,rlmode,krn) - if trace_kerns then - logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) - end - done = true + if f > 1 then + local current = prev + local i = f + local t = sweeptype == "pre" or sweeptype == "replace" + if not current and t and current == checkdisk then + current = getprev(sweepnode) + end + while current and i > 1 do -- missing getprev added / moved outside + local id = getid(current) + if id == glyph_code then + i = i - 1 + elseif id == disc_code then + if keepdisc then + keepdisc = false + if notmatchpost[current] ~= notmatchreplace[current] then + backtrackdisc = current + end + local replace = getfield(current,"replace") + while replace and i > 1 do + if getid(replace) == glyph_code then + i = i - 1 end - break + replace = getnext(replace) end + elseif notmatchpost[current] ~= notmatchreplace[current] then + head, current = flattendisk(head,current) end - return head, start, done + end + current = getprev(current) + if t and current == checkdisk then + current = getprev(sweepnode) end end end - return head, start, false -end -chainmores.gpos_pair = chainprocs.gpos_pair -- okay? + local ok = false + if lookaheaddisc then --- what pointer to return, spec says stop --- to be discussed ... is bidi changer a space? --- elseif char == zwnj and sequence[n][32] then -- brrr + local cf = start + local cl = getprev(lookaheaddisc) + local cprev = getprev(start) + local insertedmarks = 0 --- somehow l or f is global --- we don't need to pass the currentcontext, saves a bit --- make a slow variant then can be activated but with more tracing + while cprev and getid(cf) == glyph_code and getfont(cf) == currentfont and getsubtype(cf) < 256 and marks[getchar(cf)] do + insertedmarks = insertedmarks + 1 + cf = cprev + startishead = cf == head + cprev = getprev(cprev) + end + + setfield(lookaheaddisc,"prev",cprev) + if cprev then + setfield(cprev,"next",lookaheaddisc) + end + setfield(cf,"prev",nil) + setfield(cl,"next",nil) + if startishead then + head = lookaheaddisc + end + + local replace = getfield(lookaheaddisc,"replace") + local pre = getfield(lookaheaddisc,"pre") + local new = copy_node_list(cf) + local cnew = new + for i=1,insertedmarks do + cnew = getnext(cnew) + end + local clast = cnew + for i=f,l do + clast = getnext(clast) + end + if not notmatchpre[lookaheaddisc] then + cf, start, ok = chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + end + if not notmatchreplace[lookaheaddisc] then + new, cnew, ok = chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + end + if pre then + setfield(cl,"next",pre) + setfield(pre,"prev",cl) + end + if replace then + local tail = find_node_tail(new) + setfield(tail,"next",replace) + setfield(replace,"prev",tail) + end + setfield(lookaheaddisc,"pre",cf) -- also updates tail + setfield(lookaheaddisc,"replace",new) -- also updates tail + + start = getprev(lookaheaddisc) + sweephead[cf] = getnext(clast) + sweephead[new] = getnext(last) + + elseif backtrackdisc then + + local cf = getnext(backtrackdisc) + local cl = start + local cnext = getnext(start) + local insertedmarks = 0 + + while cnext and getid(cnext) == glyph_code and getfont(cnext) == currentfont and getsubtype(cnext) < 256 and marks[getchar(cnext)] do + insertedmarks = insertedmarks + 1 + cl = cnext + cnext = getnext(cnext) + end + if cnext then + setfield(cnext,"prev",backtrackdisc) + end + setfield(backtrackdisc,"next",cnext) + setfield(cf,"prev",nil) + setfield(cl,"next",nil) + local replace = getfield(backtrackdisc,"replace") + local post = getfield(backtrackdisc,"post") + local new = copy_node_list(cf) + local cnew = find_node_tail(new) + for i=1,insertedmarks do + cnew = getprev(cnew) + end + local clast = cnew + for i=f,l do + clast = getnext(clast) + end + if not notmatchpost[backtrackdisc] then + cf, start, ok = chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + end + if not notmatchreplace[backtrackdisc] then + new, cnew, ok = chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + end + if post then + local tail = find_node_tail(post) + setfield(tail,"next",cf) + setfield(cf,"prev",tail) + else + post = cf + end + if replace then + local tail = find_node_tail(replace) + setfield(tail,"next",new) + setfield(new,"prev",tail) + else + replace = new + end + setfield(backtrackdisc,"post",post) -- also updates tail + setfield(backtrackdisc,"replace",replace) -- also updates tail + start = getprev(backtrackdisc) + sweephead[post] = getnext(clast) + sweephead[replace] = getnext(last) -local function show_skip(kind,chainname,char,ck,class) - if ck[9] then - logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) else - logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) + + head, start, ok = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + end -end ---hm, do i need to deal with disc here ? + return head, start, ok +end local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) - -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] + local sweepnode = sweepnode + local sweeptype = sweeptype + local diskseen = false + local checkdisc = getprev(head) local flags = sequence.flags local done = false local skipmark = flags[1] local skipligature = flags[2] local skipbase = flags[3] - local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) - local markclass = sequence.markclass -- todo, first we need a proper test + local markclass = sequence.markclass local skipped = false - for k=1,#contexts do + for k=1,#contexts do -- i've only seen ccmp having > 1 (e.g. dejavu) local match = true local current = start local last = start @@ -2039,7 +2262,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq else -- maybe we need a better space check (maybe check for glue or category or combination) -- we cannot optimize for n=2 because there can be disc nodes - local f, l = ck[4], ck[5] + local f = ck[4] + local l = ck[5] -- current match if f == 1 and f == l then -- current only -- already a hit @@ -2052,6 +2276,10 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq local n = f + 1 last = getnext(last) while n <= l do + if not last and (sweeptype == "post" or sweeptype == "replace") then + last = getnext(sweepnode) + sweeptype = nil + end if last then local id = getid(last) if id == glyph_code then @@ -2059,7 +2287,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq local char = getchar(last) local ccd = descriptions[char] if ccd then - local class = ccd.class + local class = ccd.class or "base" if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then skipped = true if trace_skips then @@ -2084,39 +2312,59 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq break end elseif id == disc_code then - if check_discretionaries then - local replace = getfield(last,"replace") - if replace then - -- so far we never entered this branch - while replace do - if seq[n][getchar(replace)] then - n = n + 1 - replace = getnext(replace) - if not replace then - break - elseif n > l then - -- match = false - break - end - else - match = false + diskseen = true + notmatchpre[last] = nil + notmatchpost[last] = true + notmatchreplace[last] = nil + local pre = getfield(last,"pre") + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + pre = getnext(pre) + if not pre then + break + elseif n > l then break end + else + notmatchpre[last] = true + break end - if not match then + end + else + notmatchpre[last] = true + end + local replace = getfield(last,"replace") + if replace then + -- so far we never entered this branch + while replace do + if seq[n][getchar(replace)] then + n = n + 1 + replace = getnext(replace) + if not replace then + break + elseif n > l then + -- match = false + break + end + else + notmatchreplace[last] = true + if notmatchpre[last] then + match = false + end break - elseif check_discretionaries == "trace" then - report_chain("check disc action in current") end - else - last = getnext(last) -- no skipping here + end + if not match then + break end else last = getnext(last) -- no skipping here end else - match = false - break + last = getnext(last) -- no skipping here end else match = false @@ -2129,23 +2377,32 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq if match and f > 1 then local prev = getprev(start) if prev then - local n = f-1 - while n >= 1 do - if prev then - local id = getid(prev) - if id == glyph_code then - if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char - local char = getchar(prev) - local ccd = descriptions[char] - if ccd then - local class = ccd.class - if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then - skipped = true - if trace_skips then - show_skip(kind,chainname,char,ck,class) + if prev == checkdisc and (sweeptype == "pre" or sweeptype == "replace") then + prev = getprev(sweepnode) + -- sweeptype = nil + end + if prev then + local n = f-1 + while n >= 1 do + if prev then + local id = getid(prev) + if id == glyph_code then + if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char + local char = getchar(prev) + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n = n -1 + else + match = false + break end - elseif seq[n][char] then - n = n -1 else match = false break @@ -2154,57 +2411,84 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq match = false break end - else - match = false - break - end - elseif id == disc_code then - -- the special case: f i where i becomes dottless i .. - if check_discretionaries then + elseif id == disc_code then + -- the special case: f i where i becomes dottless i .. + diskseen = true + notmatchpre[prev] = true + notmatchpost[prev] = nil + notmatchreplace[prev] = nil + local pre = getfield(prev,"pre") + local post = getfield(prev,"post") local replace = getfield(prev,"replace") - if replace then - -- we seldom enter this branch (e.g. on brill efficient) - replace = find_node_tail(replace) - local finish = getprev(replace) - while replace do - if seq[n][getchar(replace)] then - n = n - 1 - replace = getprev(replace) - if not replace or replace == finish then + if pre ~= start and post ~= start and replace ~= start then + if post then + local n = n + post = find_node_tail(post) + local finish = getprev(post) + while post do + if seq[n][getchar(post)] then + n = n - 1 + post = getprev(post) + if not post or post == finish then + break + elseif n < 1 then + break + end + else + notmatchpost[prev] = true break - elseif n < 1 then - -- match = false + end + end + else + notmatchpost[prev] = true + end + if replace then + -- we seldom enter this branch (e.g. on brill efficient) + replace = find_node_tail(replace) + local finish = getprev(replace) + while replace do + if seq[n][getchar(replace)] then + n = n - 1 + replace = getprev(replace) + if not replace or replace == finish then + break + elseif n < 1 then + -- match = false + break + end + else + notmatchreplace[prev] = true + if notmatchpost[prev] then + match = false + end break end - else - match = false + end + if not match then break end - end - if not match then - break - elseif check_discretionaries == "trace" then - report_chain("check disc action in before") + else + -- skip 'm end else -- skip 'm end + elseif seq[n][32] then + n = n -1 else - -- skip 'm + match = false + break end - elseif seq[n][32] then - n = n -1 + prev = getprev(prev) + elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces + n = n - 1 else match = false break end - prev = getprev(prev) - elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces - n = n - 1 - else - match = false - break end + else + match = false end else match = false @@ -2213,6 +2497,12 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq -- after if match and s > l then local current = last and getnext(last) + if not current then + if sweeptype == "post" or sweeptype == "replace" then + current = getnext(sweepnode) + -- sweeptype = nil + end + end if current then -- removed optimization for s-l == 1, we have to deal with marks anyway local n = l + 1 @@ -2245,31 +2535,52 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq break end elseif id == disc_code then - if check_discretionaries then - local replace = getfield(current,"replace") - if replace then - -- so far we never entered this branch - while replace do - if seq[n][getchar(replace)] then - n = n + 1 - replace = getnext(replace) - if not replace then - break - elseif n > s then - break - end - else - match = false + diskseen = true + notmatchpre[current] = nil + notmatchpost[current] = true + notmatchreplace[current] = nil + local pre = getfield(current,"pre") + local replace = getfield(current,"replace") + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + pre = getnext(pre) + if not pre then + break + elseif n > s then break end + else + notmatchpre[current] = true + break end - if not match then + 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 + replace = getnext(replace) + if not replace then + break + elseif n > s then + break + end + else + notmatchreplace[current] = true + if notmatchpre[current] then + match = false + end break - elseif check_discretionaries == "trace" then - report_chain("check disc action in after") end - else - -- skip 'm + end + if not match then + break end else -- skip 'm @@ -2294,7 +2605,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq end end if match then - -- ck == currentcontext + -- can lookups be of a different type ? + local diskchain = diskseen or sweepnode if trace_contexts then local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5] local char = getchar(start) @@ -2314,10 +2626,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq local chainlookupname = chainlookups[1] local chainlookup = lookuptable[chainlookupname] if chainlookup then - local cp = chainprocs[chainlookup.type] - if cp then + local chainproc = chainprocs[chainlookup.type] + if chainproc then local ok - head, start, ok = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + if diskchain then + head, start, ok = chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc) + else + head, start, ok = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) + end if ok then done = true end @@ -2335,7 +2651,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq local char = getchar(start) local ccd = descriptions[char] if ccd then - local class = ccd.class + local class = ccd.class or "base" if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then start = getnext(start) else @@ -2353,14 +2669,18 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq -- we just advance i = i + 1 else - local cp = chainmores[chainlookup.type] - if not cp then + local chainproc = chainprocs[chainlookup.type] + if not chainproc then -- actually an error logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) i = i + 1 else local ok, n - head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) + if diskchain then + head, start, ok = chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc) + else + head, start, ok, n = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) + end -- messy since last can be changed ! if ok then done = true @@ -2401,8 +2721,16 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq end end end + if done then + break -- out of contexts (new, needs checking) + end end end + if diskchain then -- maybe move up so that we can turn checking on/off + notmatchpre = { } + notmatchpost = { } + notmatchreplace = { } + end return head, start, done end @@ -2493,7 +2821,7 @@ local function initialize(sequence,script,language,enabled) local scripts = features[kind] -- local languages = scripts[script] or scripts[wildcard] if languages and (languages[language] or languages[wildcard]) then - return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence } + return { valid, autofeatures[kind] or false, sequence, kind } end end end @@ -2537,33 +2865,6 @@ function otf.dataset(tfmdata,font) -- generic variant, overloaded in context return rl end --- elseif id == glue_code then --- if p[5] then -- chain --- local pc = pp[32] --- if pc then --- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) --- if ok then --- done = true --- end --- if start then start = getnext(start) end --- else --- start = getnext(start) --- end --- else --- start = getnext(start) --- end - --- there will be a new direction parser (pre-parsed etc) - --- less bytecode: 290 -> 254 --- --- attr = attr or false --- --- local a = getattr(start,0) --- if (a == attr and (not attribute or getprop(start,a_state) == attribute)) or (not attribute or getprop(start,a_state) == attribute) then --- -- the action --- end - -- assumptions: -- -- * languages that use complex disc nodes @@ -2576,20 +2877,29 @@ local function kernrun(disc,run) -- we can assume that prev and next are glyphs report_run("kern") -- will be more detailed end -- - local prev = getprev(disc) -- todo, keep these in the main loop - local next = getnext(disc) -- todo, keep these in the main loop + local prev = getprev(disc) -- todo, keep these in the main loop + local next = getnext(disc) -- todo, keep these in the main loop -- - local pre = getfield(disc,"pre") - local post = getfield(disc,"post") - local replace = getfield(disc,"replace") + local pre = getfield(disc,"pre") + local post = getfield(disc,"post") + local replace = getfield(disc,"replace") + -- + local prevmarks = prev -- - if pre or replace then - if not (prev and getid(prev) == glyph_code and getfont(prev) == currentfont and getsubtype(prev)<256) then + -- can be optional: + -- + -- while prevmarks and getid(prevmarks) == glyph_code and getfont(prevmarks) == currentfont and marks[getchar(prevmarks)] and getsubtype(prevmarks) < 256 do + while prevmarks and getid(prevmarks) == glyph_code and marks[getchar(prevmarks)] and getfont(prevmarks) == currentfont and getsubtype(prevmarks) < 256 do + prevmarks = getprev(prevmarks) + end + -- + if prev and (pre or replace) then + if not (getid(prev) == glyph_code and getfont(prev) == currentfont and getsubtype(prev)<256) then prev = false end end - if post or replace then - if not (next and getid(next) == glyph_code and getfont(next) == currentfont and getsubtype(next)<256) then + if next and (post or replace) then + if not (getid(next) == glyph_code and getfont(next) == currentfont and getsubtype(next)<256) then next = false end end @@ -2600,7 +2910,7 @@ local function kernrun(disc,run) -- we can assume that prev and next are glyphs local nest = getprev(pre) setfield(pre,"prev",prev) setfield(prev,"next",pre) - run(prev,"preinjections") + run(prevmarks,"preinjections") setfield(pre,"prev",nest) setfield(prev,"next",disc) else @@ -2613,7 +2923,7 @@ local function kernrun(disc,run) -- we can assume that prev and next are glyphs local tail = find_node_tail(post) setfield(tail,"next",next) setfield(next,"prev",tail) - run(post,"postinjections",tail) + run(post,"postinjections",next) setfield(tail,"next",nil) setfield(next,"prev",disc) else @@ -2622,6 +2932,11 @@ local function kernrun(disc,run) -- we can assume that prev and next are glyphs -- if not replace and prev and next then -- this should be already done by discfound + setfield(prev,"next",next) + setfield(next,"prev",prev) + run(prevmarks,"injections",next) + setfield(prev,"next",disc) + setfield(next,"prev",disc) elseif prev and next then local tail = find_node_tail(replace) local nest = getprev(replace) @@ -2629,7 +2944,7 @@ local function kernrun(disc,run) -- we can assume that prev and next are glyphs setfield(prev,"next",replace) setfield(tail,"next",next) setfield(next,"prev",tail) - run(prev,"replaceinjections",tail) + run(prevmarks,"replaceinjections",next) setfield(replace,"prev",nest) setfield(prev,"next",disc) setfield(tail,"next",nil) @@ -2638,14 +2953,14 @@ local function kernrun(disc,run) -- we can assume that prev and next are glyphs local nest = getprev(replace) setfield(replace,"prev",prev) setfield(prev,"next",replace) - run(prev,"replaceinjections") + run(prevmarks,"replaceinjections") setfield(replace,"prev",nest) setfield(prev,"next",disc) elseif next then local tail = find_node_tail(replace) setfield(tail,"next",next) setfield(next,"prev",tail) - run(replace,"replaceinjections",tail) + run(replace,"replaceinjections",next) setfield(tail,"next",nil) setfield(next,"prev",disc) else @@ -2663,6 +2978,8 @@ local function comprun(disc,run) -- local pre = getfield(disc,"pre") if pre then + sweepnode = disc + sweeptype = "pre" -- in alternative code preinjections is used (also used then for proeprties, saves a variable) local new, done = run(pre) if done then setfield(disc,"pre",new) @@ -2671,6 +2988,8 @@ local function comprun(disc,run) -- local post = getfield(disc,"post") if post then + sweepnode = disc + sweeptype = "post" local new, done = run(post) if done then setfield(disc,"post",new) @@ -2679,14 +2998,18 @@ local function comprun(disc,run) -- local replace = getfield(disc,"replace") if replace then + sweepnode = disc + sweeptype = "replace" local new, done = run(replace) if done then setfield(disc,"replace",new) end end + sweepnode = nil + sweeptype = nil end -local function testrun(disc,trun,crun) +local function testrun(disc,trun,crun) -- use helper local next = getnext(disc) if next then local replace = getfield(disc,"replace") @@ -2784,6 +3107,7 @@ local function featuresprocessor(head,font,attr) currentfont = font rlmode = 0 + sweephead = { } local sequences = resources.sequences local done = false @@ -2810,18 +3134,18 @@ local function featuresprocessor(head,font,attr) local dataset = datasets[s] featurevalue = dataset[1] -- todo: pass to function instead of using a global local attribute = dataset[2] - local chain = dataset[3] -- sequence.chain or 0 + local sequence = dataset[3] -- sequences[s] -- also dataset[5] local kind = dataset[4] - local sequence = dataset[5] -- sequences[s] -- also dataset[5] + ----- chain = dataset[5] -- sequence.chain or 0 local rlparmode = 0 local topstack = 0 local success = false local typ = sequence.type - local gpossing = typ == "gpos_single" or typ == "gpos_pair" + local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- maybe all of them local subtables = sequence.subtables local handler = handlers[typ] - if chain < 0 then + if typ == "gsub_reversecontextchain" then -- chain < 0 -- this is a limited case, no special treatments like 'init' etc -- we need to get rid of this slide! probably no longer needed in latest luatex local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo @@ -2876,9 +3200,14 @@ local function featuresprocessor(head,font,attr) report_missing_cache(typ,lookupname) else - local function c_run(start) -- no need to check for 256 and attr probably also the same - local head = start - local done = false + local function c_run(head) -- no need to check for 256 and attr probably also the same + local done = false + local start = sweephead[head] + if start then + sweephead[head] = nil + else + start = head + end while start do local id = getid(start) if id ~= glyph_code then @@ -3057,17 +3386,17 @@ local function featuresprocessor(head,font,attr) local subtype = getsubtype(start) if subtype == dir_code then local dir = getfield(start,"dir") - if dir == "+TRT" or dir == "+TLT" then + if dir == "+TLT" then topstack = topstack + 1 dirstack[topstack] = dir - elseif dir == "-TRT" or dir == "-TLT" then - topstack = topstack - 1 - end - local newdir = dirstack[topstack] - if newdir == "+TRT" then - rlmode = -1 - elseif newdir == "+TLT" then rlmode = 1 + elseif dir == "+TRT" then + topstack = topstack + 1 + dirstack[topstack] = dir + rlmode = -1 + elseif dir == "-TLT" or dir == "-TRT" then + topstack = topstack - 1 + rlmode = dirstack[topstack] == "+TLT" and 1 or -1 else rlmode = rlparmode end @@ -3100,9 +3429,14 @@ local function featuresprocessor(head,font,attr) else - local function c_run(start) - local head = start - local done = false + local function c_run(head) + local done = false + local start = sweephead[head] + if start then + sweephead[head] = nil + else + start = head + end while start do local id = getid(start) if id ~= glyph_code then @@ -3121,7 +3455,6 @@ local function featuresprocessor(head,font,attr) local lookupname = subtables[i] local lookupcache = lookuphash[lookupname] if lookupcache then - -- local lookupmatch = lookupcache[getchar(start)] local lookupmatch = lookupcache[char] if lookupmatch then -- we could move all code inline but that makes things even more unreadable @@ -3333,18 +3666,17 @@ local function featuresprocessor(head,font,attr) elseif id == whatsit_code then local subtype = getsubtype(start) if subtype == dir_code then - local dir = getfield(start,"dir") - if dir == "+TRT" or dir == "+TLT" then + if dir == "+TLT" then topstack = topstack + 1 dirstack[topstack] = dir - elseif dir == "-TRT" or dir == "-TLT" then - topstack = topstack - 1 - end - local newdir = dirstack[topstack] - if newdir == "+TRT" then - rlmode = -1 - elseif newdir == "+TLT" then rlmode = 1 + elseif dir == "+TRT" then + topstack = topstack + 1 + dirstack[topstack] = dir + rlmode = -1 + elseif dir == "-TLT" or dir == "-TRT" then + topstack = topstack - 1 + rlmode = dirstack[topstack] == "+TLT" and 1 or -1 else rlmode = rlparmode end diff --git a/tex/context/base/font-ots.lua b/tex/context/base/font-ots.lua index 74d7ac60b..27e0f161e 100644 --- a/tex/context/base/font-ots.lua +++ b/tex/context/base/font-ots.lua @@ -11,6 +11,10 @@ if not modules then modules = { } end modules ['font-ots'] = { -- sequences -- cursives don't cross discretionaries -- marks precede bases +-- pitfalls: +-- +-- when we append to a dics field we need to set the field in order to update tail + -- This is a version of font-otn.lua adapted to the new font loader code. It -- is a context version which can contain experimental code, but when we -- have serious patches we will backport to the font-otn files. There will @@ -121,7 +125,6 @@ local trace_discruns = false registertracker("otf.discruns", function(v local trace_compruns = false registertracker("otf.compruns", function(v) trace_compruns = v end) local quit_on_no_replacement = true -- maybe per font -local check_discretionaries = true -- "trace" local zwnjruns = true registerdirective("otf.zwnjruns", function(v) zwnjruns = v end) @@ -131,9 +134,10 @@ local report_direct = logs.reporter("fonts","otf direct") local report_subchain = logs.reporter("fonts","otf subchain") local report_chain = logs.reporter("fonts","otf chain") local report_process = logs.reporter("fonts","otf process") -local report_prepare = logs.reporter("fonts","otf prepare") +----- report_prepare = logs.reporter("fonts","otf prepare") local report_warning = logs.reporter("fonts","otf warning") local report_run = logs.reporter("fonts","otf run") +local report_check = logs.reporter("fonts","otf check") registertracker("otf.replacements", "otf.singles,otf.multiples,otf.alternatives,otf.ligatures") registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") @@ -244,6 +248,15 @@ local marks = false local currentfont = false local factor = 0 +local sweepnode = nil +local sweepprev = nil +local sweepnext = nil +local sweephead = { } + +local notmatchpre = { } +local notmatchpost = { } +local notmatchreplace = { } + -- head is always a whatsit so we can safely assume that head is not changed -- handlers .whatever(head,start, dataset,sequence,kerns, step,i,injection) @@ -341,50 +354,157 @@ local function copy_glyph(g) -- next and prev are untouched ! end end --- temp here (context) - -local function collapse_disc(start,next) - local replace1 = getfield(start,"replace") - local replace2 = getfield(next,"replace") - if replace1 and replace2 then - local pre2 = getfield(next,"pre") - local post2 = getfield(next,"post") - setfield(replace1,"prev",nil) - if pre2 then - local pre1 = getfield(start,"pre") - if pre1 then - flush_node_list(pre1) +-- temp here (context) - watch out: we need to set post/pre/replace in order to update its tail + +-- local function collapsedisc(start,next) +-- local replace1 = getfield(start,"replace") +-- local replace2 = getfield(next,"replace") +-- if replace1 and replace2 then +-- local pre2 = getfield(next,"pre") +-- local post2 = getfield(next,"post") +-- setfield(replace1,"prev",nil) +-- if pre2 then +-- local pre1 = getfield(start,"pre") +-- if pre1 then +-- flush_node_list(pre1) +-- end +-- local pre1 = copy_node_list(replace1) +-- local tail1 = find_node_tail(pre1) +-- setfield(tail1,"next",pre2) +-- setfield(pre2,"prev",tail1) +-- setfield(start,"pre",pre1) +-- setfield(next,"pre",nil) +-- else +-- setfield(start,"pre",nil) +-- end +-- if post2 then +-- local post1 = getfield(start,"post") +-- if post1 then +-- flush_node_list(post1) +-- end +-- setfield(start,"post",post2) +-- else +-- setfield(start,"post",nil) +-- end +-- local tail1 = find_node_tail(replace1) +-- setfield(tail1,"next",replace2) +-- setfield(replace2,"prev",tail1) +-- setfield(start,"replace",replace1) +-- setfield(next,"replace",nil) +-- -- +-- local nextnext = getnext(next) +-- setfield(nextnext,"prev",start) +-- setfield(start,"next",nextnext) +-- free_node(next) +-- else +-- -- maybe remove it +-- end +-- end + +-- local function prependdisc(first,last,prev) +-- local prev = prev or getprev(first) +-- local pre = getfield(last,"pre") +-- local replace = getfield(last,"replace") +-- local rs = getfield(first,"replace") +-- local ps = copy_node_list(rs) +-- local rt = ps and find_node_tail(rs) +-- local pt = rs and find_node_tail(ps) +-- if pre then +-- setfield(pre,"prev",pt) +-- setfield(pt,"next",pre) +-- end +-- if replace then +-- setfield(replace,"prev",rt) +-- setfield(rt,"next",replace) +-- end +-- setfield(last,"pre",ps) +-- setfield(last,"replace",rs) +-- setfield(first,"replace",nil) +-- free_node(first) +-- setfield(last,"prev",prev) +-- setfield(prev,"next",last) +-- return prev -- if nil then last is head +-- end + +-- local function prependglyph(first,last,prev) +-- local prev = prev or getprev(first) +-- local pre = getfield(last,"pre") +-- local replace = getfield(last,"replace") +-- local rs = first +-- local ps = copy_node(first) +-- if pre then +-- setfield(pre,"prev",ps) +-- setfield(ps,"next",pre) +-- end +-- if replace then +-- setfield(replace,"prev",rs) +-- setfield(rs,"next",replace) +-- end +-- setfield(last,"pre",ps) +-- setfield(last,"replace",rs) +-- setfield(last,"prev",prev) +-- setfield(prev,"next",last) +-- return prev -- if nil then last is head +-- end + +local function flattendisk(head,disc) + local replace = getfield(disc,"replace") + setfield(disc,"replace",nil) + free_node(disc) + if head == disc then + local next = getnext(disc) + if replace then + if next then + local tail = find_node_tail(replace) + setfield(tail,"next",next) + setfield(next,"prev",tail) end - local pre1 = copy_node_list(replace1) - local tail1 = find_node_tail(pre1) - setfield(tail1,"next",pre2) - setfield(pre2,"prev",tail1) - setfield(start,"pre",pre1) - setfield(next,"pre",nil) + return replace, replace + elseif next then + return next, next else - setfield(start,"pre",nil) + return -- maybe warning end - if post2 then - local post1 = getfield(start,"post") - if post1 then - flush_node_list(post1) + else + local next = getnext(disc) + local prev = getprev(disc) + if replace then + local tail = find_node_tail(replace) + if next then + setfield(tail,"next",next) + setfield(next,"prev",tail) end - setfield(start,"post",post2) + setfield(prev,"next",replace) + setfield(replace,"prev",prev) + return head, replace else - setfield(start,"post",nil) - end - local tail1 = find_node_tail(replace1) - setfield(tail1,"next",replace2) - setfield(replace2,"prev",tail1) - setfield(start,"replace",replace1) - setfield(next,"replace",nil) - -- - local nextnext = getnext(next) - setfield(nextnext,"prev",start) - setfield(start,"next",nextnext) - free_node(next) + if next then + setfield(next,"prev",prev) + end + setfield(prev,"next",next) + return head, next + end + end +end + +local function appenddisc(disc,list) + local post = getfield(disc,"post") + local replace = getfield(disc,"replace") + local phead = list + local rhead = copy_node_list(list) + local ptail = find_node_tail(post) + local rtail = find_node_tail(replace) + if post then + setfield(ptail,"next",phead) + setfield(phead,"prev",ptail) + else + setfield(disc,"post",phead) + end + if replace then + setfield(rtail,"next",rhead) + setfield(rhead,"prev",rtail) else - -- maybe remove it + setfield(disc,"replace",rhead) end end @@ -425,8 +545,8 @@ end -- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the -- third component. -local function getcomponentindex(start) - if getid(start) ~= glyph_code then +local function getcomponentindex(start) -- we could store this offset in the glyph (nofcomponents) + if getid(start) ~= glyph_code then -- and then get rid of all components return 0 elseif getsubtype(start) == ligature_code then local i = 0 @@ -899,7 +1019,7 @@ function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,inje if b and #b > 0 then local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection) -- characters[nextchar]) if trace_kerns then - local startchar = getchar(start) + local startchar = getchar(snext) logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h) end end @@ -1225,83 +1345,7 @@ function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,c logprocess("%s: replacing single %s by %s",cref(dataset,sequence,chainindex),gref(currentchar),gref(replacement)) end resetinjection(current) - if check_discretionaries then - -- some fonts use a chain lookup to replace e.g. an f in a fi ligature - -- and there can be a disc node in between ... the next code tries to catch - -- this - local next = getnext(current) - local prev = getprev(current) -- todo: just remember it above - local done = false - if next then - if getid(next) == disc_code then - local subtype = getsubtype(next) - if subtype == discretionary_code then - setfield(next,"prev",prev) - setfield(prev,"next",next) - setfield(current,"prev",nil) - setfield(current,"next",nil) - local replace = getfield(next,"replace") - local pre = getfield(next,"pre") - local new = copy_node(current) - setfield(new,"char",replacement) - if replace then - setfield(new,"next",replace) - setfield(replace,"prev",new) - end - if pre then - setfield(current,"next",pre) - setfield(pre,"prev",current) - end - setfield(next,"replace",new) -- also updates tail - setfield(next,"pre",current) -- also updates tail - end - start = next - done = true - local next = getnext(start) - if next and getid(next) == disc_code then - collapse_disc(start,next) - end - end - end - if not done and prev then - if getid(prev) == disc_code then - local subtype = getsubtype(prev) - if subtype == discretionary_code then - setfield(next,"prev",prev) - setfield(prev,"next",next) - setfield(current,"prev",nil) - setfield(current,"next",nil) - local replace = getfield(prev,"replace") - local post = getfield(prev,"post") - local new = copy_node(current) - setfield(new,"char",replacement) - if replace then - local tail = find_node_tail(replace) - setfield(tail,"next",new) - setfield(new,"prev",tail) - else - replace = new - end - if post then - local tail = find_node_tail(post) - setfield(tail,"next",current) - setfield(current,"prev",tail) - else - post = current - end - setfield(prev,"replace",replace) -- also updates tail - setfield(prev,"post",post) -- also updates tail - start = prev - done = true - end - end - end - if not done then - setfield(current,"char",replacement) - end - else - setfield(current,"char",replacement) - end + setfield(current,"char",replacement) end return head, start, true elseif current == stop then @@ -1317,8 +1361,6 @@ end <p>Here we replace start by a sequence of new glyphs.</p> --ldx]]-- --- disc? - function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup) local steps = currentlookup.steps local nofsteps = currentlookup.nofsteps @@ -1352,10 +1394,6 @@ end -- marks come last anyway -- are there cases where we need to delete the mark --- maybe we can share them ... - --- disc ? - function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup) local steps = currentlookup.steps local nofsteps = currentlookup.nofsteps @@ -1504,8 +1542,6 @@ function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,r return head, start, false end --- when machines become faster i will make a shared function - function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,chainindex) local steps = currentlookup.steps local nofsteps = currentlookup.nofsteps @@ -1783,7 +1819,382 @@ local function show_skip(dataset,sequence,char,ck,class) logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(dataset,sequence),gref(char),class,ck[1],ck[8]) end +-- A previous version had disc collapsing code in the (single sub) handler plus some +-- checking in the main loop, but that left the pre/post sequences undone. The best +-- solution is to add some checking there and backtrack when a replace/post matches +-- but it takes a bit of work to figure out an efficient way (this is what the sweep* +-- names refer to). I might look into that variant one day again as it can replace +-- some other code too. In that approach we can have a special version for gub and pos +-- which gains some speed. This method does the test and passes info to the handlers +-- (sweepnode, sweepmode, sweepprev, sweepnext, etc). Here collapsing is handled in the +-- main loop which also makes code elsewhere simpler (i.e. no need for the other special +-- runners and disc code in ligature building). I also experimented with pushing preceding +-- glyphs sequences in the replace/pre fields beforehand which saves checking afterwards +-- but at the cost of duplicate glyphs (memory) but it's too much overhead (runtime). +-- +-- In the meantime Kai had moved the code from the single chain into a more general handler +-- and this one (renamed to chaindisk) is used now. I optimized the code a bit and brought +-- it in sycn with the other code. Hopefully I didn't introduce errors. Note: this somewhat +-- complex approach is meant for fonts that implement (for instance) ligatures by character +-- replacement which to some extend is not that suitable for hyphenation. I also use some +-- helpers. This method passes some states but reparses the list. There is room for a bit of +-- speed up but that will be done in the context version. (In fact a partial rewrite of all +-- code can bring some more efficientry.) +-- +-- I didn't test it with extremes but successive disc nodes still can give issues but in +-- order to handle that we need more complex code which also slows down even more. The main +-- loop variant could deal with that: test, collapse, backtrack. + +local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,ck,chainproc) + + if not start then + return -- safeguard + end + + local startishead = start == head + local seq = ck[3] + local f = ck[4] + local l = ck[5] + local s = #seq + local done = false + local sweepnode = sweepnode + local sweeptype = sweeptype + local sweepoverflow = false + local checkdisc = getprev(head) -- hm bad name head + local keepdisc = not sweepnode + local lookaheaddisc = nil + local backtrackdisc = nil + local current = start + local last = start + local prev = getprev(start) + + -- fishy: so we can overflow and then go on in the sweep? + + local i = f + while i <= l do + local id = getid(current) + if id == glyph_code then + i = i + 1 + last = current + current = getnext(current) + elseif id == disc_code then + if keepdisc then + keepdisc = false + if notmatchpre[current] ~= notmatchreplace[current] then + lookaheaddisc = current + end + local replace = getfield(current,"replace") + while replace and i <= l do + if getid(replace) == glyph_code then + i = i + 1 + end + replace = getnext(replace) + end + last = current + current = getnext(c) + else + head, current = flattendisk(head,current) + end + else + last = current + current = getnext(current) + end + if current then + -- go on + elseif sweepoverflow then + -- we already are folling up on sweepnode + break + elseif sweeptype == "post" or sweeptype == "replace" then + current = getnext(sweepnode) + if current then + sweeptype = nil + sweepoverflow = true + else + break + end + end + end + + if sweepoverflow then + local prev = current and getprev(current) + if not current or prev ~= sweepnode then + local head = getnext(sweepnode) + local tail = nil + if prev then + tail = prev + setfield(current,"prev",sweepnode) + else + tail = find_node_tail(head) + end + setfield(sweepnode,"next",current) + setfield(head,"prev",nil) + setfield(tail,"next",nil) + appenddisc(sweepnode,head) + end + end + + if l < s then + local i = l + local t = sweeptype == "post" or sweeptype == "replace" + while current and i < s do + local id = getid(current) + if id == glyph_code then + i = i + 1 + current = getnext(current) + elseif id == disc_code then + if keepdisc then + keepdisc = false + if notmatchpre[current] ~= notmatchreplace[current] then + lookaheaddisc = current + end + local replace = getfield(c,"replace") + while replace and i < s do + if getid(replace) == glyph_code then + i = i + 1 + end + replace = getnext(replace) + end + current = getnext(current) + elseif notmatchpre[current] ~= notmatchreplace[current] then + head, current = flattendisk(head,current) + else + current = getnext(current) -- HH + end + else + current = getnext(current) + end + if not current and t then + current = getnext(sweepnode) + if current then + sweeptype = nil + end + end + end + end + + if f > 1 then + local current = prev + local i = f + local t = sweeptype == "pre" or sweeptype == "replace" + if not current and t and current == checkdisk then + current = getprev(sweepnode) + end + while current and i > 1 do -- missing getprev added / moved outside + local id = getid(current) + if id == glyph_code then + i = i - 1 + elseif id == disc_code then + if keepdisc then + keepdisc = false + if notmatchpost[current] ~= notmatchreplace[current] then + backtrackdisc = current + end + local replace = getfield(current,"replace") + while replace and i > 1 do + if getid(replace) == glyph_code then + i = i - 1 + end + replace = getnext(replace) + end + elseif notmatchpost[current] ~= notmatchreplace[current] then + head, current = flattendisk(head,current) + end + end + current = getprev(current) + if t and current == checkdisk then + current = getprev(sweepnode) + end + end + end + + local ok = false + if lookaheaddisc then + + local cf = start + local cl = getprev(lookaheaddisc) + local cprev = getprev(start) + local insertedmarks = 0 + + while cprev and getid(cf) == glyph_code and getfont(cf) == currentfont and marks[getchar(cf)] and getsubtype(cf) < 256 do + insertedmarks = insertedmarks + 1 + cf = cprev + startishead = cf == head + cprev = getprev(cprev) + end + + setfield(lookaheaddisc,"prev",cprev) + if cprev then + setfield(cprev,"next",lookaheaddisc) + end + setfield(cf,"prev",nil) + setfield(cl,"next",nil) + if startishead then + head = lookaheaddisc + end + + local replace = getfield(lookaheaddisc,"replace") + local pre = getfield(lookaheaddisc,"pre") + local new = copy_node_list(cf) + local cnew = new + for i=1,insertedmarks do + cnew = getnext(cnew) + end + local clast = cnew + for i=f,l do + clast = getnext(clast) + end + if not notmatchpre[lookaheaddisc] then + cf, start, ok = chainproc(cf,start,last,dataset,sequence,chainlookup,rlmode,k) + end + if not notmatchreplace[lookaheaddisc] then + new, cnew, ok = chainproc(new,cnew,clast,dataset,sequence,chainlookup,rlmode,k) + end + if pre then + setfield(cl,"next",pre) + setfield(pre,"prev",cl) + end + if replace then + local tail = find_node_tail(new) + setfield(tail,"next",replace) + setfield(replace,"prev",tail) + end + setfield(lookaheaddisc,"pre",cf) -- also updates pre-tail + setfield(lookaheaddisc,"replace",new) -- also updates replace-tail + + start = getprev(lookaheaddisc) + sweephead[cf] = getnext(clast) + sweephead[new] = getnext(last) + + elseif backtrackdisc then + + local cf = getnext(backtrackdisc) + local cl = start + local cnext = getnext(start) + local insertedmarks = 0 + + while cnext and getid(cnext) == glyph_code and getfont(cnext) == currentfont and marks[getchar(cnext)] and getsubtype(cnext) < 256 do + insertedmarks = insertedmarks + 1 + cl = cnext + cnext = getnext(cnext) + end + if cnext then + setfield(cnext,"prev",backtrackdisc) + end + setfield(backtrackdisc,"next",cnext) + setfield(cf,"prev",nil) + setfield(cl,"next",nil) + local replace = getfield(backtrackdisc,"replace") + local post = getfield(backtrackdisc,"post") + local new = copy_node_list(cf) + local cnew = find_node_tail(new) + for i=1,insertedmarks do + cnew = getprev(cnew) + end + local clast = cnew + for i=f,l do + clast = getnext(clast) + end + if not notmatchpost[backtrackdisc] then + cf, start, ok = chainproc(cf,start,last,dataset,sequence,chainlookup,rlmode,k) + end + if not notmatchreplace[backtrackdisc] then + new, cnew, ok = chainproc(new,cnew,clast,dataset,sequence,chainlookup,rlmode,k) + end + if post then + local tail = find_node_tail(post) + setfield(tail,"next",cf) + setfield(cf,"prev",tail) + else + post = cf + end + if replace then + local tail = find_node_tail(replace) + setfield(tail,"next",new) + setfield(new,"prev",tail) + else + replace = new + end + setfield(backtrackdisc,"post",post) -- also updates post-tail + setfield(backtrackdisc,"replace",replace) -- also updates replace-tail + start = getprev(backtrackdisc) + sweephead[post] = getnext(clast) + sweephead[replace] = getnext(last) + + else + + head, start, ok = chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,k) + + end + + return head, start, ok +end + +-- helpers from elsewhere + +-- local function currentmatch(current,n,l) +-- while current do +-- if getid(current) ~= glyph_code then +-- return false +-- elseif seq[n][getchar(current)] then +-- n = n + 1 +-- current = getnext(current) +-- if not current then +-- return true, n, current +-- elseif n > l then +-- -- match = false +-- return true, n, current +-- end +-- else +-- return false +-- end +-- end +-- end +-- +-- local function aftermatch(current,n,l) +-- while current do +-- if getid(current) ~= glyph_code then +-- return false +-- elseif seq[n][getchar(current)] then +-- n = n + 1 +-- current = getnext(current) +-- if not current then +-- return true, n, current +-- elseif n > l then +-- -- match = false +-- return true, n, current +-- end +-- else +-- return false +-- end +-- end +-- end +-- +-- local function beforematch(current,n) +-- local finish = getprev(current) +-- local current = find_node_tail(current) +-- while current do +-- if getid(current) ~= glyph_code then +-- return false +-- elseif seq[n][getchar(current)] then +-- n = n - 1 +-- current = getprev(current) +-- if not current or current == finish then +-- return true, n, current +-- elseif n < 1 then +-- -- match = false +-- return true, n, current +-- end +-- else +-- return false +-- end +-- end +-- end + local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) + local sweepnode = sweepnode + local sweeptype = sweeptype + local diskseen = false + local checkdisc = getprev(head) local flags = sequence.flags local done = false local skipmark = flags[1] @@ -1791,7 +2202,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) local skipbase = flags[3] local markclass = sequence.markclass local skipped = false - for k=1,#contexts do + for k=1,#contexts do -- i've only seen ccmp having > 1 (e.g. dejavu) local match = true local current = start local last = start @@ -1809,7 +2220,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) local l = ck[5] -- current match if f == 1 and f == l then -- current only - -- already a hit -- do we need to check for mark? + -- already a hit -- match = true else -- before/current/after | before/current | current/after -- no need to test first hit (to be optimized) @@ -1819,6 +2230,10 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) local n = f + 1 last = getnext(last) while n <= l do + if not last and (sweeptype == "post" or sweeptype == "replace") then + last = getnext(sweepnode) + sweeptype = nil + end if last then local id = getid(last) if id == glyph_code then @@ -1851,39 +2266,59 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) break end elseif id == disc_code then - if check_discretionaries then - local replace = getfield(last,"replace") - if replace then - -- so far we never entered this branch - while replace do - if seq[n][getchar(replace)] then - n = n + 1 - replace = getnext(replace) - if not replace then - break - elseif n > l then - -- match = false - break - end - else - match = false + diskseen = true + notmatchpre[last] = nil + notmatchpost[last] = true + notmatchreplace[last] = nil + local pre = getfield(last,"pre") + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + pre = getnext(pre) + if not pre then + break + elseif n > l then break end + else + notmatchpre[last] = true + break end - if not match then + end + else + notmatchpre[last] = true + end + local replace = getfield(last,"replace") + if replace then + -- so far we never entered this branch + while replace do + if seq[n][getchar(replace)] then + n = n + 1 + replace = getnext(replace) + if not replace then + break + elseif n > l then + -- match = false + break + end + else + notmatchreplace[last] = true + if notmatchpre[last] then + match = false + end break - elseif check_discretionaries == "trace" then - report_chain("check disc action in current") end - else - last = getnext(last) -- no skipping here + end + if not match then + break end else last = getnext(last) -- no skipping here end else - match = false - break + last = getnext(last) -- no skipping here end else match = false @@ -1896,23 +2331,32 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) if match and f > 1 then local prev = getprev(start) if prev then - local n = f-1 - while n >= 1 do - if prev then - local id = getid(prev) - if id == glyph_code then - if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char - local char = getchar(prev) - local ccd = descriptions[char] - if ccd then - local class = ccd.class or "base" - if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then - skipped = true - if trace_skips then - show_skip(dataset,sequence,char,ck,class) + if prev == checkdisc and (sweeptype == "pre" or sweeptype == "replace") then + prev = getprev(sweepnode) + -- sweeptype = nil + end + if prev then + local n = f-1 + while n >= 1 do + if prev then + local id = getid(prev) + if id == glyph_code then + if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char + local char = getchar(prev) + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + skipped = true + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end + elseif seq[n][char] then + n = n -1 + else + match = false + break end - elseif seq[n][char] then - n = n -1 else match = false break @@ -1921,57 +2365,84 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) match = false break end - else - match = false - break - end - elseif id == disc_code then - -- the special case: f i where i becomes dottless i .. - if check_discretionaries then + elseif id == disc_code then + -- the special case: f i where i becomes dottless i .. + diskseen = true + notmatchpre[prev] = true + notmatchpost[prev] = nil + notmatchreplace[prev] = nil + local pre = getfield(prev,"pre") + local post = getfield(prev,"post") local replace = getfield(prev,"replace") - if replace then - -- we seldom enter this branch (e.g. on brill efficient) - replace = find_node_tail(replace) - local finish = getprev(replace) - while replace do - if seq[n][getchar(replace)] then - n = n - 1 - replace = getprev(replace) - if not replace or replace == finish then + if pre ~= start and post ~= start and replace ~= start then + if post then + local n = n + post = find_node_tail(post) + local finish = getprev(post) + while post do + if seq[n][getchar(post)] then + n = n - 1 + post = getprev(post) + if not post or post == finish then + break + elseif n < 1 then + break + end + else + notmatchpost[prev] = true break - elseif n < 1 then - -- match = false + end + end + else + notmatchpost[prev] = true + end + if replace then + -- we seldom enter this branch (e.g. on brill efficient) + replace = find_node_tail(replace) + local finish = getprev(replace) + while replace do + if seq[n][getchar(replace)] then + n = n - 1 + replace = getprev(replace) + if not replace or replace == finish then + break + elseif n < 1 then + -- match = false + break + end + else + notmatchreplace[prev] = true + if notmatchpost[prev] then + match = false + end break end - else - match = false + end + if not match then break end - end - if not match then - break - elseif check_discretionaries == "trace" then - report_chain("check disc action in before") + else + -- skip 'm end else -- skip 'm end + elseif seq[n][32] then + n = n -1 else - -- skip 'm + match = false + break end - elseif seq[n][32] then - n = n -1 + prev = getprev(prev) + elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces + n = n - 1 else match = false break end - prev = getprev(prev) - elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces - n = n - 1 - else - match = false - break end + else + match = false end else match = false @@ -1980,6 +2451,12 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) -- after if match and s > l then local current = last and getnext(last) + if not current then + if sweeptype == "post" or sweeptype == "replace" then + current = getnext(sweepnode) + -- sweeptype = nil + end + end if current then -- removed optimization for s-l == 1, we have to deal with marks anyway local n = l + 1 @@ -1989,13 +2466,13 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) if id == glyph_code then if getfont(current) == currentfont and getsubtype(current)<256 then -- normal char local char = getchar(current) - local ccd = descriptions[char] -- TODO: we have a marks array ! + local ccd = descriptions[char] if ccd then - local class = ccd.class or "base" + local class = ccd.class if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then skipped = true if trace_skips then - show_skip(dataset,sequence,char,ck,class) + show_skip(kind,chainname,char,ck,class) end elseif seq[n][char] then n = n + 1 @@ -2012,31 +2489,52 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) break end elseif id == disc_code then - if check_discretionaries then - local replace = getfield(current,"replace") - if replace then - -- so far we never entered this branch - while replace do - if seq[n][getchar(replace)] then - n = n + 1 - replace = getnext(replace) - if not replace then - break - elseif n > s then - break - end - else - match = false + diskseen = true + notmatchpre[current] = nil + notmatchpost[current] = true + notmatchreplace[current] = nil + local pre = getfield(current,"pre") + local replace = getfield(current,"replace") + if pre then + local n = n + while pre do + if seq[n][getchar(pre)] then + n = n + 1 + pre = getnext(pre) + if not pre then + break + elseif n > s then break end + else + notmatchpre[current] = true + break end - if not match then + 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 + replace = getnext(replace) + if not replace then + break + elseif n > s then + break + end + else + notmatchreplace[current] = true + if notmatchpre[current] then + match = false + end break - elseif check_discretionaries == "trace" then - report_chain("check disc action in after") end - else - -- skip 'm + end + if not match then + break end else -- skip 'm @@ -2062,6 +2560,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end if match then -- can lookups be of a different type ? + local diskchain = diskseen or sweepnode if trace_contexts then local rule = ck[1] local lookuptype = ck[8] @@ -2081,7 +2580,11 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) local chainproc = chainprocs[chainkind] if chainproc then local ok - head, start, ok = chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,1) + if diskchain then + head, start, ok = chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,1,ck,chainproc) + else + head, start, ok = chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,1) + end if ok then done = true end @@ -2117,7 +2620,11 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) local chainproc = chainprocs[chainkind] if chainproc then local ok, n - head, start, ok, n = chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,i) + if diskchain then + head, start, ok = chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,i,ck,chainproc) + else + head, start, ok, n = chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,i) + end -- messy since last can be changed ! if ok then done = true @@ -2161,8 +2668,16 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) end end end + if done then + break -- out of contexts (new, needs checking) + end end end + if diskseen then + notmatchpre = { } + notmatchpost = { } + notmatchreplace = { } + end return head, start, done end @@ -2221,13 +2736,7 @@ local function initialize(sequence,script,language,enabled) local scripts = features[kind] -- local languages = scripts[script] or scripts[wildcard] if languages and (languages[language] or languages[wildcard]) then - return { - valid, - autofeatures[kind] or false, - sequence.chain or 0, - kind, - sequence, - } + return { valid, autofeatures[kind] or false, sequence, kind } end end end @@ -2283,20 +2792,29 @@ local function kernrun(disc,run) report_run("kern") -- will be more detailed end -- - local prev = getprev(disc) -- todo, keep these in the main loop - local next = getnext(disc) -- todo, keep these in the main loop + local prev = getprev(disc) -- todo, keep these in the main loop + local next = getnext(disc) -- todo, keep these in the main loop -- - local pre = getfield(disc,"pre") - local post = getfield(disc,"post") - local replace = getfield(disc,"replace") + local pre = getfield(disc,"pre") + local post = getfield(disc,"post") + local replace = getfield(disc,"replace") -- - if pre or replace then - if not (prev and getid(prev) == glyph_code and getfont(prev) == currentfont and getsubtype(prev)<256) then + local prevmarks = prev + -- + -- can be optional, because why on earth do we get a disc after a mark (okay, maybe when a ccmp + -- has happened but then it should be in the disc so basically this test indicates an error) + -- + while prevmarks and getid(prevmarks) == glyph_code and marks[getchar(prevmarks)] and getfont(prevmarks) == currentfont and getsubtype(prevmarks) < 256 do + prevmarks = getprev(prevmarks) + end + -- + if prev and (pre or replace) then + if not (getid(prev) == glyph_code and getfont(prev) == currentfont and getsubtype(prev)<256) then prev = false end end - if post or replace then - if not (next and getid(next) == glyph_code and getfont(next) == currentfont and getsubtype(next)<256) then + if next and (post or replace) then + if not (getid(next) == glyph_code and getfont(next) == currentfont and getsubtype(next)<256) then next = false end end @@ -2307,7 +2825,7 @@ local function kernrun(disc,run) local nest = getprev(pre) setfield(pre,"prev",prev) setfield(prev,"next",pre) - run(prev,"preinjections") + run(prevmarks,"preinjections") setfield(pre,"prev",nest) setfield(prev,"next",disc) else @@ -2320,7 +2838,7 @@ local function kernrun(disc,run) local tail = find_node_tail(post) setfield(tail,"next",next) setfield(next,"prev",tail) - run(post,"postinjections",tail) + run(post,"postinjections",next) setfield(tail,"next",nil) setfield(next,"prev",disc) else @@ -2329,6 +2847,11 @@ local function kernrun(disc,run) -- if not replace and prev and next then -- this should be already done by discfound + setfield(prev,"next",next) + setfield(next,"prev",prev) + run(prevmarks,"injections",next) + setfield(prev,"next",disc) + setfield(next,"prev",disc) elseif prev and next then local tail = find_node_tail(replace) local nest = getprev(replace) @@ -2336,7 +2859,7 @@ local function kernrun(disc,run) setfield(prev,"next",replace) setfield(tail,"next",next) setfield(next,"prev",tail) - run(prev,"replaceinjections",tail) + run(prevmarks,"replaceinjections",next) setfield(replace,"prev",nest) setfield(prev,"next",disc) setfield(tail,"next",nil) @@ -2345,14 +2868,14 @@ local function kernrun(disc,run) local nest = getprev(replace) setfield(replace,"prev",prev) setfield(prev,"next",replace) - run(prev,"replaceinjections") + run(prevmarks,"replaceinjections") setfield(replace,"prev",nest) setfield(prev,"next",disc) - elseif next then + elseif next then local tail = find_node_tail(replace) setfield(tail,"next",next) setfield(next,"prev",tail) - run(replace,"replaceinjections",tail) + run(replace,"replaceinjections",next) setfield(tail,"next",nil) setfield(next,"prev",disc) else @@ -2363,6 +2886,15 @@ end -- the if new test might be dangerous as luatex will check / set some tail stuff -- in a temp node +local function checkdisc(str,d) -- only used when debugging + report_check("%s : [%s][%s][%s]", + str, + nodes.toutf(getfield(d,"pre")), + nodes.toutf(getfield(d,"post")), + nodes.toutf(getfield(d,"replace")) + ) +end + local function comprun(disc,run) if trace_compruns then report_run("comp: %s",languages.serializediscretionary(disc)) @@ -2370,6 +2902,8 @@ local function comprun(disc,run) -- local pre = getfield(disc,"pre") if pre then + sweepnode = disc + sweeptype = "pre" -- in alternative code preinjections is used (also used then for proeprties, saves a variable) local new, done = run(pre) if done then setfield(disc,"pre",new) @@ -2378,6 +2912,8 @@ local function comprun(disc,run) -- local post = getfield(disc,"post") if post then + sweepnode = disc + sweeptype = "post" local new, done = run(post) if done then setfield(disc,"post",new) @@ -2386,14 +2922,18 @@ local function comprun(disc,run) -- local replace = getfield(disc,"replace") if replace then + sweepnode = disc + sweeptype = "replace" local new, done = run(replace) if done then setfield(disc,"replace",new) end end + sweepnode = nil + sweeptype = nil end -local function testrun(disc,trun,crun) +local function testrun(disc,trun,crun) -- use helper local next = getnext(disc) if next then local replace = getfield(disc,"replace") @@ -2464,6 +3004,28 @@ end -- todo: maybe run lr and rl stretches +local function txtdirstate(start,stack,top,rlparmode) + local dir = getfield(start,"dir") + if dir == "+TRT" then + top = top + 1 + stack[top] = dir + return top, -1 + elseif dir == "+TLT" then + top = top + 1 + stack[top] = dir + return top, 1 + end + if dir == "-TRT" or dir == "-TLT" then + top = top - 1 + if dir == "+TRT" then + return top, -1 + else + return top, 1 + end + end + return top, rlparmode +end + local nesting = 0 local function featuresprocessor(head,font,attr) @@ -2500,11 +3062,14 @@ local function featuresprocessor(head,font,attr) end local rlmode = 0 + local done = false local datasets = otf.dataset(tfmdata,font,attr) local dirstack = { } -- could move outside function + sweephead = { } + -- We could work on sub start-stop ranges instead but I wonder if there is that -- much speed gain (experiments showed that it made not much sense) and we need -- to keep track of directions anyway. Also at some point I want to play with @@ -2514,7 +3079,7 @@ local function featuresprocessor(head,font,attr) -- so that multiple cases are also covered.) -- We don't goto the next node of a disc node is created so that we can then treat - -- the pre, post and replace. It's abit of a hack but works out ok for most cases. + -- the pre, post and replace. It's a bit of a hack but works out ok for most cases. -- there can be less subtype and attr checking in the comprun etc helpers @@ -2522,18 +3087,17 @@ local function featuresprocessor(head,font,attr) local dataset = datasets[s] ----- featurevalue = dataset[1] -- todo: pass to function instead of using a global local attribute = dataset[2] - local chain = dataset[3] -- sequence.chain or 0 - ----- kind = dataset[4] - local sequence = dataset[5] -- sequences[s] -- also dataset[5] + local sequence = dataset[3] -- sequences[s] -- also dataset[5] local rlparmode = 0 local topstack = 0 local success = false local typ = sequence.type - local gpossing = typ == "gpos_single" or typ == "gpos_pair" + 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 chain < 0 then + + if typ == "gsub_reversecontextchain" then -- chain < 0 -- this is a limited case, no special treatments like 'init' etc -- we need to get rid of this slide! probably no longer needed in latest luatex local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo @@ -2586,9 +3150,14 @@ local function featuresprocessor(head,font,attr) report_missing_cache(dataset,sequence) else - local function c_run(start) -- no need to check for 256 and attr probably also the same - local head = start - local done = false + local function c_run(head) -- no need to check for 256 and attr probably also the same + local done = false + local start = sweephead[head] + if start then + sweephead[head] = nil + else + start = head + end while start do local id = getid(start) if id ~= glyph_code then @@ -2596,12 +3165,13 @@ local function featuresprocessor(head,font,attr) start = getnext(start) elseif getfont(start) == font and getsubtype(start) < 256 then local a = 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 - if a then +-- 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 +-- if a then +if not a or (a == attr) then local lookupmatch = lookupcache[getchar(start)] if lookupmatch then -- sequence kan weg @@ -2630,12 +3200,13 @@ local function featuresprocessor(head,font,attr) local id = getid(start) if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then local a = 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 - if a then +-- 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 +-- if a then +if not a or (a == attr) then local lookupmatch = lookupcache[getchar(start)] if lookupmatch then -- hm, hyphens can match (tlig) so we need to really check -- if we need more than ligatures we can outline the code and use functions @@ -2664,12 +3235,13 @@ local function featuresprocessor(head,font,attr) local function d_run(prev) -- we can assume that prev and next are glyphs local a = getattr(prev,0) - if a then - a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute) - else - a = not attribute or getprop(prev,a_state) == attribute - end - if a then +-- if a then +-- a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute) +-- else +-- a = not attribute or getprop(prev,a_state) == attribute +-- end +-- if a then +if not a or (a == attr) then local lookupmatch = lookupcache[getchar(prev)] if lookupmatch then -- sequence kan weg @@ -2684,12 +3256,13 @@ local function featuresprocessor(head,font,attr) local function k_run(sub,injection,last) local a = getattr(sub,0) - if a then - a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute) - else - a = not attribute or getprop(sub,a_state) == attribute - end - if a then +-- if a then +-- a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute) +-- else +-- a = not attribute or getprop(sub,a_state) == attribute +-- end +-- if a then +if not a or (a == attr) then -- sequence kan weg for n in traverse_nodes(sub) do -- only gpos if n == last then @@ -2767,17 +3340,17 @@ local function featuresprocessor(head,font,attr) local subtype = getsubtype(start) if subtype == dir_code then local dir = getfield(start,"dir") - if dir == "+TRT" or dir == "+TLT" then + if dir == "+TLT" then topstack = topstack + 1 dirstack[topstack] = dir - elseif dir == "-TRT" or dir == "-TLT" then - topstack = topstack - 1 - end - local newdir = dirstack[topstack] - if newdir == "+TRT" then - rlmode = -1 - elseif newdir == "+TLT" then rlmode = 1 + elseif dir == "+TRT" then + topstack = topstack + 1 + dirstack[topstack] = dir + rlmode = -1 + elseif dir == "-TLT" or dir == "-TRT" then + topstack = topstack - 1 + rlmode = dirstack[topstack] == "+TLT" and 1 or -1 else rlmode = rlparmode end @@ -2810,9 +3383,14 @@ local function featuresprocessor(head,font,attr) else - local function c_run(start) - local head = start - local done = false + local function c_run(head) + local done = false + local start = sweephead[head] + if start then + sweephead[head] = nil + else + start = head + end while start do local id = getid(start) if id ~= glyph_code then @@ -2820,15 +3398,16 @@ local function featuresprocessor(head,font,attr) start = getnext(start) elseif getfont(start) == font and getsubtype(start) < 256 then local a = 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 - if a then +-- 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 +-- if a then +if not a or (a == attr) then local char = getchar(start) for i=1,nofsteps do - local step = steps[i] + local step = steps[i] local lookupcache = step.coverage if lookupcache then local lookupmatch = lookupcache[char] @@ -2864,12 +3443,13 @@ local function featuresprocessor(head,font,attr) local function d_run(prev) local a = getattr(prev,0) - if a then - a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute) - else - a = not attribute or getprop(prev,a_state) == attribute - end - if a then +-- if a then +-- a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute) +-- else +-- a = not attribute or getprop(prev,a_state) == attribute +-- end +-- if a then +if not a or (a == attr) then -- brr prev can be disc local char = getchar(prev) for i=1,nofsteps do @@ -2894,12 +3474,13 @@ local function featuresprocessor(head,font,attr) local function k_run(sub,injection,last) local a = getattr(sub,0) - if a then - a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute) - else - a = not attribute or getprop(sub,a_state) == attribute - end - if a then +-- if a then +-- a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute) +-- else +-- a = not attribute or getprop(sub,a_state) == attribute +-- end +-- if a then +if not a or (a == attr) then for n in traverse_nodes(sub) do -- only gpos if n == last then break @@ -2908,7 +3489,7 @@ local function featuresprocessor(head,font,attr) if id == glyph_code then local char = getchar(n) for i=1,nofsteps do - local step = steps[i] + local step = steps[i] local lookupcache = step.coverage if lookupcache then local lookupmatch = lookupcache[char] @@ -2935,12 +3516,13 @@ local function featuresprocessor(head,font,attr) local id = getid(start) if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then local a = 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 - if a then +-- 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 +-- if a then +if not a or (a == attr) then local char = getchar(start) for i=1,nofsteps do local step = steps[i] @@ -2988,7 +3570,7 @@ local function featuresprocessor(head,font,attr) end if a then for i=1,nofsteps do - local step = steps[i] + local step = steps[i] local lookupcache = step.coverage if lookupcache then local char = getchar(start) @@ -3042,18 +3624,17 @@ local function featuresprocessor(head,font,attr) elseif id == whatsit_code then local subtype = getsubtype(start) if subtype == dir_code then - local dir = getfield(start,"dir") - if dir == "+TRT" or dir == "+TLT" then + if dir == "+TLT" then topstack = topstack + 1 dirstack[topstack] = dir - elseif dir == "-TRT" or dir == "-TLT" then - topstack = topstack - 1 - end - local newdir = dirstack[topstack] - if newdir == "+TRT" then - rlmode = -1 - elseif newdir == "+TLT" then rlmode = 1 + elseif dir == "+TRT" then + topstack = topstack + 1 + dirstack[topstack] = dir + rlmode = -1 + elseif dir == "-TLT" or dir == "-TRT" then + topstack = topstack - 1 + rlmode = dirstack[topstack] == "+TLT" and 1 or -1 else rlmode = rlparmode end diff --git a/tex/context/base/lang-dis.lua b/tex/context/base/lang-dis.lua index bacd7f04b..91b0e9460 100644 --- a/tex/context/base/lang-dis.lua +++ b/tex/context/base/lang-dis.lua @@ -26,6 +26,8 @@ local getfont = nuts.getfont local getattr = nuts.getattr local getsubtype = nuts.getsubtype local getchar = nuts.getchar +local getdisc = nuts.getdisc +local setdisc = nuts.setdisc local copy_node = nuts.copy local free_node = nuts.free @@ -45,6 +47,103 @@ local setattribute = tex.setattribute local getlanguagedata = languages.getdata +-- local expanders = { +-- [disccodes.discretionary] = function(d,template) +-- -- \discretionary +-- return template +-- end, +-- [disccodes.explicit] = function(d,template) +-- -- \- +-- local pre = getfield(d,"pre") +-- if pre and getid(pre) == glyph_code and getchar(pre) <= 0 then +-- setfield(d,"pre",nil) +-- end +-- local post = getfield(d,"post") +-- if post and getid(post) == glyph_code and getchar(post) <= 0 then +-- setfield(d,"post",nil) +-- end +-- setfield(d,"subtype",discretionary_code) -- to be checked +-- return template +-- end, +-- [disccodes.automatic] = function(d,template) +-- -- following a - : the pre and post chars are already appended and set +-- -- so we have pre=preex and post=postex .. however, the previous +-- -- hyphen is already injected ... downside: the font handler sees this +-- -- so this is another argument for doing a hyphenation pass in context +-- if getfield(d,"pre") then +-- -- we have a preex characters and want that one to replace the +-- -- character in front which is the trigger +-- if not template then +-- -- can there be font kerns already? +-- template = getprev(d) +-- if template and getid(template) ~= glyph_code then +-- template = getnext(d) +-- if template and getid(template) ~= glyph_code then +-- template = nil +-- end +-- end +-- end +-- if template then +-- local pseudohead = getprev(template) +-- if pseudohead then +-- while template ~= d do +-- pseudohead, template, removed = remove_node(pseudohead,template) +-- setfield(d,"replace",removed) +-- -- break ? +-- end +-- else +-- -- can't happen +-- end +-- setfield(d,"subtype",discretionary_code) +-- else +-- -- print("lone regular discretionary ignored") +-- end +-- else +-- setfield(d,"subtype",discretionary_code) +-- end +-- return template +-- end, +-- [disccodes.regular] = function(d,template) +-- -- simple +-- if not template then +-- -- can there be font kerns already? +-- template = getprev(d) +-- if template and getid(template) ~= glyph_code then +-- template = getnext(d) +-- if template and getid(template) ~= glyph_code then +-- template = nil +-- end +-- end +-- end +-- if template then +-- local language = template and getfield(template,"lang") +-- local data = getlanguagedata(language) +-- local prechar = data.prehyphenchar +-- local postchar = data.posthyphenchar +-- if prechar and prechar > 0 then +-- local c = copy_node(template) +-- setfield(c,"char",prechar) +-- setfield(d,"pre",c) +-- end +-- if postchar and postchar > 0 then +-- local c = copy_node(template) +-- setfield(c,"char",postchar) +-- setfield(d,"post",c) +-- end +-- setfield(d,"subtype",discretionary_code) +-- else +-- -- print("lone regular discretionary ignored") +-- end +-- return template +-- end, +-- [disccodes.first] = function() +-- -- forget about them +-- end, +-- [disccodes.second] = function() +-- -- forget about them +-- end, +-- } + local expanders = { [disccodes.discretionary] = function(d,template) -- \discretionary @@ -52,15 +151,19 @@ local expanders = { end, [disccodes.explicit] = function(d,template) -- \- - local pre = getfield(d,"pre") + local pre, post, replace = getdisc(d) + local done = false if pre and getid(pre) == glyph_code and getchar(pre) <= 0 then - setfield(d,"pre",nil) + done = true + pre = nil end - local post = getfield(d,"post") if post and getid(post) == glyph_code and getchar(post) <= 0 then - setfield(d,"post",nil) + done = true + post = nil + end + if done then + setdisc(d,pre,post,replace,discretionary_code) end - setfield(d,"subtype",discretionary_code) -- to be checked return template end, [disccodes.automatic] = function(d,template) @@ -68,7 +171,8 @@ local expanders = { -- so we have pre=preex and post=postex .. however, the previous -- hyphen is already injected ... downside: the font handler sees this -- so this is another argument for doing a hyphenation pass in context - if getfield(d,"pre") then + local pre, post, replace = getdisc(d) + if pre then -- we have a preex characters and want that one to replace the -- character in front which is the trigger if not template then @@ -86,18 +190,19 @@ local expanders = { if pseudohead then while template ~= d do pseudohead, template, removed = remove_node(pseudohead,template) - setfield(d,"replace",removed) + -- free old replace ? + replace = removed -- break ? end else -- can't happen end - setfield(d,"subtype",discretionary_code) + setdisc(d,pre,post,replace,discretionary_code) else -- print("lone regular discretionary ignored") end else - setfield(d,"subtype",discretionary_code) + setdisc(d,pre,post,replace,discretionary_code) end return template end, @@ -118,17 +223,21 @@ local expanders = { local data = getlanguagedata(language) local prechar = data.prehyphenchar local postchar = data.posthyphenchar + local pre, post, replace = getdisc(d) -- pre can be set + local done = false if prechar and prechar > 0 then - local c = copy_node(template) - setfield(c,"char",prechar) - setfield(d,"pre",c) + done = true + pre = copy_node(template) + setfield(pre,"char",prechar) end if postchar and postchar > 0 then - local c = copy_node(template) - setfield(c,"char",postchar) - setfield(d,"post",c) + done = true + post = copy_node(template) + setfield(post,"char",postchar) + end + if done then + setdisc(d,pre,post,replace,discretionary_code) end - setfield(d,"subtype",discretionary_code) else -- print("lone regular discretionary ignored") end @@ -158,9 +267,7 @@ local setlistcolor = nodes.tracers.colors.setlist function languages.visualizediscretionaries(head) for d in traverse_id(disc_code,tonut(head)) do if getattr(d,a_visualize) then - local pre = getfield(d,"pre") - local post = getfield(d,"post") - local replace = getfield(d,"replace") + local pre, post, replace = getdisc(d) if pre then setlistcolor(pre,"darkred") end @@ -196,10 +303,11 @@ interfaces.implement { local toutf = nodes.listtoutf function languages.serializediscretionary(d) -- will move to tracer + local pre, post, replace = getdisc(d) return string.formatters["{%s}{%s}{%s}"]( - toutf(getfield(d,"pre")) or "", - toutf(getfield(d,"post")) or "", - toutf(getfield(d,"replace")) or "" + pre and toutf(pre) or "", + post and toutf(post) or "", + replace and toutf(replace) or "" ) end diff --git a/tex/context/base/m-newotf.mkiv b/tex/context/base/m-newotf.mkiv index 0bdd6ec8e..df91cbe02 100644 --- a/tex/context/base/m-newotf.mkiv +++ b/tex/context/base/m-newotf.mkiv @@ -26,7 +26,7 @@ "font-oup", "font-otl", "font-ots", -"font-ots-xxx", -- for me, testing +"font-ots-new", -- for testing (15% faster variant, but luatex update needed) "font-oto", "font-otd", "font-otc", diff --git a/tex/context/base/node-nut.lua b/tex/context/base/node-nut.lua index 554d74ec5..de03fd433 100644 --- a/tex/context/base/node-nut.lua +++ b/tex/context/base/node-nut.lua @@ -240,6 +240,109 @@ if not direct.mlist_to_hlist then end +-- new, a few experimental extra helpers that can speed up font handling 15% +-- especially a mix of complex (latin) features and discretionaries or complex +-- scripts with lots of contextual chains (for other use there is not that +-- much gain) + +if not direct.getdisc then + + local getid = nuts.getid + local getsubtype = nuts.getsubtype + local getfont = nuts.getfont + local getfield = nuts.getfield + local setfield = nuts.setfield + local findtail = nuts.tail + + local glyph_code = nodecodes.glyph + + -- this one saves finding tails (i.e. extra calls and passes) + + function direct.getdisc(n,tailtoo) + local pre = getfield(n,"pre") + local post = getfield(n,"post") + local replace = getfield(n,"replace") + if tailtoo then + return pre, post, replace, + pre and findtail(pre), + post and findtail(post), + replace and findtail(replace) + + else + return pre, post, replace + end + end + + -- this one is more efficient than three assignments and we need to + -- do it in order to updat ethe internal tail data (will change) + + function direct.setdisc(n,pre,post,replace,subtype) + setfield(n,"pre",pre) + setfield(n,"post",post) + setfield(n,"replace",replace) + if subtype then + setfield(n,"subtype",subtype) + end + end + + -- very small speedup but more convenient + + function direct.setchar(n,chr) + setfield(n,"char",chr) + end + + function direct.setnext(n,next) + setfield(n,"next",next) + end + + function direct.setprev(g,prev) + setfield(n,"prev",prev) + end + + function direct.setboth(n,prev,next) + if n then + setfield(n,"next",next) + setfield(n,"prev",prev) + end + end + + function direct.getboth(n) + if n then + return getfield(n,"prev"), getfield(n,"prev") + end + end + + function direct.setlink(a,b) + if a then + if b then + setfield(a,"next",b) + setfield(b,"prev",a) + else + setfield(a,"next",nil) + end + elseif b then + setfield(b,"prev",nil) + end + end + + -- this one saves a lot (one call instead of 3) + + function direct.is_char(g,font) + return getid(g) == glyph_code and getsubtype(g) < 256 and (not font or getfont(g) == font) + end + +end + +nuts.getdisc = direct.getdisc +nuts.setdisc = direct.setdisc +nuts.setchar = direct.setchar +nuts.setnext = direct.setnext +nuts.setprev = direct.setprev +nuts.setboth = direct.setboth +nuts.getboth = direct.getboth +nuts.setlink = direct.setlink +nuts.is_char = direct.is_char + -- local d_remove_node = direct.remove diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf Binary files differindex 4d56f3d84..7ccbf6cdc 100644 --- a/tex/context/base/status-files.pdf +++ b/tex/context/base/status-files.pdf diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf Binary files differindex c12c6a530..c8718cfe4 100644 --- a/tex/context/base/status-lua.pdf +++ b/tex/context/base/status-lua.pdf diff --git a/tex/context/base/util-fil.lua b/tex/context/base/util-fil.lua index 3e8c8ea23..42bbe37b3 100644 --- a/tex/context/base/util-fil.lua +++ b/tex/context/base/util-fil.lua @@ -21,7 +21,9 @@ local zerobased = { } function files.open(filename,zb) local f = io.open(filename,"rb") - zerobased[f] = zb or false + if f then + zerobased[f] = zb or false + end return f end |