diff options
Diffstat (limited to 'tex')
-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 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts-inj.lua | 291 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts-merged.lua | 1234 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts-otn.lua | 1225 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts.lua | 2 |
17 files changed, 4159 insertions, 1513 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 diff --git a/tex/generic/context/luatex/luatex-fonts-inj.lua b/tex/generic/context/luatex/luatex-fonts-inj.lua index 332e92033..cdf14b935 100644 --- a/tex/generic/context/luatex/luatex-fonts-inj.lua +++ b/tex/generic/context/luatex/luatex-fonts-inj.lua @@ -8,7 +8,16 @@ if not modules then modules = { } end modules ['font-inj'] = { -- This property based variant is not faster but looks nicer than the attribute one. We -- need to use rawget (which is apbout 4 times slower than a direct access but we cannot --- get/set that one for our purpose! +-- get/set that one for our purpose! This version does a bit more with discretionaries +-- (and Kai has tested it with his collection of weird fonts.) + +-- There is some duplicate code here (especially in the the pre/post/replace branches) but +-- we go for speed. We could store a list of glyph and mark nodes when registering but it's +-- cleaner to have an identification pass here. Also, I need to keep tracing in mind so +-- being too clever here is dangerous. + +-- The subtype test is not needed as there will be no (new) properties set, given that we +-- reset the properties. if not nodes.properties then return end @@ -80,8 +89,7 @@ function injections.resetcounts() keepregisteredcounts = false end --- We need to make sure that a possible metatable will not kick in --- unexpectedly. +-- We need to make sure that a possible metatable will not kick in unexpectedly. function injections.reset(n) local p = rawget(properties,n) @@ -146,7 +154,8 @@ end function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) -- hm: nuts or nodes local dx = factor*(exit[1]-entry[1]) local dy = -factor*(exit[2]-entry[2]) - local ws, wn = tfmstart.width, tfmnext.width + local ws = tfmstart.width + local wn = tfmnext.width nofregisteredcursives = nofregisteredcursives + 1 if rlmode < 0 then dx = -(dx + wn) @@ -205,9 +214,12 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l if rlmode and rlmode < 0 then leftkern, rightkern = rightkern, leftkern end + if not injection then + injection = "injections" + end local p = rawget(properties,current) if p then - local i = rawget(p,"injections") + local i = rawget(p,injection) if i then if leftkern ~= 0 then i.leftkern = (i.leftkern or 0) + leftkern @@ -219,19 +231,19 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l i.yoffset = (i.yoffset or 0) + yoffset end elseif leftkern ~= 0 or rightkern ~= 0 then - p.injections = { + p[injection] = { leftkern = leftkern, rightkern = rightkern, yoffset = yoffset, } else - p.injections = { + p[injection] = { yoffset = yoffset, } end elseif leftkern ~= 0 or rightkern ~= 0 then properties[current] = { - injections = { + [injection] = { leftkern = leftkern, rightkern = rightkern, yoffset = yoffset, @@ -239,7 +251,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l } else properties[current] = { - injections = { + [injection] = { yoffset = yoffset, }, } @@ -250,10 +262,9 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l return x, y, w, h -- no bound end --- this needs checking for rl < 0 but it is unlikely that a r2l script --- uses kernclasses between glyphs so we're probably safe (KE has a --- problematic font where marks interfere with rl < 0 in the previous --- case) +-- This needs checking for rl < 0 but it is unlikely that a r2l script uses kernclasses between +-- glyphs so we're probably safe (KE has a problematic font where marks interfere with rl < 0 in +-- the previous case) function injections.setkern(current,factor,rlmode,x,injection) local dx = factor * x @@ -285,7 +296,7 @@ function injections.setkern(current,factor,rlmode,x,injection) end end -function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=baseanchor, ma=markanchor +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=baseanchor, ma=markanchor local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) nofregisteredmarks = nofregisteredmarks + 1 -- markanchors[nofregisteredmarks] = base @@ -293,14 +304,20 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean dx = tfmbase.width - dx -- see later commented ox end local p = rawget(properties,start) + -- hm, dejavu serif does a sloppy mark2mark before mark2base if p then local i = rawget(p,"injections") if i then - i.markx = dx - i.marky = dy - i.markdir = rlmode or 0 - i.markbase = nofregisteredmarks - i.markbasenode = base + if i.markmark then + -- out of order mkmk: yes or no or option + else + i.markx = dx + i.marky = dy + i.markdir = rlmode or 0 + i.markbase = nofregisteredmarks + i.markbasenode = base + i.markmark = mkmk + end else p.injections = { markx = dx, @@ -308,6 +325,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean markdir = rlmode or 0, markbase = nofregisteredmarks, markbasenode = base, + markmark = mkmk, } end else @@ -318,6 +336,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean markdir = rlmode or 0, markbase = nofregisteredmarks, markbasenode = base, + markmark = mkmk, }, } end @@ -430,30 +449,32 @@ local function show_result(head) end end --- we could also check for marks here but maybe not all are registered (needs checking) - -local function collect_glyphs_1(head) - local glyphs, nofglyphs = { }, 0 - local marks, nofmarks = { }, 0 +local function collect_glyphs(head,offsets) + local glyphs, glyphi, nofglyphs = { }, { }, 0 + local marks, marki, nofmarks = { }, { }, 0 local nf, tm = nil, nil - for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts - if getsubtype(n) < 256 then - local f = getfont(n) - if f ~= nf then - nf = f - tm = fontdata[nf].resources.marks -- other hash in ctx - end - if tm and tm[getchar(n)] then - nofmarks = nofmarks + 1 - marks[nofmarks] = n - else - nofglyphs = nofglyphs + 1 - glyphs[nofglyphs] = n - end + local n = head + + local function identify(n,what) + local f = getfont(n) + if f ~= nf then + nf = f + tm = fontdata[nf].resources.marks -- other hash in ctx + end + if tm and tm[getchar(n)] then + nofmarks = nofmarks + 1 + marks[nofmarks] = n + marki[nofmarks] = "injections" + else + nofglyphs = nofglyphs + 1 + glyphs[nofglyphs] = n + glyphi[nofglyphs] = what + end + if offsets then -- yoffsets can influence curs steps local p = rawget(properties,n) if p then - local i = rawget(p,"injections") + local i = rawget(p,what) if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then @@ -463,38 +484,50 @@ local function collect_glyphs_1(head) end end end - return glyphs, nofglyphs, marks, nofmarks -end -local function collect_glyphs_2(head) - local glyphs, nofglyphs = { }, 0 - local marks, nofmarks = { }, 0 - local nf, tm = nil, nil - for n in traverse_id(glyph_code,head) do - if getsubtype(n) < 256 then - local f = getfont(n) - if f ~= nf then - nf = f - tm = fontdata[nf].resources.marks -- other hash in ctx - end - if tm and tm[getchar(n)] then - nofmarks = nofmarks + 1 - marks[nofmarks] = n - else - nofglyphs = nofglyphs + 1 - glyphs[nofglyphs] = n - end + while n do -- only needed for relevant fonts + local id = getid(n) + if id == glyph_code then + identify(n,"injections") + elseif id == disc_code then + local d = getfield(n,"pre") + if d then + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + identify(n,"preinjections") + end + end + end + local d = getfield(n,"post") + if d then + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + identify(n,"postinjections") + end + end + end + local d = getfield(n,"replace") + if d then + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + identify(n,"replaceinjections") + end + end + end end + n = getnext(n) end - return glyphs, nofglyphs, marks, nofmarks + + return glyphs, glyphi, nofglyphs, marks, marki, nofmarks end -local function inject_marks(marks,nofmarks) +local function inject_marks(marks,marki,nofmarks) for i=1,nofmarks do - local n = marks[i] + local n = marks[i] local pn = rawget(properties,n) if pn then - pn = rawget(pn,"injections") + local ni = marki[i] + local pn = rawget(pn,ni) if pn then local p = pn.markbasenode if p then @@ -503,7 +536,7 @@ local function inject_marks(marks,nofmarks) local rightkern = nil local pp = rawget(properties,p) if pp then - pp = rawget(pp,"injections") + pp = rawget(pp,ni) if pp then rightkern = pp.rightkern end @@ -518,11 +551,10 @@ local function inject_marks(marks,nofmarks) -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern local leftkern = pp.leftkern if leftkern then - ox = px - pn.markx - else ox = px - pn.markx - leftkern + else + ox = px - pn.markx end --- report_injections("l2r case 1: %p",ox) end else -- we need to deal with fonts that have marks with width @@ -548,12 +580,13 @@ local function inject_marks(marks,nofmarks) setfield(n,"xoffset",ox) -- local py = getfield(p,"yoffset") - local oy = 0 - if marks[p] then - oy = py + pn.marky - else - oy = getfield(n,"yoffset") + py + pn.marky - end +-- local oy = 0 +-- if marks[p] then +-- oy = py + pn.marky +-- else +-- oy = getfield(n,"yoffset") + py + pn.marky +-- end + local oy = getfield(n,"yoffset") + py + pn.marky setfield(n,"yoffset",oy) else -- normally this can't happen (only when in trace mode which is a special case anyway) @@ -564,14 +597,14 @@ local function inject_marks(marks,nofmarks) end end -local function inject_cursives(glyphs,nofglyphs) +local function inject_cursives(glyphs,glyphi,nofglyphs) local cursiveanchor, lastanchor = nil, nil local minc, maxc, last = 0, 0, nil for i=1,nofglyphs do - local n = glyphs[i] + local n = glyphs[i] local pn = rawget(properties,n) if pn then - pn = rawget(pn,"injections") + pn = rawget(pn,glyphi[i]) end if pn then local cursivex = pn.cursivex @@ -630,7 +663,7 @@ local function inject_cursives(glyphs,nofglyphs) -- if maxc > 0 and not cursiveanchor then -- local ny = getfield(n,"yoffset") -- for i=maxc,minc,-1 do - -- local ti = glyphs[i] + -- local ti = glyphs[i][1] -- ny = ny + properties[ti].cursivedy -- setfield(ti,"yoffset",ny) -- why not add ? -- end @@ -647,23 +680,67 @@ local function inject_cursives(glyphs,nofglyphs) end end -local function inject_kerns(head,list,length) - -- todo: pre/post/replace +-- G +D-pre G +-- D-post+ +-- +D-replace+ +-- +-- G +D-pre +D-pre +-- D-post +D-post +-- +D-replace +D-replace + +local function inject_kerns(head,glist,ilist,length) -- not complete ! compare with inject_kerns_only (but unlikely disc here) for i=1,length do - local n = list[i] + local n = glist[i] local pn = rawget(properties,n) if pn then - local i = rawget(pn,"injections") - if i then - local leftkern = i.leftkern - if leftkern and leftkern ~= 0 then - insert_node_before(head,n,newkern(leftkern)) -- type 0/2 - end - local rightkern = i.rightkern - if rightkern and rightkern ~= 0 then - insert_node_after(head,n,newkern(rightkern)) -- type 0/2 - end - end + local dp = nil + local dr = nil + local ni = ilist[i] + local p = nil + if ni == "injections" then + p = getprev(n) + if p then + local id = getid(p) + if id == disc_code then + dp = getfield(p,"post") + dr = getfield(p,"replace") + end + end + end + if dp then + local i = rawget(pn,"postinjections") + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + local t = find_tail(dp) + insert_node_after(dp,t,newkern(leftkern)) +setfield(p,"post",dp) -- currently we need to force a tail refresh + end + end + end + if dr then + local i = rawget(pn,"replaceinjections") + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + local t = find_tail(dr) + insert_node_after(dr,t,newkern(leftkern)) +setfield(p,"replace",dr) -- currently we need to force a tail refresh + end + end + else + local i = rawget(pn,ni) + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + insert_node_before(head,n,newkern(leftkern)) -- type 0/2 + end + local rightkern = i.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) -- type 0/2 + end + end + end end end end @@ -673,23 +750,18 @@ local function inject_everything(head,where) if trace_injections then trace(head,"everything") end - local glyphs, nofglyphs, marks, nofmarks - if nofregisteredpairs > 0 then - glyphs, nofglyphs, marks, nofmarks = collect_glyphs_1(head) - else - glyphs, nofglyphs, marks, nofmarks = collect_glyphs_2(head) - end + local glyphs, glyphi, nofglyphs, marks, marki, nofmarks = collect_glyphs(head,nofregisteredpairs > 0) if nofglyphs > 0 then if nofregisteredcursives > 0 then - inject_cursives(glyphs,nofglyphs) + inject_cursives(glyphs,glyphi,nofglyphs) end if nofregisteredmarks > 0 then -- and nofmarks > 0 - inject_marks(marks,nofmarks) + inject_marks(marks,marki,nofmarks) end - inject_kerns(head,glyphs,nofglyphs) + inject_kerns(head,glyphs,glyphi,nofglyphs) end if nofmarks > 0 then - inject_kerns(head,marks,nofmarks) + inject_kerns(head,marks,marki,nofmarks) end if keepregisteredcounts then keepregisteredcounts = false @@ -702,6 +774,14 @@ local function inject_everything(head,where) return tonode(head), true end +-- G +D-pre G +-- D-post+ +-- +D-replace+ +-- +-- G +D-pre +D-pre +-- D-post +D-post +-- +D-replace +D-replace + local function inject_kerns_only(head,where) head = tonut(head) if trace_injections then @@ -724,6 +804,7 @@ local function inject_kerns_only(head,where) if leftkern and leftkern ~= 0 then local t = find_tail(d) insert_node_after(d,t,newkern(leftkern)) +setfield(p,"post",d) -- currently we need to force a tail refresh end end end @@ -735,6 +816,7 @@ local function inject_kerns_only(head,where) if leftkern and leftkern ~= 0 then local t = find_tail(d) insert_node_after(d,t,newkern(leftkern)) +setfield(p,"replace",d) -- currently we need to force a tail refresh end end else @@ -784,6 +866,7 @@ local function inject_kerns_only(head,where) setfield(n,"pre",h) end end + -- weird local d = getfield(n,"post") if d then local h = d @@ -852,7 +935,7 @@ local function inject_pairs_only(head,where) end -- local n = head - local p = nil + local p = nil -- disc node when non-nil while n do local id = getid(n) if id == glyph_code then @@ -868,6 +951,7 @@ local function inject_pairs_only(head,where) if leftkern and leftkern ~= 0 then local t = find_tail(d) insert_node_after(d,t,newkern(leftkern)) +setfield(p,"post",d) -- currently we need to force a tail refresh end -- local rightkern = i.rightkern -- if rightkern and rightkern ~= 0 then @@ -884,6 +968,7 @@ local function inject_pairs_only(head,where) if leftkern and leftkern ~= 0 then local t = find_tail(d) insert_node_after(d,t,newkern(leftkern)) +setfield(p,"replace",d) -- currently we need to force a tail refresh end -- local rightkern = i.rightkern -- if rightkern and rightkern ~= 0 then @@ -1042,7 +1127,7 @@ local function inject_pairs_only(head,where) return tonode(head), true end -function injections.handler(head,where) -- optimize for n=1 ? +function injections.handler(head,where) if nofregisteredmarks > 0 or nofregisteredcursives > 0 then return inject_everything(head,where) elseif nofregisteredpairs > 0 then diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 97222c733..da21fa6d8 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 08/30/15 23:03:00 +-- merge date : 09/01/15 11:10:11 do -- begin closure to overcome local limits and interference @@ -10205,7 +10205,8 @@ end function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) local dx=factor*(exit[1]-entry[1]) local dy=-factor*(exit[2]-entry[2]) - local ws,wn=tfmstart.width,tfmnext.width + local ws=tfmstart.width + local wn=tfmnext.width nofregisteredcursives=nofregisteredcursives+1 if rlmode<0 then dx=-(dx+wn) @@ -10262,9 +10263,12 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) if rlmode and rlmode<0 then leftkern,rightkern=rightkern,leftkern end + if not injection then + injection="injections" + end local p=rawget(properties,current) if p then - local i=rawget(p,"injections") + local i=rawget(p,injection) if i then if leftkern~=0 then i.leftkern=(i.leftkern or 0)+leftkern @@ -10276,19 +10280,19 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) i.yoffset=(i.yoffset or 0)+yoffset end elseif leftkern~=0 or rightkern~=0 then - p.injections={ + p[injection]={ leftkern=leftkern, rightkern=rightkern, yoffset=yoffset, } else - p.injections={ + p[injection]={ yoffset=yoffset, } end elseif leftkern~=0 or rightkern~=0 then properties[current]={ - injections={ + [injection]={ leftkern=leftkern, rightkern=rightkern, yoffset=yoffset, @@ -10296,7 +10300,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) } else properties[current]={ - injections={ + [injection]={ yoffset=yoffset, }, } @@ -10335,7 +10339,7 @@ function injections.setkern(current,factor,rlmode,x,injection) return 0,0 end end -function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) nofregisteredmarks=nofregisteredmarks+1 if rlmode>=0 then @@ -10345,11 +10349,15 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) if p then local i=rawget(p,"injections") if i then - i.markx=dx - i.marky=dy - i.markdir=rlmode or 0 - i.markbase=nofregisteredmarks - i.markbasenode=base + if i.markmark then + else + i.markx=dx + i.marky=dy + i.markdir=rlmode or 0 + i.markbase=nofregisteredmarks + i.markbasenode=base + i.markmark=mkmk + end else p.injections={ markx=dx, @@ -10357,6 +10365,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) markdir=rlmode or 0, markbase=nofregisteredmarks, markbasenode=base, + markmark=mkmk, } end else @@ -10367,6 +10376,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) markdir=rlmode or 0, markbase=nofregisteredmarks, markbasenode=base, + markmark=mkmk, }, } end @@ -10471,27 +10481,30 @@ local function show_result(head) current=getnext(current) end end -local function collect_glyphs_1(head) - local glyphs,nofglyphs={},0 - local marks,nofmarks={},0 +local function collect_glyphs(head,offsets) + local glyphs,glyphi,nofglyphs={},{},0 + local marks,marki,nofmarks={},{},0 local nf,tm=nil,nil - for n in traverse_id(glyph_code,head) do - if getsubtype(n)<256 then - local f=getfont(n) - if f~=nf then - nf=f - tm=fontdata[nf].resources.marks - end - if tm and tm[getchar(n)] then - nofmarks=nofmarks+1 - marks[nofmarks]=n - else - nofglyphs=nofglyphs+1 - glyphs[nofglyphs]=n - end + local n=head + local function identify(n,what) + local f=getfont(n) + if f~=nf then + nf=f + tm=fontdata[nf].resources.marks + end + if tm and tm[getchar(n)] then + nofmarks=nofmarks+1 + marks[nofmarks]=n + marki[nofmarks]="injections" + else + nofglyphs=nofglyphs+1 + glyphs[nofglyphs]=n + glyphi[nofglyphs]=what + end + if offsets then local p=rawget(properties,n) if p then - local i=rawget(p,"injections") + local i=rawget(p,what) if i then local yoffset=i.yoffset if yoffset and yoffset~=0 then @@ -10501,36 +10514,47 @@ local function collect_glyphs_1(head) end end end - return glyphs,nofglyphs,marks,nofmarks -end -local function collect_glyphs_2(head) - local glyphs,nofglyphs={},0 - local marks,nofmarks={},0 - local nf,tm=nil,nil - for n in traverse_id(glyph_code,head) do - if getsubtype(n)<256 then - local f=getfont(n) - if f~=nf then - nf=f - tm=fontdata[nf].resources.marks - end - if tm and tm[getchar(n)] then - nofmarks=nofmarks+1 - marks[nofmarks]=n - else - nofglyphs=nofglyphs+1 - glyphs[nofglyphs]=n - end + while n do + local id=getid(n) + if id==glyph_code then + identify(n,"injections") + elseif id==disc_code then + local d=getfield(n,"pre") + if d then + for n in traverse_id(glyph_code,d) do + if getsubtype(n)<256 then + identify(n,"preinjections") + end + end + end + local d=getfield(n,"post") + if d then + for n in traverse_id(glyph_code,d) do + if getsubtype(n)<256 then + identify(n,"postinjections") + end + end + end + local d=getfield(n,"replace") + if d then + for n in traverse_id(glyph_code,d) do + if getsubtype(n)<256 then + identify(n,"replaceinjections") + end + end + end end + n=getnext(n) end - return glyphs,nofglyphs,marks,nofmarks + return glyphs,glyphi,nofglyphs,marks,marki,nofmarks end -local function inject_marks(marks,nofmarks) +local function inject_marks(marks,marki,nofmarks) for i=1,nofmarks do local n=marks[i] local pn=rawget(properties,n) if pn then - pn=rawget(pn,"injections") + local ni=marki[i] + local pn=rawget(pn,ni) if pn then local p=pn.markbasenode if p then @@ -10539,7 +10563,7 @@ local function inject_marks(marks,nofmarks) local rightkern=nil local pp=rawget(properties,p) if pp then - pp=rawget(pp,"injections") + pp=rawget(pp,ni) if pp then rightkern=pp.rightkern end @@ -10550,9 +10574,9 @@ local function inject_marks(marks,nofmarks) else local leftkern=pp.leftkern if leftkern then - ox=px-pn.markx - else ox=px-pn.markx-leftkern + else + ox=px-pn.markx end end else @@ -10565,12 +10589,7 @@ local function inject_marks(marks,nofmarks) end setfield(n,"xoffset",ox) local py=getfield(p,"yoffset") - local oy=0 - if marks[p] then - oy=py+pn.marky - else - oy=getfield(n,"yoffset")+py+pn.marky - end + local oy=getfield(n,"yoffset")+py+pn.marky setfield(n,"yoffset",oy) else end @@ -10578,14 +10597,14 @@ local function inject_marks(marks,nofmarks) end end end -local function inject_cursives(glyphs,nofglyphs) +local function inject_cursives(glyphs,glyphi,nofglyphs) local cursiveanchor,lastanchor=nil,nil local minc,maxc,last=0,0,nil for i=1,nofglyphs do local n=glyphs[i] local pn=rawget(properties,n) if pn then - pn=rawget(pn,"injections") + pn=rawget(pn,glyphi[i]) end if pn then local cursivex=pn.cursivex @@ -10651,22 +10670,59 @@ local function inject_cursives(glyphs,nofglyphs) end end end -local function inject_kerns(head,list,length) +local function inject_kerns(head,glist,ilist,length) for i=1,length do - local n=list[i] + local n=glist[i] local pn=rawget(properties,n) if pn then - local i=rawget(pn,"injections") - if i then - local leftkern=i.leftkern - if leftkern and leftkern~=0 then - insert_node_before(head,n,newkern(leftkern)) - end - local rightkern=i.rightkern - if rightkern and rightkern~=0 then - insert_node_after(head,n,newkern(rightkern)) - end - end + local dp=nil + local dr=nil + local ni=ilist[i] + local p=nil + if ni=="injections" then + p=getprev(n) + if p then + local id=getid(p) + if id==disc_code then + dp=getfield(p,"post") + dr=getfield(p,"replace") + end + end + end + if dp then + local i=rawget(pn,"postinjections") + if i then + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + local t=find_tail(dp) + insert_node_after(dp,t,newkern(leftkern)) +setfield(p,"post",dp) + end + end + end + if dr then + local i=rawget(pn,"replaceinjections") + if i then + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + local t=find_tail(dr) + insert_node_after(dr,t,newkern(leftkern)) +setfield(p,"replace",dr) + end + end + else + local i=rawget(pn,ni) + if i then + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + insert_node_before(head,n,newkern(leftkern)) + end + local rightkern=i.rightkern + if rightkern and rightkern~=0 then + insert_node_after(head,n,newkern(rightkern)) + end + end + end end end end @@ -10675,23 +10731,18 @@ local function inject_everything(head,where) if trace_injections then trace(head,"everything") end - local glyphs,nofglyphs,marks,nofmarks - if nofregisteredpairs>0 then - glyphs,nofglyphs,marks,nofmarks=collect_glyphs_1(head) - else - glyphs,nofglyphs,marks,nofmarks=collect_glyphs_2(head) - end + local glyphs,glyphi,nofglyphs,marks,marki,nofmarks=collect_glyphs(head,nofregisteredpairs>0) if nofglyphs>0 then if nofregisteredcursives>0 then - inject_cursives(glyphs,nofglyphs) + inject_cursives(glyphs,glyphi,nofglyphs) end if nofregisteredmarks>0 then - inject_marks(marks,nofmarks) + inject_marks(marks,marki,nofmarks) end - inject_kerns(head,glyphs,nofglyphs) + inject_kerns(head,glyphs,glyphi,nofglyphs) end if nofmarks>0 then - inject_kerns(head,marks,nofmarks) + inject_kerns(head,marks,marki,nofmarks) end if keepregisteredcounts then keepregisteredcounts=false @@ -10725,6 +10776,7 @@ local function inject_kerns_only(head,where) if leftkern and leftkern~=0 then local t=find_tail(d) insert_node_after(d,t,newkern(leftkern)) +setfield(p,"post",d) end end end @@ -10736,6 +10788,7 @@ local function inject_kerns_only(head,where) if leftkern and leftkern~=0 then local t=find_tail(d) insert_node_after(d,t,newkern(leftkern)) +setfield(p,"replace",d) end end else @@ -10850,7 +10903,7 @@ local function inject_pairs_only(head,where) trace(head,"pairs") end local n=head - local p=nil + local p=nil while n do local id=getid(n) if id==glyph_code then @@ -10866,6 +10919,7 @@ local function inject_pairs_only(head,where) if leftkern and leftkern~=0 then local t=find_tail(d) insert_node_after(d,t,newkern(leftkern)) +setfield(p,"post",d) end end end @@ -10877,6 +10931,7 @@ local function inject_pairs_only(head,where) if leftkern and leftkern~=0 then local t=find_tail(d) insert_node_after(d,t,newkern(leftkern)) +setfield(p,"replace",d) end end else @@ -11022,7 +11077,7 @@ local function inject_pairs_only(head,where) end return tonode(head),true end -function injections.handler(head,where) +function injections.handler(head,where) if nofregisteredmarks>0 or nofregisteredcursives>0 then return inject_everything(head,where) elseif nofregisteredpairs>0 then @@ -11422,14 +11477,12 @@ if not modules then modules={} end modules ['font-otn']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files", } -local concat,insert,remove=table.concat,table.insert,table.remove -local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip -local type,next,tonumber,tostring=type,next,tonumber,tostring -local lpegmatch=lpeg.match +local type,next,tonumber=type,next,tonumber local random=math.random local formatters=string.formatters local logs,trackers,nodes,attributes=logs,trackers,nodes,attributes local registertracker=trackers.register +local registerdirective=directives.register local fonts=fonts local otf=fonts.handlers.otf local trace_lookups=false registertracker("otf.lookups",function(v) trace_lookups=v end) @@ -11448,6 +11501,14 @@ local trace_applied=false registertracker("otf.applied",function(v) trace_applie local trace_steps=false registertracker("otf.steps",function(v) trace_steps=v end) local trace_skips=false registertracker("otf.skips",function(v) trace_skips=v end) local trace_directions=false registertracker("otf.directions",function(v) trace_directions=v end) +local trace_kernruns=false registertracker("otf.kernruns",function(v) trace_kernruns=v end) +local trace_discruns=false registertracker("otf.discruns",function(v) trace_discruns=v end) +local trace_compruns=false registertracker("otf.compruns",function(v) trace_compruns=v end) +local quit_on_no_replacement=true +local check_discretionaries=true +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") @@ -11610,6 +11671,48 @@ local function copy_glyph(g) return n end end +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) + 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 + end +end local function markstoligature(kind,lookupname,head,start,stop,char) if start==stop and getchar(start)==char then return head,start @@ -11654,14 +11757,66 @@ local function getcomponentindex(start) return 0 end end +local a_noligature=attributes.private("noligature") +local prehyphenchar=languages and languages.prehyphenchar +local posthyphenchar=languages and languages.posthyphenchar +if prehyphenchar then +elseif context then + report_warning("no language support") os.exit() +else + local newlang=lang.new + local getpre=lang.prehyphenchar + local getpost=lang.posthyphenchar + prehyphenchar=function(l) local l=newlang(l) return l and getpre (l) or -1 end + posthyphenchar=function(l) local l=newlang(l) return l and getpost (l) or -1 end +end +local function addhyphens(template,pre,post) + local l=getfield(template,"lang") + local p=prehyphenchar(l) + if p and p>0 then + local c=copy_node(template) + setfield(c,"char",p) + if pre then + local t=find_node_tail(pre) + setfield(t,"next",c) + setfield(c,"prev",t) + else + pre=c + end + end + local p=posthyphenchar(l) + if p and p>0 then + local c=copy_node(template) + setfield(c,"char",p) + if post then + local prev=getprev(post) + setfield(c,"next",post) + setfield(post,"prev",c) + if prev then + setfield(prev,"next",c) + setfield(c,"prev",prev) + end + else + post=c + end + end + return pre,post +end local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) + if getattr(start,a_noligature)==1 then + return head,start + end if start==stop and getchar(start)==char then resetinjection(start) setfield(start,"char",char) return head,start end + local components=getfield(start,"components") + if components then + end local prev=getprev(start) local next=getnext(stop) + local comp=start setfield(start,"prev",nil) setfield(stop,"next",nil) local base=copy_glyph(start) @@ -11671,7 +11826,7 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun resetinjection(base) setfield(base,"char",char) setfield(base,"subtype",ligature_code) - setfield(base,"components",start) + setfield(base,"components",comp) if prev then setfield(prev,"next",base) end @@ -11697,7 +11852,9 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun if trace_marks then logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start)) end - head,current=insert_node_after(head,current,copy_node(start)) + local n=copy_node(start) + copyinjection(n,start) + head,current=insert_node_after(head,current,n) elseif trace_marks then logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char)) end @@ -11716,6 +11873,66 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun end start=getnext(start) end + else + local discprev=getfield(discfound,"prev") + local discnext=getfield(discfound,"next") + if discprev and discnext then + local subtype=getsubtype(discfound) + if subtype==discretionary_code then + local pre=getfield(discfound,"pre") + local post=getfield(discfound,"post") + local replace=getfield(discfound,"replace") + if not replace then + local prev=getfield(base,"prev") + local copied=copy_node_list(comp) + setfield(discnext,"prev",nil) + setfield(discprev,"next",nil) + if pre then + setfield(discprev,"next",pre) + setfield(pre,"prev",discprev) + end + pre=comp + if post then + local tail=find_node_tail(post) + setfield(tail,"next",discnext) + setfield(discnext,"prev",tail) + setfield(post,"prev",nil) + else + post=discnext + end + setfield(prev,"next",discfound) + setfield(next,"prev",discfound) + setfield(discfound,"next",next) + setfield(discfound,"prev",prev) + setfield(base,"next",nil) + setfield(base,"prev",nil) + setfield(base,"components",copied) + setfield(discfound,"pre",pre) + setfield(discfound,"post",post) + setfield(discfound,"replace",base) + setfield(discfound,"subtype",discretionary_code) + base=prev + end + elseif subtype==regular_code then + local copied=copy_node_list(comp) + setfield(discnext,"prev",nil) + setfield(discprev,"next",nil) + local pre,post=addhyphens(comp,comp,discnext,subtype) + setfield(prev,"next",discfound) + setfield(next,"prev",discfound) + setfield(discfound,"next",next) + setfield(discfound,"prev",prev) + setfield(base,"next",nil) + setfield(base,"prev",nil) + setfield(base,"components",copied) + setfield(discfound,"pre",pre) + setfield(discfound,"post",post) + setfield(discfound,"replace",base) + setfield(discfound,"subtype",discretionary_code) + base=next + else + end + end end return head,base end @@ -11809,7 +12026,7 @@ function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence) return multiple_glyphs(head,start,multiple,sequence.flags[1]) end function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) - local s,stop,discfound=getnext(start),nil,false + local s,stop=getnext(start),nil local startchar=getchar(start) if marks[startchar] then while s do @@ -11837,23 +12054,29 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) else head,start=markstoligature(kind,lookupname,head,start,stop,lig) end - return head,start,true + return head,start,true,false else end end else local skipmark=sequence.flags[1] + local discfound=false + local lastdisc=nil while s do local id=getid(s) - if id==glyph_code and getsubtype(s)<256 then - if getfont(s)==currentfont then + if id==glyph_code and getsubtype(s)<256 then + if getfont(s)==currentfont then local char=getchar(s) if skipmark and marks[char] then s=getnext(s) - else - local lg=ligature[char] + else + local lg=ligature[char] if lg then - stop=s + if not discfound and lastdisc then + discfound=lastdisc + lastdisc=nil + end + stop=s ligature=lg s=getnext(s) else @@ -11864,13 +12087,13 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) break end elseif id==disc_code then - discfound=true + lastdisc=s s=getnext(s) else break end end - local lig=ligature.ligature + local lig=ligature.ligature if lig then if stop then if trace_ligatures then @@ -11887,11 +12110,11 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig)) end end - return head,start,true + return head,start,true,discfound else end end - return head,start,false + return head,start,false,discfound end function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) local markchar=getchar(start) @@ -12045,7 +12268,7 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence if al[anchor] then local ma=markanchors[anchor] if ma then - local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar]) + local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -12123,15 +12346,15 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) return head,start,false end end -function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) +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,characters[startchar]) + local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection) 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) +function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection) local snext=getnext(start) if not snext then return head,start,false @@ -12149,17 +12372,17 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) if not krn then elseif type(krn)=="table" then if lookuptype=="pair" then - local a,b=krn[2],krn[3] + 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]) + local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,injection) 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,characters[nextchar]) + local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection) 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 @@ -12169,7 +12392,7 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) end done=true elseif krn~=0 then - local k=setkern(snext,factor,rlmode,krn) + 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 @@ -12223,7 +12446,7 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo local current=start local subtables=currentlookup.subtables if #subtables>1 then - logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) + logwarning("todo: check if we need to loop over the replacements: % t",subtables) end while current do if getid(current)==glyph_code then @@ -12245,7 +12468,80 @@ 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) - setfield(current,"char",replacement) + if check_discretionaries then + local next=getnext(current) + local prev=getprev(current) + 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) + setfield(next,"pre",current) + 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) + setfield(prev,"post",post) + start=prev + done=true + end + end + end + if not done then + setfield(current,"char",replacement) + end + else + setfield(current,"char",replacement) + end end end return head,start,true @@ -12349,11 +12645,17 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, while s do local id=getid(s) if id==disc_code then - s=getnext(s) - discfound=true + if not discfound then + discfound=s + end + if s==stop then + break + else + s=getnext(s) + end else local schar=getchar(s) - if skipmark and marks[schar] then + if skipmark and marks[schar] then s=getnext(s) else local lg=ligatures[schar] @@ -12383,7 +12685,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, end end head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) - return head,start,true,nofreplacements + return head,start,true,nofreplacements,discfound elseif trace_bugs then if start==stop then logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) @@ -12393,7 +12695,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, end end end - return head,start,false,0 + return head,start,false,0,false end chainmores.gsub_ligature=chainprocs.gsub_ligature function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) @@ -12559,7 +12861,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext if al[anchor] then local ma=markanchors[anchor] if ma then - local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar]) + local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -12654,7 +12956,7 @@ function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lo if kerns then kerns=kerns[startchar] if kerns then - local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns) 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 @@ -12689,30 +12991,20 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look 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]) + local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a) 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]) + local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b) 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 @@ -12739,10 +13031,6 @@ local function show_skip(kind,chainname,char,ck,class) logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) end end -local quit_on_no_replacement=true -directives.register("otf.chain.quitonnoreplacement",function(value) - quit_on_no_replacement=value -end) local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) local flags=sequence.flags local done=false @@ -12802,7 +13090,34 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq break end elseif id==disc_code then - last=getnext(last) + if check_discretionaries then + local replace=getfield(last,"replace") + if replace then + while replace do + if seq[n][getchar(replace)] then + n=n+1 + replace=getnext(replace) + if not replace then + break + elseif n>l then + break + end + else + match=false + break + end + end + if not match then + break + elseif check_discretionaries=="trace" then + report_chain("check disc action in current") + end + else + last=getnext(last) + end + else + last=getnext(last) + end else match=false break @@ -12847,6 +13162,34 @@ 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(prev,"replace") + if replace then + 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 + break + end + else + match=false + break + end + end + if not match then + break + elseif check_discretionaries=="trace" then + report_chain("check disc action in before") + end + else + end + else + end elseif seq[n][32] then n=n -1 else @@ -12855,7 +13198,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq end prev=getprev(prev) elseif seq[n][32] then - n=n -1 + n=n-1 else match=false break @@ -12898,6 +13241,32 @@ 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 + 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 + break + end + end + if not match then + break + elseif check_discretionaries=="trace" then + report_chain("check disc action in after") + end + else + end + else + end elseif seq[n][32] then n=n+1 else @@ -13082,7 +13451,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 @@ -13122,6 +13491,162 @@ function otf.dataset(tfmdata,font) end return rl end +local function kernrun(disc,run) + if trace_kernruns then + report_run("kern") + end + local prev=getprev(disc) + local next=getnext(disc) + 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 + 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 + next=false + end + end + if not pre then + elseif prev then + local nest=getprev(pre) + setfield(pre,"prev",prev) + setfield(prev,"next",pre) + run(prev,"preinjections") + setfield(pre,"prev",nest) + setfield(prev,"next",disc) + else + run(pre,"preinjections") + end + if not post then + elseif next then + local tail=find_node_tail(post) + setfield(tail,"next",next) + setfield(next,"prev",tail) + run(post,"postinjections",tail) + setfield(tail,"next",nil) + setfield(next,"prev",disc) + else + run(post,"postinjections") + end + if not replace and prev and next then + elseif prev and next then + local tail=find_node_tail(replace) + local nest=getprev(replace) + setfield(replace,"prev",prev) + setfield(prev,"next",replace) + setfield(tail,"next",next) + setfield(next,"prev",tail) + run(prev,"replaceinjections",tail) + setfield(replace,"prev",nest) + setfield(prev,"next",disc) + setfield(tail,"next",nil) + setfield(next,"prev",disc) + elseif prev then + local nest=getprev(replace) + setfield(replace,"prev",prev) + setfield(prev,"next",replace) + run(prev,"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) + setfield(tail,"next",nil) + setfield(next,"prev",disc) + else + run(replace,"replaceinjections") + end +end +local function comprun(disc,run) + if trace_compruns then + report_run("comp: %s",languages.serializediscretionary(disc)) + end + local pre=getfield(disc,"pre") + if pre then + local new,done=run(pre) + if done then + setfield(disc,"pre",new) + end + end + local post=getfield(disc,"post") + if post then + local new,done=run(post) + if done then + setfield(disc,"post",new) + end + end + local replace=getfield(disc,"replace") + if replace then + local new,done=run(replace) + if done then + setfield(disc,"replace",new) + end + end +end +local function testrun(disc,trun,crun) + local next=getnext(disc) + if next then + local replace=getfield(disc,"replace") + if replace then + local prev=getprev(disc) + if prev then + local tail=find_node_tail(replace) + setfield(tail,"next",next) + setfield(next,"prev",tail) + if trun(replace,next) then + setfield(disc,"replace",nil) + setfield(prev,"next",replace) + setfield(replace,"prev",prev) + setfield(next,"prev",tail) + setfield(tail,"next",next) + setfield(disc,"prev",nil) + setfield(disc,"next",nil) + flush_node_list(disc) + return replace + else + setfield(tail,"next",nil) + setfield(next,"prev",disc) + end + else + end + else + end + else + end + comprun(disc,crun) + return next +end +local function discrun(disc,drun,krun) + local next=getnext(disc) + local prev=getprev(disc) + if trace_discruns then + report_run("disc") + end + if next and prev then + setfield(prev,"next",next) + drun(prev) + setfield(prev,"next",disc) + end + local pre=getfield(disc,"pre") + if not pre then + elseif prev then + local nest=getprev(pre) + setfield(pre,"prev",prev) + setfield(prev,"next",pre) + krun(prev,"preinjections") + setfield(pre,"prev",nest) + setfield(prev,"next",disc) + else + krun(pre,"preinjections") + end + return next +end local function featuresprocessor(head,font,attr) local lookuphash=lookuphashes[font] if not lookuphash then @@ -13148,17 +13673,18 @@ local function featuresprocessor(head,font,attr) local dirstack={} for s=1,#datasets do local dataset=datasets[s] - featurevalue=dataset[1] - local sequence=dataset[5] + featurevalue=dataset[1] + local attribute=dataset[2] + local sequence=dataset[3] + local kind=dataset[4] local rlparmode=0 local topstack=0 local success=false - local attribute=dataset[2] - local chain=dataset[3] local typ=sequence.type + local gpossing=typ=="gpos_single" or typ=="gpos_pair" local subtables=sequence.subtables - if chain<0 then - local handler=handlers[typ] + local handler=handlers[typ] + if typ=="gsub_reversecontextchain" then local start=find_node_tail(head) while start do local id=getid(start) @@ -13171,13 +13697,14 @@ local function featuresprocessor(head,font,attr) a=true end if a then + local char=getchar(start) for i=1,#subtables do local lookupname=subtables[i] local lookupcache=lookuphash[lookupname] if lookupcache then - local lookupmatch=lookupcache[getchar(start)] + local lookupmatch=lookupcache[start] if lookupmatch then - head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + head,start,success=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i) if success then break end @@ -13198,7 +13725,6 @@ local function featuresprocessor(head,font,attr) end end else - local handler=handlers[typ] local ns=#subtables local start=head rlmode=0 @@ -13208,12 +13734,14 @@ local function featuresprocessor(head,font,attr) if not lookupcache then report_missing_cache(typ,lookupname) else - local function subrun(start) + local function c_run(start) local head=start local done=false while start do local id=getid(start) - if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then + if id~=glyph_code then + 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) @@ -13224,7 +13752,7 @@ local function featuresprocessor(head,font,attr) local lookupmatch=lookupcache[getchar(start)] if lookupmatch then local ok - head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1) if ok then done=true end @@ -13234,43 +13762,98 @@ local function featuresprocessor(head,font,attr) start=getnext(start) end else - start=getnext(start) + return head,false end end if done then - success=true - return head + success=true end + return head,done end - local function kerndisc(disc) - local prev=getprev(disc) - local next=getnext(disc) - if prev and next then - setfield(prev,"next",next) - local a=getattr(prev,0) - if a then - a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute) + local function t_run(start,stop) + while start~=stop do + 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 + local lookupmatch=lookupcache[getchar(start)] + if lookupmatch then + local s=getnext(start) + local l=nil + while s do + local lg=lookupmatch[getchar(s)] + if lg then + l=lg + s=getnext(s) + else + break + end + end + if l and l.ligature then + return true + end + end + end + start=getnext(start) else - a=not attribute or getprop(prev,a_state)==attribute + break end - if a then - local lookupmatch=lookupcache[getchar(prev)] - if lookupmatch then - local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) - if ok then - done=true - success=true + end + end + 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 + local lookupmatch=lookupcache[getchar(prev)] + if lookupmatch then + local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + done=true + success=true + end + end + end + end + 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 + for n in traverse_nodes(sub) do + if n==last then + break + end + local id=getid(n) + if id==glyph_code then + local lookupmatch=lookupcache[getchar(n)] + if lookupmatch then + local h,d,ok=handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection) + if ok then + done=true + success=true + end end + else end end - setfield(prev,"next",disc) end - return next end while start do local id=getid(start) if id==glyph_code then - if getfont(start)==font and getsubtype(start)<256 then + if 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) @@ -13278,13 +13861,18 @@ local function featuresprocessor(head,font,attr) a=not attribute or getprop(start,a_state)==attribute end if a then - local lookupmatch=lookupcache[getchar(start)] + local char=getchar(start) + local lookupmatch=lookupcache[char] if lookupmatch then local ok - head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1) if ok then success=true + elseif gpossing and zwnjruns and char==zwnj then + discrun(start,d_run) end + elseif gpossing and zwnjruns and char==zwnj then + discrun(start,d_run) end if start then start=getnext(start) end else @@ -13294,26 +13882,24 @@ local function featuresprocessor(head,font,attr) start=getnext(start) end elseif id==disc_code then - if getsubtype(start)==discretionary_code then - local pre=getfield(start,"pre") - if pre then - local new=subrun(pre) - if new then setfield(start,"pre",new) end - end - local post=getfield(start,"post") - if post then - local new=subrun(post) - if new then setfield(start,"post",new) end + local discretionary=getsubtype(start)==discretionary_code + if gpossing then + if discretionary then + kernrun(start,k_run) + else + discrun(start,d_run,k_run) end - local replace=getfield(start,"replace") - if replace then - local new=subrun(replace) - if new then setfield(start,"replace",new) end + start=getnext(start) + elseif discretionary then + if typ=="gsub_ligature" then + start=testrun(start,t_run,c_run) + else + comprun(start,c_run) + start=getnext(start) end -elseif typ=="gpos_single" or typ=="gpos_pair" then - kerndisc(start) + else + start=getnext(start) end - start=getnext(start) elseif id==whatsit_code then local subtype=getsubtype(start) if subtype==dir_code then @@ -13358,12 +13944,14 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then end end else - local function subrun(start) + local function c_run(start) local head=start local done=false while start do local id=getid(start) - if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then + if id~=glyph_code then + 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) @@ -13371,14 +13959,15 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then a=not attribute or getprop(start,a_state)==attribute end if a then + local char=getchar(start) for i=1,ns do local lookupname=subtables[i] local lookupcache=lookuphash[lookupname] if lookupcache then - local lookupmatch=lookupcache[getchar(start)] + local lookupmatch=lookupcache[char] if lookupmatch then local ok - head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i) if ok then done=true break @@ -13395,46 +13984,120 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then start=getnext(start) end else - start=getnext(start) + return head,false end end if done then success=true - return head end + return head,done end - local function kerndisc(disc) - local prev=getprev(disc) - local next=getnext(disc) - if prev and next then - setfield(prev,"next",next) - 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 + 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 + local char=getchar(prev) + for i=1,ns do + local lookupname=subtables[i] + local lookupcache=lookuphash[lookupname] + if lookupcache then + local lookupmatch=lookupcache[char] + if lookupmatch then + local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + done=true + break + end + end + else + report_missing_cache(typ,lookupname) + end end - if a then - for i=1,ns do - local lookupname=subtables[i] - local lookupcache=lookuphash[lookupname] - if lookupcache then - local lookupmatch=lookupcache[getchar(prev)] - if lookupmatch then - local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if ok then - done=true - break + end + end + 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 + for n in traverse_nodes(sub) do + if n==last then + break + end + local id=getid(n) + if id==glyph_code then + local char=getchar(n) + for i=1,ns do + local lookupname=subtables[i] + local lookupcache=lookuphash[lookupname] + if lookupcache then + local lookupmatch=lookupcache[char] + if lookupmatch then + local h,d,ok=handler(head,n,kind,lookupname,lookupmatch,sequence,lookuphash,i,injection) + if ok then + done=true + break + end end + else + report_missing_cache(typ,lookupname) + end + end + else + end + end + end + end + local function t_run(start,stop) + while start~=stop do + 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 + local char=getchar(start) + for i=1,ns do + local lookupname=subtables[i] + local lookupcache=lookuphash[lookupname] + if lookupcache then + local lookupmatch=lookupcache[char] + if lookupmatch then + local s=getnext(start) + local l=nil + while s do + local lg=lookupmatch[getchar(s)] + if lg then + l=lg + s=getnext(s) + else + break + end + end + if l and l.ligature then + return true + end + end + else + report_missing_cache(typ,lookupname) end - else - report_missing_cache(typ,lookupname) end end + start=getnext(start) + else + break end - setfield(prev,"next",disc) end - return next end while start do local id=getid(start) @@ -13451,16 +14114,21 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then local lookupname=subtables[i] local lookupcache=lookuphash[lookupname] if lookupcache then - local lookupmatch=lookupcache[getchar(start)] + local char=getchar(start) + local lookupmatch=lookupcache[char] if lookupmatch then local ok - head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i) if ok then success=true break elseif not start then break + elseif gpossing and zwnjruns and char==zwnj then + discrun(start,d_run) end + elseif gpossing and zwnjruns and char==zwnj then + discrun(start,d_run) end else report_missing_cache(typ,lookupname) @@ -13474,26 +14142,24 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then start=getnext(start) end elseif id==disc_code then - if getsubtype(start)==discretionary_code then - local pre=getfield(start,"pre") - if pre then - local new=subrun(pre) - if new then setfield(start,"pre",new) end - end - local post=getfield(start,"post") - if post then - local new=subrun(post) - if new then setfield(start,"post",new) end + local discretionary=getsubtype(start)==discretionary_code + if gpossing then + if discretionary then + kernrun(start,k_run) + else + discrun(start,d_run,k_run) end - local replace=getfield(start,"replace") - if replace then - local new=subrun(replace) - if new then setfield(start,"replace",new) end + start=getnext(start) + elseif discretionary then + if typ=="gsub_ligature" then + start=testrun(start,t_run,c_run) + else + comprun(start,c_run) + start=getnext(start) end -elseif typ=="gpos_single" or typ=="gpos_pair" then - kerndisc(start) + else + start=getnext(start) end - start=getnext(start) elseif id==whatsit_code then local subtype=getsubtype(start) if subtype==dir_code then @@ -13556,43 +14222,46 @@ local function generic(lookupdata,lookupname,unicode,lookuphash) lookuphash[lookupname]={ [unicode]=lookupdata } end end +local function ligature(lookupdata,lookupname,unicode,lookuphash) + local target=lookuphash[lookupname] + if not target then + target={} + lookuphash[lookupname]=target + end + for i=1,#lookupdata do + local li=lookupdata[i] + local tu=target[li] + if not tu then + tu={} + target[li]=tu + end + target=tu + end + target.ligature=unicode +end +local function pair(lookupdata,lookupname,unicode,lookuphash) + local target=lookuphash[lookupname] + if not target then + target={} + lookuphash[lookupname]=target + end + local others=target[unicode] + local paired=lookupdata[1] + if others then + others[paired]=lookupdata + else + others={ [paired]=lookupdata } + target[unicode]=others + end +end local action={ substitution=generic, multiple=generic, alternate=generic, position=generic, - ligature=function(lookupdata,lookupname,unicode,lookuphash) - local target=lookuphash[lookupname] - if not target then - target={} - lookuphash[lookupname]=target - end - for i=1,#lookupdata do - local li=lookupdata[i] - local tu=target[li] - if not tu then - tu={} - target[li]=tu - end - target=tu - end - target.ligature=unicode - end, - pair=function(lookupdata,lookupname,unicode,lookuphash) - local target=lookuphash[lookupname] - if not target then - target={} - lookuphash[lookupname]=target - end - local others=target[unicode] - local paired=lookupdata[1] - if others then - others[paired]=lookupdata - else - others={ [paired]=lookupdata } - target[unicode]=others - end - end, + ligature=ligature, + pair=pair, + kern=pair, } local function prepare_lookups(tfmdata) local rawdata=tfmdata.shared.rawdata @@ -13603,13 +14272,14 @@ local function prepare_lookups(tfmdata) local lookuptypes=resources.lookuptypes local characters=tfmdata.characters local descriptions=tfmdata.descriptions + local duplicates=resources.duplicates for unicode,character in next,characters do local description=descriptions[unicode] if description then local lookups=description.slookups if lookups then for lookupname,lookupdata in next,lookups do - action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) + action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash,duplicates) end end local lookups=description.mlookups @@ -13618,7 +14288,7 @@ local function prepare_lookups(tfmdata) local lookuptype=lookuptypes[lookupname] for l=1,#lookuplist do local lookupdata=lookuplist[l] - action[lookuptype](lookupdata,lookupname,unicode,lookuphash) + action[lookuptype](lookupdata,lookupname,unicode,lookuphash,duplicates) end end end @@ -13640,7 +14310,7 @@ local function prepare_lookups(tfmdata) for name,anchor in next,anchors do local lookups=anchor_to_lookup[name] if lookups then - for lookup,_ in next,lookups do + for lookup in next,lookups do local target=lookuphash[lookup] if target then target[unicode]=anchors @@ -13722,7 +14392,7 @@ local function prepare_contextchains(tfmdata) if sequence[1] then nt=nt+1 t[nt]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements } - for unic,_ in next,sequence[start] do + for unic in next,sequence[start] do local cu=contexts[unic] if not cu then contexts[unic]=t diff --git a/tex/generic/context/luatex/luatex-fonts-otn.lua b/tex/generic/context/luatex/luatex-fonts-otn.lua index 0bf231fff..be2c48a09 100644 --- a/tex/generic/context/luatex/luatex-fonts-otn.lua +++ b/tex/generic/context/luatex/luatex-fonts-otn.lua @@ -12,6 +12,13 @@ if not modules then modules = { } end modules ['font-otn'] = { -- 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 +-- at some point i might decide to convert the whole list into a table and then +-- run over that instead (but it has some drawbacks as we also need to deal with +-- attributes and such so we need to keep a lot of track - which is why i rejected +-- that method - although it has become a bit easier in the meantime so it might +-- become an alternative (by that time i probably have gone completely lua) .. the +-- usual chicken-egg issues ... maybe mkix as it's no real tex any more then + -- preprocessors = { "nodes" } -- anchor class : mark, mkmk, curs, mklg (todo) @@ -40,7 +47,18 @@ if not modules then modules = { } end modules ['font-otn'] = { -- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests) -- remove some optimizations (when I have a faster machine) -- --- maybe redo the lot some way (more context specific) +-- beware: +-- +-- we do some disc jugling where we need to keep in mind that the +-- pre, post and replace fields can have prev pointers to a nesting +-- node ... i wonder if that is still needed +-- +-- not possible: +-- +-- \discretionary {alpha-} {betagammadelta} +-- {\discretionary {alphabeta-} {gammadelta} +-- {\discretionary {alphabetagamma-} {delta} +-- {alphabetagammadelta}}} --[[ldx-- <p>This module is a bit more split up that I'd like but since we also want to test @@ -123,7 +141,6 @@ results in different tables.</p> -- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij) -- -- remark: the 'not implemented yet' variants will be done when we have fonts that use them --- remark: we need to check what to do with discretionaries -- We used to have independent hashes for lookups but as the tags are unique -- we now use only one hash. If needed we can have multiple again but in that @@ -131,16 +148,14 @@ results in different tables.</p> -- Todo: make plugin feature that operates on char/glyphnode arrays -local concat, insert, remove = table.concat, table.insert, table.remove -local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip -local type, next, tonumber, tostring = type, next, tonumber, tostring -local lpegmatch = lpeg.match +local type, next, tonumber = type, next, tonumber local random = math.random local formatters = string.formatters local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes -local registertracker = trackers.register +local registertracker = trackers.register +local registerdirective = directives.register local fonts = fonts local otf = fonts.handlers.otf @@ -162,6 +177,18 @@ local trace_steps = false registertracker("otf.steps", function(v local trace_skips = false registertracker("otf.skips", function(v) trace_skips = v end) local trace_directions = false registertracker("otf.directions", function(v) trace_directions = v end) +local trace_kernruns = false registertracker("otf.kernruns", function(v) trace_kernruns = v end) +local trace_discruns = false registertracker("otf.discruns", function(v) trace_discruns = v end) +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") @@ -376,8 +403,52 @@ 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) + 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 -- start is a mark and we need to keep that one @@ -434,16 +505,90 @@ local function getcomponentindex(start) end end --- eventually we will do positioning in an other way (needs addional w/h/d fields) +local a_noligature = attributes.private("noligature") +local prehyphenchar = languages and languages.prehyphenchar +local posthyphenchar = languages and languages.posthyphenchar +----- preexhyphenchar = languages and languages.preexhyphenchar +----- postexhyphenchar = languages and languages.postexhyphenchar + +if prehyphenchar then + + -- okay + +elseif context then + + report_warning("no language support") os.exit() + +else + + local newlang = lang.new + local getpre = lang.prehyphenchar + local getpost = lang.posthyphenchar + -- local getpreex = lang.preexhyphenchar + -- local getpostex = lang.postexhyphenchar + + prehyphenchar = function(l) local l = newlang(l) return l and getpre (l) or -1 end + posthyphenchar = function(l) local l = newlang(l) return l and getpost (l) or -1 end + -- preexhyphenchar = function(l) local l = newlang(l) return l and getpreex (l) or -1 end + -- postexhyphenchar = function(l) local l = newlang(l) return l and getpostex(l) or -1 end + +end + +local function addhyphens(template,pre,post) + -- inserted by hyphenation algorithm + local l = getfield(template,"lang") + local p = prehyphenchar(l) + if p and p > 0 then + local c = copy_node(template) + setfield(c,"char",p) + if pre then + local t = find_node_tail(pre) + setfield(t,"next",c) + setfield(c,"prev",t) + else + pre = c + end + end + local p = posthyphenchar(l) + if p and p > 0 then + local c = copy_node(template) + setfield(c,"char",p) + if post then + -- post has a prev nesting node .. alternatively we could + local prev = getprev(post) + setfield(c,"next",post) + setfield(post,"prev",c) + if prev then + setfield(prev,"next",c) + setfield(c,"prev",prev) + end + else + post = c + end + end + return pre, post +end local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head + if getattr(start,a_noligature) == 1 then + -- so we can do: e\noligature{ff}e e\noligature{f}fie (we only look at the first) + return head, start + end if start == stop and getchar(start) == char then resetinjection(start) setfield(start,"char",char) return head, start end + -- needs testing (side effects): + local components = getfield(start,"components") + if components then +-- we get a double free .. needs checking +-- flush_node_list(components) + end + -- local prev = getprev(start) local next = getnext(stop) + local comp = start setfield(start,"prev",nil) setfield(stop,"next",nil) local base = copy_glyph(start) @@ -453,7 +598,7 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun resetinjection(base) setfield(base,"char",char) setfield(base,"subtype",ligature_code) - setfield(base,"components",start) -- start can have components + setfield(base,"components",comp) -- start can have components .. do we need to flush? if prev then setfield(prev,"next",base) end @@ -480,7 +625,9 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun if trace_marks then logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start)) end - head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components + local n = copy_node(start) + copyinjection(n,start) + head, current = insert_node_after(head,current,n) -- unlikely that mark has components elseif trace_marks then logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char)) end @@ -501,6 +648,70 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun end start = getnext(start) end + else + -- discfound ... forget about marks .. probably no scripts that hyphenate and have marks + local discprev = getfield(discfound,"prev") + local discnext = getfield(discfound,"next") + if discprev and discnext then + local subtype = getsubtype(discfound) + if subtype == discretionary_code then + local pre = getfield(discfound,"pre") + local post = getfield(discfound,"post") + local replace = getfield(discfound,"replace") + if not replace then -- todo: signal simple hyphen + local prev = getfield(base,"prev") + local copied = copy_node_list(comp) + setfield(discnext,"prev",nil) -- also blocks funny assignments + setfield(discprev,"next",nil) -- also blocks funny assignments + if pre then + setfield(discprev,"next",pre) + setfield(pre,"prev",discprev) + end + pre = comp + if post then + local tail = find_node_tail(post) + setfield(tail,"next",discnext) + setfield(discnext,"prev",tail) + setfield(post,"prev",nil) + else + post = discnext + end + setfield(prev,"next",discfound) + setfield(next,"prev",discfound) + setfield(discfound,"next",next) + setfield(discfound,"prev",prev) + setfield(base,"next",nil) + setfield(base,"prev",nil) + setfield(base,"components",copied) + setfield(discfound,"pre",pre) + setfield(discfound,"post",post) + setfield(discfound,"replace",base) + setfield(discfound,"subtype",discretionary_code) + base = prev -- restart + end + elseif subtype == regular_code then + -- local prev = getfield(base,"prev") + -- local next = getfield(base,"next") + local copied = copy_node_list(comp) + setfield(discnext,"prev",nil) -- also blocks funny assignments + setfield(discprev,"next",nil) -- also blocks funny assignments + local pre, post = addhyphens(comp,comp,discnext,subtype) -- takes from components + setfield(prev,"next",discfound) + setfield(next,"prev",discfound) + setfield(discfound,"next",next) + setfield(discfound,"prev",prev) + setfield(base,"next",nil) + setfield(base,"prev",nil) + setfield(base,"components",copied) + setfield(discfound,"pre",pre) + setfield(discfound,"post",post) + setfield(discfound,"replace",base) + setfield(discfound,"subtype",discretionary_code) + base = next -- or restart + else + -- forget about it in generic usage + end + end end return head, base end @@ -605,7 +816,7 @@ function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence) end function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) - local s, stop, discfound = getnext(start), nil, false + local s, stop = getnext(start), nil local startchar = getchar(start) if marks[startchar] then while s do @@ -633,24 +844,30 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) else head, start = markstoligature(kind,lookupname,head,start,stop,lig) end - return head, start, true + return head, start, true, false else -- ok, goto next lookup end end else - local skipmark = sequence.flags[1] + local skipmark = sequence.flags[1] + local discfound = false + local lastdisc = nil while s do local id = getid(s) - if id == glyph_code and getsubtype(s)<256 then - if getfont(s) == currentfont then + if id == glyph_code and getsubtype(s)<256 then -- not needed + if getfont(s) == currentfont then -- also not needed only when mark local char = getchar(s) if skipmark and marks[char] then s = getnext(s) - else - local lg = ligature[char] + else -- ligature is a tree + local lg = ligature[char] -- can there be multiple in a row? maybe in a bad font if lg then - stop = s + if not discfound and lastdisc then + discfound = lastdisc + lastdisc = nil + end + stop = s -- needed for fake so outside then ligature = lg s = getnext(s) else @@ -661,13 +878,13 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) break end elseif id == disc_code then - discfound = true + lastdisc = s s = getnext(s) else break end end - local lig = ligature.ligature + local lig = ligature.ligature -- can't we get rid of this .ligature? if lig then if stop then if trace_ligatures then @@ -685,14 +902,28 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig)) end end - return head, start, true + return head, start, true, discfound else - -- weird but happens + -- weird but happens, pseudo ligatures ... just the components end end - return head, start, false + 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 + --[[ldx-- <p>We get hits on a mark, but we're not sure if the it has to be applied so we need to explicitly test for basechar, baselig and basemark entries.</p> @@ -855,7 +1086,7 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar]) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -938,16 +1169,16 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) end end -function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) +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,characters[startchar]) + 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) +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) @@ -968,17 +1199,17 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) -- skip elseif type(krn) == "table" then if lookuptype == "pair" then -- probably not needed - local a, b = krn[2], krn[3] + 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]) + 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,characters[nextchar]) + 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 @@ -998,7 +1229,7 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) end done = true elseif krn ~= 0 then - local k = setkern(snext,factor,rlmode,krn) + 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 @@ -1116,8 +1347,7 @@ as less as needed but that would also make the code even more messy.</p> -- end --[[ldx-- -<p>Here we replace start by a single variant, First we delete the rest of the -match.</p> +<p>Here we replace start by a single variant.</p> --ldx]]-- function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) @@ -1125,7 +1355,7 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo local current = start local subtables = currentlookup.subtables if #subtables > 1 then - logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) + logwarning("todo: check if we need to loop over the replacements: % t",subtables) end while current do if getid(current) == glyph_code then @@ -1147,7 +1377,83 @@ 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) - setfield(current,"char",replacement) + 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 end end return head, start, true @@ -1163,8 +1469,7 @@ end chainmores.gsub_single = chainprocs.gsub_single --[[ldx-- -<p>Here we replace start by a sequence of new glyphs. First we delete the rest of -the match.</p> +<p>Here we replace start by a sequence of new glyphs.</p> --ldx]]-- function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) @@ -1281,12 +1586,22 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, while s do local id = getid(s) if id == disc_code then - s = getnext(s) - discfound = true + if not discfound then + discfound = s + end + if s == stop then + break -- okay? or before the disc + else + s = getnext(s) + end else local schar = getchar(s) if skipmark and marks[schar] then -- marks +-- if s == stop then -- maybe add this +-- break +-- else s = getnext(s) +-- end else local lg = ligatures[schar] if lg then @@ -1315,7 +1630,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, end end head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) - return head, start, true, nofreplacements + return head, start, true, nofreplacements, discfound elseif trace_bugs then if start == stop then logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) @@ -1325,7 +1640,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext, end end end - return head, start, false, 0 + return head, start, false, 0, false end chainmores.gsub_ligature = chainprocs.gsub_ligature @@ -1497,7 +1812,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar]) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -1597,7 +1912,7 @@ function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lo 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]) + 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 @@ -1637,30 +1952,30 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look 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]) + 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]) + 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 + -- 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 @@ -1698,11 +2013,7 @@ local function show_skip(kind,chainname,char,ck,class) end end -local quit_on_no_replacement = true - -directives.register("otf.chain.quitonnoreplacement",function(value) -- maybe per font - quit_on_no_replacement = value -end) +--hm, do i need to deal with disc here ? 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] @@ -1773,7 +2084,36 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq break end elseif id == disc_code then - last = getnext(last) + 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 + break + end + end + if not match then + break + elseif check_discretionaries == "trace" then + report_chain("check disc action in current") + end + else + last = getnext(last) -- no skipping here + end + else + last = getnext(last) -- no skipping here + end else match = false break @@ -1819,7 +2159,39 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq break end elseif id == disc_code then - -- skip 'm + -- the special case: f i where i becomes dottless i .. + if check_discretionaries then + 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 + break + elseif n < 1 then + -- match = false + break + end + else + match = false + break + end + end + if not match then + break + elseif check_discretionaries == "trace" then + report_chain("check disc action in before") + end + else + -- skip 'm + end + else + -- skip 'm + end elseif seq[n][32] then n = n -1 else @@ -1828,7 +2200,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq end prev = getprev(prev) elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces - n = n -1 + n = n - 1 else match = false break @@ -1873,7 +2245,35 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq break end elseif id == disc_code then - -- skip 'm + 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 + break + end + end + if not match then + break + elseif check_discretionaries == "trace" then + report_chain("check disc action in after") + end + else + -- skip 'm + end + else + -- skip 'm + end elseif seq[n][32] then -- brrr n = n + 1 else @@ -1966,7 +2366,10 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq done = true if n and n > 1 then -- we have a ligature (cf the spec we advance one but we really need to test it - -- as there are fonts out there that are fuzzy and have too many lookups + -- as there are fonts out there that are fuzzy and have too many lookups: + -- + -- U+1105 U+119E U+1105 U+119E : sourcehansansklight: script=hang ccmp=yes + -- if i + n > nofchainlookups then -- if trace_contexts then -- logprocess("%s: quitting lookups",cref(kind,chainname)) @@ -2090,7 +2493,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 @@ -2161,6 +2564,199 @@ end -- -- the action -- end +-- assumptions: +-- +-- * languages that use complex disc nodes + +local function kernrun(disc,run) -- we can assume that prev and next are glyphs + -- + -- we catch <font 1><disc font 2> + -- + if trace_kernruns then + 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 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 + 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 + next = false + end + end + -- + if not pre then + -- go on + elseif prev then + local nest = getprev(pre) + setfield(pre,"prev",prev) + setfield(prev,"next",pre) + run(prev,"preinjections") + setfield(pre,"prev",nest) + setfield(prev,"next",disc) + else + run(pre,"preinjections") + end + -- + if not post then + -- go on + elseif next then + local tail = find_node_tail(post) + setfield(tail,"next",next) + setfield(next,"prev",tail) + run(post,"postinjections",tail) + setfield(tail,"next",nil) + setfield(next,"prev",disc) + else + run(post,"postinjections") + end + -- + if not replace and prev and next then + -- this should be already done by discfound + elseif prev and next then + local tail = find_node_tail(replace) + local nest = getprev(replace) + setfield(replace,"prev",prev) + setfield(prev,"next",replace) + setfield(tail,"next",next) + setfield(next,"prev",tail) + run(prev,"replaceinjections",tail) + setfield(replace,"prev",nest) + setfield(prev,"next",disc) + setfield(tail,"next",nil) + setfield(next,"prev",disc) + elseif prev then + local nest = getprev(replace) + setfield(replace,"prev",prev) + setfield(prev,"next",replace) + run(prev,"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) + setfield(tail,"next",nil) + setfield(next,"prev",disc) + else + run(replace,"replaceinjections") + end +end + +-- the if new test might be dangerous as luatex will check / set some tail stuff +-- in a temp node + +local function comprun(disc,run) + if trace_compruns then + report_run("comp: %s",languages.serializediscretionary(disc)) + end + -- + local pre = getfield(disc,"pre") + if pre then + local new, done = run(pre) + if done then + setfield(disc,"pre",new) + end + end + -- + local post = getfield(disc,"post") + if post then + local new, done = run(post) + if done then + setfield(disc,"post",new) + end + end + -- + local replace = getfield(disc,"replace") + if replace then + local new, done = run(replace) + if done then + setfield(disc,"replace",new) + end + end +end + +local function testrun(disc,trun,crun) + local next = getnext(disc) + if next then + local replace = getfield(disc,"replace") + if replace then + local prev = getprev(disc) + if prev then + -- only look ahead + local tail = find_node_tail(replace) + -- local nest = getprev(replace) + setfield(tail,"next",next) + setfield(next,"prev",tail) + if trun(replace,next) then + setfield(disc,"replace",nil) -- beware, side effects of nest so first + setfield(prev,"next",replace) + setfield(replace,"prev",prev) + setfield(next,"prev",tail) + setfield(tail,"next",next) + setfield(disc,"prev",nil) + setfield(disc,"next",nil) + flush_node_list(disc) + return replace -- restart + else + setfield(tail,"next",nil) + setfield(next,"prev",disc) + end + else + -- weird case + end + else + -- no need + end + else + -- weird case + end + comprun(disc,crun) + return next +end + +local function discrun(disc,drun,krun) + local next = getnext(disc) + local prev = getprev(disc) + if trace_discruns then + report_run("disc") -- will be more detailed + end + if next and prev then + setfield(prev,"next",next) + -- setfield(next,"prev",prev) + drun(prev) + setfield(prev,"next",disc) + -- setfield(next,"prev",disc) + end + -- + local pre = getfield(disc,"pre") + if not pre then + -- go on + elseif prev then + local nest = getprev(pre) + setfield(pre,"prev",prev) + setfield(prev,"next",pre) + krun(prev,"preinjections") + setfield(pre,"prev",nest) + setfield(prev,"next",disc) + else + krun(pre,"preinjections") + end + return next +end + +-- todo: maybe run lr and rl stretches + local function featuresprocessor(head,font,attr) local lookuphash = lookuphashes[font] -- we can also check sequences here @@ -2205,21 +2801,28 @@ local function featuresprocessor(head,font,attr) -- todo: retain prev + -- 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 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 + for s=1,#datasets do - local dataset = datasets[s] - featurevalue = dataset[1] -- todo: pass to function instead of using a global - - local sequence = dataset[5] -- sequences[s] -- also dataset[5] - local rlparmode = 0 - local topstack = 0 - local success = false - local attribute = dataset[2] - local chain = dataset[3] -- sequence.chain or 0 - local typ = sequence.type - local subtables = sequence.subtables - if chain < 0 then + local dataset = datasets[s] + featurevalue = dataset[1] -- todo: pass to function instead of using a global + local attribute = dataset[2] + local sequence = dataset[3] -- sequences[s] -- also dataset[5] + local kind = dataset[4] + ----- 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 subtables = sequence.subtables + local handler = handlers[typ] + + if typ == "gsub_reversecontextchain" then -- chain < 0 -- this is a limited case, no special treatments like 'init' etc - local handler = handlers[typ] -- 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 while start do @@ -2233,13 +2836,16 @@ local function featuresprocessor(head,font,attr) a = true end if a then + local char = getchar(start) for i=1,#subtables do local lookupname = subtables[i] local lookupcache = lookuphash[lookupname] if lookupcache then - local lookupmatch = lookupcache[getchar(start)] + -- local lookupmatch = lookupcache[getchar(start)] + local lookupmatch = lookupcache[start] if lookupmatch then - head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + -- todo: disc? + head, start, success = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i) if success then break end @@ -2260,7 +2866,6 @@ local function featuresprocessor(head,font,attr) end end else - local handler = handlers[typ] local ns = #subtables local start = head -- local ? rlmode = 0 -- to be checked ? @@ -2271,13 +2876,15 @@ local function featuresprocessor(head,font,attr) report_missing_cache(typ,lookupname) else - local function subrun(start) - -- mostly for gsub, gpos would demand a more clever approach + local function c_run(start) -- no need to check for 256 and attr probably also the same local head = start local done = false while start do local id = getid(start) - if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then + if id ~= glyph_code then + -- very unlikely + 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) @@ -2289,7 +2896,7 @@ local function featuresprocessor(head,font,attr) if lookupmatch then -- sequence kan weg local ok - head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1) if ok then done = true end @@ -2299,48 +2906,106 @@ local function featuresprocessor(head,font,attr) start = getnext(start) end else - start = getnext(start) + return head, false end end if done then - success = true - return head + success = true -- needed in this subrun? end + return head, done end - local function kerndisc(disc) -- we can assume that prev and next are glyphs - local prev = getprev(disc) - local next = getnext(disc) - if prev and next then - setfield(prev,"next",next) - -- setfield(next,"prev",prev) - local a = getattr(prev,0) - if a then - a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute) + local function t_run(start,stop) + while start ~= stop do + 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 + 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 + local s = getnext(start) + local l = nil + while s do + local lg = lookupmatch[getchar(s)] + if lg then + l = lg + s = getnext(s) + else + break + end + end + if l and l.ligature then + return true + end + end + end + start = getnext(start) else - a = not attribute or getprop(prev,a_state) == attribute + break end - if a then - local lookupmatch = lookupcache[getchar(prev)] - if lookupmatch then - -- sequence kan weg - local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) - if ok then - done = true - success = true + end + end + + 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 + local lookupmatch = lookupcache[getchar(prev)] + if lookupmatch then + -- sequence kan weg + local h, d, ok = handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1) + if ok then + done = true + success = true + end + end + end + end + + 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 + -- sequence kan weg + for n in traverse_nodes(sub) do -- only gpos + if n == last then + break + end + local id = getid(n) + if id == glyph_code then + local lookupmatch = lookupcache[getchar(n)] + if lookupmatch then + local h, d, ok = handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection) + if ok then + done = true + success = true + end end + else + -- message end end - setfield(prev,"next",disc) - -- setfield(next,"prev",disc) end - return next end while start do local id = getid(start) if id == glyph_code then - if getfont(start) == font and getsubtype(start) < 256 then + if getfont(start) == font and getsubtype(start) < 256 then -- why a 256 test ... local a = getattr(start,0) if a then a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) @@ -2348,44 +3013,46 @@ local function featuresprocessor(head,font,attr) a = not attribute or getprop(start,a_state) == attribute end if a then - local lookupmatch = lookupcache[getchar(start)] + local char = getchar(start) + local lookupmatch = lookupcache[char] if lookupmatch then -- sequence kan weg local ok - head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) + head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1) if ok then success = true + elseif gpossing and zwnjruns and char == zwnj then + discrun(start,d_run) end + elseif gpossing and zwnjruns and char == zwnj then + discrun(start,d_run) end if start then start = getnext(start) end else - start = getnext(start) + start = getnext(start) end else start = getnext(start) end elseif id == disc_code then - -- mostly for gsub - if getsubtype(start) == discretionary_code then - local pre = getfield(start,"pre") - if pre then - local new = subrun(pre) - if new then setfield(start,"pre",new) end - end - local post = getfield(start,"post") - if post then - local new = subrun(post) - if new then setfield(start,"post",new) end - end - local replace = getfield(start,"replace") - if replace then - local new = subrun(replace) - if new then setfield(start,"replace",new) end - end -elseif typ == "gpos_single" or typ == "gpos_pair" then - kerndisc(start) - end - start = getnext(start) + local discretionary = getsubtype(start) == discretionary_code + if gpossing then + if discretionary then + kernrun(start,k_run) + else + discrun(start,d_run,k_run) + end + start = getnext(start) + elseif discretionary then + if typ == "gsub_ligature" then + start = testrun(start,t_run,c_run) + else + comprun(start,c_run) + start = getnext(start) + end + else + start = getnext(start) + end elseif id == whatsit_code then -- will be function local subtype = getsubtype(start) if subtype == dir_code then @@ -2430,15 +3097,18 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then end end end + else - local function subrun(start) - -- mostly for gsub, gpos would demand a more clever approach + local function c_run(start) local head = start local done = false while start do local id = getid(start) - if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then + if id ~= glyph_code then + -- very unlikely + 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) @@ -2446,15 +3116,17 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then a = not attribute or getprop(start,a_state) == attribute end if a then + local char = getchar(start) for i=1,ns do local lookupname = subtables[i] local lookupcache = lookuphash[lookupname] if lookupcache then - local lookupmatch = lookupcache[getchar(start)] + -- 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 local ok - head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i) if ok then done = true break @@ -2472,50 +3144,127 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then start = getnext(start) end else - start = getnext(start) + return head, false end end if done then success = true - return head end + return head, done end - local function kerndisc(disc) -- we can assume that prev and next are glyphs - local prev = getprev(disc) - local next = getnext(disc) - if prev and next then - setfield(prev,"next",next) - -- setfield(next,"prev",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 + 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 + -- brr prev can be disc + local char = getchar(prev) + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local h, d, ok = handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,i) + if ok then + done = true + break + end + end + else + report_missing_cache(typ,lookupname) + end end - if a then - for i=1,ns do - local lookupname = subtables[i] - local lookupcache = lookuphash[lookupname] - if lookupcache then - local lookupmatch = lookupcache[getchar(prev)] - if lookupmatch then - -- we could move all code inline but that makes things even more unreadable - local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) - if ok then - done = true - break + end + end + + 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 + for n in traverse_nodes(sub) do -- only gpos + if n == last then + break + end + local id = getid(n) + if id == glyph_code then + local char = getchar(n) + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[char] + if lookupmatch then + local h, d, ok = handler(head,n,kind,lookupname,lookupmatch,sequence,lookuphash,i,injection) + if ok then + done = true + break + end end + else + report_missing_cache(typ,lookupname) end - else - report_missing_cache(typ,lookupname) end + else + -- message end end - setfield(prev,"next",disc) - -- setfield(next,"prev",disc) end - return next + end + + local function t_run(start,stop) + while start ~= stop do + 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 + local char = getchar(start) + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = lookuphash[lookupname] + if lookupcache then + local lookupmatch = lookupcache[char] + if lookupmatch then + -- if we need more than ligatures we can outline the code and use functions + local s = getnext(start) + local l = nil + while s do + local lg = lookupmatch[getchar(s)] + if lg then + l = lg + s = getnext(s) + else + break + end + end + if l and l.ligature then + return true + end + end + else + report_missing_cache(typ,lookupname) + end + end + end + start = getnext(start) + else + break + end + end end while start do @@ -2533,18 +3282,23 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then local lookupname = subtables[i] local lookupcache = lookuphash[lookupname] if lookupcache then - local lookupmatch = lookupcache[getchar(start)] + local char = getchar(start) + local lookupmatch = lookupcache[char] if lookupmatch then -- we could move all code inline but that makes things even more unreadable local ok - head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) + head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i) if ok then success = true break elseif not start then -- don't ask why ... shouldn't happen break + elseif gpossing and zwnjruns and char == zwnj then + discrun(start,d_run) end + elseif gpossing and zwnjruns and char == zwnj then + discrun(start,d_run) end else report_missing_cache(typ,lookupname) @@ -2558,27 +3312,24 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then start = getnext(start) end elseif id == disc_code then - -- mostly for gsub - if getsubtype(start) == discretionary_code then - local pre = getfield(start,"pre") - if pre then - local new = subrun(pre) - if new then setfield(start,"pre",new) end - end - local post = getfield(start,"post") - if post then - local new = subrun(post) - if new then setfield(start,"post",new) end + local discretionary = getsubtype(start) == discretionary_code + if gpossing then + if discretionary then + kernrun(start,k_run) + else + discrun(start,d_run,k_run) end - local replace = getfield(start,"replace") - if replace then - local new = subrun(replace) - if new then setfield(start,"replace",new) end + start = getnext(start) + elseif discretionary then + if typ == "gsub_ligature" then + start = testrun(start,t_run,c_run) + else + comprun(start,c_run) + start = getnext(start) end -elseif typ == "gpos_single" or typ == "gpos_pair" then - kerndisc(start) + else + start = getnext(start) end - start = getnext(start) elseif id == whatsit_code then local subtype = getsubtype(start) if subtype == dir_code then @@ -2637,6 +3388,8 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then return head, done end +-- this might move to the loader + local function generic(lookupdata,lookupname,unicode,lookuphash) local target = lookuphash[lookupname] if target then @@ -2646,47 +3399,48 @@ local function generic(lookupdata,lookupname,unicode,lookuphash) end end -local action = { +local function ligature(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target + end + for i=1,#lookupdata do + local li = lookupdata[i] + local tu = target[li] + if not tu then + tu = { } + target[li] = tu + end + target = tu + end + target.ligature = unicode +end +local function pair(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target + end + local others = target[unicode] + local paired = lookupdata[1] + if others then + others[paired] = lookupdata + else + others = { [paired] = lookupdata } + target[unicode] = others + end +end + +local action = { substitution = generic, multiple = generic, alternate = generic, position = generic, - - ligature = function(lookupdata,lookupname,unicode,lookuphash) - local target = lookuphash[lookupname] - if not target then - target = { } - lookuphash[lookupname] = target - end - for i=1,#lookupdata do - local li = lookupdata[i] - local tu = target[li] - if not tu then - tu = { } - target[li] = tu - end - target = tu - end - target.ligature = unicode - end, - - pair = function(lookupdata,lookupname,unicode,lookuphash) - local target = lookuphash[lookupname] - if not target then - target = { } - lookuphash[lookupname] = target - end - local others = target[unicode] - local paired = lookupdata[1] - if others then - others[paired] = lookupdata - else - others = { [paired] = lookupdata } - target[unicode] = others - end - end, - + ligature = ligature, + pair = pair, + kern = pair, } local function prepare_lookups(tfmdata) @@ -2699,12 +3453,17 @@ local function prepare_lookups(tfmdata) local lookuptypes = resources.lookuptypes local characters = tfmdata.characters local descriptions = tfmdata.descriptions + local duplicates = resources.duplicates -- we cannot free the entries in the descriptions as sometimes we access -- then directly (for instance anchors) ... selectively freeing does save -- much memory as it's only a reference to a table and the slot in the -- description hash is not freed anyway + -- we can delay this using metatables so that we don't make the hashes for + -- features we don't use but then we need to loop over the characters + -- many times so we gain nothing + for unicode, character in next, characters do -- we cannot loop over descriptions ! local description = descriptions[unicode] @@ -2714,7 +3473,7 @@ local function prepare_lookups(tfmdata) local lookups = description.slookups if lookups then for lookupname, lookupdata in next, lookups do - action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) + action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash,duplicates) end end @@ -2724,7 +3483,7 @@ local function prepare_lookups(tfmdata) local lookuptype = lookuptypes[lookupname] for l=1,#lookuplist do local lookupdata = lookuplist[l] - action[lookuptype](lookupdata,lookupname,unicode,lookuphash) + action[lookuptype](lookupdata,lookupname,unicode,lookuphash,duplicates) end end end @@ -2748,7 +3507,7 @@ local function prepare_lookups(tfmdata) for name, anchor in next, anchors do local lookups = anchor_to_lookup[name] if lookups then - for lookup, _ in next, lookups do + for lookup in next, lookups do local target = lookuphash[lookup] if target then target[unicode] = anchors @@ -2768,6 +3527,8 @@ local function prepare_lookups(tfmdata) end +-- so far + local function split(replacement,original) local result = { } for i=1,#replacement do @@ -2843,7 +3604,7 @@ local function prepare_contextchains(tfmdata) -- use sequence[start] instead but it's somewhat ugly. nt = nt + 1 t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } - for unic, _ in next, sequence[start] do + for unic in next, sequence[start] do local cu = contexts[unic] if not cu then contexts[unic] = t diff --git a/tex/generic/context/luatex/luatex-fonts.lua b/tex/generic/context/luatex/luatex-fonts.lua index fe0e9de77..738118945 100644 --- a/tex/generic/context/luatex/luatex-fonts.lua +++ b/tex/generic/context/luatex/luatex-fonts.lua @@ -29,7 +29,7 @@ if not modules then modules = { } end modules ['luatex-fonts'] = { -- texio.write_nl("") -- texio.write_nl("--------------------------------------------------------------------------------") --- texio.write_nl("The font code has been brought in sync with the context version of 2014.12.21 so") +-- texio.write_nl("The font code has been brought in sync with the context version of 2015.08.31 so") -- texio.write_nl("if things don't work out as expected the interfacing needs to be checked. When") -- texio.write_nl("this works as expected a second upgrade will happen that gives a more complete") -- texio.write_nl("support and another sync with the context code (that new code is currently being") |