diff options
24 files changed, 2121 insertions, 535 deletions
| diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv index fb713cf57..0d629b2fb 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.09.01 11:10} +\newcontextversion{2015.09.04 11:00}  %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.pdfBinary files differ index b2d52084f..5340fc16b 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 31e02d6e5..4a84d5af8 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.09.01 11:10} +\edef\contextversion{2015.09.04 11:00}  \edef\contextkind   {beta}  %D For those who want to use this: @@ -207,7 +207,7 @@  \loadmarkfile{hand-ini}  \loadmarkfile{lang-ini} -\loadmarkfile{lang-hyp} +\loadmarkfile{lang-hyp} % also loads dis  \loadmarkfile{lang-lab}  \loadmarkfile{unic-ini} @@ -365,7 +365,7 @@  \loadmarkfile{char-enc} % will move up -\loadmkvifile{font-lib} % way too late +\loadmkvifile{font-lib} % way too late but after language  \loadmkvifile{font-fil}  \loadmkvifile{font-var}  \loadmkvifile{font-fea} diff --git a/tex/context/base/font-aux.lua b/tex/context/base/font-aux.lua index 2a605d224..fc6c90bc1 100644 --- a/tex/context/base/font-aux.lua +++ b/tex/context/base/font-aux.lua @@ -11,6 +11,11 @@ local tonumber, type = tonumber, type  local fonts, font = fonts, font +local fonts       = fonts +local handlers    = fonts.handlers +local otf         = handlers.otf -- brrr +local afm         = handlers.afm -- brrr +  local iterators   = { }  fonts.iterators   = iterators @@ -163,3 +168,74 @@ function iterators.glyphs(data)          return dummy      end  end + +-- for the moment here, it might move to some other file later + +function afm.getkern(tfmdata,left,right) +    local c = tfmdata.characters[left] +    if c then +        local kerns = c.kerns +        if kerns then +            return kerns[right] -- already scaled +        end +    end +    return 0 +end + +local getters = { -- maybe better getters[format][...] +    kern = { +        ["type1"]    = afm.getkern, +        ["opentype"] = otf.getkern, +    }, +    substitution = { +        ["opentype"] = otf.getsubstitution, +    }, +    alternate = { +        ["opentype"] = otf.getalternate, +    }, +    multiple = { +        ["opentype"] = otf.getmultiple, +    } +} + +fonts.getters = getters + +function fonts.getkern(tfmdata,left,right) +    local format = tfmdata.properties.format +    local getter = getters.kern[format] +    if getter then +        return getter(tfmdata,left,right) +    else +        return 0 +    end +end + +function fonts.getsubstitution(tfmdata,k,kind) +    local format = tfmdata.properties.format +    local getter = getters.substitution[format] +    if getter then +        return getter(tfmdata,k,kind,value) +    else +        return 0 +    end +end + +function fonts.getalternate(tfmdata,k,kind,value) +    local format = tfmdata.properties.format +    local getter = getters.substitution[format] +    if getter then +        return getter(tfmdata,k,kind,value) +    else +        return 0 +    end +end + +function fonts.getmultiple(tfmdata,k,kind) +    local format = tfmdata.properties.format +    local getter = getters.substitution[format] +    if getter then +        return getter(tfmdata,k,kind,value) +    else +        return 0 +    end +end diff --git a/tex/context/base/font-ctx.lua b/tex/context/base/font-ctx.lua index ca9a83086..4dd75b480 100644 --- a/tex/context/base/font-ctx.lua +++ b/tex/context/base/font-ctx.lua @@ -50,6 +50,7 @@ local implement           = interfaces.implement  local fonts               = fonts  local handlers            = fonts.handlers  local otf                 = handlers.otf -- brrr +----- afm                 = handlers.afm -- brrr  local names               = fonts.names  local definers            = fonts.definers  local specifiers          = fonts.specifiers @@ -1366,14 +1367,14 @@ function constructors.calculatescale(tfmdata,scaledpoints,relativeid,specificati          if special then              -- we also have available specification.textsize              local parameters = tfmdata.parameters -            local designsize = parameters.designsize +         -- local designsize = parameters.designsize              if     special == "ht" then ---         inspect(parameters) -                local height = parameters.ascender * designsize / parameters.units -                scaledpoints = (scaledpoints/height) * designsize +                local height = parameters.ascender / parameters.units +                scaledpoints = scaledpoints / height              elseif special == "cp" then -                local height = (tfmdata.descriptions[utf.byte("X")].height or parameters.ascender) * designsize / parameters.units -                scaledpoints = (scaledpoints/height) * designsize +                local glyph  = tfmdata.descriptions[utfbyte("X")] +                local height = (glyph and glyph.height or parameters.ascender) / parameters.units +                scaledpoints = scaledpoints / height              end          end      end diff --git a/tex/context/base/font-dsp.lua b/tex/context/base/font-dsp.lua index 8cdd645c8..148889020 100644 --- a/tex/context/base/font-dsp.lua +++ b/tex/context/base/font-dsp.lua @@ -1745,6 +1745,7 @@ do              },              nofsteps  = 1,              type      = "gpos_pair", +         -- type      = "gpos_single", -- maybe better              flags     = { false, false, false, false },              order     = { name },              features  = { [name] = feature }, diff --git a/tex/context/base/font-inj.lua b/tex/context/base/font-inj.lua index cdf14b935..da1364d9d 100644 --- a/tex/context/base/font-inj.lua +++ b/tex/context/base/font-inj.lua @@ -204,7 +204,10 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne  end  function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2lflag & tfmchr not used -    local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] +    local x = factor*spec[1] +    local y = factor*spec[2] +    local w = factor*spec[3] +    local h = factor*spec[4]      if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay?          local yoffset   = y - h          local leftkern  = x      -- both kerns are set in a pair kern compared @@ -549,9 +552,19 @@ local function inject_marks(marks,marki,nofmarks)                          else                              -- kern(x) glyph(p) kern(w-x) mark(n)                           -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern -                            local leftkern = pp.leftkern -                            if leftkern then -                                ox = px - pn.markx - leftkern +                            -- +							-- According to Kai we don't need to handle leftkern here but I'm +                            -- pretty sure I've run into a case where it was needed so maybe +	                        -- some day we need something more clever here. +                            -- +							if false then +                                -- a mark with kerning +                                local leftkern = pp.leftkern +                                if leftkern then +                                    ox = px - pn.markx - leftkern +                                else +                                    ox = px - pn.markx +                                end                              else                                  ox = px - pn.markx                              end @@ -714,7 +727,7 @@ local function inject_kerns(head,glist,ilist,length) -- not complete ! compare w  					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 +                        setfield(p,"post",dp) -- currently we need to force a tail refresh  					end  				end  			end @@ -725,7 +738,7 @@ setfield(p,"post",dp) -- currently we need to force a tail refresh  					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 +                        setfield(p,"replace",dr) -- currently we need to force a tail refresh  					end  				end  			else @@ -788,7 +801,7 @@ local function inject_kerns_only(head,where)          trace(head,"kerns")      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 @@ -804,7 +817,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 +                                    setfield(p,"post",d) -- currently we need to force a tail refresh                                  end                              end                          end @@ -816,7 +829,7 @@ setfield(p,"post",d) -- currently we need to force a tail refresh                                  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 +                                    setfield(p,"replace",d) -- currently we need to force a tail refresh                                  end                              end                          else @@ -829,6 +842,7 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                              end                          end                      else +                        -- this is the most common case                          local i = rawget(pn,"injections")                          if i then                              local leftkern = i.leftkern @@ -838,8 +852,6 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                          end                      end                  end -            else -                break              end              p = nil          elseif id == disc_code then @@ -866,7 +878,6 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                      setfield(n,"pre",h)                  end              end -            -- weird              local d = getfield(n,"post")              if d then                  local h = d @@ -895,7 +906,7 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                  local h = d                  for n in traverse_id(glyph_code,d) do                      if getsubtype(n) < 256 then -                        local pn = rawget(properties,n) -- why can it be empty { } +                        local pn = rawget(properties,n)                          if pn then                              local i = rawget(pn,"replaceinjections")                              if i then @@ -933,7 +944,6 @@ local function inject_pairs_only(head,where)      if trace_injections then          trace(head,"pairs")      end -    --      local n = head      local p = nil -- disc node when non-nil      while n do @@ -951,7 +961,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 +                                    setfield(p,"post",d) -- currently we need to force a tail refresh                                  end                               -- local rightkern = i.rightkern                               -- if rightkern and rightkern ~= 0 then @@ -968,7 +978,7 @@ setfield(p,"post",d) -- currently we need to force a tail refresh                                  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 +                                    setfield(p,"replace",d) -- currently we need to force a tail refresh                                  end                               -- local rightkern = i.rightkern                               -- if rightkern and rightkern ~= 0 then @@ -994,24 +1004,22 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                          -- this is the most common case                          local i = rawget(pn,"injections")                          if i then -                            local yoffset = i.yoffset -                            if yoffset and yoffset ~= 0 then -                                setfield(n,"yoffset",yoffset) -                            end                              local leftkern = i.leftkern                              if leftkern and leftkern ~= 0 then -                                insert_node_before(head,n,newkern(leftkern)) +                                head = 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))                                  n = getnext(n) -- to be checked                              end +                            local yoffset = i.yoffset +                            if yoffset and yoffset ~= 0 then +                                setfield(n,"yoffset",yoffset) +                            end                          end                      end                  end -            else -                break              end              p = nil          elseif id == disc_code then @@ -1020,16 +1028,12 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                  local h = d                  for n in traverse_id(glyph_code,d) do                      if getsubtype(n) < 256 then -                        local p = rawget(properties,n) -                        if p then -                            local i = rawget(p,"preinjections") +                        local pn = rawget(properties,n) +                        if pn then +                            local i = rawget(pn,"preinjections")                              if i then -                                local yoffset = i.yoffset -                                if yoffset and yoffset ~= 0 then -                                    setfield(n,"yoffset",yoffset) -                                end                                  local leftkern = i.leftkern -                                if leftkern ~= 0 then +                                if leftkern and leftkern ~= 0 then                                      h = insert_node_before(h,n,newkern(leftkern))                                  end                                  local rightkern = i.rightkern @@ -1037,6 +1041,10 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                                      insert_node_after(head,n,newkern(rightkern))                                      n = getnext(n) -- to be checked                                  end +                                local yoffset = i.yoffset +                                if yoffset and yoffset ~= 0 then +                                    setfield(n,"yoffset",yoffset) +                                end                              end                          end                      else @@ -1052,14 +1060,10 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                  local h = d                  for n in traverse_id(glyph_code,d) do                      if getsubtype(n) < 256 then -                        local p = rawget(properties,n) -                        if p then -                            local i = rawget(p,"postinjections") +                        local pn = rawget(properties,n) +                        if pn then +                            local i = rawget(pn,"postinjections")                              if i then -                                local yoffset = i.yoffset -                                if yoffset and yoffset ~= 0 then -                                    setfield(n,"yoffset",yoffset) -                                end                                  local leftkern = i.leftkern                                  if leftkern and leftkern ~= 0 then                                      h = insert_node_before(h,n,newkern(leftkern)) @@ -1069,6 +1073,10 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                                      insert_node_after(head,n,newkern(rightkern))                                      n = getnext(n) -- to be checked                                  end +                                local yoffset = i.yoffset +                                if yoffset and yoffset ~= 0 then +                                    setfield(n,"yoffset",yoffset) +                                end                              end                          end                      else @@ -1084,14 +1092,10 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                  local h = d                  for n in traverse_id(glyph_code,d) do                      if getsubtype(n) < 256 then -                        local p = rawget(properties,n) -                        if p then -                            local i = rawget(p,"replaceinjections") +                        local pn = rawget(properties,n) +                        if pn then +                            local i = rawget(pn,"replaceinjections")                              if i then -                                local yoffset = i.yoffset -                                if yoffset and yoffset ~= 0 then -                                    setfield(n,"yoffset",yoffset) -                                end                                  local leftkern = i.leftkern                                  if leftkern and leftkern ~= 0 then                                      h = insert_node_before(h,n,newkern(leftkern)) @@ -1101,6 +1105,10 @@ setfield(p,"replace",d) -- currently we need to force a tail refresh                                      insert_node_after(head,n,newkern(rightkern))                                      n = getnext(n) -- to be checked                                  end +                                local yoffset = i.yoffset +                                if yoffset and yoffset ~= 0 then +                                    setfield(n,"yoffset",yoffset) +                                end                              end                          end                      else diff --git a/tex/context/base/font-nod.lua b/tex/context/base/font-nod.lua index e2000be7e..8d199f868 100644 --- a/tex/context/base/font-nod.lua +++ b/tex/context/base/font-nod.lua @@ -184,6 +184,7 @@ function char_tracers.string(t)  end  local f_unicode = formatters["%U"] +local f_badcode = formatters["{%i}"]  function char_tracers.unicodes(t,decimal)      local tt = { } @@ -489,30 +490,35 @@ local function toutf(list,result,nofresult,stopcriterium)                      result, nofresult = toutf(components,result,nofresult)                  else                      local c = getchar(n) -                    local fc = fontcharacters[getfont(n)] -                    if fc then -                        local fcc = fc[c] -                        if fcc then -                            local u = fcc.unicode -                            if not u then -                                nofresult = nofresult + 1 -                                result[nofresult] = utfchar(c) -                            elseif type(u) == "table" then -                                for i=1,#u do +                    if c > 0 then +                        local fc = fontcharacters[getfont(n)] +                        if fc then +                            local fcc = fc[c] +                            if fcc then +                                local u = fcc.unicode +                                if not u then                                      nofresult = nofresult + 1 -                                    result[nofresult] = utfchar(u[i]) +                                    result[nofresult] = utfchar(c) +                                elseif type(u) == "table" then +                                    for i=1,#u do +                                        nofresult = nofresult + 1 +                                        result[nofresult] = utfchar(u[i]) +                                    end +                                else +                                    nofresult = nofresult + 1 +                                    result[nofresult] = utfchar(u)                                  end                              else                                  nofresult = nofresult + 1 -                                result[nofresult] = utfchar(u) +                                result[nofresult] = utfchar(c)                              end                          else                              nofresult = nofresult + 1 -                            result[nofresult] = utfchar(c) +                            result[nofresult] = f_unicode(c)                          end                      else                          nofresult = nofresult + 1 -                        result[nofresult] = f_unicode(c) +                        result[nofresult] = f_badcode(c)                      end                  end              elseif id == disc_code then diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua index 79d3ac60d..851a25cca 100644 --- a/tex/context/base/font-otf.lua +++ b/tex/context/base/font-otf.lua @@ -2997,3 +2997,20 @@ function otf.getmultiple(tfmdata,k,kind)      end      return { k }  end + +function otf.getkern(tfmdata,left,right,kind) +    local kerns = getgsub(tfmdata,left,kind or "kern",true) -- for now we use getsub +    if kerns then +        local found = kerns[right] +        local kind  = type(found) +        if kind == "table" then +            found = found[1][3] -- can be more clever +        elseif kind ~= "number" then +            found = false +        end +        if found then +            return found * tfmdata.parameters.factor +        end +    end +    return 0 +end diff --git a/tex/context/base/font-otj.lua b/tex/context/base/font-otj.lua new file mode 100644 index 000000000..a8069b2dc --- /dev/null +++ b/tex/context/base/font-otj.lua @@ -0,0 +1,1286 @@ +if not modules then modules = { } end modules ['font-otj'] = { +    version   = 1.001, +    comment   = "companion to font-lib.mkiv", +    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", +    copyright = "PRAGMA ADE / ConTeXt Development Team", +    license   = "see context related readme files", +} + +-- 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! 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 + +local next, rawget = next, rawget +local utfchar = utf.char +local fastcopy = table.fastcopy + +local trace_injections = false  trackers.register("fonts.injections", function(v) trace_injections = v end) + +local report_injections = logs.reporter("fonts","injections") + +local attributes, nodes, node = attributes, nodes, node + +fonts                    = fonts +local fontdata           = fonts.hashes.identifiers + +nodes.injections         = nodes.injections or { } +local injections         = nodes.injections + +local nodecodes          = nodes.nodecodes +local glyph_code         = nodecodes.glyph +local disc_code          = nodecodes.disc +local kern_code          = nodecodes.kern + +local nuts               = nodes.nuts +local nodepool           = nuts.pool + +local newkern            = nodepool.kern + +local tonode             = nuts.tonode +local tonut              = nuts.tonut + +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getfont            = nuts.getfont +local getsubtype         = nuts.getsubtype +local getchar            = nuts.getchar + +local traverse_id        = nuts.traverse_id +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after +local find_tail          = nuts.tail + +local properties         = nodes.properties.data + +function injections.installnewkern(nk) +    newkern = nk or newkern +end + +local nofregisteredkerns    = 0 +local nofregisteredpairs    = 0 +local nofregisteredmarks    = 0 +local nofregisteredcursives = 0 +local keepregisteredcounts  = false + +function injections.keepcounts() +    keepregisteredcounts = true +end + +function injections.resetcounts() +    nofregisteredkerns    = 0 +    nofregisteredpairs    = 0 +    nofregisteredmarks    = 0 +    nofregisteredcursives = 0 +    keepregisteredcounts  = false +end + +-- We need to make sure that a possible metatable will not kick in unexpectedly. + +function injections.reset(n) +    local p = rawget(properties,n) +    if p and rawget(p,"injections") then +        p.injections = nil +    end +end + +function injections.copy(target,source) +    local sp = rawget(properties,source) +    if sp then +        local tp = rawget(properties,target) +        local si = rawget(sp,"injections") +        if si then +            si = fastcopy(si) +            if tp then +                tp.injections = si +            else +                propertydata[target] = { +                    injections = si, +                } +            end +        else +            if tp then +                tp.injections = nil +            end +        end +    end +end + +function injections.setligaindex(n,index) +    local p = rawget(properties,n) +    if p then +        local i = rawget(p,"injections") +        if i then +            i.ligaindex = index +        else +            p.injections = { +                ligaindex = index +            } +        end +    else +        properties[n] = { +            injections = { +                ligaindex = index +            } +        } +    end +end + +function injections.getligaindex(n,default) +    local p = rawget(properties,n) +    if p then +        local i = rawget(p,"injections") +        if i then +            return i.ligaindex or default +        end +    end +    return default +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 = tfmstart.width +    local wn = tfmnext.width +    nofregisteredcursives = nofregisteredcursives + 1 +    if rlmode < 0 then +        dx = -(dx + wn) +    else +        dx = dx - ws +    end +    -- +    local p = rawget(properties,start) +    if p then +        local i = rawget(p,"injections") +        if i then +            i.cursiveanchor = true +        else +            p.injections = { +                cursiveanchor = true, +            } +        end +    else +        properties[start] = { +            injections = { +                cursiveanchor = true, +            }, +        } +    end +    local p = rawget(properties,nxt) +    if p then +        local i = rawget(p,"injections") +        if i then +            i.cursivex = dx +            i.cursivey = dy +        else +            p.injections = { +                cursivex = dx, +                cursivey = dy, +            } +        end +    else +        properties[nxt] = { +            injections = { +                cursivex = dx, +                cursivey = dy, +            }, +        } +    end +    return dx, dy, nofregisteredcursives +end + +function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2lflag & tfmchr not used +    local x = factor*spec[1] +    local y = factor*spec[2] +    local w = factor*spec[3] +    local h = factor*spec[4] +    if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay? +        local yoffset   = y - h +        local leftkern  = x      -- both kerns are set in a pair kern compared +        local rightkern = w - x  -- to normal kerns where we set only leftkern +        if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then +            nofregisteredpairs = nofregisteredpairs + 1 +            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,injection) +                if i then +                    if leftkern ~= 0 then +                        i.leftkern  = (i.leftkern  or 0) + leftkern +                    end +                    if rightkern ~= 0 then +                        i.rightkern = (i.rightkern or 0) + rightkern +                    end +                    if yoffset ~= 0 then +                        i.yoffset = (i.yoffset or 0) + yoffset +                    end +                elseif leftkern ~= 0 or rightkern ~= 0 then +                    p[injection] = { +                        leftkern  = leftkern, +                        rightkern = rightkern, +                        yoffset   = yoffset, +                    } +                else +                    p[injection] = { +                        yoffset = yoffset, +                    } +                end +            elseif leftkern ~= 0 or rightkern ~= 0 then +                properties[current] = { +                    [injection] = { +                        leftkern  = leftkern, +                        rightkern = rightkern, +                        yoffset   = yoffset, +                    }, +                } +            else +                properties[current] = { +                    [injection] = { +                        yoffset = yoffset, +                    }, +                } +            end +            return x, y, w, h, nofregisteredpairs +         end +    end +    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) + +function injections.setkern(current,factor,rlmode,x,injection) +    local dx = factor * x +    if dx ~= 0 then +        nofregisteredkerns = nofregisteredkerns + 1 +        local p = rawget(properties,current) +        if not injection then +            injection = "injections" +        end +        if p then +            local i = rawget(p,injection) +            if i then +                i.leftkern = dx + (i.leftkern or 0) +            else +                p[injection] = { +                    leftkern = dx, +                } +            end +        else +            properties[current] = { +                [injection] = { +                    leftkern = dx, +                }, +            } +        end +        return dx, nofregisteredkerns +    else +        return 0, 0 +    end +end + +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 +    if rlmode >= 0 then +        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 +            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, +                marky        = dy, +                markdir      = rlmode or 0, +                markbase     = nofregisteredmarks, +                markbasenode = base, +                markmark     = mkmk, +            } +        end +    else +        properties[start] = { +            injections = { +                markx        = dx, +                marky        = dy, +                markdir      = rlmode or 0, +                markbase     = nofregisteredmarks, +                markbasenode = base, +                markmark     = mkmk, +            }, +        } +    end +    return dx, dy, nofregisteredmarks +end + +local function dir(n) +    return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" +end + +local function showchar(n,nested) +    local char = getchar(n) +    report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char) +end + +local function show(n,what,nested,symbol) +    if n then +        local p = rawget(properties,n) +        if p then +            local i = rawget(p,what) +            if i then +                local leftkern  = i.leftkern  or 0 +                local rightkern = i.rightkern or 0 +                local yoffset   = i.yoffset   or 0 +                local markx     = i.markx     or 0 +                local marky     = i.marky     or 0 +                local markdir   = i.markdir   or 0 +                local markbase  = i.markbase  or 0 -- will be markbasenode +                local cursivex  = i.cursivex  or 0 +                local cursivey  = i.cursivey  or 0 +                local ligaindex = i.ligaindex or 0 +                local margin    = nested and 4 or 2 +                -- +                if rightkern ~= 0 or yoffset ~= 0 then +                    report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset) +                elseif leftkern ~= 0 then +                    report_injections("%w%s kern: dx %p",margin,symbol,leftkern) +                end +                if markx ~= 0 or marky ~= 0 or markbase ~= 0 then +                    report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase ~= 0 and "yes" or "no") +                end +                if cursivex ~= 0 or cursivey ~= 0 then +                    report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey) +                end +                if ligaindex ~= 0 then +                    report_injections("%w%s liga: index %i",margin,symbol,ligaindex) +                end +            end +        end +    end +end + +local function showsub(n,what,where) +    report_injections("begin subrun: %s",where) +    for n in traverse_id(glyph_code,n) do +        showchar(n,where) +        show(n,what,where," ") +    end +    report_injections("end subrun") +end + +local function trace(head,where) +    report_injections("begin run %s: %s kerns, %s pairs, %s marks and %s cursives registered", +        where or "",nofregisteredkerns,nofregisteredpairs,nofregisteredmarks,nofregisteredcursives) +    local n = head +    while n do +        local id = getid(n) +        if id == glyph_code then +            showchar(n) +            show(n,"injections",false," ") +            show(n,"preinjections",false,"<") +            show(n,"postinjections",false,">") +            show(n,"replaceinjections",false,"=") +            show(n,"emptyinjections",false,"*") +        elseif id == disc_code then +            local pre     = getfield(n,"pre") +            local post    = getfield(n,"post") +            local replace = getfield(n,"replace") +            if pre then +                showsub(pre,"preinjections","pre") +            end +            if post then +                showsub(post,"postinjections","post") +            end +            if replace then +                showsub(replace,"replaceinjections","replace") +            end +            show(n,"emptyinjections",false,"*") +        end +        n = getnext(n) +    end +    report_injections("end run") +end + +local function show_result(head) +    local current  = head +    local skipping = false +    while current do +        local id = getid(current) +        if id == glyph_code then +            report_injections("char: %C, width %p, xoffset %p, yoffset %p", +                getchar(current),getfield(current,"width"),getfield(current,"xoffset"),getfield(current,"yoffset")) +            skipping = false +        elseif id == kern_code then +            report_injections("kern: %p",getfield(current,"kern")) +            skipping = false +        elseif not skipping then +            report_injections() +            skipping = true +        end +        current = getnext(current) +    end +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 +        trace(head,"kerns") +    end +    local current   = head +    local prev      = nil +    local next      = nil +    local prevdisc  = nil +    local prevglyph = nil +    local pre       = nil -- saves a lookup +    local post      = nil -- saves a lookup +    local replace   = nil -- saves a lookup +    while current do +        local id   = getid(current) +        local next = getnext(current) +        if id == glyph_code then +            if getsubtype(current) < 256 then +                local p = rawget(properties,current) +                if p then +                    local i = rawget(p,"injections") +                    if i then +                        -- left|glyph|right +                        local leftkern = i.leftkern +                        if leftkern and leftkern ~= 0 then +                            insert_node_before(head,current,newkern(leftkern)) +                        end +                    end +                    if prevdisc then +                        local postdone    = false +                        local replacedone = false +                        if post then +                            local i = rawget(p,"postinjections") +                            if i then +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    local posttail = find_tail(post) +                                    insert_node_after(post,posttail,newkern(leftkern)) +                                    postdone = true +                                end +                            end +                        end +                        if replace then +                            local i = rawget(p,"replaceinjections") +                            if i then +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    local replacetail = find_tail(replace) +                                    insert_node_after(replace,replacetail,newkern(leftkern)) +                                    replacedone = true +                                end +                            end +                        else +-- local i = rawget(p,"emptyinjections") +-- if i then +-- inspect(i) +--     local leftkern = i.leftkern +--     if leftkern and leftkern ~= 0 then +--         replace = newkern(leftkern) +--         done = true +--     end +-- end +                        end +                        if postdone then +                            setfield(prevdisc,"post",post) +                        end +                        if replacedone then +                            setfield(prevdisc,"replace",replace) +                        end +                    end +                end +            end +            prevdisc  = nil +            prevglyph = current +        elseif id == disc_code then +            pre     = getfield(current,"pre") +            post    = getfield(current,"post") +            replace = getfield(current,"replace") +            local predone     = false +            local postdone    = false +            local replacedone = false +            if pre then +                -- left|pre glyphs|right +                for n in traverse_id(glyph_code,pre) do +                    if getsubtype(n) < 256 then +                        local p = rawget(properties,n) +                        if p then +                            local i = rawget(p,"injections") or rawget(p,"preinjections") +                            if i then +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    pre     = insert_node_before(pre,n,newkern(leftkern)) +                                    predone = true +                                end +                            end +                        end +                    end +                end +            end +            if post then +                -- left|post glyphs|right +                for n in traverse_id(glyph_code,post) do +                    if getsubtype(n) < 256 then +                        local p = rawget(properties,n) +                        if p then +                            local i = rawget(p,"injections") or rawget(p,"postinjections") +                            if i then +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    post     = insert_node_before(post,n,newkern(leftkern)) +                                    postdone = true +                                end +                            end +                        end +                    end +                end +            end +            if replace then +                -- left|replace glyphs|right +                for n in traverse_id(glyph_code,replace) do +                    if getsubtype(n) < 256 then +                        local p = rawget(properties,n) +                        if p then +                            local i = rawget(p,"injections") or rawget(p,"replaceinjections") +                            if i then +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    replace     = insert_node_before(replace,n,newkern(leftkern)) +                                    replacedone = true +                                end +                            end +                        end +                    end +                end +            end +            if predone then +                setfield(current,"pre",pre) +            end +            if postdone then +                setfield(current,"post",post) +            end +            if replacedone then +                setfield(current,"replace",replace) +            end +            prevglyph = nil +            prevdisc  = current +        else +            prevglyph = nil +            prevdisc  = nil +        end +        prev    = current +        current = next +    end +    -- +    if keepregisteredcounts then +        keepregisteredcounts = false +    else +        nofregisteredkerns   = 0 +    end +    return tonode(head), true +end + +local function inject_pairs_only(head,where) +    head = tonut(head) +    if trace_injections then +        trace(head,"pairs") +    end +    local current   = head +    local prev      = nil +    local next      = nil +    local prevdisc  = nil +    local prevglyph = nil +    local pre       = nil -- saves a lookup +    local post      = nil -- saves a lookup +    local replace   = nil -- saves a lookup +    while current do +        local id   = getid(current) +        local next = getnext(current) +        if id == glyph_code then +            if getsubtype(current) < 256 then +                local p = rawget(properties,current) +                if p then +                    local i = rawget(p,"injections") +                    if i then +                        -- left|glyph|right +                        local yoffset = i.yoffset +                        if yoffset and yoffset ~= 0 then +                            setfield(current,"yoffset",yoffset) +                        end +                        local leftkern = i.leftkern +                        if leftkern and leftkern ~= 0 then +                            insert_node_before(head,current,newkern(leftkern)) +                        end +                        local rightkern = i.rightkern +                        if rightkern and rightkern ~= 0 then +                            insert_node_after(head,current,newkern(rightkern)) +                        end +                    else +                        local i = rawget(p,"replaceinjections") +                        if i then +                            -- glyph|disc|glyph (special case) +                            local rightkern = i.rightkern +                            if rightkern and rightkern ~= 0 then +                                if next and getid(next) == disc_code then +                                    local replace = getfield(pr,"replace") +                                    if replace then +                                        -- error, we expect an empty one +                                    else +                                        setfield(next,"replace",newkern(rightkern)) -- maybe also leftkern +                                    end +                                end +                            end +                        end +                    end +                    if prevdisc and p then +                        local postdone    = false +                        local replacedone = false +                        if post then +                            local i = rawget(p,"postinjections") +                            if i then +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    local posttail = find_tail(post) +                                    insert_node_after(post,posttail,newkern(leftkern)) +                                    postdone = true +                                end +                            end +                        end +                        if replace then +                            local i = rawget(p,"replaceinjections") +                            if i then +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    local replacetail = find_tail(replace) +                                    insert_node_after(replace,replacetail,newkern(leftkern)) +                                    replacedone = true +                                end +                            end +                        end +                        if postdone then +                            setfield(prevdisc,"post",post) +                        end +                        if replacedone then +                            setfield(prevdisc,"replace",replace) +                        end +                    end +                end +            end +            prevdisc  = nil +            prevglyph = current +        elseif id == disc_code then +            pre     = getfield(current,"pre") +            post    = getfield(current,"post") +            replace = getfield(current,"replace") +            local predone     = false +            local postdone    = false +            local replacedone = false +            if pre then +                -- left|pre glyphs|right +                for n in traverse_id(glyph_code,pre) do +                    if getsubtype(n) < 256 then +                        local p = rawget(properties,n) +                        if p then +                            local i = rawget(p,"injections") or rawget(p,"preinjections") +                            if i then +                                local yoffset = i.yoffset +                                if yoffset and yoffset ~= 0 then +                                    setfield(n,"yoffset",yoffset) +                                end +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    pre = insert_node_before(pre,n,newkern(leftkern)) +                                    predone = true +                                end +                                local rightkern = i.rightkern +                                if rightkern and rightkern ~= 0 then +                                    insert_node_after(pre,n,newkern(rightkern)) +                                    predone = true +                                end +                            end +                        end +                    end +                end +            end +            if post then +                -- left|post glyphs|right +                for n in traverse_id(glyph_code,post) do +                    if getsubtype(n) < 256 then +                        local p = rawget(properties,n) +                        if p then +                            local i = rawget(p,"injections") or rawget(p,"postinjections") +                            if i then +                                local yoffset = i.yoffset +                                if yoffset and yoffset ~= 0 then +                                    setfield(n,"yoffset",yoffset) +                                end +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    post = insert_node_before(post,n,newkern(leftkern)) +                                    postdone = true +                                end +                                local rightkern = i.rightkern +                                if rightkern and rightkern ~= 0 then +                                    insert_node_after(post,n,newkern(rightkern)) +                                    postdone = true +                                end +                            end +                        end +                    end +                end +            end +            if replace then +                -- left|replace glyphs|right +                for n in traverse_id(glyph_code,replace) do +                    if getsubtype(n) < 256 then +                        local p = rawget(properties,n) +                        if p then +                            local i = rawget(p,"injections") or rawget(p,"replaceinjections") +                            if i then +                                local yoffset = i.yoffset +                                if yoffset and yoffset ~= 0 then +                                    setfield(n,"yoffset",yoffset) +                                end +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    replace     = insert_node_before(replace,n,newkern(leftkern)) +                                    replacedone = true +                                end +                                local rightkern = i.rightkern +                                if rightkern and rightkern ~= 0 then +                                    insert_node_after(replace,n,newkern(rightkern)) +                                    replacedone = true +                                end +                            end +                        end +                    end +                end +            end +            if prevglyph then +                if pre then +                    local p = rawget(properties,prevglyph) +                    if p then +                        local i = rawget(p,"preinjections") +                        if i then +                            -- glyph|pre glyphs +                            local rightkern = i.rightkern +                            if rightkern and rightkern ~= 0 then +                                pre     = insert_node_before(pre,pre,newkern(rightkern)) +                                predone = true +                            end +                        end +                    end +                end +                if replace then +                    local p = rawget(properties,prevglyph) +                    if p then +                        local i = rawget(p,"replaceinjections") +                        if i then +                            -- glyph|replace glyphs +                            local rightkern = i.rightkern +                            if rightkern and rightkern ~= 0 then +                                replace     = insert_node_before(replace,replace,newkern(rightkern)) +                                replacedone = true +                            end +                        end +                    end +                end +            end +            if predone then +                setfield(current,"pre",pre) +            end +            if postdone then +                setfield(current,"post",post) +            end +            if replacedone then +                setfield(current,"replace",replace) +            end +            prevglyph = nil +            prevdisc  = current +        else +            prevglyph = nil +            prevdisc  = nil +        end +        prev    = current +        current = next +    end +    -- +    if keepregisteredcounts then +        keepregisteredcounts = false +    else +        nofregisteredkerns   = 0 +    end +    return tonode(head), true +end + +local function inject_everything(head,where) +    head = tonut(head) +    if trace_injections then +        trace(head,"everything") +    end +    local hascursives = nofregisteredcursives > 0 +    local hasmarks    = nofregisteredmarks    > 0 +    -- +    local current   = head +    local prev      = nil +    local next      = nil +    local prevdisc  = nil +    local prevglyph = nil +    local pre       = nil -- saves a lookup +    local post      = nil -- saves a lookup +    local replace   = nil -- saves a lookup +    -- +    local cursiveanchor = nil +    local lastanchor    = nil +    local minc          = 0 +    local maxc          = 0 +    local last          = 0 +    local glyphs        = { } +    -- +    local function processmark(p,n,pn) -- p = basenode +        local px = getfield(p,"xoffset") +        local ox = 0 +        local rightkern = nil +        local pp = rawget(properties,p) +        if pp then +            pp = rawget(pp,"injections") +            if pp then +                rightkern = pp.rightkern +            end +        end +        if rightkern then -- x and w ~= 0 +            if pn.markdir < 0 then +                -- kern(w-x) glyph(p) kern(x) mark(n) +                ox = px - pn.markx - rightkern +             -- report_injections("r2l case 1: %p",ox) +            else +                -- kern(x) glyph(p) kern(w-x) mark(n) +             -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern +                -- +                -- According to Kai we don't need to handle leftkern here but I'm +                -- pretty sure I've run into a case where it was needed so maybe +                -- some day we need something more clever here. +                -- +                if false then +                    -- a mark with kerning +                    local leftkern = pp.leftkern +                    if leftkern then +                        ox = px - pn.markx - leftkern +                    else +                        ox = px - pn.markx +                    end +                else +                    ox = px - pn.markx +                end +            end +        else +            -- we need to deal with fonts that have marks with width +         -- if pn.markdir < 0 then +         --     ox = px - pn.markx +         --  -- report_injections("r2l case 3: %p",ox) +         -- else +         --  -- ox = px - getfield(p,"width") + pn.markx +                ox = px - pn.markx +             -- report_injections("l2r case 3: %p",ox) +         -- end +            local wn = getfield(n,"width") -- in arial marks have widths +            if wn ~= 0 then +                -- bad: we should center +             -- insert_node_before(head,n,newkern(-wn/2)) +             -- insert_node_after(head,n,newkern(-wn/2)) +                pn.leftkern  = -wn/2 +                pn.rightkern = -wn/2 +             -- wx[n] = { 0, -wn/2, 0, -wn } +            end +            -- so far +        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) +    end +    -- +    while current do +        local id   = getid(current) +        local next = getnext(current) +        if id == glyph_code then +            if getsubtype(current) < 256 then +                local p = rawget(properties,current) +                if p then +                    local i = rawget(p,"injections") +                    if i then +                        -- cursives +                        if hascursives then +                            local cursivex = p.cursivex +                            if cursivex then +                                if cursiveanchor then +                                    if cursivex ~= 0 then +                                        p.leftkern = (p.leftkern or 0) + cursivex +                                    end +                                    if lastanchor then +                                        if maxc == 0 then +                                            minc = 1 +                                            maxc = 1 +                                            glyphs[1] = lastanchor +                                        else +                                            maxc = maxc + 1 +                                            glyphs[maxc] = lastanchor +                                        end +                                        properties[cursiveanchor].cursivedy = p.cursivey +                                    end +                                    last = n +                                else +                                    maxc = 0 +                                end +                            elseif maxc > 0 then +                                local ny = getfield(n,"yoffset") +                                for i=maxc,minc,-1 do +                                    local ti = glyphs[i] +                                    ny = ny + properties[ti].cursivedy +                                    setfield(ti,"yoffset",ny) -- why not add ? +                                end +                                maxc = 0 +                            end +                            if p.cursiveanchor then +                                cursiveanchor = current -- no need for both now +                                lastanchor    = current +                            else +                                cursiveanchor = nil +                                lastanchor    = nil +                                if maxc > 0 then +                                    local ny = getfield(n,"yoffset") +                                    for i=maxc,minc,-1 do +                                        local ti = glyphs[i] +                                        ny = ny + properties[ti].cursivedy +                                        setfield(ti,"yoffset",ny) -- why not add ? +                                    end +                                    maxc = 0 +                                end +                            end +                        end +                        -- marks +                        if hasmarks then +                            local pm = i.markbasenode +                            if pm then +                                processmark(pm,current,i) +                            end +                        end +                        -- left|glyph|right +                        local yoffset = i.yoffset +                        if yoffset and yoffset ~= 0 then +                            setfield(current,"yoffset",yoffset) +                        end +                        local leftkern = i.leftkern +                        if leftkern and leftkern ~= 0 then +                            insert_node_before(head,current,newkern(leftkern)) +                        end +                        local rightkern = i.rightkern +                        if rightkern and rightkern ~= 0 then +                            insert_node_after(head,current,newkern(rightkern)) +                        end +                    else +                        local i = rawget(p,"replaceinjections") +                        if i then +                            -- glyph|disc|glyph (special case) +                            local rightkern = i.rightkern +                            if rightkern and rightkern ~= 0 then +                                if next and getid(next) == disc_code then +                                    local replace = getfield(pr,"replace") +                                    if replace then +                                        -- error, we expect an empty one +                                    else +                                        setfield(next,"replace",newkern(rightkern)) -- maybe also leftkern +                                    end +                                end +                            end +                        end +                    end +                    if prevdisc then +                        if p then +                            local postdone    = false +                            local replacedone = false +                            if post then +                                local i = rawget(p,"postinjections") +                                if i then +                                    local leftkern = i.leftkern +                                    if leftkern and leftkern ~= 0 then +                                        local posttail = find_tail(post) +                                        insert_node_after(post,posttail,newkern(leftkern)) +                                        postdone = true +                                    end +                                end +                            end +                            if replace then +                                local i = rawget(p,"replaceinjections") +                                if i then +                                    local leftkern = i.leftkern +                                    if leftkern and leftkern ~= 0 then +                                        local replacetail = find_tail(replace) +                                        insert_node_after(replace,replacetail,newkern(leftkern)) +                                        replacedone = true +                                    end +                                end +                            end +                            if postdone then +                                setfield(prevdisc,"post",post) +                            end +                            if replacedone then +                                setfield(prevdisc,"replace",replace) +                            end +                        end +                    end +                else +                    -- cursive +                    if hascursives and maxc > 0 then +                        local ny = getfield(current,"yoffset") +                        for i=maxc,minc,-1 do +                            local ti = glyphs[i] +                            ny = ny + properties[ti].cursivedy +                            setfield(ti,"yoffset",getfield(ti,"yoffset") + ny) -- ? +                        end +                        maxc = 0 +                        cursiveanchor = nil +                        lastanchor = nil +                    end +                end +            end +            prevdisc  = nil +            prevglyph = current +        elseif id == disc_code then +            pre     = getfield(current,"pre") +            post    = getfield(current,"post") +            replace = getfield(current,"replace") +            local predone     = false +            local postdone    = false +            local replacedone = false +            if pre then +                -- left|pre glyphs|right +                for n in traverse_id(glyph_code,pre) do +                    if getsubtype(n) < 256 then +                        local p = rawget(properties,n) +                        if p then +                            local i = rawget(p,"injections") or rawget(p,"preinjections") +                            if i then +                                local yoffset = i.yoffset +                                if yoffset and yoffset ~= 0 then +                                    setfield(n,"yoffset",yoffset) +                                end +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    pre = insert_node_before(pre,n,newkern(leftkern)) +                                    predone = true +                                end +                                local rightkern = i.rightkern +                                if rightkern and rightkern ~= 0 then +                                    insert_node_after(pre,n,newkern(rightkern)) +                                    predone = true +                                end +                            end +                            if hasmarks then +                                local pm = i.markbasenode +                                if pm then +                                    processmark(pm,current,i) +                                end +                            end +                        end +                    end +                end +            end +            if post then +                -- left|post glyphs|right +                for n in traverse_id(glyph_code,post) do +                    if getsubtype(n) < 256 then +                        local p = rawget(properties,n) +                        if p then +                            local i = rawget(p,"injections") or rawget(p,"postinjections") +                            if i then +                                local yoffset = i.yoffset +                                if yoffset and yoffset ~= 0 then +                                    setfield(n,"yoffset",yoffset) +                                end +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    post = insert_node_before(post,n,newkern(leftkern)) +                                    postdone = true +                                end +                                local rightkern = i.rightkern +                                if rightkern and rightkern ~= 0 then +                                    insert_node_after(post,n,newkern(rightkern)) +                                    postdone = true +                                end +                            end +                            if hasmarks then +                                local pm = i.markbasenode +                                if pm then +                                    processmark(pm,current,i) +                                end +                            end +                        end +                    end +                end +            end +            if replace then +                -- left|replace glyphs|right +                for n in traverse_id(glyph_code,replace) do +                    if getsubtype(n) < 256 then +                        local p = rawget(properties,n) +                        if p then +                            local i = rawget(p,"injections") or rawget(p,"replaceinjections") +                            if i then +                                local yoffset = i.yoffset +                                if yoffset and yoffset ~= 0 then +                                    setfield(n,"yoffset",yoffset) +                                end +                                local leftkern = i.leftkern +                                if leftkern and leftkern ~= 0 then +                                    insert_node_before(replace,n,newkern(leftkern)) +                                    replacedone = true +                                end +                                local rightkern = i.rightkern +                                if rightkern and rightkern ~= 0 then +                                    insert_node_after(replace,n,newkern(rightkern)) +                                    replacedone = true +                                end +                            end +                            if hasmarks then +                                local pm = i.markbasenode +                                if pm then +                                    processmark(pm,current,i) +                                end +                            end +                        end +                    end +                end +            end +            if prevglyph then +                if pre then +                    local p = rawget(properties,prevglyph) +                    if p then +                        local i = rawget(p,"preinjections") +                        if i then +                            -- glyph|pre glyphs +                            local rightkern = i.rightkern +                            if rightkern and rightkern ~= 0 then +                                pre = insert_node_before(pre,pre,newkern(rightkern)) +                                predone = true +                            end +                        end +                    end +                end +                if replace then +                    local p = rawget(properties,prevglyph) +                    if p then +                        local i = rawget(p,"replaceinjections") +                        if i then +                            -- glyph|replace glyphs +                            local rightkern = i.rightkern +                            if rightkern and rightkern ~= 0 then +                                replace = insert_node_before(replace,replace,newkern(rightkern)) +                                replacedone = true +                            end +                        end +                    end +                end +            end +            if predone then +                setfield(current,"pre",pre) +            end +            if postdone then +                setfield(current,"post",post) +            end +            if replacedone then +                setfield(current,"replace",replace) +            end +            prevglyph = nil +            prevdisc  = current +        else +            prevglyph = nil +            prevdisc  = nil +        end +        prev    = current +        current = next +    end +    -- cursive +    if hascursives then +        if last and maxc > 0 then +            local ny = getfield(last,"yoffset") +            for i=maxc,minc,-1 do +                local ti = glyphs[i] +                ny = ny + properties[ti].cursivedy +                setfield(ti,"yoffset",ny) -- why not add ? +            end +        end +    end +    -- +    if keepregisteredcounts then +        keepregisteredcounts  = false +    else +        nofregisteredkerns    = 0 +        nofregisteredpairs    = 0 +        nofregisteredmarks    = 0 +        nofregisteredcursives = 0 +    end +    return tonode(head), true +end + +function injections.handler(head,where) +    if nofregisteredmarks > 0 or nofregisteredcursives > 0 then +        return inject_everything(head,where) +    elseif nofregisteredpairs > 0 then +        return inject_pairs_only(head,where) +    elseif nofregisteredkerns > 0 then +        return inject_kerns_only(head,where) +    else +        return head, false +    end +end diff --git a/tex/context/base/font-otl.lua b/tex/context/base/font-otl.lua index 96775d0f0..5a4831835 100644 --- a/tex/context/base/font-otl.lua +++ b/tex/context/base/font-otl.lua @@ -679,7 +679,7 @@ function otf.collectlookups(rawdata,kind,script,language)      return unpack(languagelookup)  end --- moved from font-oth.lua +-- moved from font-oth.lua, todo: also afm  local function getgsub(tfmdata,k,kind,value)      local shared  = tfmdata.shared @@ -735,6 +735,23 @@ function otf.getmultiple(tfmdata,k,kind)      return { k }  end +function otf.getkern(tfmdata,left,right,kind) +    local kerns = getgsub(tfmdata,left,kind or "kern",true) -- for now we use getsub +    if kerns then +        local found = kerns[right] +        local kind  = type(found) +        if kind == "table" then +            found = found[1][3] -- can be more clever +        elseif kind ~= "number" then +            found = false +        end +        if found then +            return found * tfmdata.parameters.factor +        end +    end +    return 0 +end +  local function check_otf(forced,specification,suffix)      local name = specification.name      if forced then diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua index 9a85dcf96..9c09185a3 100644 --- a/tex/context/base/font-otn.lua +++ b/tex/context/base/font-otn.lua @@ -255,11 +255,7 @@ local math_code          = nodecodes.math  local dir_code           = whatcodes.dir  local localpar_code      = whatcodes.localpar -  local discretionary_code = disccodes.discretionary -local regular_code       = disccodes.regular ------ automatic_code     = disccodes.automatic -  local ligature_code      = glyphcodes.ligature  local privateattribute   = attributes.private @@ -508,8 +504,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 @@ -526,69 +522,7 @@ local function getcomponentindex(start)      end  end -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 a_noligature = attributes.private("noligature")  local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head      if getattr(start,a_noligature) == 1 then @@ -603,8 +537,8 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun      -- needs testing (side effects):      local components = getfield(start,"components")      if components then --- we get a double free .. needs checking ---         flush_node_list(components) +     -- we get a double free .. needs checking +     -- flush_node_list(components)      end      --      local prev = getprev(start) @@ -626,8 +560,8 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun      if next then          setfield(next,"prev",base)      end -    setfield(base,"next",next)      setfield(base,"prev",prev) +    setfield(base,"next",next)      if not discfound then          local deletemarks = markflag ~= "mark"          local components = start @@ -674,53 +608,34 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun          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") +            -- we assume normalization in context, and don't care about generic ... especially +            -- \- can give problems as there we can have a negative char but that won't match +            -- anyway +            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 -                local pre, post = addhyphens(comp,comp,discnext,subtype) -- takes from components +                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(discfound,"next",next) +                setfield(next,"prev",discfound)                  setfield(base,"next",nil)                  setfield(base,"prev",nil)                  setfield(base,"components",copied) @@ -728,9 +643,7 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun                  setfield(discfound,"post",post)                  setfield(discfound,"replace",base)                  setfield(discfound,"subtype",discretionary_code) -                base = next -- or restart -            else -                -- forget about it in generic usage +                base = prev -- restart              end          end      end @@ -753,8 +666,8 @@ local function multiple_glyphs(head,start,multiple,ignoremarks)                  local n = copy_node(start) -- ignore components                  resetinjection(n)                  setfield(n,"char",multiple[k]) -                setfield(n,"next",sn)                  setfield(n,"prev",start) +                setfield(n,"next",sn)                  if sn then                      setfield(sn,"prev",n)                  end @@ -949,7 +862,8 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash      if not snext then          return head, start, false      else -        local prev, done = start, false +        local prev   = start +        local done   = 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 @@ -1518,11 +1432,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,                  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 @@ -1955,7 +1865,7 @@ end  local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,chainindex,sequence,chainproc)      if not start then -        return -- safeguard +        return head, start, false      end      local startishead   = start == head @@ -2165,7 +2075,7 @@ local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlooku              setfield(tail,"next",replace)              setfield(replace,"prev",tail)          end -        setfield(lookaheaddisc,"pre",cf) -- also updates tail +        setfield(lookaheaddisc,"pre",cf)      -- also updates tail          setfield(lookaheaddisc,"replace",new) -- also updates tail          start          = getprev(lookaheaddisc) @@ -2248,6 +2158,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq      local skipbase     = flags[3]      local markclass    = sequence.markclass      local skipped      = false +      for k=1,#contexts do -- i've only seen ccmp having > 1 (e.g. dejavu)          local match   = true          local current = start @@ -2316,7 +2227,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                  notmatchpre[last]     = nil                                  notmatchpost[last]    = true                                  notmatchreplace[last] = nil -                                local pre = getfield(last,"pre") +                                local pre     = getfield(last,"pre") +                                local replace = getfield(last,"replace")                                  if pre then                                      local n = n                                      while pre do @@ -2336,7 +2248,6 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                  else                                      notmatchpre[last] = true                                  end -                                local replace = getfield(last,"replace")                                  if replace then                                      -- so far we never entered this branch                                      while replace do @@ -2361,10 +2272,12 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                          break                                      end                                  else -                                    last = getnext(last) -- no skipping here +                                    notmatchreplace[last] = true                                  end +                                last = getnext(last)                              else -                                last = getnext(last) -- no skipping here +                                match = false +                                break                              end                          else                              match = false @@ -2726,7 +2639,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq              end          end      end -    if diskchain then -- maybe move up so that we can turn checking on/off +    if diskseen then -- maybe move up so that we can turn checking on/off          notmatchpre     = { }          notmatchpost    = { }          notmatchreplace = { } @@ -2815,7 +2728,7 @@ local function initialize(sequence,script,language,enabled)          local order = sequence.order          if order then              for i=1,#order do -- -                local kind = order[i] -- +                local kind  = order[i] --                  local valid = enabled[kind]                  if valid then                      local scripts = features[kind] -- @@ -2869,7 +2782,7 @@ end  --  -- * languages that use complex disc nodes -local function kernrun(disc,run) -- we can assume that prev and next are glyphs +local function kernrun(disc,run)      --      -- we catch <font 1><disc font 2>      -- @@ -2886,22 +2799,18 @@ local function kernrun(disc,run) -- we can assume that prev and next are glyphs      --      local prevmarks = prev      -- -    -- can be optional: +    -- 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 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 +    if prev and (pre or replace) and not (getid(prev) == glyph_code and getfont(prev) == currentfont and getsubtype(prev)<256) then +        prev = false      end -    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 +    if next and (post or replace) and not (getid(next) == glyph_code and getfont(next) == currentfont and getsubtype(next)<256) then +        next = false      end      --      if not pre then @@ -3123,8 +3032,6 @@ local function featuresprocessor(head,font,attr)      -- Keeping track of the headnode is needed for devanagari (I generalized it a bit      -- so that multiple cases are also covered.) -    -- 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 abit of a hack but works out ok for most cases. @@ -3144,7 +3051,6 @@ local function featuresprocessor(head,font,attr)          local gpossing     = typ == "gpos_single" or typ == "gpos_pair" -- maybe all of them          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              -- we need to get rid of this slide! probably no longer needed in latest luatex @@ -3165,8 +3071,7 @@ 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[start] +                                    local lookupmatch = lookupcache[char]                                      if lookupmatch then                                          -- todo: disc?                                          head, start, success = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i) @@ -3194,7 +3099,7 @@ local function featuresprocessor(head,font,attr)              local start = head -- local ?              rlmode = 0 -- to be checked ?              if ns == 1 then -- happens often -                local lookupname = subtables[1] +                local lookupname  = subtables[1]                  local lookupcache = lookuphash[lookupname]                  if not lookupcache then -- also check for empty cache                      report_missing_cache(typ,lookupname) @@ -3294,7 +3199,7 @@ local function featuresprocessor(head,font,attr)                                  -- sequence kan weg                                  local h, d, ok = handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1)                                  if ok then -                                    done = true +                                    done    = true                                      success = true                                  end                              end @@ -3320,7 +3225,7 @@ local function featuresprocessor(head,font,attr)                                      if lookupmatch then                                          local h, d, ok = handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection)                                          if ok then -                                            done = true +                                            done    = true                                              success = true                                          end                                      end @@ -3364,24 +3269,15 @@ local function featuresprocessor(head,font,attr)                                  start = getnext(start)                              end                          elseif id == disc_code then -                           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 +                            if gpossing then +                                kernrun(start,k_run) +                                start = getnext(start) +                            elseif typ == "gsub_ligature" then +                                start = testrun(start,t_run,c_run) +                            else +                                comprun(start,c_run)                                  start = getnext(start) -                           end +                            end                          elseif id == whatsit_code then -- will be function                              local subtype = getsubtype(start)                              if subtype == dir_code then @@ -3497,7 +3393,7 @@ local function featuresprocessor(head,font,attr)                          -- brr prev can be disc                          local char = getchar(prev)                          for i=1,ns do -                            local lookupname = subtables[i] +                            local lookupname  = subtables[i]                              local lookupcache = lookuphash[lookupname]                              if lookupcache then                                  local lookupmatch = lookupcache[char] @@ -3532,7 +3428,7 @@ local function featuresprocessor(head,font,attr)                              if id == glyph_code then                                  local char = getchar(n)                                  for i=1,ns do -                                    local lookupname = subtables[i] +                                    local lookupname  = subtables[i]                                      local lookupcache = lookuphash[lookupname]                                      if lookupcache then                                          local lookupmatch = lookupcache[char] @@ -3567,7 +3463,7 @@ local function featuresprocessor(head,font,attr)                              if a then                                  local char = getchar(start)                                  for i=1,ns do -                                    local lookupname = subtables[i] +                                    local lookupname  = subtables[i]                                      local lookupcache = lookuphash[lookupname]                                      if lookupcache then                                          local lookupmatch = lookupcache[char] @@ -3612,7 +3508,7 @@ local function featuresprocessor(head,font,attr)                              end                              if a then                                  for i=1,ns do -                                    local lookupname = subtables[i] +                                    local lookupname  = subtables[i]                                      local lookupcache = lookuphash[lookupname]                                      if lookupcache then                                          local char = getchar(start) @@ -3645,27 +3541,19 @@ local function featuresprocessor(head,font,attr)                              start = getnext(start)                          end                      elseif id == disc_code then -                        local discretionary = getsubtype(start) == discretionary_code                          if gpossing then -                            if discretionary then -                                kernrun(start,k_run) -                            else -                                discrun(start,d_run,k_run) -                            end +                            kernrun(start,k_run)                              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 == "gsub_ligature" then +                            start = testrun(start,t_run,c_run)                          else +                            comprun(start,c_run)                              start = getnext(start)                          end                      elseif id == whatsit_code then                          local subtype = getsubtype(start)                          if subtype == dir_code then +                            local dir = getfield(start,"dir")                              if dir == "+TLT" then                                  topstack = topstack + 1                                  dirstack[topstack] = dir diff --git a/tex/context/base/font-ots.lua b/tex/context/base/font-ots.lua index 27e0f161e..2204d1496 100644 --- a/tex/context/base/font-ots.lua +++ b/tex/context/base/font-ots.lua @@ -126,6 +126,7 @@ local trace_compruns     = false  registertracker("otf.compruns",     function(v  local quit_on_no_replacement = true  -- maybe per font  local zwnjruns               = true +local optimizekerns          = true  registerdirective("otf.zwnjruns",                 function(v) zwnjruns = v end)  registerdirective("otf.chain.quitonnoreplacement",function(value) quit_on_no_replacement = value end) @@ -196,11 +197,7 @@ local math_code          = nodecodes.math  local dir_code           = whatcodes.dir  local localpar_code      = whatcodes.localpar -  local discretionary_code = disccodes.discretionary -local regular_code       = disccodes.regular ------ automatic_code     = disccodes.automatic -  local ligature_code      = glyphcodes.ligature  local privateattribute   = attributes.private @@ -563,69 +560,7 @@ local function getcomponentindex(start) -- we could store this offset in the gly      end  end -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 a_noligature = attributes.private("noligature")  local function toligature(head,start,stop,char,dataset,sequence,markflag,discfound) -- brr head      if getattr(start,a_noligature) == 1 then @@ -640,8 +575,8 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou      -- needs testing (side effects):      local components = getfield(start,"components")      if components then --- we get a double free .. needs checking ---         flush_node_list(components) +     -- we get a double free .. needs checking +     -- flush_node_list(components)      end      --      local prev = getprev(start) @@ -663,8 +598,8 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou      if next then          setfield(next,"prev",base)      end -    setfield(base,"next",next)      setfield(base,"prev",prev) +    setfield(base,"next",next)      if not discfound then          local deletemarks = markflag ~= "mark"          local components = start @@ -711,53 +646,34 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou          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") +            -- we assume normalization in context, and don't care about generic ... especially +            -- \- can give problems as there we can have a negative char but that won't match +            -- anyway +            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 -                local pre, post = addhyphens(comp,comp,discnext,subtype) -- takes from components +                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(discfound,"next",next) +                setfield(next,"prev",discfound)                  setfield(base,"next",nil)                  setfield(base,"prev",nil)                  setfield(base,"components",copied) @@ -765,9 +681,7 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou                  setfield(discfound,"post",post)                  setfield(discfound,"replace",base)                  setfield(discfound,"subtype",discretionary_code) -                base = next -- or restart -            else -                -- forget about it in generic usage +                base = prev -- restart              end          end      end @@ -790,8 +704,8 @@ local function multiple_glyphs(head,start,multiple,ignoremarks)                  local n = copy_node(start) -- ignore components                  resetinjection(n)                  setfield(n,"char",multiple[k]) -                setfield(n,"next",sn)                  setfield(n,"prev",start) +                setfield(n,"next",sn)                  if sn then                      setfield(sn,"prev",n)                  end @@ -980,13 +894,13 @@ function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,step,i,in      if step.format == "pair" then          local dx, dy, w, h = setpair(start,factor,rlmode,sequence.flags[4],kerns,injection)          if trace_kerns then -            logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(dataset,sequence),gref(startchar),dx,dy,w,h) +            logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),dx,dy,w,h,injection or "injections")          end      else          -- needs checking .. maybe no kerns format for single          local k = setkern(start,factor,rlmode,kerns,injection)          if trace_kerns then -            logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(startchar),k) +            logprocess("%s: shifting single %s by %p as %s",pref(dataset,sequence),gref(startchar),k,injection or "injections")          end      end      return head, start, false @@ -1009,18 +923,30 @@ function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,inje                  break              elseif step.format == "pair" then                  local a, b = krn[1], krn[2] +                if optimizekerns then +                    -- this permits a mixed table, but we could also decide to optimize this +                    -- in the loader and use format 'kern' +                    if not b and a[1] == 0 and a[2] == 0 and a[4] == 0 then +                        local k = setkern(snext,factor,rlmode,a[3],injection) +                        if trace_kerns then +                            logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k) +                        end +                        done = true +                        break +                    end +                end                  if a and #a > 0 then -                    local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,injection) -- characters[startchar]) +                    local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,injection)                      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(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h) +                        logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")                      end                  end                  if b and #b > 0 then -                    local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection) -- characters[nextchar]) +                    local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection)                      if trace_kerns then                          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) +                        logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")                      end                  end                  done = true @@ -1028,7 +954,7 @@ function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,inje              elseif krn ~= 0 then                  local k = setkern(snext,factor,rlmode,krn,injection)                  if trace_kerns then -                    logprocess("%s: inserting kern %p between %s and %s",pref(dataset,sequence),k,gref(getchar(prev)),gref(nextchar)) +                    logprocess("%s: inserting kern %p between %s and %s as %s",pref(dataset,sequence),k,gref(getchar(prev)),gref(nextchar),injection or "injections")                  end                  done = true                  break @@ -1566,16 +1492,28 @@ function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlm                      break                  elseif step.format == "pair" then                      local a, b = krn[1], krn[2] +                    if optimizekerns then +                        -- this permits a mixed table, but we could also decide to optimize this +                        -- in the loader and use format 'kern' +                        if not b and a[1] == 0 and a[2] == 0 and a[4] == 0 then +                            local k = setkern(snext,factor,rlmode,a[3],"injections") +                            if trace_kerns then +                                logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k) +                            end +                            done = true +                            break +                        end +                    end                      if a and #a > 0 then                          local startchar = getchar(start) -                        local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a) -- currentlookups flags? +                        local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,"injections") -- currentlookups flags?                          if trace_kerns then                              logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),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) +                        local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,"injections")                          if trace_kerns then                              logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)                          end @@ -1848,7 +1786,7 @@ end  local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,ck,chainproc)      if not start then -        return -- safeguard +        return head, start, false      end      local startishead   = start == head @@ -2058,8 +1996,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c              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 +        setfield(lookaheaddisc,"pre",cf)      -- also updates tail +        setfield(lookaheaddisc,"replace",new) -- also updates tail          start          = getprev(lookaheaddisc)          sweephead[cf]  = getnext(clast) @@ -2114,8 +2052,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c          else              replace = new          end -        setfield(backtrackdisc,"post",post)       -- also updates post-tail -        setfield(backtrackdisc,"replace",replace) -- also updates replace-tail +        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) @@ -2270,7 +2208,8 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)                                  notmatchpre[last]     = nil                                  notmatchpost[last]    = true                                  notmatchreplace[last] = nil -                                local pre = getfield(last,"pre") +                                local pre     = getfield(last,"pre") +                                local replace = getfield(last,"replace")                                  if pre then                                      local n = n                                      while pre do @@ -2290,7 +2229,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)                                  else                                      notmatchpre[last] = true                                  end -                                local replace = getfield(last,"replace")                                  if replace then                                      -- so far we never entered this branch                                      while replace do @@ -2315,10 +2253,12 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)                                          break                                      end                                  else -                                    last = getnext(last) -- no skipping here +                                    notmatchreplace[last] = true                                  end +                                last = getnext(last)                              else -                                last = getnext(last) -- no skipping here +                                match = false +                                break                              end                          else                              match = false @@ -2808,78 +2748,61 @@ local function kernrun(disc,run)          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 +    if prev and (pre or replace) and not (getid(prev) == glyph_code and getfont(prev) == currentfont and getsubtype(prev)<256) then +        prev = false      end -    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 +    if next and (post or replace) and not (getid(next) == glyph_code and getfont(next) == currentfont and getsubtype(next)<256) then +        next = false      end      -- -    if not pre then -        -- go on -    elseif prev then -        local nest = getprev(pre) -        setfield(pre,"prev",prev) -        setfield(prev,"next",pre) -        run(prevmarks,"preinjections") -        setfield(pre,"prev",nest) -        setfield(prev,"next",disc) -    else -        run(pre,"preinjections") +    if pre then +        run(pre,"injections") +        if prev then +            local nest = getprev(pre) +            setfield(pre,"prev",prev) +            setfield(prev,"next",pre) +            run(prevmarks,"preinjections",getnext(pre)) +            setfield(pre,"prev",nest) +            setfield(prev,"next",disc) +        end      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",next) -        setfield(tail,"next",nil) -        setfield(next,"prev",disc) -    else -        run(post,"postinjections") +    if post then +        run(post,"injections") +        if next then +            local tail = find_node_tail(post) +            setfield(tail,"next",next) +            setfield(next,"prev",tail) +            run(tail,"postinjections",next) +            setfield(tail,"next",nil) +            setfield(next,"prev",disc) +        end      end      -- -    if not replace and prev and next then -        -- this should be already done by discfound +    if replace then +        run(replace,"injections") +        if prev then +            local nest = getprev(replace) +            setfield(replace,"prev",prev) +            setfield(prev,"next",replace) +            run(prevmarks,"replaceinjections",getnext(replace)) +            setfield(replace,"prev",nest) +            setfield(prev,"next",disc) +        end +        if next then +            local tail = find_node_tail(replace) +            setfield(tail,"next",next) +            setfield(next,"prev",tail) +            run(tail,"replaceinjections",next) +            setfield(tail,"next",nil) +            setfield(next,"prev",disc) +        end +    elseif prev and next then          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) -        setfield(replace,"prev",prev) -        setfield(prev,"next",replace) -        setfield(tail,"next",next) -        setfield(next,"prev",tail) -        run(prevmarks,"replaceinjections",next) -        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(prevmarks,"replaceinjections") -        setfield(replace,"prev",nest) +        run(prevmarks,"emptyinjections",next)          setfield(prev,"next",disc) -    elseif next then -        local tail = find_node_tail(replace) -        setfield(tail,"next",next) -        setfield(next,"prev",tail) -        run(replace,"replaceinjections",next) -        setfield(tail,"next",nil)          setfield(next,"prev",disc) -    else -        run(replace,"replaceinjections")      end  end @@ -2903,7 +2826,7 @@ 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) +        sweeptype = "pre"          local new, done = run(pre)          if done then              setfield(disc,"pre",new) @@ -3144,7 +3067,7 @@ local function featuresprocessor(head,font,attr)              local start = head -- local ?              rlmode = 0 -- to be checked ?              if nofsteps == 1 then -- happens often -                local step = steps[1] +                local step        = steps[1]                  local lookupcache = step.coverage                  if not lookupcache then -- also check for empty cache                      report_missing_cache(dataset,sequence) @@ -3247,14 +3170,14 @@ if not a or (a == attr) then                                  -- sequence kan weg                                  local h, d, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)                                  if ok then -                                    done = true +                                    done    = true                                      success = true                                  end                              end                          end                      end -                    local function k_run(sub,injection,last) +                    local function k_run(sub,injection,last,bound)                          local a = getattr(sub,0)  --                         if a then  --                             a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute) @@ -3272,9 +3195,9 @@ if not a or (a == attr) then                                  if id == glyph_code then                                      local lookupmatch = lookupcache[getchar(n)]                                      if lookupmatch then -                                        local h, d, ok = handler(sub,n,dataset,sequence,lookupmatch,rlmode,step,1,injection) +                                        local h, d, ok = handler(sub,n,dataset,sequence,lookupmatch,rlmode,step,1,injection,bound)                                          if ok then -                                            done = true +                                            done    = true                                              success = true                                          end                                      end @@ -3318,24 +3241,15 @@ if not a or (a == attr) then                                  start = getnext(start)                              end                          elseif id == disc_code then -                           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 +                            if gpossing then +                                kernrun(start,k_run) +                                start = getnext(start) +                            elseif typ == "gsub_ligature" then +                                start = testrun(start,t_run,c_run) +                            else +                                comprun(start,c_run)                                  start = getnext(start) -                           end +                            end                          elseif id == whatsit_code then -- will be function                              local subtype = getsubtype(start)                              if subtype == dir_code then @@ -3472,7 +3386,7 @@ if not a or (a == attr) then                      end                  end -                local function k_run(sub,injection,last) +                local function k_run(sub,injection,last,bound)                      local a = getattr(sub,0)  --                     if a then  --                         a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute) @@ -3494,7 +3408,7 @@ if not a or (a == attr) then                                      if lookupcache then                                          local lookupmatch = lookupcache[char]                                          if lookupmatch then -                                            local h, d, ok = handler(head,n,dataset,sequence,lookupmatch,step,rlmode,i,injection) +                                            local h, d, ok = handler(head,n,dataset,sequence,lookupmatch,step,rlmode,i,injection,bound)                                              if ok then                                                  done = true                                                  break @@ -3525,7 +3439,7 @@ if not a or (a == attr) 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] @@ -3603,27 +3517,19 @@ if not a or (a == attr) then                              start = getnext(start)                          end                      elseif id == disc_code then -                        local discretionary = getsubtype(start) == discretionary_code                          if gpossing then -                            if discretionary then -                                kernrun(start,k_run) -                            else -                                discrun(start,d_run,k_run) -                            end +                            kernrun(start,k_run)                              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 == "gsub_ligature" then +                            start = testrun(start,t_run,c_run)                          else +                            comprun(start,c_run)                              start = getnext(start)                          end                      elseif id == whatsit_code then                          local subtype = getsubtype(start)                          if subtype == dir_code then +                            local dir = getfield(start,"dir")                              if dir == "+TLT" then                                  topstack = topstack + 1                                  dirstack[topstack] = dir diff --git a/tex/context/base/lang-dis.lua b/tex/context/base/lang-dis.lua index 91b0e9460..d503cdffd 100644 --- a/tex/context/base/lang-dis.lua +++ b/tex/context/base/lang-dis.lua @@ -47,6 +47,8 @@ local setattribute       = tex.setattribute  local getlanguagedata    = languages.getdata +local check_regular      = true +  -- local expanders = {  --     [disccodes.discretionary] = function(d,template)  --         -- \discretionary @@ -207,41 +209,47 @@ local expanders = {          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 check_regular then +            -- simple +            if not template then +                -- can there be font kerns already? +                template = getprev(d)                  if template and getid(template) ~= glyph_code then -                    template = nil +                    template = getnext(d) +                    if template and getid(template) ~= glyph_code then +                        template = nil +                    end                  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 -            local pre, post, replace = getdisc(d) -- pre can be set -            local done     = false -            if prechar and prechar > 0 then -                done = true -                pre  = copy_node(template) -                setfield(pre,"char",prechar) -            end -            if postchar and postchar > 0 then -                done = true -                post = copy_node(template) -                setfield(post,"char",postchar) -            end -            if done then -                setdisc(d,pre,post,replace,discretionary_code) +            if template then +                local language = template and getfield(template,"lang") +                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 +                    done = true +                    pre  = copy_node(template) +                 -- setfield(pre,"char",prechar) +                    setchar(pre,prechar) +                end +                if postchar and postchar > 0 then +                    done = true +                    post = copy_node(template) +                 -- setfield(post,"char",postchar) +                    setchar(post,postchar) +                end +                if done then +                    setdisc(d,pre,post,replace,discretionary_code) +                end +            else +             -- print("lone regular discretionary ignored")              end +            return template          else -         -- print("lone regular discretionary ignored") +            setfield(d,"subtype",discretionary_code)          end -        return template      end,      [disccodes.first] = function()          -- forget about them diff --git a/tex/context/base/lpdf-epa.lua b/tex/context/base/lpdf-epa.lua index dd5ecc609..59f62a310 100644 --- a/tex/context/base/lpdf-epa.lua +++ b/tex/context/base/lpdf-epa.lua @@ -12,6 +12,7 @@ if not modules then modules = { } end modules ['lpdf-epa'] = {  local type, tonumber = type, tonumber  local format, gsub, lower = string.format, string.gsub, string.lower  local formatters = string.formatters +local abs = math.abs  ----- lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns @@ -38,6 +39,8 @@ local escapetex      = characters.filters.utf.private.escape  local bookmarks      = structures.bookmarks +local maxdimen       = 2^30-1 +  local layerspec = { -- predefining saves time      "epdflinks"  } @@ -178,7 +181,11 @@ function codeinjections.mergereferences(specification)                          local h         = yscale * (a_ury - a_lly)                          if subtype == "Link" then                              local a = annotation.A -                            if a then +                            if not a then +                                report_link("missing link annotation") +                            elseif w > width or h > height or w < 0 or h < 0 or abs(x) > (maxdimen/2) or abs(y) > (maxdimen/2) then +                                report_link("broken link rectangle [%f %f %f %f] (max: %f)",a_llx,a_lly,a_urx,a_ury,maxdimen/2) +                            else                                  local linktype = a.S                                  if linktype == "GoTo" then                                      link_goto(x,y,w,h,document,annotation,pagedata,namespace) @@ -189,8 +196,6 @@ function codeinjections.mergereferences(specification)                                  elseif trace_links then                                      report_link("unsupported link annotation %a",linktype)                                  end -                            else -                                report_link("missing link annotation")                              end                          elseif trace_links then                              report_link("unsupported annotation %a",subtype) diff --git a/tex/context/base/m-morse.mkvi b/tex/context/base/m-morse.mkvi new file mode 100644 index 000000000..a2c20dff7 --- /dev/null +++ b/tex/context/base/m-morse.mkvi @@ -0,0 +1,273 @@ +%D \module +%D   [       file=m-morse, +%D        version=2010.12.10, +%D          title=\CONTEXT\ Extra Modules, +%D       subtitle=Morse, +%D         author=Hans Hagen, +%D           date=\currentdate, +%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% todo: act upon the node list +% make it a buffer operation +% nice in cld manual + +\startluacode + +moduledata.morse = moduledata.morse or { } +local morse      = moduledata.morse + +local utfcharacters, gsub = string.utfcharacters, string.gsub +local ucchars, shchars = characters.ucchars, characters.shchars + +local codes = { + +    ["A"] = "·—", +    ["B"] = "—···", +    ["C"] = "—·—·", +    ["D"] = "—··", +    ["E"] = "·", +    ["F"] = "··—·", +    ["G"] = "——·", +    ["H"] = "····", +    ["I"] = "··", +    ["J"] = "·———", +    ["K"] = "—·—", +    ["L"] = "·—··", +    ["M"] = "——", +    ["N"] = "—·", +    ["O"] = "———", +    ["P"] = "·——·", +    ["Q"] = "——·—", +    ["R"] = "·—·", +    ["S"] = "···", +    ["T"] = "—", +    ["U"] = "··—", +    ["V"] = "···—", +    ["W"] = "·——", +    ["X"] = "—··—", +    ["Y"] = "—·——", +    ["Z"] = "——··", + +    ["0"] = "—————", +    ["1"] = "·————", +    ["2"] = "··———", +    ["3"] = "···——", +    ["4"] = "····—", +    ["5"] = "·····", +    ["6"] = "—····", +    ["7"] = "——···", +    ["8"] = "———··", +    ["9"] = "————·", + +    ["."] = "·—·—·—", +    [","] = "——··——", +    [":"] = "———···", +    [";"] = "—·—·—", + +    ["?"] = "··——··", +    ["!"] = "—·—·——", + +    ["-"] = "—····—", +    ["/"] = "—··—· ", + +    ["("] = "—·——·", +    [")"] = "—·——·—", + +    ["="] = "—···—", +    ["@"] = "·——·—·", + +    ["'"] = "·————·", +    ['"'] = "·—··—·", + +    ["À"] =	"·——·—", +    ["Å"] =	"·——·—", +    ["Ä"] =	"·—·—", +    ["Æ"] =	"·—·—", +    ["Ç"] =	"—·—··", +    ["É"] =	"··—··", +    ["È"] =	"·—··—", +    ["Ñ"] =	"——·——", +    ["Ö"] =	"———·", +    ["Ø"] =	"———·", +    ["Ü"] =	"··——", +    ["ß"] =	"··· ···", + +} + +morse.codes = codes + +local fallbackself = false + +local function codefallback(t,k) +    if k then +        local u = ucchars[k] +        local v = rawget(t,u) or rawget(t,shchars[u]) or false +        t[k] = v +        return v +    elseif fallbackself then +        return k +    else +        return false +    end +end + +table.setmetatableindex(codes,codefallback) + +local MorseBetweenWords      = context.MorseBetweenWords +local MorseBetweenCharacters = context.MorseBetweenCharacters +local MorseLong              = context.MorseLong +local MorseShort             = context.MorseShort +local MorseSpace             = context.MorseSpace +local MorseUnknown           = context.MorseUnknown + +local function toverbose(str) +    str = gsub(str,"%s*+%s*","+") +    str = gsub(str,"%s+"," ") +    local done = false +    for m in utfcharacters(str) do +        if done then +            MorseBetweenCharacters() +        end +        if m == "·" or m == "." then +            MorseShort() +            done = true +        elseif m == "—" or m == "-" then +            MorseLong() +            done = true +        elseif m == " " then +            if done then +                MorseBetweenCharacters() +            end +            done = false +        elseif m == "+" then +            MorseBetweenWords() +            done = false +        else +            MorseUnknown(m) +        end +    end +end + +local function toregular(str) +    local inmorse = false +    for s in utfcharacters(str) do +        local m = codes[s] +        if m then +            if inmorse then +                MorseBetweenWords() +            else +                inmorse = true +            end +            local done = false +            for m in utfcharacters(m) do +                if done then +                    MorseBetweenCharacters() +                else +                    done = true +                end +                if m == "·" then +                    MorseShort() +                elseif m == "—" then +                    MorseLong() +                elseif m == " " then +                    MorseBetweenCharacters() +                end +            end +            inmorse = true +        elseif s == "\n" or s == " " then +            MorseSpace() +            inmorse = false +        else +            if inmorse then +                MorseBetweenWords() +            else +                inmorse = true +            end +            MorseUnknown(s) +        end +    end +end + +local function tomorse(str,verbose) +    if verbose then +        toverbose(str) +    else +        toregular(str) +    end +end + +morse.tomorse = tomorse + +function morse.filetomorse(name,verbose) +    tomorse(resolvers.loadtexfile(name),verbose) +end + +function morse.showtable() +    context.starttabulate { "|l|l|" } -- { "|l|l|l|" } +    for k, v in table.sortedpairs(codes) do +        context.NC() context(k) +     -- context.NC() context(v) +        context.NC() tomorse(v,true) +        context.NC() context.NR() +    end +    context.stoptabulate() +end + +\stopluacode + +\unprotect + +% todo: \setupmorse, but probably it's not worth the trouble. + +\def\MorseWidth             {0.4em} +\def\MorseHeight            {0.2em} +%def\MorseShort             {\dontleavehmode\blackrule[\c!height=\MorseHeight,\c!width=\dimexpr\MorseWidth]} +%def\MorseLong              {\dontleavehmode\blackrule[\c!height=\MorseHeight,\c!width=3\dimexpr\MorseWidth]} +\def\MorseShort             {\dontleavehmode\vrule\!!width \dimexpr\MorseWidth\!!height\MorseHeight\!!depth\zeropoint\relax} +\def\MorseLong              {\dontleavehmode\vrule\!!width3\dimexpr\MorseWidth\!!height\MorseHeight\!!depth\zeropoint\relax} +\def\MorseBetweenCharacters {\kern\MorseWidth} +\def\MorseBetweenWords      {\hskip3\dimexpr\MorseWidth\relax} +\def\MorseSpace             {\hskip7\dimexpr\MorseWidth\relax} +\def\MorseUnknown      #text{[\detokenize{#text}]} + +\unexpanded\def\MorseCode      #text{\ctxlua{moduledata.morse.tomorse(\!!bs#text\!!es,true)}} +\unexpanded\def\MorseString    #text{\ctxlua{moduledata.morse.tomorse(\!!bs#text\!!es)}} +\unexpanded\def\MorseFile      #text{\ctxlua{moduledata.morse.filetomorse("#text")}} +\unexpanded\def\MorseTable          {\ctxlua{moduledata.morse.showtable()}} + +\let\Morse     \MorseString + +%def\MorseShort             {·} +%def\MorseLong              {—} + +\protect + +\continueifinputfile{m-morse.mkvi} + +\starttext + +\MorseTable + +\startlines +\MorseCode{—·—· ——— —· — · —··— —+—— —·— ·· ···—} +\MorseCode{—·—· ——— —· — · —··— — + —— —·— ·· ···—} +\Morse{ÀÁÂÃÄÅàáâãäå} +\Morse{ÆÇæç} +\Morse{ÈÉÊËèéêë} +\Morse{ÌÍÎÏìíîï} +\Morse{Ññ} +\Morse{ÒÓÔÕÖòóôõö} +\Morse{Øø} +\Morse{ÙÚÛÜùúû} +\Morse{Ýýÿ} +\Morse{ß} +\Morse{Ţţ} +\stoplines + +\Morse{A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} + +\stoptext diff --git a/tex/context/base/m-newotf.mkiv b/tex/context/base/m-newotf.mkiv index df91cbe02..8668eb827 100644 --- a/tex/context/base/m-newotf.mkiv +++ b/tex/context/base/m-newotf.mkiv @@ -11,6 +11,8 @@  %C therefore copyrighted by \PRAGMA. See mreadme.pdf for  %C details. +% \endinput +  %D This module will go away as soon as we use the new loader code by default.  %D That will happen after extensive testing. Generic support will happen after  %D that. @@ -19,6 +21,8 @@  \startluacode      local files = { +        "font-otj", +"font-otj-new",          "font-otr",          "font-cff",          "font-ttf", @@ -67,6 +71,17 @@      directives.register("nodes.injections.fontkern", function(v) setfield(kern,"subtype",v and 0 or 1) end) +    local fonts    = fonts +    local handlers = fonts.handlers +    local otf      = handlers.otf -- brrr +    local afm      = handlers.afm -- brrr +    local getters  = fonts.getters + +    getters.kern        .opentype = otf.getkern +    getters.substitution.opentype = otf.getsubstitution +    getters.alternate   .opentype = otf.getalternate +    getters.multiple    .opentype = otf.getmultiple +  \stopluacode  \protect \endinput diff --git a/tex/context/base/node-fnt.lua b/tex/context/base/node-fnt.lua index 9cb9b487d..4272f304b 100644 --- a/tex/context/base/node-fnt.lua +++ b/tex/context/base/node-fnt.lua @@ -138,6 +138,8 @@ function fonts.setdiscexpansion(v)      end  end +fonts.setdiscexpansion(true) +  function handlers.characters(head)      -- either next or not, but definitely no already processed list      starttiming(nodes) diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdfBinary files differ index 7ccbf6cdc..b1f0fb99b 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.pdfBinary files differ index c8718cfe4..0d09a329b 100644 --- a/tex/context/base/status-lua.pdf +++ b/tex/context/base/status-lua.pdf diff --git a/tex/context/base/typo-cap.lua b/tex/context/base/typo-cap.lua index eaf14bd63..7f0656130 100644 --- a/tex/context/base/typo-cap.lua +++ b/tex/context/base/typo-cap.lua @@ -11,6 +11,7 @@ local format, insert = string.format, table.insert  local div, randomnumber = math.div, math.random  local trace_casing = false  trackers.register("typesetters.casing", function(v) trace_casing = v end) +local check_kerns  = false  directives.register("typesetters.casing.checkkerns", function(v) check_kerns = v end)  local report_casing = logs.reporter("typesetting","casing") @@ -51,6 +52,8 @@ local userskip_code   = skipcodes.userskip  local tasks           = nodes.tasks +local newkern         = nuts.pool.kern +  local fonthashes      = fonts.hashes  local fontdata        = fonthashes.identifiers  local fontchar        = fonthashes.characters @@ -199,25 +202,67 @@ local function camel(start,attr,lastfont,n,count,where,first)      return start, done_1 or done_2, true  end +-- local function mixed(start,attr,lastfont,n,count,where,first) +--     if where == "post" then +--         return +--     end +--     local used = first or start +--     local char = getchar(first) +--     local dc   = uccodes[char] +--     if not dc then +--         return start, false, true +--     elseif dc == char then +--         local lfa = lastfont[n] +--         if lfa then +--             setfield(first,"font",lfa) +--             return start, true, true +--         else +--             return start, false, true +--         end +--     else +--         replacer(first or start,uccodes) +--         return start, true, true +--     end +-- end +  local function mixed(start,attr,lastfont,n,count,where,first)      if where == "post" then          return      end      local used = first or start -    local char = getchar(first) +    local char = getchar(used)      local dc   = uccodes[char]      if not dc then          return start, false, true      elseif dc == char then          local lfa = lastfont[n]          if lfa then -            setfield(first,"font",lfa) +            setfield(used,"font",lfa)              return start, true, true          else              return start, false, true          end      else -        replacer(first or start,uccodes) +        if check_kerns then +            local p = getprev(used) +            if p and getid(p) == glyph_code then +                local c = lccodes[char] +                local c = type(c) == "table" and c[1] or c +                replacer(used,uccodes) +                local fp = getfont(p) +                local fc = getfont(used) +                if fp ~= fc then +                    local k = fonts.getkern(fontdata[fp],getchar(p),c) +                    if k ~= 0 then +                        insert_after(p,p,newkern(k)) +                    end +                end +            else +                replacer(used,uccodes) +            end +        else +            replacer(used,uccodes) +        end          return start, true, true      end  end diff --git a/tex/context/base/typo-cap.mkiv b/tex/context/base/typo-cap.mkiv index 2859ba104..114532e4e 100644 --- a/tex/context/base/typo-cap.mkiv +++ b/tex/context/base/typo-cap.mkiv @@ -266,14 +266,14 @@  %   [MixedCaps]  %   [MixedCaps*default cp \the\exheight] -\definefontfeature -  [mixeddefault] -  [default] -  [extend=1.2] +% \definefontfeature +%   [mixeddefault] +%   [default] +%   [extend=1.2]  \definefont    [MixedCaps] -  [CurrentFont*default,mixeddefault cp \the\exheight] +  [CurrentFont*default cp 1.2\exheight]  \setupcapitals    [\v!mixed] diff --git a/tex/generic/context/luatex/luatex-fonts-inj.lua b/tex/generic/context/luatex/luatex-fonts-inj.lua index cdf14b935..10108a271 100644 --- a/tex/generic/context/luatex/luatex-fonts-inj.lua +++ b/tex/generic/context/luatex/luatex-fonts-inj.lua @@ -549,9 +549,19 @@ local function inject_marks(marks,marki,nofmarks)                          else                              -- kern(x) glyph(p) kern(w-x) mark(n)                           -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern -                            local leftkern = pp.leftkern -                            if leftkern then -                                ox = px - pn.markx - leftkern +                            --  +							-- According to Kai we don't need to handle leftkern here but I'm +                            -- pretty sure I've run into a case where it was needed so maybe  +	                        -- some day we need something more clever here. +                            --  +							if false then   +                                -- a mark with kerning  +                                local leftkern = pp.leftkern +                                if leftkern then +                                    ox = px - pn.markx - leftkern +                                else +                                    ox = px - pn.markx +                                end                              else                                  ox = px - pn.markx                              end diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index da21fa6d8..7d3e981fe 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  : 09/01/15 11:10:11 +-- merge date  : 09/04/15 11:00:13  do -- begin closure to overcome local limits and interference @@ -9496,6 +9496,22 @@ function otf.getmultiple(tfmdata,k,kind)    end    return { k }  end +function otf.getkern(tfmdata,left,right,kind) +  local kerns=getgsub(tfmdata,left,kind or "kern",true)  +  if kerns then +    local found=kerns[right] +    local kind=type(found) +    if kind=="table" then +      found=found[1][3]  +    elseif kind~="number" then +      found=false +    end +    if found then +      return found*tfmdata.parameters.factor +    end +  end +  return 0 +end  end -- closure @@ -10572,9 +10588,15 @@ local function inject_marks(marks,marki,nofmarks)              if pn.markdir<0 then                ox=px-pn.markx-rightkern              else -              local leftkern=pp.leftkern -              if leftkern then -                ox=px-pn.markx-leftkern +							 +	 +							if false then +                local leftkern=pp.leftkern +                if leftkern then +                  ox=px-pn.markx-leftkern +                else +                  ox=px-pn.markx +                end                else                  ox=px-pn.markx                end | 
