diff options
Diffstat (limited to 'src')
37 files changed, 5934 insertions, 2915 deletions
| diff --git a/src/fontloader/misc/fontloader-basics-nod.lua b/src/fontloader/misc/fontloader-basics-nod.lua index 1ec2895..39400a3 100644 --- a/src/fontloader/misc/fontloader-basics-nod.lua +++ b/src/fontloader/misc/fontloader-basics-nod.lua @@ -56,6 +56,9 @@ local whatcodes    = { } for k,v in next, node.whatsits() do whatcodes[string.gs  local glyphcodes   = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" }  local disccodes    = { [0] = "discretionary", "explicit", "automatic", "regular", "first", "second" } +for i=0,#glyphcodes do glyphcodes[glyphcodes[i]] = i end +for i=0,#disccodes  do disccodes [disccodes [i]] = i end +  nodes.nodecodes    = nodecodes  nodes.whatcodes    = whatcodes  nodes.whatsitcodes = whatcodes diff --git a/src/fontloader/misc/fontloader-font-afm.lua b/src/fontloader/misc/fontloader-font-afm.lua index a96c668..329639b 100644 --- a/src/fontloader/misc/fontloader-font-afm.lua +++ b/src/fontloader/misc/fontloader-font-afm.lua @@ -152,14 +152,14 @@ end  local keys = { } -function keys.FontName    (data,line) data.metadata.fontname     = strip    (line) -- get rid of spaces -                                      data.metadata.fullname     = strip    (line) end -function keys.ItalicAngle (data,line) data.metadata.italicangle  = tonumber (line) end -function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch = toboolean(line,true) end -function keys.CharWidth   (data,line) data.metadata.charwidth    = tonumber (line) end -function keys.XHeight     (data,line) data.metadata.xheight      = tonumber (line) end -function keys.Descender   (data,line) data.metadata.descender    = tonumber (line) end -function keys.Ascender    (data,line) data.metadata.ascender     = tonumber (line) end +function keys.FontName    (data,line) data.metadata.fontname    = strip    (line) -- get rid of spaces +                                      data.metadata.fullname    = strip    (line) end +function keys.ItalicAngle (data,line) data.metadata.italicangle = tonumber (line) end +function keys.IsFixedPitch(data,line) data.metadata.monospaced  = toboolean(line,true) end +function keys.CharWidth   (data,line) data.metadata.charwidth   = tonumber (line) end +function keys.XHeight     (data,line) data.metadata.xheight     = tonumber (line) end +function keys.Descender   (data,line) data.metadata.descender   = tonumber (line) end +function keys.Ascender    (data,line) data.metadata.ascender    = tonumber (line) end  function keys.Comment     (data,line)   -- Comment DesignSize 12 (pts)   -- Comment TFM designsize: 12 (in points) @@ -640,7 +640,7 @@ local function copytotfm(data)          local spacer     = "space"          local spaceunits = 500          -- -        local monospaced  = metadata.isfixedpitch +        local monospaced  = metadata.monospaced          local charwidth   = metadata.charwidth          local italicangle = metadata.italicangle          local charxheight = metadata.xheight and metadata.xheight > 0 and metadata.xheight diff --git a/src/fontloader/misc/fontloader-font-con.lua b/src/fontloader/misc/fontloader-font-con.lua index 72fbb5c..383a403 100644 --- a/src/fontloader/misc/fontloader-font-con.lua +++ b/src/fontloader/misc/fontloader-font-con.lua @@ -191,10 +191,9 @@ in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to  excessive memory usage in CJK fonts, we no longer pass the boundingbox.)</p>  --ldx]]-- --- The scaler is only used for otf and afm and virtual fonts. If --- a virtual font has italic correction make sure to set the --- hasitalics flag. Some more flags will be added in --- the future. +-- The scaler is only used for otf and afm and virtual fonts. If a virtual font has italic +-- correction make sure to set the hasitalics flag. Some more flags will be added in the +-- future.  --[[ldx--  <p>The reason why the scaler was originally split, is that for a while we experimented @@ -426,6 +425,7 @@ function constructors.scale(tfmdata,specification)      local vdelta         = delta      --      target.designsize    = parameters.designsize -- not really needed so it might become obsolete +    target.units         = units      target.units_per_em  = units                 -- just a trigger for the backend      --      local direction      = properties.direction or tfmdata.direction or 0 -- pointless, as we don't use omf fonts at all @@ -562,26 +562,27 @@ function constructors.scale(tfmdata,specification)          target.mathparameters = nil -- nop      end      -- -    local italickey  = "italic" -    local useitalics = true -- something context -    -- -    -- some context specific trickery (this will move to a plugin) +    -- Here we support some context specific trickery (this might move to a plugin). During the +    -- transition to opentype the engine had troubles with italics so we had some additional code +    -- for fixing that. In node mode (text) we don't care much if italics gets passed because +    -- the engine does nothign with them then.      --      if hasmath then -     -- the latest luatex can deal with it itself so we now disable this -     -- mechanism here -     -- -     -- if properties.mathitalics then -     --     italickey = "italic_correction" -     --     if trace_defining then -     --         report_defining("math italics disabled for font %a, fullname %a, filename %a",name,fullname,filename) -     --     end -     -- end -        autoitalicamount = false -- new -    elseif properties.textitalics then -        italickey = "italic_correction" -        useitalics = false -        if properties.delaytextitalics then +        local mathitalics = properties.mathitalics +        if mathitalics == false then +            if trace_defining then +                report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename) +            end +            hasitalics       = false +            autoitalicamount = false +        end +    else +        local textitalics = properties.textitalics +        if textitalics == false then +            if trace_defining then +                report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename) +            end +            hasitalics       = false              autoitalicamount = false          end      end @@ -590,8 +591,7 @@ function constructors.scale(tfmdata,specification)      --      if trace_defining then          report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a", -            name,fullname,filename,hdelta,vdelta, -            hasmath and "enabled" or "disabled",useitalics and "enabled" or "disabled") +            name,fullname,filename,hdelta,vdelta,hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")      end      --      constructors.beforecopyingcharacters(target,tfmdata) @@ -606,15 +606,15 @@ function constructors.scale(tfmdata,specification)              local c = changed[unicode]              if c then                  description = descriptions[c] or descriptions[unicode] or character -                character = characters[c] or character -                index = description.index or c +                character   = characters[c] or character +                index       = description.index or c              else                  description = descriptions[unicode] or character -                index = description.index or unicode +                index       = description.index or unicode              end          else              description = descriptions[unicode] or character -            index = description.index or unicode +            index       = description.index or unicode          end          local width  = description.width          local height = description.height @@ -699,23 +699,6 @@ function constructors.scale(tfmdata,specification)              end          end          -- -        if autoitalicamount then -            local vi = description.italic -            if not vi then -                local vi = description.boundingbox[3] - description.width + autoitalicamount -                if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic -                    chr[italickey] = vi*hdelta -                end -            elseif vi ~= 0 then -                chr[italickey] = vi*hdelta -            end -        elseif hasitalics then -            local vi = description.italic -            if vi and vi ~= 0 then -                chr[italickey] = vi*hdelta -            end -        end -        -- to be tested          if hasmath then              -- todo, just operate on descriptions.math              local vn = character.next @@ -754,7 +737,7 @@ function constructors.scale(tfmdata,specification)                      end                  end              end -            local va = character.top_accent +            local va = character.accent              if va then                  chr.top_accent = vdelta*va              end @@ -777,6 +760,27 @@ function constructors.scale(tfmdata,specification)                      chr.mathkern = kerns -- singular -> should be patched in luatex !                  end              end +            if hasitalics then +                local vi = character.italic +                if vi and vi ~= 0 then +                    chr.italic = vi*hdelta +                end +            end +        elseif autoitalicamount then -- itlc feature +            local vi = description.italic +            if not vi then +                local vi = description.boundingbox[3] - description.width + autoitalicamount +                if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic +                    chr.italic = vi*hdelta +                end +            elseif vi ~= 0 then +                chr.italic = vi*hdelta +            end +        elseif hasitalics then -- unlikely +            local vi = character.italic +            if vi and vi ~= 0 then +                chr.italic = vi*hdelta +            end          end          if haskerns then              local vk = character.kerns @@ -843,6 +847,8 @@ function constructors.scale(tfmdata,specification)          targetcharacters[unicode] = chr      end      -- +    properties.setitalics = hasitalics -- for postprocessing +    --      constructors.aftercopyingcharacters(target,tfmdata)      --      constructors.trytosharefont(target,tfmdata) @@ -895,12 +901,21 @@ function constructors.finalize(tfmdata)          parameters.slantfactor = tfmdata.slant or 0      end      -- -    if not parameters.designsize then -        parameters.designsize = tfmdata.designsize or (factors.pt * 10) +    local designsize = parameters.designsize +    if designsize then +        parameters.minsize = tfmdata.minsize or designsize +        parameters.maxsize = tfmdata.maxsize or designsize +    else +        designsize = factors.pt * 10 +        parameters.designsize = designsize +        parameters.minsize    = designsize +        parameters.maxsize    = designsize      end +    parameters.minsize = tfmdata.minsize or parameters.designsize +    parameters.maxsize = tfmdata.maxsize or parameters.designsize      --      if not parameters.units then -        parameters.units = tfmdata.units_per_em or 1000 +        parameters.units = tfmdata.units or tfmdata.units_per_em or 1000      end      --      if not tfmdata.descriptions then @@ -976,6 +991,7 @@ function constructors.finalize(tfmdata)      tfmdata.auto_protrude  = nil      tfmdata.extend         = nil      tfmdata.slant          = nil +    tfmdata.units          = nil      tfmdata.units_per_em   = nil      --      tfmdata.cache          = nil diff --git a/src/fontloader/misc/fontloader-font-def.lua b/src/fontloader/misc/fontloader-font-def.lua index fdded3c..add42ee 100644 --- a/src/fontloader/misc/fontloader-font-def.lua +++ b/src/fontloader/misc/fontloader-font-def.lua @@ -183,10 +183,11 @@ end  function resolvers.name(specification)      local resolve = fonts.names.resolve      if resolve then -        local resolved, sub = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions +        local resolved, sub, subindex = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions          if resolved then              specification.resolved = resolved              specification.sub      = sub +            specification.subindex = subindex              local suffix = lower(suffixonly(resolved))              if fonts.formats[suffix] then                  specification.forced     = suffix @@ -204,10 +205,11 @@ end  function resolvers.spec(specification)      local resolvespec = fonts.names.resolvespec      if resolvespec then -        local resolved, sub = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions +        local resolved, sub, subindex = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions          if resolved then              specification.resolved   = resolved              specification.sub        = sub +            specification.subindex   = subindex              specification.forced     = lower(suffixonly(resolved))              specification.forcedname = resolved              specification.name       = removesuffix(resolved) diff --git a/src/fontloader/misc/fontloader-font-map.lua b/src/fontloader/misc/fontloader-font-map.lua index 69474ba..b645d9a 100644 --- a/src/fontloader/misc/fontloader-font-map.lua +++ b/src/fontloader/misc/fontloader-font-map.lua @@ -31,25 +31,27 @@ of obsolete. Some code may move to runtime or auxiliary modules.</p>  <p>The name to unciode related code will stay of course.</p>  --ldx]]-- -local function loadlumtable(filename) -- will move to font goodies -    local lumname = file.replacesuffix(file.basename(filename),"lum") -    local lumfile = resolvers.findfile(lumname,"map") or "" -    if lumfile ~= "" and lfs.isfile(lumfile) then -        if trace_loading or trace_mapping then -            report_fonts("loading map table %a",lumfile) -        end -        lumunic = dofile(lumfile) -        return lumunic, lumfile -    end -end +-- local function loadlumtable(filename) -- will move to font goodies +--     local lumname = file.replacesuffix(file.basename(filename),"lum") +--     local lumfile = resolvers.findfile(lumname,"map") or "" +--     if lumfile ~= "" and lfs.isfile(lumfile) then +--         if trace_loading or trace_mapping then +--             report_fonts("loading map table %a",lumfile) +--         end +--         lumunic = dofile(lumfile) +--         return lumunic, lumfile +--     end +-- end  local hex     = R("AF","09") -local hexfour = (hex*hex*hex*hex)         / function(s) return tonumber(s,16) end -local hexsix  = (hex*hex*hex*hex*hex*hex) / function(s) return tonumber(s,16) end +----- hexfour = (hex*hex*hex*hex)         / function(s) return tonumber(s,16) end +----- hexsix  = (hex*hex*hex*hex*hex*hex) / function(s) return tonumber(s,16) end +local hexfour = (hex*hex*hex^-2) / function(s) return tonumber(s,16) end +local hexsix  = (hex*hex*hex^-4) / function(s) return tonumber(s,16) end  local dec     = (R("09")^1) / tonumber  local period  = P(".") -local unicode = P("uni")   * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true)) -local ucode   = P("u")     * (hexsix  * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true)) +local unicode = (P("uni") + P("UNI")) * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true)) -- base planes +local ucode   = (P("u")   + P("U")  ) * (hexsix  * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true)) -- extended  local index   = P("index") * dec * Cc(false)  local parser  = unicode + ucode + index @@ -168,7 +170,6 @@ end  --     return s  -- end -mappings.loadlumtable        = loadlumtable  mappings.makenameparser      = makenameparser  mappings.tounicode           = tounicode  mappings.tounicode16         = tounicode16 @@ -179,13 +180,13 @@ local ligseparator = P("_")  local varseparator = P(".")  local namesplitter = Ct(C((1 - ligseparator - varseparator)^1) * (ligseparator * C((1 - ligseparator - varseparator)^1))^0) +-- maybe: ff fi fl ffi ffl => f_f f_i f_l f_f_i f_f_l +  -- local function test(name)  --     local split = lpegmatch(namesplitter,name)  --     print(string.formatters["%s: [% t]"](name,split))  -- end --- maybe: ff fi fl ffi ffl => f_f f_i f_l f_f_i f_f_l -  -- test("i.f_")  -- test("this")  -- test("this.that") @@ -221,332 +222,184 @@ end  mappings.overloads = overloads -function mappings.addtounicode(data,filename) -    local resources    = data.resources -    local properties   = data.properties -    local descriptions = data.descriptions -    local unicodes     = resources.unicodes -    local lookuptypes  = resources.lookuptypes +function mappings.addtounicode(data,filename,checklookups) +    local resources = data.resources +    local unicodes  = resources.unicodes      if not unicodes then          return      end +    local properties    = data.properties +    local descriptions  = data.descriptions      -- we need to move this code      unicodes['space']   = unicodes['space']  or 32      unicodes['hyphen']  = unicodes['hyphen'] or 45      unicodes['zwj']     = unicodes['zwj']    or 0x200D      unicodes['zwnj']    = unicodes['zwnj']   or 0x200C -    local private       = fonts.constructors.privateoffset -    local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context -    ----- namevector    = fonts.encodings.agl.names    -- loaded runtime in context -    local missing       = { } -    local lumunic, uparser, oparser -    local cidinfo, cidnames, cidcodes, usedmap -    -- -    cidinfo = properties.cidinfo -    usedmap = cidinfo and fonts.cid.getmap(cidinfo)      -- +    local private       = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF +    local unicodevector = fonts.encodings.agl.unicodes or { } -- loaded runtime in context +    local contextvector = fonts.encodings.agl.ctxcodes or { } -- loaded runtime in context +    local missing       = { } +    local nofmissing    = 0 +    local oparser       = nil +    local cidnames      = nil +    local cidcodes      = nil +    local cidinfo       = properties.cidinfo +    local usedmap       = cidinfo and fonts.cid.getmap(cidinfo) +    local uparser       = makenameparser() -- hm, every time?      if usedmap then -        oparser  = usedmap and makenameparser(cidinfo.ordering) -        cidnames = usedmap.names -        cidcodes = usedmap.unicodes +          oparser       = usedmap and makenameparser(cidinfo.ordering) +          cidnames      = usedmap.names +          cidcodes      = usedmap.unicodes      end -    uparser = makenameparser() -    local ns, nl = 0, 0 +    local ns            = 0 +    local nl            = 0 +    --      for unic, glyph in next, descriptions do -        local index = glyph.index -        local name  = glyph.name -        local r = overloads[name] -        if r then -            -- get rid of weird ligatures -         -- glyph.name    = r.name -            glyph.unicode = r.unicode -        elseif unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then -            local unicode = lumunic and lumunic[name] or unicodevector[name] -            if unicode then -                glyph.unicode = unicode -                ns            = ns + 1 -            end -            -- cidmap heuristics, beware, there is no guarantee for a match unless -            -- the chain resolves -            if (not unicode) and usedmap then -                local foundindex = lpegmatch(oparser,name) -                if foundindex then -                    unicode = cidcodes[foundindex] -- name to number -                    if unicode then -                        glyph.unicode = unicode -                        ns            = ns + 1 -                    else -                        local reference = cidnames[foundindex] -- number to name -                        if reference then -                            local foundindex = lpegmatch(oparser,reference) -                            if foundindex then -                                unicode = cidcodes[foundindex] -                                if unicode then -                                    glyph.unicode = unicode -                                    ns            = ns + 1 +        local name = glyph.name +        if name then +            local index = glyph.index +            local r = overloads[name] +            if r then +                -- get rid of weird ligatures +             -- glyph.name    = r.name +                glyph.unicode = r.unicode +            elseif not unic or unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then +                local unicode = unicodevector[name] or contextvector[name] +                if unicode then +                    glyph.unicode = unicode +                    ns            = ns + 1 +                end +                -- cidmap heuristics, beware, there is no guarantee for a match unless +                -- the chain resolves +                if (not unicode) and usedmap then +                    local foundindex = lpegmatch(oparser,name) +                    if foundindex then +                        unicode = cidcodes[foundindex] -- name to number +                        if unicode then +                            glyph.unicode = unicode +                            ns            = ns + 1 +                        else +                            local reference = cidnames[foundindex] -- number to name +                            if reference then +                                local foundindex = lpegmatch(oparser,reference) +                                if foundindex then +                                    unicode = cidcodes[foundindex] +                                    if unicode then +                                        glyph.unicode = unicode +                                        ns            = ns + 1 +                                    end                                  end -                            end -                            if not unicode or unicode == "" then -                                local foundcodes, multiple = lpegmatch(uparser,reference) -                                if foundcodes then -                                    glyph.unicode = foundcodes -                                    if multiple then -                                        nl      = nl + 1 -                                        unicode = true -                                    else -                                        ns      = ns + 1 -                                        unicode = foundcodes +                                if not unicode or unicode == "" then +                                    local foundcodes, multiple = lpegmatch(uparser,reference) +                                    if foundcodes then +                                        glyph.unicode = foundcodes +                                        if multiple then +                                            nl      = nl + 1 +                                            unicode = true +                                        else +                                            ns      = ns + 1 +                                            unicode = foundcodes +                                        end                                      end                                  end                              end                          end                      end                  end -            end -            -- a.whatever or a_b_c.whatever or a_b_c (no numbers) a.b_ -            -- -            -- It is not trivial to find a solution that suits all fonts. We tried several alternatives -            -- and this one seems to work reasonable also with fonts that use less standardized naming -            -- schemes. The extra private test is tested by KE and seems to work okay with non-typical -            -- fonts as well. -            -- -            -- The next time I look into this, I'll add an extra analysis step to the otf loader (we can -            -- resolve some tounicodes by looking into the gsub data tables that are bound to glyphs. -            -- --- a real tricky last resort: --- ---             local lookups = glyph.lookups ---             if lookups then ---                 for _, lookup in next, lookups do -- assume consistency else we need to sort ---                     for i=1,#lookup do ---                         local l = lookup[i] ---                         if l.type == "ligature" then ---                             local s = l.specification ---                             if s.char == glyph.name then ---                                 local components = s.components ---                                 if components then ---                                     local t, n = { }, 0 ---                                     unicode = true ---                                     for l=1,#components do ---                                         local base = components[l] ---                                         local u = unicodes[base] or unicodevector[base] ---                                         if not u then ---                                             break ---                                         elseif type(u) == "table" then ---                                             if u[1] >= private then ---                                                 unicode = false ---                                                 break ---                                             end ---                                             n = n + 1 ---                                             t[n] = u[1] ---                                         else ---                                             if u >= private then ---                                                 unicode = false ---                                                 break ---                                             end ---                                             n = n + 1 ---                                             t[n] = u ---                                         end ---                                     end ---                                     if n == 0 then -- done then ---                                         -- nothing ---                                     elseif n == 1 then ---                                         glyph.unicode = t[1] ---                                     else ---                                         glyph.unicode = t ---                                     end ---                                     nl = nl + 1 ---                                     break ---                                 end ---                             end ---                         end ---                     end ---                     if unicode then ---                         break ---                     end ---                 end ---             end -            if not unicode or unicode == "" then -                local split = lpegmatch(namesplitter,name) -                local nsplit = split and #split or 0 -                local t, n = { }, 0 -                unicode = true -                for l=1,nsplit do -                    local base = split[l] -                    local u = unicodes[base] or unicodevector[base] -                    if not u then -                        break -                    elseif type(u) == "table" then -                        if u[1] >= private then -                            unicode = false -                            break +                -- a.whatever or a_b_c.whatever or a_b_c (no numbers) a.b_ +                -- +                -- It is not trivial to find a solution that suits all fonts. We tried several alternatives +                -- and this one seems to work reasonable also with fonts that use less standardized naming +                -- schemes. The extra private test is tested by KE and seems to work okay with non-typical +                -- fonts as well. +                -- +                if not unicode or unicode == "" then +                    local split  = lpegmatch(namesplitter,name) +                    local nsplit = split and #split or 0 -- add if +                    if nsplit == 0 then +                        -- skip +                    elseif nsplit == 1 then +                        local base = split[1] +                        local u = unicodes[base] or unicodevector[base] or contextvector[name] +                        if not u then +                            -- skip +                        elseif type(u) == "table" then +                            -- unlikely +                            if u[1] < private then +                                unicode = u +                                glyph.unicode = unicode +                            end +                        elseif u < private then +                            unicode = u +                            glyph.unicode = unicode                          end -                        n = n + 1 -                        t[n] = u[1]                      else -                        if u >= private then -                            unicode = false -                            break -                        end -                        n = n + 1 -                        t[n] = u -                    end -                end -                if n == 0 then -- done then -                    -- nothing -                elseif n == 1 then -                    glyph.unicode = t[1] -                else -                    glyph.unicode = t -                end -                nl = nl + 1 -            end -            -- last resort (we might need to catch private here as well) -            if not unicode or unicode == "" then -                local foundcodes, multiple = lpegmatch(uparser,name) -                if foundcodes then -                    glyph.unicode = foundcodes -                    if multiple then -                        nl      = nl + 1 -                        unicode = true -                    else -                        ns      = ns + 1 -                        unicode = foundcodes -                    end -                end -            end -            -- check using substitutes and alternates -            local r = overloads[unicode] -            if r then -                unicode = r.unicode -                glyph.unicode = unicode -            end -            -- -            if not unicode then -                missing[name] = true -            end -        end -    end -    if next(missing) then -        local guess  = { } -        -- helper -        local function check(gname,code,unicode) -            local description = descriptions[code] -            -- no need to add a self reference -            local variant = description.name -            if variant == gname then -                return -            end -            -- the variant already has a unicode (normally that resultrs in a default tounicode to self) -            local unic = unicodes[variant] -            if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then -                -- no default mapping and therefore maybe no tounicode yet -            else -                return -            end -            -- the variant already has a tounicode -            if descriptions[code].unicode then -                return -            end -            -- add to the list -            local g = guess[variant] -         -- local r = overloads[unicode] -         -- if r then -         --     unicode = r.unicode -         -- end -            if g then -                g[gname] = unicode -            else -                guess[variant] = { [gname] = unicode } -            end -        end -        -- -        for unicode, description in next, descriptions do -            local slookups = description.slookups -            if slookups then -                local gname = description.name -                for tag, data in next, slookups do -                    local lookuptype = lookuptypes[tag] -                    if lookuptype == "alternate" then -                        for i=1,#data do -                            check(gname,data[i],unicode) -                        end -                    elseif lookuptype == "substitution" then -                        check(gname,data,unicode) -                    end -                end -            end -            local mlookups = description.mlookups -            if mlookups then -                local gname = description.name -                for tag, list in next, mlookups do -                    local lookuptype = lookuptypes[tag] -                    if lookuptype == "alternate" then -                        for i=1,#list do -                            local data = list[i] -                            for i=1,#data do -                                check(gname,data[i],unicode) +                        local t, n = { }, 0 +                        for l=1,nsplit do +                            local base = split[l] +                            local u = unicodes[base] or unicodevector[base] or contextvector[name] +                            if not u then +                                break +                            elseif type(u) == "table" then +                                if u[1] >= private then +                                    break +                                end +                                n = n + 1 +                                t[n] = u[1] +                            else +                                if u >= private then +                                    break +                                end +                                n = n + 1 +                                t[n] = u                              end                          end -                    elseif lookuptype == "substitution" then -                        for i=1,#list do -                            check(gname,list[i],unicode) +                        if n > 0 then +                            if n == 1 then +                                unicode = t[1] +                            else +                                unicode = t +                            end +                            glyph.unicode = unicode                          end                      end +                    nl = nl + 1                  end -            end -        end -        -- resolve references -        local done = true -        while done do -            done = false -            for k, v in next, guess do -                if type(v) ~= "number" then -                    for kk, vv in next, v do -                        if vv == -1 or vv >= private or (vv >= 0xE000 and vv <= 0xF8FF) or vv == 0xFFFE or vv == 0xFFFF then -                            local uu = guess[kk] -                            if type(uu) == "number" then -                                guess[k] = uu -                                done = true -                            end +                -- last resort (we might need to catch private here as well) +                if not unicode or unicode == "" then +                    local foundcodes, multiple = lpegmatch(uparser,name) +                    if foundcodes then +                        glyph.unicode = foundcodes +                        if multiple then +                            nl      = nl + 1 +                            unicode = true                          else -                            guess[k] = vv -                            done = true +                            ns      = ns + 1 +                            unicode = foundcodes                          end                      end                  end -            end -        end -        -- wrap up -        local orphans = 0 -        local guessed = 0 -        for k, v in next, guess do -            if type(v) == "number" then -                descriptions[unicodes[k]].unicode = descriptions[v].unicode or v -- can also be a table -                guessed = guessed + 1 -            else -                local t = nil -                local l = lower(k) -                local u = unicodes[l] -                if not u then -                    orphans = orphans + 1 -                elseif u == -1 or u >= private or (u >= 0xE000 and u <= 0xF8FF) or u == 0xFFFE or u == 0xFFFF then -                    local unicode = descriptions[u].unicode -                    if unicode then -                        descriptions[unicodes[k]].unicode = unicode -                        guessed = guessed + 1 -                    else -                        orphans = orphans + 1 -                    end -                else -                    orphans = orphans + 1 +                -- check using substitutes and alternates +                local r = overloads[unicode] +                if r then +                    unicode = r.unicode +                    glyph.unicode = unicode +                end +                -- +                if not unicode then +                    missing[unic] = true +                    nofmissing    = nofmissing + 1                  end              end +        else +            -- no name          end -        if trace_loading and orphans > 0 or guessed > 0 then -            report_fonts("%s glyphs with no related unicode, %s guessed, %s orphans",guessed+orphans,guessed,orphans) -        end      end +    if type(checklookups) == "function" then +        checklookups(data,missing,nofmissing) +    end +    -- todo: go lowercase      if trace_mapping then          for unic, glyph in table.sortedhash(descriptions) do              local name    = glyph.name diff --git a/src/fontloader/misc/fontloader-font-otb.lua b/src/fontloader/misc/fontloader-font-otb.lua index 4e955a1..c9f5d4a 100644 --- a/src/fontloader/misc/fontloader-font-otb.lua +++ b/src/fontloader/misc/fontloader-font-otb.lua @@ -321,14 +321,14 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis      for unicode, character in next, characters do          local description = descriptions[unicode] -        local lookups = description.slookups +        local lookups     = description.slookups          if lookups then              for l=1,#lookuplist do                  local lookupname = lookuplist[l]                  local lookupdata = lookups[lookupname]                  if lookupdata then                      local lookuptype = lookuptypes[lookupname] -                    local action = actions[lookuptype] +                    local action     = actions[lookuptype]                      if action then                          action(lookupdata,lookuptags,lookupname,description,unicode)                      end @@ -342,7 +342,7 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis                  local lookuplist = lookups[lookupname]                  if lookuplist then                      local lookuptype = lookuptypes[lookupname] -                    local action = actions[lookuptype] +                    local action     = actions[lookuptype]                      if action then                          for i=1,#lookuplist do                              action(lookuplist[i],lookuptags,lookupname,description,unicode) @@ -614,8 +614,8 @@ local function featuresinitializer(tfmdata,value)              local collectlookups    = otf.collectlookups              local rawdata           = tfmdata.shared.rawdata              local properties        = tfmdata.properties -            local script            = properties.script -            local language          = properties.language +            local script            = properties.script   -- or "dflt" -- can be nil +            local language          = properties.language -- or "dflt" -- can be nil              local basesubstitutions = rawdata.resources.features.gsub              local basepositionings  = rawdata.resources.features.gpos              -- diff --git a/src/fontloader/misc/fontloader-font-otf.lua b/src/fontloader/misc/fontloader-font-otf.lua index e7a97c6..0ca1e98 100644 --- a/src/fontloader/misc/fontloader-font-otf.lua +++ b/src/fontloader/misc/fontloader-font-otf.lua @@ -12,15 +12,19 @@ if not modules then modules = { } end modules ['font-otf'] = {  -- to_table -> totable  -- ascent descent +-- to be checked: combinations like: +-- +-- current="ABCD" with [A]=nothing, [BC]=ligature, [D]=single (applied to result of BC so funny index) +-- +-- unlikely but possible +  -- more checking against low level calls of functions  local utfbyte = utf.byte -local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip +local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip  local type, next, tonumber, tostring = type, next, tonumber, tostring  local abs = math.abs -local insert = table.insert -local lpegmatch = lpeg.match -local reversed, concat, remove, sortedkeys = table.reversed, table.concat, table.remove, table.sortedkeys +local reversed, concat, insert, remove, sortedkeys = table.reversed, table.concat, table.insert, table.remove, table.sortedkeys  local ioflush = io.flush  local fastcopy, tohash, derivetable = table.fastcopy, table.tohash, table.derive  local formatters = string.formatters @@ -54,7 +58,7 @@ local otf                = fonts.handlers.otf  otf.glists               = { "gsub", "gpos" } -otf.version              = 2.812 -- beware: also sync font-mis.lua +otf.version              = 2.819 -- beware: also sync font-mis.lua and in mtx-fonts  otf.cache                = containers.define("fonts", "otf", otf.version, true)  local hashes             = fonts.hashes @@ -283,13 +287,17 @@ local ordered_enhancers = {      "check glyphs",      "check metadata", -    "check extra features", -- after metadata +--     "check extra features", -- after metadata      "prepare tounicode",      "check encoding", -- moved      "add duplicates", +    "expand lookups", -- a temp hack awaiting the lua loader + +--     "check extra features", -- after metadata and duplicates +      "cleanup tables",      "compact lookups", @@ -386,6 +394,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone      if featurefile then          name = name .. "@" .. file.removesuffix(file.basename(featurefile))      end +    -- or: sub = tonumber(sub)      if sub == "" then          sub = false      end @@ -442,6 +451,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone          end       end       if reload then +        starttiming("fontloader")          report_otf("loading %a, hash %a",filename,hash)          local fontdata, messages          if sub then @@ -476,6 +486,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone              data = {                  size        = size,                  time        = time, +                subfont     = sub,                  format      = otf_format(filename),                  featuredata = featurefiles,                  resources   = { @@ -512,7 +523,6 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone                      tounicodetable = Ct(splitter),                  },              } -            starttiming(data)              report_otf("file size: %s", size)              enhancers.apply(data,filename,fontdata)              local packtime = { } @@ -529,10 +539,10 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone              if cleanup > 1 then                  collectgarbage("collect")              end -            stoptiming(data) +            stoptiming("fontloader")              if elapsedtime then -- not in generic -                report_otf("preprocessing and caching time %s, packtime %s", -                    elapsedtime(data),packdata and elapsedtime(packtime) or 0) +                report_otf("loading, optimizing, packing and caching time %s, pack time %s", +                    elapsedtime("fontloader"),packdata and elapsedtime(packtime) or 0)              end              close_font(fontdata) -- free memory              if cleanup > 3 then @@ -543,6 +553,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone                  collectgarbage("collect")              end          else +            stoptiming("fontloader")              data = nil              report_otf("loading failed due to read error")          end @@ -589,6 +600,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone              applyruntimefixes(filename,data)          end          enhance("add dimensions",data,filename,nil,false) +enhance("check extra features",data,filename)          if trace_sequences then              showfeatureorder(data,filename)          end @@ -697,7 +709,7 @@ local function somecopy(old) -- fast one      end  end --- not setting hasitalics and class (when nil) during table cronstruction can save some mem +-- not setting hasitalics and class (when nil) during table construction can save some mem  actions["prepare glyphs"] = function(data,filename,raw)      local tableversion = tonumber(raw.table_version) or 0 @@ -780,7 +792,7 @@ actions["prepare glyphs"] = function(data,filename,raw)                                  end                                  if not unicode or unicode == -1 then -- or unicode >= criterium then                                      if not name then -                                        name = format("u%06X.ctx",private) +                                        name = formatters["u%06X.ctx"](private)                                      end                                      unicode = private                                      unicodes[name] = private @@ -803,7 +815,7 @@ actions["prepare glyphs"] = function(data,filename,raw)                                   --     end                                   -- end                                      if not name then -                                        name = format("u%06X.ctx",unicode) +                                        name = formatters["u%06X.ctx"](unicode)                                      end                                      unicodes[name] = unicode                                      nofunicodes = nofunicodes + 1 @@ -819,35 +831,35 @@ actions["prepare glyphs"] = function(data,filename,raw)                                      glyph       = glyph,                                  }                                  descriptions[unicode] = description -local altuni = glyph.altuni -if altuni then - -- local d -    for i=1,#altuni do -        local a = altuni[i] -        local u = a.unicode -        if u ~= unicode then -            local v = a.variant -            if v then -                -- tricky: no addition to d? needs checking but in practice such dups are either very simple -                -- shapes or e.g cjk with not that many features -                local vv = variants[v] -                if vv then -                    vv[u] = unicode -                else -- xits-math has some: -                    vv = { [u] = unicode } -                    variants[v] = vv -                end -         -- elseif d then -         --     d[#d+1] = u -         -- else -         --     d = { u } -            end -        end -    end - -- if d then - --     duplicates[unicode] = d -- is this needed ? - -- end -end +                                local altuni = glyph.altuni +                                if altuni then +                                 -- local d +                                    for i=1,#altuni do +                                        local a = altuni[i] +                                        local u = a.unicode +                                        if u ~= unicode then +                                            local v = a.variant +                                            if v then +                                                -- tricky: no addition to d? needs checking but in practice such dups are either very simple +                                                -- shapes or e.g cjk with not that many features +                                                local vv = variants[v] +                                                if vv then +                                                    vv[u] = unicode +                                                else -- xits-math has some: +                                                    vv = { [u] = unicode } +                                                    variants[v] = vv +                                                end +                                         -- elseif d then +                                         --     d[#d+1] = u +                                         -- else +                                         --     d = { u } +                                            end +                                        end +                                    end +                                 -- if d then +                                 --     duplicates[unicode] = d -- is this needed ? +                                 -- end +                                end                              end                          end                      else @@ -916,7 +928,7 @@ end                      end                      indices[index] = unicode                   -- if not name then -                 --     name = format("u%06X",unicode) -- u%06X.ctx +                 --     name = formatters["u%06X"](unicode) -- u%06X.ctx                   -- end                      descriptions[unicode] = {                       -- width       = glyph.width, @@ -1089,7 +1101,7 @@ actions["add duplicates"] = function(data,filename,raw)                      end                      if u > 0 then -- and                          local duplicate = table.copy(description) -- else packing problem -                        duplicate.comment = format("copy of U+%05X", unicode) +                        duplicate.comment = formatters["copy of %U"](unicode)                          descriptions[u] = duplicate                       -- validduplicates[#validduplicates+1] = u                          if trace_loading then @@ -1107,16 +1119,16 @@ end  -- boundingbox: split into ht/dp takes more memory (larger tables and less sharing)  actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this in the previous -    local descriptions      = data.descriptions -    local resources         = data.resources -    local metadata          = data.metadata -    local properties        = data.properties -    local hasitalics        = false -    local widths            = { } -    local marks             = { } -- always present (saves checking) +    local descriptions = data.descriptions +    local resources    = data.resources +    local metadata     = data.metadata +    local properties   = data.properties +    local hasitalics   = false +    local widths       = { } +    local marks        = { } -- always present (saves checking)      for unicode, description in next, descriptions do -        local glyph = description.glyph -        local italic = glyph.italic_correction +        local glyph  = description.glyph +        local italic = glyph.italic_correction -- only in a math font (we also have vert/horiz)          if not italic then              -- skip          elseif italic == 0 then @@ -1185,7 +1197,8 @@ end  actions["reorganize features"] = function(data,filename,raw) -- combine with other      local features = { }      data.resources.features = features -    for k, what in next, otf.glists do +    for k=1,#otf.glists do +        local what = otf.glists[k]          local dw = raw[what]          if dw then              local f = { } @@ -1254,6 +1267,140 @@ actions["reorganize anchor classes"] = function(data,filename,raw)      end  end +-- local function checklookups(data,missing,nofmissing) +--     local resources    = data.resources +--     local unicodes     = resources.unicodes +--     local lookuptypes  = resources.lookuptypes +--     if not unicodes or not lookuptypes then +--         return +--     elseif nofmissing <= 0 then +--         return +--     end +--     local descriptions = data.descriptions +--     local private      = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF +--     -- +--     local ns, nl = 0, 0 + +--     local guess  = { } +--     -- helper +--     local function check(gname,code,unicode) +--         local description = descriptions[code] +--         -- no need to add a self reference +--         local variant = description.name +--         if variant == gname then +--             return +--         end +--         -- the variant already has a unicode (normally that results in a default tounicode to self) +--         local unic = unicodes[variant] +--         if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then +--             -- no default mapping and therefore maybe no tounicode yet +--         else +--             return +--         end +--         -- the variant already has a tounicode +--         if descriptions[code].unicode then +--             return +--         end +--         -- add to the list +--         local g = guess[variant] +--      -- local r = overloads[unicode] +--      -- if r then +--      --     unicode = r.unicode +--      -- end +--         if g then +--             g[gname] = unicode +--         else +--             guess[variant] = { [gname] = unicode } +--         end +--     end +--     -- +--     for unicode, description in next, descriptions do +--         local slookups = description.slookups +--         if slookups then +--             local gname = description.name +--             for tag, data in next, slookups do +--                 local lookuptype = lookuptypes[tag] +--                 if lookuptype == "alternate" then +--                     for i=1,#data do +--                         check(gname,data[i],unicode) +--                     end +--                 elseif lookuptype == "substitution" then +--                     check(gname,data,unicode) +--                 end +--             end +--         end +--         local mlookups = description.mlookups +--         if mlookups then +--             local gname = description.name +--             for tag, list in next, mlookups do +--                 local lookuptype = lookuptypes[tag] +--                 if lookuptype == "alternate" then +--                     for i=1,#list do +--                         local data = list[i] +--                         for i=1,#data do +--                             check(gname,data[i],unicode) +--                         end +--                     end +--                 elseif lookuptype == "substitution" then +--                     for i=1,#list do +--                         check(gname,list[i],unicode) +--                     end +--                 end +--             end +--         end +--     end +--     -- resolve references +--     local done = true +--     while done do +--         done = false +--         for k, v in next, guess do +--             if type(v) ~= "number" then +--                 for kk, vv in next, v do +--                     if vv == -1 or vv >= private or (vv >= 0xE000 and vv <= 0xF8FF) or vv == 0xFFFE or vv == 0xFFFF then +--                         local uu = guess[kk] +--                         if type(uu) == "number" then +--                             guess[k] = uu +--                             done = true +--                         end +--                     else +--                         guess[k] = vv +--                         done = true +--                     end +--                 end +--             end +--         end +--     end +--     -- wrap up +--     local orphans = 0 +--     local guessed = 0 +--     for k, v in next, guess do +--         if type(v) == "number" then +--             descriptions[unicodes[k]].unicode = descriptions[v].unicode or v -- can also be a table +--             guessed = guessed + 1 +--         else +--             local t = nil +--             local l = lower(k) +--             local u = unicodes[l] +--             if not u then +--                 orphans = orphans + 1 +--             elseif u == -1 or u >= private or (u >= 0xE000 and u <= 0xF8FF) or u == 0xFFFE or u == 0xFFFF then +--                 local unicode = descriptions[u].unicode +--                 if unicode then +--                     descriptions[unicodes[k]].unicode = unicode +--                     guessed = guessed + 1 +--                 else +--                     orphans = orphans + 1 +--                 end +--             else +--                 orphans = orphans + 1 +--             end +--         end +--     end +--     if trace_loading and orphans > 0 or guessed > 0 then +--         report_otf("%s glyphs with no related unicode, %s guessed, %s orphans",guessed+orphans,guessed,orphans) +--     end +-- end +  actions["prepare tounicode"] = function(data,filename,raw)      fonts.mappings.addtounicode(data,filename)  end @@ -1288,8 +1435,9 @@ actions["reorganize subtables"] = function(data,filename,raw)      local lookups         = { }      local chainedfeatures = { }      resources.sequences   = sequences -    resources.lookups     = lookups -    for _, what in next, otf.glists do +    resources.lookups     = lookups -- we also have lookups in data itself +    for k=1,#otf.glists do +        local what = otf.glists[k]          local dw = raw[what]          if dw then              for k=1,#dw do @@ -1375,12 +1523,6 @@ actions["reorganize subtables"] = function(data,filename,raw)      end  end --- test this: --- ---    for _, what in next, otf.glists do ---        raw[what] = nil ---    end -  actions["prepare lookups"] = function(data,filename,raw)      local lookups = raw.lookups      if lookups then @@ -1494,12 +1636,16 @@ end  actions["reorganize lookups"] = function(data,filename,raw) -- we could check for "" and n == 0      -- we prefer the before lookups in a normal order      if data.lookups then -        local splitter = data.helpers.tounicodetable -        local t_u_cache = { } -        local s_u_cache = t_u_cache -- string keys -        local t_h_cache = { } -        local s_h_cache = t_h_cache -- table keys (so we could use one cache) -        local r_u_cache = { } -- maybe shared +        local helpers      = data.helpers +        local duplicates   = data.resources.duplicates +        local splitter     = helpers.tounicodetable +        local t_u_cache    = { } +        local s_u_cache    = t_u_cache -- string keys +        local t_h_cache    = { } +        local s_h_cache    = t_h_cache -- table keys (so we could use one cache) +        local r_u_cache    = { } -- maybe shared +        helpers.matchcache = t_h_cache -- so that we can add duplicates +        --          for _, lookup in next, data.lookups do              local rules = lookup.rules              if rules then @@ -1653,6 +1799,50 @@ actions["reorganize lookups"] = function(data,filename,raw) -- we could check fo      end  end +actions["expand lookups"] = function(data,filename,raw) -- we could check for "" and n == 0 +    if data.lookups then +        local cache = data.helpers.matchcache +        if cache then +            local duplicates = data.resources.duplicates +            for key, hash in next, cache do +                local done = nil +                for key in next, hash do +                    local unicode = duplicates[key] +                    if not unicode then +                        -- no duplicate +                    elseif type(unicode) == "table" then +                        -- multiple duplicates +                        for i=1,#unicode do +                            local u = unicode[i] +                            if hash[u] then +                                -- already in set +                            elseif done then +                                done[u] = key +                            else +                                done = { [u] = key } +                            end +                        end +                    else +                        -- one duplicate +                        if hash[unicode] then +                            -- already in set +                        elseif done then +                            done[unicode] = key +                        else +                            done = { [unicode] = key } +                        end +                    end +                end +                if done then +                    for u in next, done do +                        hash[u] = true +                    end +                end +            end +        end +    end +end +  local function check_variants(unicode,the_variants,splitter,unicodes)      local variants = the_variants.variants      if variants then -- use splitter @@ -1693,11 +1883,11 @@ local function check_variants(unicode,the_variants,splitter,unicodes)              parts = nil          end      end -    local italic_correction = the_variants.italic_correction -    if italic_correction and italic_correction == 0 then -        italic_correction = nil +    local italic = the_variants.italic +    if italic and italic == 0 then +        italic = nil      end -    return variants, parts, italic_correction +    return variants, parts, italic  end  actions["analyze math"] = function(data,filename,raw) @@ -1706,15 +1896,16 @@ actions["analyze math"] = function(data,filename,raw)          local unicodes = data.resources.unicodes          local splitter = data.helpers.tounicodetable          for unicode, description in next, data.descriptions do -            local glyph          = description.glyph -            local mathkerns      = glyph.mathkern -- singular -            local horiz_variants = glyph.horiz_variants -            local vert_variants  = glyph.vert_variants -            local top_accent     = glyph.top_accent -            if mathkerns or horiz_variants or vert_variants or top_accent then +            local glyph        = description.glyph +            local mathkerns    = glyph.mathkern -- singular +            local hvariants    = glyph.horiz_variants +            local vvariants    = glyph.vert_variants +            local accent       = glyph.top_accent +            local italic       = glyph.italic_correction +            if mathkerns or hvariants or vvariants or accent or italic then                  local math = { } -                if top_accent then -                    math.top_accent = top_accent +                if accent then +                    math.accent = accent                  end                  if mathkerns then                      for k, v in next, mathkerns do @@ -1730,15 +1921,14 @@ actions["analyze math"] = function(data,filename,raw)                      end                      math.kerns = mathkerns                  end -                if horiz_variants then -                    math.horiz_variants, math.horiz_parts, math.horiz_italic_correction = check_variants(unicode,horiz_variants,splitter,unicodes) +                if hvariants then +                    math.hvariants, math.hparts, math.hitalic = check_variants(unicode,hvariants,splitter,unicodes)                  end -                if vert_variants then -                    math.vert_variants, math.vert_parts, math.vert_italic_correction = check_variants(unicode,vert_variants,splitter,unicodes) +                if vvariants then +                    math.vvariants, math.vparts, math.vitalic = check_variants(unicode,vvariants,splitter,unicodes)                  end -                local italic_correction = description.italic -                if italic_correction and italic_correction ~= 0 then -                    math.italic_correction = italic_correction +                if italic and italic ~= 0 then +                    math.italic = italic                  end                  description.math = math              end @@ -1903,7 +2093,7 @@ actions["merge kern classes"] = function(data,filename,raw)              report_otf("%s kern overloads ignored",ignored)          end          if blocked > 0 then -            report_otf("%s succesive kerns blocked",blocked) +            report_otf("%s successive kerns blocked",blocked)          end      end  end @@ -1940,18 +2130,21 @@ actions["check metadata"] = function(data,filename,raw)          end      end      -- +    local names = raw.names +    --      if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then          -- the ff library does a bit too much (and wrong) checking ... so we need to catch this          -- at least for now          local function valid(what) -            local names = raw.names -            for i=1,#names do -                local list = names[i] -                local names = list.names -                if names then -                    local name = names[what] -                    if name and valid_ps_name(name) then -                        return name +            if names then +                for i=1,#names do +                    local list = names[i] +                    local names = list.names +                    if names then +                        local name = names[what] +                        if name and valid_ps_name(name) then +                            return name +                        end                      end                  end              end @@ -1975,6 +2168,33 @@ actions["check metadata"] = function(data,filename,raw)          check("fullname")      end      -- +    if names then +        local psname = metadata.psname +        if not psname or psname == "" then +            for i=1,#names do +                local name = names[i] +                -- Currently we use the same restricted search as in the new context (specific) font loader +                -- but we might add more lang checks (it worked ok in the new loaded so now we're in sync) +                -- This check here is also because there are (esp) cjk fonts out there with psnames different +                -- from fontnames (gives a bad lookup in backend). +                if lower(name.lang) == "english (us)" then +                    local specification = name.names +                    if specification then +                        local postscriptname = specification.postscriptname +                        if postscriptname then +                            psname = postscriptname +                        end +                    end +                end +                break +            end +        end +        if psname ~= metadata.fontname then +            report_otf("fontname %a, fullname %a, psname %a",metadata.fontname,metadata.fullname,psname) +        end +        metadata.psname = psname +    end +    --  end  actions["cleanup tables"] = function(data,filename,raw) @@ -2000,7 +2220,9 @@ end  -- we can share { } as it is never set ---- ligatures have an extra specification.char entry that we don't use +-- ligatures have an extra specification.char entry that we don't use + +-- mlookups only with pairs and ligatures  actions["reorganize glyph lookups"] = function(data,filename,raw)      local resources    = data.resources @@ -2081,14 +2303,14 @@ actions["reorganize glyph lookups"] = function(data,filename,raw)              if mlookups then                  description.mlookups = mlookups              end +         -- description.lookups = nil          end      end -  end  local zero = { 0, 0 } -actions["reorganize glyph anchors"] = function(data,filename,raw) -- when we replace inplace we safe entries +actions["reorganize glyph anchors"] = function(data,filename,raw)      local descriptions = data.descriptions      for unicode, description in next, descriptions do          local anchors = description.glyph.anchors @@ -2311,7 +2533,7 @@ end  -- we cannot share descriptions as virtual fonts might extend them (ok,  -- we could use a cache with a hash  -- --- we already assing an empty tabel to characters as we can add for +-- we already assign an empty tabel to characters as we can add for  -- instance protruding info and loop over characters; one is not supposed  -- to change descriptions and if one does so one should make a copy! @@ -2334,10 +2556,14 @@ local function copytotfm(data,cache_id)          local spaceunits     = 500          local spacer         = "space"          local designsize     = metadata.designsize or metadata.design_size or 100 +        local minsize        = metadata.minsize or metadata.design_range_bottom or designsize +        local maxsize        = metadata.maxsize or metadata.design_range_top    or designsize          local mathspecs      = metadata.math          --          if designsize == 0 then              designsize = 100 +            minsize    = 100 +            maxsize    = 100          end          if mathspecs then              for name, value in next, mathspecs do @@ -2355,8 +2581,11 @@ local function copytotfm(data,cache_id)                  local m = d.math                  if m then                      -- watch out: luatex uses horiz_variants for the parts -                    local variants = m.horiz_variants -                    local parts    = m.horiz_parts +                    -- +                    local italic   = m.italic +                    -- +                    local variants = m.hvariants +                    local parts    = m.hparts                   -- local done     = { [unicode] = true }                      if variants then                          local c = character @@ -2373,9 +2602,11 @@ local function copytotfm(data,cache_id)                          c.horiz_variants = parts                      elseif parts then                          character.horiz_variants = parts +                        italic = m.hitalic                      end -                    local variants = m.vert_variants -                    local parts    = m.vert_parts +                    -- +                    local variants = m.vvariants +                    local parts    = m.vparts                   -- local done     = { [unicode] = true }                      if variants then                          local c = character @@ -2392,15 +2623,18 @@ local function copytotfm(data,cache_id)                          c.vert_variants = parts                      elseif parts then                          character.vert_variants = parts +                        italic = m.vitalic                      end -                    local italic_correction = m.vert_italic_correction -                    if italic_correction then -                        character.vert_italic_correction = italic_correction -- was c. +                    -- +                    if italic and italic ~= 0 then +                        character.italic = italic -- overload                      end -                    local top_accent = m.top_accent -                    if top_accent then -                        character.top_accent = top_accent +                    -- +                    local accent = m.accent +                    if accent then +                        character.accent = accent                      end +                    --                      local kerns = m.kerns                      if kerns then                          character.mathkerns = kerns @@ -2413,16 +2647,16 @@ local function copytotfm(data,cache_id)          local filename = constructors.checkedfilename(resources)          local fontname = metadata.fontname          local fullname = metadata.fullname or fontname -        local psname   = fontname or fullname -        local units    = metadata.units_per_em or 1000 +        local psname   = metadata.psname or fontname or fullname +        local units    = metadata.units or metadata.units_per_em or 1000          --          if units == 0 then -- catch bugs in fonts              units = 1000 -- maybe 2000 when ttf -            metadata.units_per_em = 1000 +            metadata.units = 1000              report_otf("changing %a units to %a",0,units)          end          -- -        local monospaced  = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced") +        local monospaced  = metadata.monospaced or metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced")          local charwidth   = pfminfo.avgwidth -- or unset          local charxheight = pfminfo.os2_xheight and pfminfo.os2_xheight > 0 and pfminfo.os2_xheight  -- charwidth = charwidth * units/1000 @@ -2492,17 +2726,16 @@ local function copytotfm(data,cache_id)              end          end          -- -        parameters.designsize = (designsize/10)*65536 -        parameters.ascender   = abs(metadata.ascent  or 0) -        parameters.descender  = abs(metadata.descent or 0) -        parameters.units      = units +        parameters.designsize    = (designsize/10)*65536 +        parameters.minsize       = (minsize   /10)*65536 +        parameters.maxsize       = (maxsize   /10)*65536 +        parameters.ascender      = abs(metadata.ascender  or metadata.ascent  or 0) +        parameters.descender     = abs(metadata.descender or metadata.descent or 0) +        parameters.units         = units          --          properties.space         = spacer          properties.encodingbytes = 2          properties.format        = data.format or otf_format(filename) or formats.otf --- if units ~= 1000 and format ~= "truetype" then ---     properties.format = "truetype" --- end          properties.noglyphnames  = true          properties.filename      = filename          properties.fontname      = fontname @@ -2703,3 +2936,111 @@ function otf.scriptandlanguage(tfmdata,attr)      local properties = tfmdata.properties      return properties.script or "dflt", properties.language or "dflt"  end + +-- a little bit of abstraction + +local function justset(coverage,unicode,replacement) +    coverage[unicode] = replacement +end + +otf.coverup = { +    stepkey = "subtables", +    actions = { +        substitution = justset, +        alternate    = justset, +        multiple     = justset, +        ligature     = justset, +        kern         = justset, +    }, +    register = function(coverage,lookuptype,format,feature,n,descriptions,resources) +        local name = formatters["ctx_%s_%s"](feature,n) +        if lookuptype == "kern" then +            resources.lookuptypes[name] = "position" +        else +            resources.lookuptypes[name] = lookuptype +        end +        for u, c in next, coverage do +            local description = descriptions[u] +            local slookups = description.slookups +            if slookups then +                slookups[name] = c +            else +                description.slookups = { [name] = c } +            end +-- inspect(feature,description) +        end +        return name +    end +} + +-- moved from font-oth.lua + +local function getgsub(tfmdata,k,kind) +    local description = tfmdata.descriptions[k] +    if description then +        local slookups = description.slookups -- we assume only slookups (we can always extend) +        if slookups then +            local shared = tfmdata.shared +            local rawdata = shared and shared.rawdata +            if rawdata then +                local lookuptypes = rawdata.resources.lookuptypes +                if lookuptypes then +                    local properties = tfmdata.properties +                    -- we could cache these +                    local validlookups, lookuplist = otf.collectlookups(rawdata,kind,properties.script,properties.language) +                    if validlookups then +                        for l=1,#lookuplist do +                            local lookup = lookuplist[l] +                            local found  = slookups[lookup] +                            if found then +                                return found, lookuptypes[lookup] +                            end +                        end +                    end +                end +            end +        end +    end +end + +otf.getgsub = getgsub -- returns value, gsub_kind + +function otf.getsubstitution(tfmdata,k,kind,value) +    local found, kind = getgsub(tfmdata,k,kind) +    if not found then +        -- +    elseif kind == "substitution" then +        return found +    elseif kind == "alternate" then +        local choice = tonumber(value) or 1 -- no random here (yet) +        return found[choice] or found[1] or k +    end +    return k +end + +otf.getalternate = otf.getsubstitution + +function otf.getmultiple(tfmdata,k,kind) +    local found, kind = getgsub(tfmdata,k,kind) +    if found and kind == "multiple" then +        return found +    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/src/fontloader/misc/fontloader-font-otp.lua b/src/fontloader/misc/fontloader-font-otp.lua index ebf36ed..91bd05b 100644 --- a/src/fontloader/misc/fontloader-font-otp.lua +++ b/src/fontloader/misc/fontloader-font-otp.lua @@ -12,9 +12,8 @@ if not modules then modules = { } end modules ['font-otp'] = {  --  -- unless we sort all hashes we can get a different pack order (no big deal but size can differ) -local next, type = next, type +local next, type, tostring = next, type, tostring  local sort, concat = table.sort, table.concat -local sortedhash = table.sortedhash  local trace_packing = false  trackers.register("otf.packing", function(v) trace_packing = v end)  local trace_loading = false  trackers.register("otf.loading", function(v) trace_loading = v end) @@ -148,6 +147,7 @@ end  -- we then need to sort more thanks to random hashing  local function packdata(data) +      if data then       -- stripdata(data)          local h, t, c = { }, { }, { } @@ -537,6 +537,7 @@ local unpacked_mt = {  }  local function unpackdata(data) +      if data then          local tables = data.tables          if tables then diff --git a/src/fontloader/misc/fontloader-font-tfm.lua b/src/fontloader/misc/fontloader-font-tfm.lua index ab03788..401dc83 100644 --- a/src/fontloader/misc/fontloader-font-tfm.lua +++ b/src/fontloader/misc/fontloader-font-tfm.lua @@ -24,6 +24,7 @@ local constructors             = fonts.constructors  local encodings                = fonts.encodings  local tfm                      = constructors.newhandler("tfm") +tfm.version                    = 1.000  local tfmfeatures              = constructors.newfeatures("tfm")  local registertfmfeature       = tfmfeatures.register diff --git a/src/fontloader/misc/fontloader-fonts-cbk.lua b/src/fontloader/misc/fontloader-fonts-cbk.lua index 81b5b6e..9da8151 100644 --- a/src/fontloader/misc/fontloader-fonts-cbk.lua +++ b/src/fontloader/misc/fontloader-fonts-cbk.lua @@ -160,12 +160,31 @@ function nodes.handlers.nodepass(head)                  local range = basefonts[i]                  local start = range[1]                  local stop  = range[2] -                if stop then -                    start, stop = ligaturing(start,stop) -                    start, stop = kerning(start,stop) -                elseif start then -                    start = ligaturing(start) -                    start = kerning(start) +                -- maybe even: if start and start ~= stop then +                if start or stop then +                    local prev  = nil +                    local next  = nil +                    local front = start == head +                    if stop then +                        next  = stop.next +                        start, stop = ligaturing(start,stop) +                        start, stop = kerning(start,stop) +                    elseif start then +                        prev  = start.prev +                        start = ligaturing(start) +                        start = kerning(start) +                    end +                    if prev then +                        start.prev = prev +                        prev.next  = start +                    end +                    if next then +                        stop.next  = next +                        next.prev  = stop +                    end +                    if front then +                        head = start +                    end                  end              end          end @@ -176,7 +195,7 @@ function nodes.handlers.nodepass(head)  end  function nodes.handlers.basepass(head) -    if not basepass then +    if basepass then          head = ligaturing(head)          head = kerning(head)      end diff --git a/src/fontloader/misc/fontloader-fonts-inj.lua b/src/fontloader/misc/fontloader-fonts-inj.lua index 332e920..36781f7 100644 --- a/src/fontloader/misc/fontloader-fonts-inj.lua +++ b/src/fontloader/misc/fontloader-fonts-inj.lua @@ -8,7 +8,16 @@ if not modules then modules = { } end modules ['font-inj'] = {  -- This property based variant is not faster but looks nicer than the attribute one. We  -- need to use rawget (which is apbout 4 times slower than a direct access but we cannot --- get/set that one for our purpose! +-- get/set that one for our purpose! This version does a bit more with discretionaries +-- (and Kai has tested it with his collection of weird fonts.) + +-- There is some duplicate code here (especially in the the pre/post/replace branches) but +-- we go for speed. We could store a list of glyph and mark nodes when registering but it's +-- cleaner to have an identification pass here. Also, I need to keep tracing in mind so +-- being too clever here is dangerous. + +-- The subtype test is not needed as there will be no (new) properties set, given that we +-- reset the properties.  if not nodes.properties then return end @@ -80,8 +89,7 @@ function injections.resetcounts()      keepregisteredcounts  = false  end --- We need to make sure that a possible metatable will not kick in --- unexpectedly. +-- We need to make sure that a possible metatable will not kick in unexpectedly.  function injections.reset(n)      local p = rawget(properties,n) @@ -146,7 +154,8 @@ end  function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) -- hm: nuts or nodes      local dx =  factor*(exit[1]-entry[1])      local dy = -factor*(exit[2]-entry[2]) -    local ws, wn = tfmstart.width, tfmnext.width +    local ws = tfmstart.width +    local wn = tfmnext.width      nofregisteredcursives = nofregisteredcursives + 1      if rlmode < 0 then          dx = -(dx + wn) @@ -195,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 @@ -205,9 +217,12 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l              if rlmode and rlmode < 0 then                  leftkern, rightkern = rightkern, leftkern              end +            if not injection then +                injection = "injections" +            end              local p = rawget(properties,current)              if p then -                local i = rawget(p,"injections") +                local i = rawget(p,injection)                  if i then                      if leftkern ~= 0 then                          i.leftkern  = (i.leftkern  or 0) + leftkern @@ -219,19 +234,19 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l                          i.yoffset = (i.yoffset or 0) + yoffset                      end                  elseif leftkern ~= 0 or rightkern ~= 0 then -                    p.injections = { +                    p[injection] = {                          leftkern  = leftkern,                          rightkern = rightkern,                          yoffset   = yoffset,                      }                  else -                    p.injections = { +                    p[injection] = {                          yoffset = yoffset,                      }                  end              elseif leftkern ~= 0 or rightkern ~= 0 then                  properties[current] = { -                    injections = { +                    [injection] = {                          leftkern  = leftkern,                          rightkern = rightkern,                          yoffset   = yoffset, @@ -239,7 +254,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l                  }              else                  properties[current] = { -                    injections = { +                    [injection] = {                          yoffset = yoffset,                      },                  } @@ -250,10 +265,9 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l      return x, y, w, h -- no bound  end --- this needs checking for rl < 0 but it is unlikely that a r2l script --- uses kernclasses between glyphs so we're probably safe (KE has a --- problematic font where marks interfere with rl < 0 in the previous --- case) +-- This needs checking for rl < 0 but it is unlikely that a r2l script uses kernclasses between +-- glyphs so we're probably safe (KE has a problematic font where marks interfere with rl < 0 in +-- the previous case)  function injections.setkern(current,factor,rlmode,x,injection)      local dx = factor * x @@ -285,7 +299,7 @@ function injections.setkern(current,factor,rlmode,x,injection)      end  end -function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=baseanchor, ma=markanchor +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=baseanchor, ma=markanchor      local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2])      nofregisteredmarks = nofregisteredmarks + 1   -- markanchors[nofregisteredmarks] = base @@ -293,14 +307,20 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean          dx = tfmbase.width - dx -- see later commented ox      end      local p = rawget(properties,start) +    -- hm, dejavu serif does a sloppy mark2mark before mark2base      if p then          local i = rawget(p,"injections")          if i then -            i.markx        = dx -            i.marky        = dy -            i.markdir      = rlmode or 0 -            i.markbase     = nofregisteredmarks -            i.markbasenode = base +            if i.markmark then +                -- out of order mkmk: yes or no or option +            else +                i.markx        = dx +                i.marky        = dy +                i.markdir      = rlmode or 0 +                i.markbase     = nofregisteredmarks +                i.markbasenode = base +                i.markmark     = mkmk +            end          else              p.injections = {                  markx        = dx, @@ -308,6 +328,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean                  markdir      = rlmode or 0,                  markbase     = nofregisteredmarks,                  markbasenode = base, +                markmark     = mkmk,              }          end      else @@ -318,6 +339,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean                  markdir      = rlmode or 0,                  markbase     = nofregisteredmarks,                  markbasenode = base, +                markmark     = mkmk,              },          }      end @@ -430,30 +452,36 @@ local function show_result(head)      end  end --- we could also check for marks here but maybe not all are registered (needs checking) - -local function collect_glyphs_1(head) -    local glyphs, nofglyphs = { }, 0 -    local marks, nofmarks = { }, 0 +local function collect_glyphs(head,offsets) +    local glyphs, glyphi, nofglyphs = { }, { }, 0 +    local marks, marki, nofmarks = { }, { }, 0      local nf, tm = nil, nil -    for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts -        if getsubtype(n) < 256 then -            local f = getfont(n) -            if f ~= nf then -                nf = f -                tm = fontdata[nf].resources.marks -- other hash in ctx -            end -            if tm and tm[getchar(n)] then -                nofmarks = nofmarks + 1 -                marks[nofmarks] = n -            else -                nofglyphs = nofglyphs + 1 -                glyphs[nofglyphs] = n +    local n = head + +    local function identify(n,what) +        local f = getfont(n) +        if f ~= nf then +            nf = f +            -- other hash in ctx: +            tm = fontdata[nf].resources +            if tm then +                tm = tm.marks              end +        end +        if tm and tm[getchar(n)] then +            nofmarks = nofmarks + 1 +            marks[nofmarks] = n +            marki[nofmarks] = "injections" +        else +            nofglyphs = nofglyphs + 1 +            glyphs[nofglyphs] = n +            glyphi[nofglyphs] = what +        end +        if offsets then              -- yoffsets can influence curs steps              local p = rawget(properties,n)              if p then -                local i = rawget(p,"injections") +                local i = rawget(p,what)                  if i then                      local yoffset = i.yoffset                      if yoffset and yoffset ~= 0 then @@ -463,38 +491,50 @@ local function collect_glyphs_1(head)              end          end      end -    return glyphs, nofglyphs, marks, nofmarks -end -local function collect_glyphs_2(head) -    local glyphs, nofglyphs = { }, 0 -    local marks, nofmarks = { }, 0 -    local nf, tm = nil, nil -    for n in traverse_id(glyph_code,head) do -        if getsubtype(n) < 256 then -            local f = getfont(n) -            if f ~= nf then -                nf = f -                tm = fontdata[nf].resources.marks -- other hash in ctx -            end -            if tm and tm[getchar(n)] then -                nofmarks = nofmarks + 1 -                marks[nofmarks] = n -            else -                nofglyphs = nofglyphs + 1 -                glyphs[nofglyphs] = n -            end +    while n do -- only needed for relevant fonts +        local id = getid(n) +        if id == glyph_code then +            identify(n,"injections") +        elseif id == disc_code then +            local d = getfield(n,"pre") +            if d then +                for n in traverse_id(glyph_code,d) do +                    if getsubtype(n) < 256 then +                        identify(n,"preinjections") +                    end +                end +			end +            local d = getfield(n,"post") +            if d then +                for n in traverse_id(glyph_code,d) do +                    if getsubtype(n) < 256 then +                        identify(n,"postinjections") +                    end +                end +			end +            local d = getfield(n,"replace") +            if d then +                for n in traverse_id(glyph_code,d) do +                    if getsubtype(n) < 256 then +                        identify(n,"replaceinjections") +                    end +                end +			end          end +		n = getnext(n)      end -    return glyphs, nofglyphs, marks, nofmarks + +    return glyphs, glyphi, nofglyphs, marks, marki, nofmarks  end -local function inject_marks(marks,nofmarks) +local function inject_marks(marks,marki,nofmarks)      for i=1,nofmarks do -        local n = marks[i] +        local n  = marks[i]          local pn = rawget(properties,n)          if pn then -            pn = rawget(pn,"injections") +            local ni = marki[i] +            local pn = rawget(pn,ni)              if pn then                  local p = pn.markbasenode                  if p then @@ -503,7 +543,7 @@ local function inject_marks(marks,nofmarks)                      local rightkern = nil                      local pp = rawget(properties,p)                      if pp then -                        pp = rawget(pp,"injections") +                        pp = rawget(pp,ni)                          if pp then                              rightkern = pp.rightkern                          end @@ -516,13 +556,22 @@ local function inject_marks(marks,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 +                            -- +							-- 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 - leftkern +                                ox = px - pn.markx                              end --- report_injections("l2r case 1: %p",ox)                          end                      else                          -- we need to deal with fonts that have marks with width @@ -548,12 +597,13 @@ local function inject_marks(marks,nofmarks)                      setfield(n,"xoffset",ox)                      --                      local py = getfield(p,"yoffset") -                    local oy = 0 -                    if marks[p] then -                        oy = py + pn.marky -                    else -                        oy = getfield(n,"yoffset") + py + pn.marky -                    end +--                     local oy = 0 +--                     if marks[p] then +--                         oy = py + pn.marky +--                     else +--                         oy = getfield(n,"yoffset") + py + pn.marky +--                     end +                    local oy = getfield(n,"yoffset") + py + pn.marky                      setfield(n,"yoffset",oy)                  else                      -- normally this can't happen (only when in trace mode which is a special case anyway) @@ -564,14 +614,14 @@ local function inject_marks(marks,nofmarks)      end  end -local function inject_cursives(glyphs,nofglyphs) +local function inject_cursives(glyphs,glyphi,nofglyphs)      local cursiveanchor, lastanchor = nil, nil      local minc, maxc, last = 0, 0, nil      for i=1,nofglyphs do -        local n = glyphs[i] +        local n  = glyphs[i]          local pn = rawget(properties,n)          if pn then -            pn = rawget(pn,"injections") +            pn = rawget(pn,glyphi[i])          end          if pn then              local cursivex = pn.cursivex @@ -630,7 +680,7 @@ local function inject_cursives(glyphs,nofglyphs)       -- if maxc > 0 and not cursiveanchor then       --     local ny = getfield(n,"yoffset")       --     for i=maxc,minc,-1 do -     --         local ti = glyphs[i] +     --         local ti = glyphs[i][1]       --         ny = ny + properties[ti].cursivedy       --         setfield(ti,"yoffset",ny) -- why not add ?       --     end @@ -647,23 +697,67 @@ local function inject_cursives(glyphs,nofglyphs)      end  end -local function inject_kerns(head,list,length) - -- todo: pre/post/replace +-- G  +D-pre        G +--     D-post+ +--    +D-replace+ +-- +-- G  +D-pre       +D-pre +--     D-post      +D-post +--    +D-replace   +D-replace + +local function inject_kerns(head,glist,ilist,length) -- not complete ! compare with inject_kerns_only (but unlikely disc here)      for i=1,length do -        local n  = list[i] +        local n  = glist[i]          local pn = rawget(properties,n)          if pn then -            local i = rawget(pn,"injections") -            if i then -                local leftkern = i.leftkern -                if leftkern and leftkern ~= 0 then -                    insert_node_before(head,n,newkern(leftkern)) -- type 0/2 -                end -                local rightkern = i.rightkern -                if rightkern and rightkern ~= 0 then -                    insert_node_after(head,n,newkern(rightkern)) -- type 0/2 -                end -            end +			local dp = nil +			local dr = nil +            local ni = ilist[i] +            local p  = nil +			if ni == "injections" then +				p = getprev(n) +				if p then +					local id = getid(p) +					if id == disc_code then +						dp = getfield(p,"post") +						dr = getfield(p,"replace") +					end +				end +			end +			if dp then +				local i = rawget(pn,"postinjections") +				if i then +					local leftkern = i.leftkern +					if leftkern and leftkern ~= 0 then +						local t = find_tail(dp) +						insert_node_after(dp,t,newkern(leftkern)) +                        setfield(p,"post",dp) -- currently we need to force a tail refresh +					end +				end +			end +			if dr then +				local i = rawget(pn,"replaceinjections") +				if i then +					local leftkern = i.leftkern +					if leftkern and leftkern ~= 0 then +						local t = find_tail(dr) +						insert_node_after(dr,t,newkern(leftkern)) +                        setfield(p,"replace",dr) -- currently we need to force a tail refresh +					end +				end +			else +				local i = rawget(pn,ni) +				if i then +					local leftkern = i.leftkern +					if leftkern and leftkern ~= 0 then +						insert_node_before(head,n,newkern(leftkern)) -- type 0/2 +					end +					local rightkern = i.rightkern +					if rightkern and rightkern ~= 0 then +						insert_node_after(head,n,newkern(rightkern)) -- type 0/2 +					end +				end +			end          end      end  end @@ -673,23 +767,18 @@ local function inject_everything(head,where)      if trace_injections then          trace(head,"everything")      end -    local glyphs, nofglyphs, marks, nofmarks -    if nofregisteredpairs > 0 then -        glyphs, nofglyphs, marks, nofmarks = collect_glyphs_1(head) -    else -        glyphs, nofglyphs, marks, nofmarks = collect_glyphs_2(head) -    end +    local glyphs, glyphi, nofglyphs, marks, marki, nofmarks = collect_glyphs(head,nofregisteredpairs > 0)      if nofglyphs > 0 then          if nofregisteredcursives > 0 then -            inject_cursives(glyphs,nofglyphs) +            inject_cursives(glyphs,glyphi,nofglyphs)          end          if nofregisteredmarks > 0 then -- and nofmarks > 0 -            inject_marks(marks,nofmarks) +            inject_marks(marks,marki,nofmarks)          end -        inject_kerns(head,glyphs,nofglyphs) +        inject_kerns(head,glyphs,glyphi,nofglyphs)      end      if nofmarks > 0 then -        inject_kerns(head,marks,nofmarks) +        inject_kerns(head,marks,marki,nofmarks)  	end      if keepregisteredcounts then          keepregisteredcounts  = false @@ -702,13 +791,21 @@ local function inject_everything(head,where)      return tonode(head), true  end +-- G  +D-pre        G +--     D-post+ +--    +D-replace+ +-- +-- G  +D-pre       +D-pre +--     D-post      +D-post +--    +D-replace   +D-replace +  local function inject_kerns_only(head,where)      head = tonut(head)      if trace_injections then          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 @@ -724,6 +821,7 @@ local function inject_kerns_only(head,where)                                  if leftkern and leftkern ~= 0 then                                      local t = find_tail(d)                                      insert_node_after(d,t,newkern(leftkern)) +                                    setfield(p,"post",d) -- currently we need to force a tail refresh                                  end                              end                          end @@ -735,6 +833,7 @@ local function inject_kerns_only(head,where)                                  if leftkern and leftkern ~= 0 then                                      local t = find_tail(d)                                      insert_node_after(d,t,newkern(leftkern)) +                                    setfield(p,"replace",d) -- currently we need to force a tail refresh                                  end                              end                          else @@ -747,6 +846,7 @@ local function inject_kerns_only(head,where)                              end                          end                      else +                        -- this is the most common case                          local i = rawget(pn,"injections")                          if i then                              local leftkern = i.leftkern @@ -756,8 +856,6 @@ local function inject_kerns_only(head,where)                          end                      end                  end -            else -                break              end              p = nil          elseif id == disc_code then @@ -812,7 +910,7 @@ local function inject_kerns_only(head,where)                  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 @@ -850,9 +948,8 @@ local function inject_pairs_only(head,where)      if trace_injections then          trace(head,"pairs")      end -    --      local n = head -    local p = nil +    local p = nil -- disc node when non-nil      while n do          local id = getid(n)          if id == glyph_code then @@ -868,6 +965,7 @@ local function inject_pairs_only(head,where)                                  if leftkern and leftkern ~= 0 then                                      local t = find_tail(d)                                      insert_node_after(d,t,newkern(leftkern)) +                                    setfield(p,"post",d) -- currently we need to force a tail refresh                                  end                               -- local rightkern = i.rightkern                               -- if rightkern and rightkern ~= 0 then @@ -884,6 +982,7 @@ local function inject_pairs_only(head,where)                                  if leftkern and leftkern ~= 0 then                                      local t = find_tail(d)                                      insert_node_after(d,t,newkern(leftkern)) +                                    setfield(p,"replace",d) -- currently we need to force a tail refresh                                  end                               -- local rightkern = i.rightkern                               -- if rightkern and rightkern ~= 0 then @@ -909,24 +1008,22 @@ local function inject_pairs_only(head,where)                          -- 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 @@ -935,16 +1032,12 @@ local function inject_pairs_only(head,where)                  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 @@ -952,6 +1045,10 @@ local function inject_pairs_only(head,where)                                      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 @@ -967,14 +1064,10 @@ local function inject_pairs_only(head,where)                  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)) @@ -984,6 +1077,10 @@ local function inject_pairs_only(head,where)                                      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 @@ -999,14 +1096,10 @@ local function inject_pairs_only(head,where)                  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)) @@ -1016,6 +1109,10 @@ local function inject_pairs_only(head,where)                                      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 @@ -1042,7 +1139,7 @@ local function inject_pairs_only(head,where)      return tonode(head), true  end -function injections.handler(head,where) -- optimize for n=1 ? +function injections.handler(head,where)      if nofregisteredmarks > 0 or nofregisteredcursives > 0 then          return inject_everything(head,where)      elseif nofregisteredpairs > 0 then diff --git a/src/fontloader/misc/fontloader-fonts-otn.lua b/src/fontloader/misc/fontloader-fonts-otn.lua index dd3aa61..1b99c56 100644 --- a/src/fontloader/misc/fontloader-fonts-otn.lua +++ b/src/fontloader/misc/fontloader-fonts-otn.lua @@ -6,12 +6,16 @@ if not modules then modules = { } end modules ['font-otn'] = {      license   = "see context related readme files",  } --- todo: looks like we have a leak somewhere (probably in ligatures) --- todo: copy attributes to disc -  -- this is a context version which can contain experimental code, but when we  -- have serious patches we also need to change the other two font-otn files +-- at some point i might decide to convert the whole list into a table and then +-- run over that instead (but it has some drawbacks as we also need to deal with +-- attributes and such so we need to keep a lot of track - which is why i rejected +-- that method - although it has become a bit easier in the meantime so it might +-- become an alternative (by that time i probably have gone completely lua) .. the +-- usual chicken-egg issues ... maybe mkix as it's no real tex any more then +  -- preprocessors = { "nodes" }  -- anchor class : mark, mkmk, curs, mklg (todo) @@ -40,7 +44,18 @@ if not modules then modules = { } end modules ['font-otn'] = {  -- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests)  -- remove some optimizations (when I have a faster machine)  -- --- maybe redo the lot some way (more context specific) +-- beware: +-- +-- we do some disc jugling where we need to keep in mind that the +-- pre, post and replace fields can have prev pointers to a nesting +-- node ... i wonder if that is still needed +-- +-- not possible: +-- +-- \discretionary {alpha-} {betagammadelta} +--   {\discretionary {alphabeta-} {gammadelta} +--      {\discretionary {alphabetagamma-} {delta} +--         {alphabetagammadelta}}}  --[[ldx--  <p>This module is a bit more split up that I'd like but since we also want to test @@ -65,9 +80,12 @@ is currently acceptable. Not all functions are implemented yet, often because I  lack the fonts for testing. Many scripts are not yet supported either, but I will  look into them as soon as <l n='context'/> users ask for it.</p> -<p>Because there are different interpretations possible, I will extend the code -with more (configureable) variants. I can also add hooks for users so that they can -write their own extensions.</p> +<p>The specification leaves room for interpretation. In case of doubt the microsoft +implementation is the reference as it is the most complete one. As they deal with +lots of scripts and fonts, Kai and Ivo did a lot of testing of the generic code and +their suggestions help improve the code. I'm aware that not all border cases can be +taken care of, unless we accept excessive runtime, and even then the interference +with other mechanisms (like hyphenation) are not trivial.</p>  <p>Glyphs are indexed not by unicode but in their own way. This is because there is no  relationship with unicode at all, apart from the fact that a font might cover certain @@ -94,12 +112,12 @@ when there's a fix in the <l n='fontforge'/> library or <l n='lua'/> code that  results in different tables.</p>  --ldx]]-- --- action                    handler     chainproc             chainmore              comment +-- action                    handler     chainproc  -- --- gsub_single               ok          ok                    ok --- gsub_multiple             ok          ok                    not implemented yet --- gsub_alternate            ok          ok                    not implemented yet --- gsub_ligature             ok          ok                    ok +-- gsub_single               ok          ok +-- gsub_multiple             ok          ok +-- gsub_alternate            ok          ok +-- gsub_ligature             ok          ok  -- gsub_context              ok          --  -- gsub_contextchain         ok          --  -- gsub_reversecontextchain  ok          -- @@ -123,7 +141,6 @@ results in different tables.</p>  -- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij)  --  -- remark: the 'not implemented yet' variants will be done when we have fonts that use them --- remark: we need to check what to do with discretionaries  -- We used to have independent hashes for lookups but as the tags are unique  -- we now use only one hash. If needed we can have multiple again but in that @@ -131,16 +148,14 @@ results in different tables.</p>  -- Todo: make plugin feature that operates on char/glyphnode arrays -local concat, insert, remove = table.concat, table.insert, table.remove -local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip -local type, next, tonumber, tostring = type, next, tonumber, tostring -local lpegmatch = lpeg.match +local type, next, tonumber = type, next, tonumber  local random = math.random  local formatters = string.formatters  local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes -local registertracker = trackers.register +local registertracker   = trackers.register +local registerdirective = directives.register  local fonts = fonts  local otf   = fonts.handlers.otf @@ -162,6 +177,16 @@ local trace_steps        = false  registertracker("otf.steps",        function(v  local trace_skips        = false  registertracker("otf.skips",        function(v) trace_skips        = v end)  local trace_directions   = false  registertracker("otf.directions",   function(v) trace_directions   = v end) +local trace_kernruns     = false  registertracker("otf.kernruns",     function(v) trace_kernruns     = v end) +local trace_discruns     = false  registertracker("otf.discruns",     function(v) trace_discruns     = v end) +local trace_compruns     = false  registertracker("otf.compruns",     function(v) trace_compruns     = v end) + +local quit_on_no_replacement = true  -- maybe per font +local zwnjruns               = true + +registerdirective("otf.zwnjruns",                 function(v) zwnjruns = v end) +registerdirective("otf.chain.quitonnoreplacement",function(value) quit_on_no_replacement = value end) +  local report_direct   = logs.reporter("fonts","otf direct")  local report_subchain = logs.reporter("fonts","otf subchain")  local report_chain    = logs.reporter("fonts","otf chain") @@ -230,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 -local automatic_code     = disccodes.automatic -  local ligature_code      = glyphcodes.ligature  local privateattribute   = attributes.private @@ -286,6 +307,15 @@ local handlers            = { }  local rlmode              = 0  local featurevalue        = false +local sweephead           = { } +local sweepnode           = nil +local sweepprev           = nil +local sweepnext           = nil + +local notmatchpre         = { } +local notmatchpost        = { } +local notmatchreplace     = { } +  -- head is always a whatsit so we can safely assume that head is not changed  -- we use this for special testing and documentation @@ -376,8 +406,66 @@ local function copy_glyph(g) -- next and prev are untouched !      end  end --- +local function flattendisk(head,disc) +    local replace = getfield(disc,"replace") +    setfield(disc,"replace",nil) +    free_node(disc) +    if head == disc then +        local next = getnext(disc) +        if replace then +            if next then +                local tail = find_node_tail(replace) +                setfield(tail,"next",next) +                setfield(next,"prev",tail) +            end +            return replace, replace +        elseif next then +            return next, next +        else +            return -- maybe warning +        end +    else +        local next = getnext(disc) +        local prev = getprev(disc) +        if replace then +            local tail = find_node_tail(replace) +            if next then +                setfield(tail,"next",next) +                setfield(next,"prev",tail) +            end +            setfield(prev,"next",replace) +            setfield(replace,"prev",prev) +            return head, replace +        else +            if next then +                setfield(next,"prev",prev) +            end +            setfield(prev,"next",next) +            return head, next +        end +    end +end +local function appenddisc(disc,list) +    local post    = getfield(disc,"post") +    local replace = getfield(disc,"replace") +    local phead   = list +    local rhead   = copy_node_list(list) +    local ptail   = find_node_tail(post) +    local rtail   = find_node_tail(replace) +    if post then +        setfield(ptail,"next",phead) +        setfield(phead,"prev",ptail) +    else +        setfield(disc,"post",phead) +    end +    if replace then +        setfield(rtail,"next",rhead) +        setfield(rhead,"prev",rtail) +    else +        setfield(disc,"replace",rhead) +    end +end  -- start is a mark and we need to keep that one @@ -416,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 @@ -434,16 +522,28 @@ local function getcomponentindex(start)      end  end --- eventually we will do positioning in an other way (needs addional w/h/d fields) +local a_noligature = attributes.private("noligature")  local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head +    if getattr(start,a_noligature) == 1 then +        -- so we can do: e\noligature{ff}e e\noligature{f}fie (we only look at the first) +        return head, start +    end      if start == stop and getchar(start) == char then          resetinjection(start)          setfield(start,"char",char)          return head, start      end +    -- needs testing (side effects): +    local components = getfield(start,"components") +    if components then +     -- we get a double free .. needs checking +     -- flush_node_list(components) +    end +    --      local prev = getprev(start)      local next = getnext(stop) +    local comp = start      setfield(start,"prev",nil)      setfield(stop,"next",nil)      local base = copy_glyph(start) @@ -453,15 +553,15 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun      resetinjection(base)      setfield(base,"char",char)      setfield(base,"subtype",ligature_code) -    setfield(base,"components",start) -- start can have components +    setfield(base,"components",comp) -- start can have components ... do we need to flush?      if prev then          setfield(prev,"next",base)      end      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 @@ -480,7 +580,9 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun                  if trace_marks then                      logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start))                  end -                head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components +                local n = copy_node(start) +                copyinjection(n,start) +                head, current = insert_node_after(head,current,n) -- unlikely that mark has components              elseif trace_marks then                  logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))              end @@ -501,17 +603,85 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun              end              start = getnext(start)          end +    else +        -- discfound ... forget about marks .. probably no scripts that hyphenate and have marks +        local discprev = getfield(discfound,"prev") +        local discnext = getfield(discfound,"next") +        if discprev and discnext then +            -- 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 +                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(discfound,"prev",prev) +                setfield(discfound,"next",next) +                setfield(next,"prev",discfound) +                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 +        end      end      return head, base  end -function handlers.gsub_single(head,start,kind,lookupname,replacement) -    if trace_singles then -        logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement)) +local function multiple_glyphs(head,start,multiple,ignoremarks) +    local nofmultiples = #multiple +    if nofmultiples > 0 then +        resetinjection(start) +        setfield(start,"char",multiple[1]) +        if nofmultiples > 1 then +            local sn = getnext(start) +            for k=2,nofmultiples do -- todo: use insert_node +-- untested: +-- +-- while ignoremarks and marks[getchar(sn)] then +--     local sn = getnext(sn) +-- end +                local n = copy_node(start) -- ignore components +                resetinjection(n) +                setfield(n,"char",multiple[k]) +                setfield(n,"prev",start) +                setfield(n,"next",sn) +                if sn then +                    setfield(sn,"prev",n) +                end +                setfield(start,"next",n) +                start = n +            end +        end +        return head, start, true +    else +        if trace_multiples then +            logprocess("no multiple for %s",gref(getchar(start))) +        end +        return head, start, false      end -    resetinjection(start) -    setfield(start,"char",replacement) -    return head, start, true  end  local function get_alternative_glyph(start,alternatives,value,trace_alternatives) @@ -546,38 +716,15 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives      end  end -local function multiple_glyphs(head,start,multiple,ignoremarks) -    local nofmultiples = #multiple -    if nofmultiples > 0 then -        resetinjection(start) -        setfield(start,"char",multiple[1]) -        if nofmultiples > 1 then -            local sn = getnext(start) -            for k=2,nofmultiples do -- todo: use insert_node --- untested: --- --- while ignoremarks and marks[getchar(sn)] then ---     local sn = getnext(sn) --- end -                local n = copy_node(start) -- ignore components -                resetinjection(n) -                setfield(n,"char",multiple[k]) -                setfield(n,"next",sn) -                setfield(n,"prev",start) -                if sn then -                    setfield(sn,"prev",n) -                end -                setfield(start,"next",n) -                start = n -            end -        end -        return head, start, true -    else -        if trace_multiples then -            logprocess("no multiple for %s",gref(getchar(start))) -        end -        return head, start, false +-- handlers + +function handlers.gsub_single(head,start,kind,lookupname,replacement) +    if trace_singles then +        logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))      end +    resetinjection(start) +    setfield(start,"char",replacement) +    return head, start, true  end  function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence) @@ -605,7 +752,7 @@ function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)  end  function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) -    local s, stop, discfound = getnext(start), nil, false +    local s, stop = getnext(start), nil      local startchar = getchar(start)      if marks[startchar] then          while s do @@ -633,24 +780,30 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)                  else                      head, start = markstoligature(kind,lookupname,head,start,stop,lig)                  end -                return head, start, true +                return head, start, true, false              else                  -- ok, goto next lookup              end          end      else -        local skipmark = sequence.flags[1] +        local skipmark  = sequence.flags[1] +        local discfound = false +        local lastdisc  = nil          while s do              local id = getid(s) -            if id == glyph_code and getsubtype(s)<256 then -                if getfont(s) == currentfont then +            if id == glyph_code and getsubtype(s)<256 then -- not needed +                if getfont(s) == currentfont then          -- also not needed only when mark                      local char = getchar(s)                      if skipmark and marks[char] then                          s = getnext(s) -                    else -                        local lg = ligature[char] +                    else -- ligature is a tree +                        local lg = ligature[char] -- can there be multiple in a row? maybe in a bad font                          if lg then -                            stop = s +                            if not discfound and lastdisc then +                                discfound = lastdisc +                                lastdisc  = nil +                            end +                            stop = s -- needed for fake so outside then                              ligature = lg                              s = getnext(s)                          else @@ -661,13 +814,13 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)                      break                  end              elseif id == disc_code then -                discfound = true +                lastdisc = s                  s = getnext(s)              else                  break              end          end -        local lig = ligature.ligature +        local lig = ligature.ligature -- can't we get rid of this .ligature?          if lig then              if stop then                  if trace_ligatures then @@ -685,14 +838,88 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)                      logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))                  end              end -            return head, start, true +            return head, start, true, discfound          else -            -- weird but happens +            -- weird but happens, pseudo ligatures ... just the components          end      end +    return head, start, false, discfound +end + +function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence,injection) +    local startchar = getchar(start) +    local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection) -- ,characters[startchar]) +    if trace_kerns then +        logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) +    end      return head, start, false  end +function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection) +    -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too +    -- todo: kerns in components of ligatures +    local snext = getnext(start) +    if not snext then +        return head, start, false +    else +        local prev   = 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 +            local nextchar = getchar(snext) +            local krn = kerns[nextchar] +            if not krn and marks[nextchar] then +                prev = snext +                snext = getnext(snext) +            else +                if not krn then +                    -- skip +                elseif type(krn) == "table" then +                    if lookuptype == "pair" then -- probably not needed +                        local a, b = krn[2], krn[3] +                        if a and #a > 0 then +                            local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,injection) -- characters[startchar]) +                            if trace_kerns then +                                local startchar = getchar(start) +                                logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) +                            end +                        end +                        if b and #b > 0 then +                            local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection) -- characters[nextchar]) +                            if trace_kerns then +                                local startchar = getchar(start) +                                logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) +                            end +                        end +                    else -- wrong ... position has different entries +                        report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) +                     -- local a, b = krn[2], krn[6] +                     -- if a and a ~= 0 then +                     --     local k = setkern(snext,factor,rlmode,a) +                     --     if trace_kerns then +                     --         logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) +                     --     end +                     -- end +                     -- if b and b ~= 0 then +                     --     logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) +                     -- end +                    end +                    done = true +                elseif krn ~= 0 then +                    local k = setkern(snext,factor,rlmode,krn,injection) +                    if trace_kerns then +                        logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) -- prev? +                    end +                    done = true +                end +                break +            end +        end +        return head, start, done +    end +end +  --[[ldx--  <p>We get hits on a mark, but we're not sure if the it has to be applied so  we need to explicitly test for basechar, baselig and basemark entries.</p> @@ -855,7 +1082,7 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence                              if al[anchor] then                                  local ma = markanchors[anchor]                                  if ma then -                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar]) +                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)                                      if trace_marks then                                          logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",                                              pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -938,85 +1165,11 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)      end  end -function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) -    local startchar = getchar(start) -    local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) -    if trace_kerns then -        logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) -    end -    return head, start, false -end - -function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) -    -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too -    -- todo: kerns in components of ligatures -    local snext = getnext(start) -    if not snext then -        return head, start, false -    else -        local prev, done = start, false -        local factor = tfmdata.parameters.factor -        local lookuptype = lookuptypes[lookupname] -        while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do -            local nextchar = getchar(snext) -            local krn = kerns[nextchar] -            if not krn and marks[nextchar] then -                prev = snext -                snext = getnext(snext) -            else -                if not krn then -                    -- skip -                elseif type(krn) == "table" then -                    if lookuptype == "pair" then -- probably not needed -                        local a, b = krn[2], krn[3] -                        if a and #a > 0 then -                            local startchar = getchar(start) -                            local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) -                            if trace_kerns then -                                logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) -                            end -                        end -                        if b and #b > 0 then -                            local startchar = getchar(start) -                            local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) -                            if trace_kerns then -                                logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) -                            end -                        end -                    else -- wrong ... position has different entries -                        report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) -                     -- local a, b = krn[2], krn[6] -                     -- if a and a ~= 0 then -                     --     local k = setkern(snext,factor,rlmode,a) -                     --     if trace_kerns then -                     --         logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) -                     --     end -                     -- end -                     -- if b and b ~= 0 then -                     --     logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) -                     -- end -                    end -                    done = true -                elseif krn ~= 0 then -                    local k = setkern(snext,factor,rlmode,krn) -                    if trace_kerns then -                        logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) -                    end -                    done = true -                end -                break -            end -        end -        return head, start, done -    end -end -  --[[ldx--  <p>I will implement multiple chain replacements once I run into a font that uses  it. It's not that complex to handle.</p>  --ldx]]-- -local chainmores = { }  local chainprocs = { }  local function logprocess(...) @@ -1045,11 +1198,6 @@ function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,looku      return head, start, false  end -function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) -    logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) -    return head, start, false -end -  -- The reversesub is a special case, which is why we need to store the replacements  -- in a bit weird way. There is no lookup and the replacement comes from the lookup  -- itself. It is meant mostly for dealing with Urdu. @@ -1116,8 +1264,7 @@ as less as needed but that would also make the code even more messy.</p>  -- end  --[[ldx-- -<p>Here we replace start by a single variant, First we delete the rest of the -match.</p> +<p>Here we replace start by a single variant.</p>  --ldx]]--  function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) @@ -1125,7 +1272,7 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo      local current = start      local subtables = currentlookup.subtables      if #subtables > 1 then -        logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) +        logwarning("todo: check if we need to loop over the replacements: % t",subtables)      end      while current do          if getid(current) == glyph_code then @@ -1160,11 +1307,8 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo      return head, start, false  end -chainmores.gsub_single = chainprocs.gsub_single -  --[[ldx-- -<p>Here we replace start by a sequence of new glyphs. First we delete the rest of -the match.</p> +<p>Here we replace start by a sequence of new glyphs.</p>  --ldx]]--  function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) @@ -1193,8 +1337,6 @@ function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,      return head, start, false  end -chainmores.gsub_multiple = chainprocs.gsub_multiple -  --[[ldx--  <p>Here we replace start by new glyph. First we delete the rest of the match.</p>  --ldx]]-- @@ -1249,8 +1391,6 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext      return head, start, false  end -chainmores.gsub_alternate = chainprocs.gsub_alternate -  --[[ldx--  <p>When we replace ligatures we use a helper that handles the marks. I might change  this function (move code inline and handle the marks by a separate function). We @@ -1276,13 +1416,19 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,              local s = getnext(start)              local discfound = false              local last = stop -            local nofreplacements = 0 +            local nofreplacements = 1              local skipmark = currentlookup.flags[1]              while s do                  local id = getid(s)                  if id == disc_code then -                    s = getnext(s) -                    discfound = true +                    if not discfound then +                        discfound = s +                    end +                    if s == stop then +                        break -- okay? or before the disc +                    else +                        s = getnext(s) +                    end                  else                      local schar = getchar(s)                      if skipmark and marks[schar] then -- marks @@ -1315,7 +1461,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,                      end                  end                  head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) -                return head, start, true, nofreplacements +                return head, start, true, nofreplacements, discfound              elseif trace_bugs then                  if start == stop then                      logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) @@ -1325,10 +1471,96 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,              end          end      end -    return head, start, false, 0 +    return head, start, false, 0, false  end -chainmores.gsub_ligature = chainprocs.gsub_ligature +function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) +    -- untested .. needs checking for the new model +    local startchar = getchar(start) +    local subtables = currentlookup.subtables +    local lookupname = subtables[1] +    local kerns = lookuphash[lookupname] +    if kerns then +        kerns = kerns[startchar] -- needed ? +        if kerns then +            local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns) -- ,characters[startchar]) +            if trace_kerns then +                logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) +            end +        end +    end +    return head, start, false +end + +function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) +    local snext = getnext(start) +    if snext then +        local startchar = getchar(start) +        local subtables = currentlookup.subtables +        local lookupname = subtables[1] +        local kerns = lookuphash[lookupname] +        if kerns then +            kerns = kerns[startchar] +            if kerns then +                local lookuptype = lookuptypes[lookupname] +                local prev, done = start, false +                local factor = tfmdata.parameters.factor +                while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do +                    local nextchar = getchar(snext) +                    local krn = kerns[nextchar] +                    if not krn and marks[nextchar] then +                        prev = snext +                        snext = getnext(snext) +                    else +                        if not krn then +                            -- skip +                        elseif type(krn) == "table" then +                            if lookuptype == "pair" then +                                local a, b = krn[2], krn[3] +                                if a and #a > 0 then +                                    local startchar = getchar(start) +                                    local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a) -- ,characters[startchar]) +                                    if trace_kerns then +                                        logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) +                                    end +                                end +                                if b and #b > 0 then +                                    local startchar = getchar(start) +                                    local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b) -- ,characters[nextchar]) +                                    if trace_kerns then +                                        logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) +                                    end +                                end +                            else +                                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) +                             -- local a, b = krn[2], krn[6] +                             -- if a and a ~= 0 then +                             --     local k = setkern(snext,factor,rlmode,a) +                             --     if trace_kerns then +                             --         logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) +                             --     end +                             -- end +                             -- if b and b ~= 0 then +                             --     logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) +                             -- end +                            end +                            done = true +                        elseif krn ~= 0 then +                            local k = setkern(snext,factor,rlmode,krn) +                            if trace_kerns then +                                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) +                            end +                            done = true +                        end +                        break +                    end +                end +                return head, start, done +            end +        end +    end +    return head, start, false +end  function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)      local markchar = getchar(start) @@ -1497,7 +1729,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext                              if al[anchor] then                                  local ma = markanchors[anchor]                                  if ma then -                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar]) +                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)                                      if trace_marks then                                          logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",                                              cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -1588,133 +1820,346 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l      return head, start, false  end -function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -    -- untested .. needs checking for the new model -    local startchar = getchar(start) -    local subtables = currentlookup.subtables -    local lookupname = subtables[1] -    local kerns = lookuphash[lookupname] -    if kerns then -        kerns = kerns[startchar] -- needed ? -        if kerns then -            local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) -            if trace_kerns then -                logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) +-- what pointer to return, spec says stop +-- to be discussed ... is bidi changer a space? +-- elseif char == zwnj and sequence[n][32] then -- brrr + +-- somehow l or f is global +-- we don't need to pass the currentcontext, saves a bit +-- make a slow variant then can be activated but with more tracing + +local function show_skip(kind,chainname,char,ck,class) +    if ck[9] then +        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) +    else +        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) +    end +end + +-- A previous version had disc collapsing code in the (single sub) handler plus some +-- checking in the main loop, but that left the pre/post sequences undone. The best +-- solution is to add some checking there and backtrack when a replace/post matches +-- but it takes a bit of work to figure out an efficient way (this is what the sweep* +-- names refer to). I might look into that variant one day again as it can replace +-- some other code too. In that approach we can have a special version for gub and pos +-- which gains some speed. This method does the test and passes info to the handlers +-- (sweepnode, sweepmode, sweepprev, sweepnext, etc). Here collapsing is handled in the +-- main loop which also makes code elsewhere simpler (i.e. no need for the other special +-- runners and disc code in ligature building). I also experimented with pushing preceding +-- glyphs sequences in the replace/pre fields beforehand which saves checking afterwards +-- but at the cost of duplicate glyphs (memory) but it's too much overhead (runtime). +-- +-- In the meantime Kai had moved the code from the single chain into a more general handler +-- and this one (renamed to chaindisk) is used now. I optimized the code a bit and brought +-- it in sycn with the other code. Hopefully I didn't introduce errors. Note: this somewhat +-- complex approach is meant for fonts that implement (for instance) ligatures by character +-- replacement which to some extend is not that suitable for hyphenation. I also use some +-- helpers. This method passes some states but reparses the list. There is room for a bit of +-- speed up but that will be done in the context version. (In fact a partial rewrite of all +-- code can bring some more efficientry.) +-- +-- I didn't test it with extremes but successive disc nodes still can give issues but in +-- order to handle that we need more complex code which also slows down even more. The main +-- loop variant could deal with that: test, collapse, backtrack. + +local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,chainindex,sequence,chainproc) + +    if not start then +        return head, start, false +    end + +    local startishead   = start == head +    local seq           = ck[3] +    local f             = ck[4] +    local l             = ck[5] +    local s             = #seq +    local done          = false +    local sweepnode     = sweepnode +    local sweeptype     = sweeptype +    local sweepoverflow = false +    local checkdisc     = getprev(head) -- hm bad name head +    local keepdisc      = not sweepnode +    local lookaheaddisc = nil +    local backtrackdisc = nil +    local current       = start +    local last          = start +    local prev          = getprev(start) + +    -- fishy: so we can overflow and then go on in the sweep? + +    local i = f +    while i <= l do +        local id = getid(current) +        if id == glyph_code then +            i       = i + 1 +            last    = current +            current = getnext(current) +        elseif id == disc_code then +            if keepdisc then +                keepdisc = false +                if notmatchpre[current] ~= notmatchreplace[current] then +                    lookaheaddisc = current +                end +                local replace = getfield(current,"replace") +                while replace and i <= l do +                    if getid(replace) == glyph_code then +                        i = i + 1 +                    end +                    replace = getnext(replace) +                end +                last    = current +                current = getnext(c) +            else +                head, current = flattendisk(head,current) +            end +        else +            last    = current +            current = getnext(current) +        end +        if current then +            -- go on +        elseif sweepoverflow then +            -- we already are folling up on sweepnode +            break +        elseif sweeptype == "post" or sweeptype == "replace" then +            current = getnext(sweepnode) +            if current then +                sweeptype     = nil +                sweepoverflow = true +            else +                break              end          end      end -    return head, start, false -end -chainmores.gpos_single = chainprocs.gpos_single -- okay? +    if sweepoverflow then +        local prev = current and getprev(current) +        if not current or prev ~= sweepnode then +            local head = getnext(sweepnode) +            local tail = nil +            if prev then +                tail = prev +                setfield(current,"prev",sweepnode) +            else +                tail = find_node_tail(head) +            end +            setfield(sweepnode,"next",current) +            setfield(head,"prev",nil) +            setfield(tail,"next",nil) +            appenddisc(sweepnode,head) +        end +    end --- when machines become faster i will make a shared function +    if l < s then +        local i = l +        local t = sweeptype == "post" or sweeptype == "replace" +        while current and i < s do +            local id = getid(current) +            if id == glyph_code then +                i       = i + 1 +                current = getnext(current) +            elseif id == disc_code then +                if keepdisc then +                    keepdisc = false +                    if notmatchpre[current] ~= notmatchreplace[current] then +                        lookaheaddisc = current +                    end +                    local replace = getfield(c,"replace") +                    while replace and i < s do +                        if getid(replace) == glyph_code then +                            i = i + 1 +                        end +                        replace = getnext(replace) +                    end +                    current = getnext(current) +                elseif notmatchpre[current] ~= notmatchreplace[current] then +                    head, current = flattendisk(head,current) +                else +                    current = getnext(current) -- HH +                end +            else +                current = getnext(current) +            end +            if not current and t then +                current = getnext(sweepnode) +                if current then +                    sweeptype = nil +                end +            end +        end +    end -function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -    local snext = getnext(start) -    if snext then -        local startchar = getchar(start) -        local subtables = currentlookup.subtables -        local lookupname = subtables[1] -        local kerns = lookuphash[lookupname] -        if kerns then -            kerns = kerns[startchar] -            if kerns then -                local lookuptype = lookuptypes[lookupname] -                local prev, done = start, false -                local factor = tfmdata.parameters.factor -                while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do -                    local nextchar = getchar(snext) -                    local krn = kerns[nextchar] -                    if not krn and marks[nextchar] then -                        prev = snext -                        snext = getnext(snext) -                    else -                        if not krn then -                            -- skip -                        elseif type(krn) == "table" then -                            if lookuptype == "pair" then -                                local a, b = krn[2], krn[3] -                                if a and #a > 0 then -                                    local startchar = getchar(start) -                                    local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) -                                    if trace_kerns then -                                        logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) -                                    end -                                end -                                if b and #b > 0 then -                                    local startchar = getchar(start) -                                    local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) -                                    if trace_kerns then -                                        logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) -                                    end -                                end -                            else -                                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) -                                local a, b = krn[2], krn[6] -                                if a and a ~= 0 then -                                    local k = setkern(snext,factor,rlmode,a) -                                    if trace_kerns then -                                        logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) -                                    end -                                end -                                if b and b ~= 0 then -                                    logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) -                                end -                            end -                            done = true -                        elseif krn ~= 0 then -                            local k = setkern(snext,factor,rlmode,krn) -                            if trace_kerns then -                                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) -                            end -                            done = true +    if f > 1 then +        local current = prev +        local i       = f +        local t       = sweeptype == "pre" or sweeptype == "replace" +        if not current and t and current == checkdisk then +            current = getprev(sweepnode) +        end +        while current and i > 1 do -- missing getprev added / moved outside +            local id = getid(current) +            if id == glyph_code then +                i = i - 1 +            elseif id == disc_code then +                if keepdisc then +                    keepdisc = false +                    if notmatchpost[current] ~= notmatchreplace[current] then +                        backtrackdisc = current +                    end +                    local replace = getfield(current,"replace") +                    while replace and i > 1 do +                        if getid(replace) == glyph_code then +                            i = i - 1                          end -                        break +                        replace = getnext(replace)                      end +                elseif notmatchpost[current] ~= notmatchreplace[current] then +                    head, current = flattendisk(head,current)                  end -                return head, start, done +            end +            current = getprev(current) +            if t and current == checkdisk then +                current = getprev(sweepnode)              end          end      end -    return head, start, false -end -chainmores.gpos_pair = chainprocs.gpos_pair -- okay? +    local ok = false +    if lookaheaddisc then --- what pointer to return, spec says stop --- to be discussed ... is bidi changer a space? --- elseif char == zwnj and sequence[n][32] then -- brrr +        local cf            = start +        local cl            = getprev(lookaheaddisc) +        local cprev         = getprev(start) +        local insertedmarks = 0 --- somehow l or f is global --- we don't need to pass the currentcontext, saves a bit --- make a slow variant then can be activated but with more tracing +        while cprev and getid(cf) == glyph_code and getfont(cf) == currentfont and getsubtype(cf) < 256 and marks[getchar(cf)] do +            insertedmarks = insertedmarks + 1 +            cf            = cprev +            startishead   = cf == head +            cprev         = getprev(cprev) +        end + +        setfield(lookaheaddisc,"prev",cprev) +        if cprev then +            setfield(cprev,"next",lookaheaddisc) +        end +        setfield(cf,"prev",nil) +        setfield(cl,"next",nil) +        if startishead then +            head = lookaheaddisc +        end + +        local replace = getfield(lookaheaddisc,"replace") +        local pre     = getfield(lookaheaddisc,"pre") +        local new     = copy_node_list(cf) +        local cnew = new +        for i=1,insertedmarks do +            cnew = getnext(cnew) +        end +        local clast = cnew +        for i=f,l do +            clast = getnext(clast) +        end +        if not notmatchpre[lookaheaddisc] then +            cf, start, ok = chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +        end +        if not notmatchreplace[lookaheaddisc] then +            new, cnew, ok = chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +        end +        if pre then +            setfield(cl,"next",pre) +            setfield(pre,"prev",cl) +        end +        if replace then +            local tail = find_node_tail(new) +            setfield(tail,"next",replace) +            setfield(replace,"prev",tail) +        end +        setfield(lookaheaddisc,"pre",cf)      -- also updates tail +        setfield(lookaheaddisc,"replace",new) -- also updates tail + +        start          = getprev(lookaheaddisc) +        sweephead[cf]  = getnext(clast) +        sweephead[new] = getnext(last) + +    elseif backtrackdisc then + +        local cf            = getnext(backtrackdisc) +        local cl            = start +        local cnext         = getnext(start) +        local insertedmarks = 0 + +        while cnext and getid(cnext) == glyph_code and getfont(cnext) == currentfont and getsubtype(cnext) < 256 and marks[getchar(cnext)] do +            insertedmarks = insertedmarks + 1 +            cl            = cnext +            cnext         = getnext(cnext) +        end +        if cnext then +            setfield(cnext,"prev",backtrackdisc) +        end +        setfield(backtrackdisc,"next",cnext) +        setfield(cf,"prev",nil) +        setfield(cl,"next",nil) +        local replace = getfield(backtrackdisc,"replace") +        local post    = getfield(backtrackdisc,"post") +        local new     = copy_node_list(cf) +        local cnew    = find_node_tail(new) +        for i=1,insertedmarks do +            cnew = getprev(cnew) +        end +        local clast = cnew +        for i=f,l do +            clast = getnext(clast) +        end +        if not notmatchpost[backtrackdisc] then +            cf, start, ok = chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +        end +        if not notmatchreplace[backtrackdisc] then +            new, cnew, ok = chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +        end +        if post then +            local tail = find_node_tail(post) +            setfield(tail,"next",cf) +            setfield(cf,"prev",tail) +        else +            post = cf +        end +        if replace then +            local tail = find_node_tail(replace) +            setfield(tail,"next",new) +            setfield(new,"prev",tail) +        else +            replace = new +        end +        setfield(backtrackdisc,"post",post)       -- also updates tail +        setfield(backtrackdisc,"replace",replace) -- also updates tail +        start              = getprev(backtrackdisc) +        sweephead[post]    = getnext(clast) +        sweephead[replace] = getnext(last) -local function show_skip(kind,chainname,char,ck,class) -    if ck[9] then -        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])      else -        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) -    end -end -local quit_on_no_replacement = true +        head, start, ok = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) -directives.register("otf.chain.quitonnoreplacement",function(value) -- maybe per font -    quit_on_no_replacement = value -end) +    end + +    return head, start, ok +end  local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) -    --  local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] +    local sweepnode    = sweepnode +    local sweeptype    = sweeptype +    local diskseen     = false +    local checkdisc    = getprev(head)      local flags        = sequence.flags      local done         = false      local skipmark     = flags[1]      local skipligature = flags[2]      local skipbase     = flags[3] -    local someskip     = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) -    local markclass    = sequence.markclass                   -- todo, first we need a proper test +    local markclass    = sequence.markclass      local skipped      = false -    for k=1,#contexts do + +    for k=1,#contexts do -- i've only seen ccmp having > 1 (e.g. dejavu)          local match   = true          local current = start          local last    = start @@ -1728,7 +2173,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq          else              -- maybe we need a better space check (maybe check for glue or category or combination)              -- we cannot optimize for n=2 because there can be disc nodes -            local f, l = ck[4], ck[5] +            local f = ck[4] +            local l = ck[5]              -- current match              if f == 1 and f == l then -- current only                  -- already a hit @@ -1738,9 +2184,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                  if f == l then -- new, else last out of sync (f is > 1)                   -- match = true                  else +                    local discfound = nil                      local n = f + 1                      last = getnext(last)                      while n <= l do +                        if not last and (sweeptype == "post" or sweeptype == "replace") then +                            last      = getnext(sweepnode) +                            sweeptype = nil +                        end                          if last then                              local id = getid(last)                              if id == glyph_code then @@ -1748,7 +2199,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                      local char = getchar(last)                                      local ccd = descriptions[char]                                      if ccd then -                                        local class = ccd.class +                                        local class = ccd.class or "base"                                          if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then                                              skipped = true                                              if trace_skips then @@ -1761,18 +2212,77 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                              end                                              n = n + 1                                          else -                                            match = false +                                            if discfound then +                                                notmatchreplace[discfound] = true +                                                match = not notmatchpre[discfound] +                                            else +                                                match = false +                                            end                                              break                                          end                                      else -                                        match = false +                                        if discfound then +                                            notmatchreplace[discfound] = true +                                            match = not notmatchpre[discfound] +                                        else +                                            match = false +                                        end                                          break                                      end                                  else -                                    match = false +                                    if discfound then +                                        notmatchreplace[discfound] = true +                                        match = not notmatchpre[discfound] +                                    else +                                        match = false +                                    end                                      break                                  end                              elseif id == disc_code then +                                diskseen              = true +                                discfound             = last +                                notmatchpre[last]     = nil +                                notmatchpost[last]    = true +                                notmatchreplace[last] = nil +                                local pre     = getfield(last,"pre") +                                local replace = getfield(last,"replace") +                                if pre then +                                    local n = n +                                    while pre do +                                        if seq[n][getchar(pre)] then +                                            n = n + 1 +                                            pre = getnext(pre) +                                            if n > l then +                                                break +                                            end +                                        else +                                            notmatchpre[last] = true +                                            break +                                        end +                                    end +                                    if n <= l then +                                        notmatchpre[last] = true +                                    end +                                else +                                    notmatchpre[last] = true +                                end +                                if replace then +                                    -- so far we never entered this branch +                                    while replace do +                                        if seq[n][getchar(replace)] then +                                            n = n + 1 +                                            replace = getnext(replace) +                                            if n > l then +                                                break +                                            end +                                        else +                                            notmatchreplace[last] = true +                                            match = not notmatchpre[last] +                                            break +                                        end +                                    end +                                    match = not notmatchpre[last] +                                end                                  last = getnext(last)                              else                                  match = false @@ -1789,50 +2299,137 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq              if match and f > 1 then                  local prev = getprev(start)                  if prev then -                    local n = f-1 -                    while n >= 1 do -                        if prev then -                            local id = getid(prev) -                            if id == glyph_code then -                                if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char -                                    local char = getchar(prev) -                                    local ccd = descriptions[char] -                                    if ccd then -                                        local class = ccd.class -                                        if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then -                                            skipped = true -                                            if trace_skips then -                                                show_skip(kind,chainname,char,ck,class) +                    if prev == checkdisc and (sweeptype == "pre" or sweeptype == "replace") then +                        prev      = getprev(sweepnode) +                     -- sweeptype = nil +                    end +                    if prev then +                        local discfound = nil +                        local n = f - 1 +                        while n >= 1 do +                            if prev then +                                local id = getid(prev) +                                if id == glyph_code then +                                    if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char +                                        local char = getchar(prev) +                                        local ccd = descriptions[char] +                                        if ccd then +                                            local class = ccd.class +                                            if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then +                                                skipped = true +                                                if trace_skips then +                                                    show_skip(kind,chainname,char,ck,class) +                                                end +                                            elseif seq[n][char] then +                                                n = n -1 +                                            else +                                                if discfound then +                                                    notmatchreplace[discfound] = true +                                                    match = not notmatchpost[discfound] +                                                else +                                                    match = false +                                                end +                                                break                                              end -                                        elseif seq[n][char] then -                                            n = n -1                                          else -                                            match = false +                                            if discfound then +                                                notmatchreplace[discfound] = true +                                                match = not notmatchpost[discfound] +                                            else +                                                match = false +                                            end                                              break                                          end                                      else -                                        match = false +                                        if discfound then +                                            notmatchreplace[discfound] = true +                                            match = not notmatchpost[discfound] +                                        else +                                            match = false +                                        end                                          break                                      end +                                elseif id == disc_code then +                                    -- the special case: f i where i becomes dottless i .. +                                    diskseen              = true +                                    discfound             = prev +                                    notmatchpre[prev]     = true +                                    notmatchpost[prev]    = nil +                                    notmatchreplace[prev] = nil +                                    local pre     = getfield(prev,"pre") +                                    local post    = getfield(prev,"post") +                                    local replace = getfield(prev,"replace") +                                    if pre ~= start and post ~= start and replace ~= start then +                                        if post then +                                            local n = n +                                            local posttail = find_node_tail(post) +                                            while posttail do +                                                if seq[n][getchar(posttail)] then +                                                    n = n - 1 +                                                    if posttail == post then +                                                        break +                                                    else +                                                        posttail = getprev(posttail) +                                                        if n < 1 then +                                                            break +                                                        end +                                                    end +                                                else +                                                    notmatchpost[prev] = true +                                                    break +                                                end +                                            end +                                            if n >= 1 then +                                                notmatchpost[prev] = true +                                            end +                                        else +                                            notmatchpost[prev] = true +                                        end +                                        if replace then +                                            -- we seldom enter this branch (e.g. on brill efficient) +                                            local replacetail = find_node_tail(replace) +                                            while replacetail do +                                                if seq[n][getchar(replacetail)] then +                                                    n = n - 1 +                                                    if replacetail == replace then +                                                        break +                                                    else +                                                        replacetail = getprev(replacetail) +                                                        if n < 1 then +                                                            break +                                                        end +                                                    end +                                                else +                                                    notmatchreplace[prev] = true +                                                    match = not notmatchpost[prev] +                                                    break +                                                end +                                            end +                                            if not match then +                                                break +                                            end +                                        else +                                            -- skip 'm +                                        end +                                    else +                                        -- skip 'm +                                    end +                                elseif seq[n][32] then +                                    n = n -1                                  else                                      match = false                                      break                                  end -                            elseif id == disc_code then -                                -- skip 'm -                            elseif seq[n][32] then -                                n = n -1 +                                prev = getprev(prev) +                            elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces +                                n = n - 1                              else                                  match = false                                  break                              end -                            prev = getprev(prev) -                        elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces -                            n = n -1 -                        else -                            match = false -                            break                          end +                    else +                        match = false                      end                  else                      match = false @@ -1841,7 +2438,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq              -- after              if match and s > l then                  local current = last and getnext(last) +                if not current then +                    if sweeptype == "post" or sweeptype == "replace" then +                        current   = getnext(sweepnode) +                     -- sweeptype = nil +                    end +                end                  if current then +                    local discfound = nil                      -- removed optimization for s-l == 1, we have to deal with marks anyway                      local n = l + 1                      while n <= s do @@ -1861,19 +2465,81 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                          elseif seq[n][char] then                                              n = n + 1                                          else -                                            match = false +                                            if discfound then +                                                notmatchreplace[discfound] = true +                                                match = not notmatchpre[discfound] +                                            else +                                                match = false +                                            end                                              break                                          end                                      else -                                        match = false +                                        if discfound then +                                            notmatchreplace[discfound] = true +                                            match = not notmatchpre[discfound] +                                        else +                                            match = false +                                        end                                          break                                      end                                  else -                                    match = false +                                    if discfound then +                                        notmatchreplace[discfound] = true +                                        match = not notmatchpre[discfound] +                                    else +                                        match = false +                                    end                                      break                                  end                              elseif id == disc_code then -                                -- skip 'm +                                diskseen                 = true +                                discfound                = current +                                notmatchpre[current]     = nil +                                notmatchpost[current]    = true +                                notmatchreplace[current] = nil +                                local pre     = getfield(current,"pre") +                                local replace = getfield(current,"replace") +                                if pre then +                                    local n = n +                                    while pre do +                                        if seq[n][getchar(pre)] then +                                            n = n + 1 +                                            pre = getnext(pre) +                                            if n > s then +                                                break +                                            end +                                        else +                                            notmatchpre[current] = true +                                            break +                                        end +                                    end +                                    if n <= s then +                                        notmatchpre[current] = true +                                    end +                                else +                                    notmatchpre[current] = true +                                end +                                if replace then +                                    -- so far we never entered this branch +                                    while replace do +                                        if seq[n][getchar(replace)] then +                                            n = n + 1 +                                            replace = getnext(replace) +                                            if n > s then +                                                break +                                            end +                                        else +                                            notmatchreplace[current] = true +                                            match = notmatchpre[current] +                                            break +                                        end +                                    end +                                    if not match then +                                        break +                                    end +                                else +                                    -- skip 'm +                                end                              elseif seq[n][32] then -- brrr                                  n = n + 1                              else @@ -1894,7 +2560,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq              end          end          if match then -            -- ck == currentcontext +            -- can lookups be of a different type ? +            local diskchain = diskseen or sweepnode              if trace_contexts then                  local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5]                  local char = getchar(start) @@ -1914,10 +2581,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                      local chainlookupname = chainlookups[1]                      local chainlookup = lookuptable[chainlookupname]                      if chainlookup then -                        local cp = chainprocs[chainlookup.type] -                        if cp then +                        local chainproc = chainprocs[chainlookup.type] +                        if chainproc then                              local ok -                            head, start, ok = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +                            if diskchain then +                                head, start, ok = chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc) +                            else +                                head, start, ok = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +                            end                              if ok then                                  done = true                              end @@ -1929,13 +2600,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                      end                   else                      local i = 1 -                    while true do +                    while start and true do                          if skipped then -                            while true do +                            while true do -- todo: use properties                                  local char = getchar(start)                                  local ccd = descriptions[char]                                  if ccd then -                                    local class = ccd.class +                                    local class = ccd.class or "base"                                      if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then                                          start = getnext(start)                                      else @@ -1946,36 +2617,51 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                  end                              end                          end +                        -- see remark in ms standard under : LookupType 5: Contextual Substitution Subtable                          local chainlookupname = chainlookups[i]                          local chainlookup = lookuptable[chainlookupname]                          if not chainlookup then -                            -- okay, n matches, < n replacements +                            -- we just advance                              i = i + 1                          else -                            local cp = chainmores[chainlookup.type] -                            if not cp then +                            local chainproc = chainprocs[chainlookup.type] +                            if not chainproc then                                  -- actually an error                                  logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)                                  i = i + 1                              else                                  local ok, n -                                head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) +                                if diskchain then +                                    head, start, ok    = chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc) +                                else +                                    head, start, ok, n = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) +                                end                                  -- messy since last can be changed !                                  if ok then                                      done = true -                                    -- skip next one(s) if ligature -                                    i = i + (n or 1) -                                else -                                    i = i + 1 +                                    if n and n > 1 then +                                        -- we have a ligature (cf the spec we advance one but we really need to test it +                                        -- as there are fonts out there that are fuzzy and have too many lookups: +                                        -- +                                        -- U+1105 U+119E U+1105 U+119E : sourcehansansklight: script=hang ccmp=yes +                                        -- +                                        if i + n > nofchainlookups then +                                         -- if trace_contexts then +                                         --     logprocess("%s: quitting lookups",cref(kind,chainname)) +                                         -- end +                                            break +                                        else +                                            -- we need to carry one +                                        end +                                    end                                  end +                                i = i + 1                              end                          end -                        if i > nofchainlookups then +                        if i > nofchainlookups or not start then                              break                          elseif start then                              start = getnext(start) -                        else -                            -- weird                          end                      end                  end @@ -1990,8 +2676,16 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                      end                  end              end +            if done then +                break -- out of contexts (new, needs checking) +            end          end      end +    if diskseen then -- maybe move up so that we can turn checking on/off +        notmatchpre     = { } +        notmatchpost    = { } +        notmatchreplace = { } +    end      return head, start, done  end @@ -2076,13 +2770,13 @@ 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] --                      local languages = scripts[script] or scripts[wildcard]                      if languages and (languages[language] or languages[wildcard]) then -                        return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence } +                        return { valid, autofeatures[kind] or false, sequence, kind }                      end                  end              end @@ -2126,32 +2820,216 @@ function otf.dataset(tfmdata,font) -- generic variant, overloaded in context      return rl  end --- elseif id == glue_code then ---     if p[5] then -- chain ---         local pc = pp[32] ---         if pc then ---             start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) ---             if ok then ---                 done = true ---             end ---             if start then start = getnext(start) end ---         else ---             start = getnext(start) ---         end ---     else ---         start = getnext(start) ---     end +-- assumptions: +-- +-- * languages that use complex disc nodes + +local function kernrun(disc,run) +    -- +    -- we catch <font 1><disc font 2> +    -- +    if trace_kernruns then +        report_run("kern") -- will be more detailed +    end +    -- +    local prev      = getprev(disc) -- todo, keep these in the main loop +    local next      = getnext(disc) -- todo, keep these in the main loop +    -- +    local pre       = getfield(disc,"pre") +    local post      = getfield(disc,"post") +    local replace   = getfield(disc,"replace") +    -- +    local prevmarks = prev +    -- +    -- can be optional, because why on earth do we get a disc after a mark (okay, maybe when a ccmp +    -- has happened but then it should be in the disc so basically this test indicates an error) +    -- +    while prevmarks and getid(prevmarks) == glyph_code and marks[getchar(prevmarks)] and getfont(prevmarks) == currentfont and getsubtype(prevmarks) < 256 do +        prevmarks = getprev(prevmarks) +    end +    -- +    if prev and (pre or replace) and not (getid(prev) == glyph_code and getfont(prev) == currentfont and getsubtype(prev)<256) then +        prev = 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") +    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") +    end +    -- +    if not replace and prev and next then +        -- this should be already done by discfound +        setfield(prev,"next",next) +        setfield(next,"prev",prev) +        run(prevmarks,"injections",next) +        setfield(prev,"next",disc) +        setfield(next,"prev",disc) +    elseif prev and next then +        local tail = find_node_tail(replace) +        local nest = getprev(replace) +        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) +        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 --- there will be a new direction parser (pre-parsed etc) +-- the if new test might be dangerous as luatex will check / set some tail stuff +-- in a temp node --- less bytecode: 290 -> 254 --- --- attr = attr or false --- --- local a = getattr(start,0) --- if (a == attr and (not attribute or getprop(start,a_state) == attribute)) or (not attribute or getprop(start,a_state) == attribute) then ---     -- the action --- end +local function comprun(disc,run) +    if trace_compruns then +        report_run("comp: %s",languages.serializediscretionary(disc)) +    end +    -- +    local pre = getfield(disc,"pre") +    if pre then +        sweepnode = disc +        sweeptype = "pre" -- in alternative code preinjections is used (also used then for proeprties, saves a variable) +        local new, done = run(pre) +        if done then +            setfield(disc,"pre",new) +        end +    end +    -- +    local post = getfield(disc,"post") +    if post then +        sweepnode = disc +        sweeptype = "post" +        local new, done = run(post) +        if done then +            setfield(disc,"post",new) +        end +    end +    -- +    local replace = getfield(disc,"replace") +    if replace then +        sweepnode = disc +        sweeptype = "replace" +        local new, done = run(replace) +        if done then +            setfield(disc,"replace",new) +        end +    end +    sweepnode = nil +    sweeptype = nil +end + +local function testrun(disc,trun,crun) -- use helper +    local next = getnext(disc) +    if next then +        local replace = getfield(disc,"replace") +        if replace then +            local prev = getprev(disc) +            if prev then +                -- only look ahead +                local tail = find_node_tail(replace) +             -- local nest = getprev(replace) +                setfield(tail,"next",next) +                setfield(next,"prev",tail) +                if trun(replace,next) then +                    setfield(disc,"replace",nil) -- beware, side effects of nest so first +                    setfield(prev,"next",replace) +                    setfield(replace,"prev",prev) +                    setfield(next,"prev",tail) +                    setfield(tail,"next",next) +                    setfield(disc,"prev",nil) +                    setfield(disc,"next",nil) +                    flush_node_list(disc) +                    return replace -- restart +                else +                    setfield(tail,"next",nil) +                    setfield(next,"prev",disc) +                end +            else +                -- weird case +            end +        else +            -- no need +        end +    else +        -- weird case +    end +    comprun(disc,crun) +    return next +end + +local function discrun(disc,drun,krun) +    local next = getnext(disc) +    local prev = getprev(disc) +    if trace_discruns then +        report_run("disc") -- will be more detailed +    end +    if next and prev then +        setfield(prev,"next",next) +     -- setfield(next,"prev",prev) +        drun(prev) +        setfield(prev,"next",disc) +     -- setfield(next,"prev",disc) +    end +    -- +    local pre = getfield(disc,"pre") +    if not pre then +        -- go on +    elseif prev then +        local nest = getprev(pre) +        setfield(pre,"prev",prev) +        setfield(prev,"next",pre) +        krun(prev,"preinjections") +        setfield(pre,"prev",nest) +        setfield(prev,"next",disc) +    else +        krun(pre,"preinjections") +    end +    return next +end + +-- todo: maybe run lr and rl stretches  local function featuresprocessor(head,font,attr) @@ -2180,6 +3058,7 @@ local function featuresprocessor(head,font,attr)      currentfont     = font      rlmode          = 0 +    sweephead       = { }      local sequences = resources.sequences      local done      = false @@ -2195,23 +3074,27 @@ 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. + +    -- there can be less subtype and attr checking in the comprun etc helpers      for s=1,#datasets do -        local dataset = datasets[s] -        featurevalue = dataset[1] -- todo: pass to function instead of using a global - -        local sequence  = dataset[5] -- sequences[s] -- also dataset[5] -        local rlparmode = 0 -        local topstack  = 0 -        local success   = false -        local attribute = dataset[2] -        local chain     = dataset[3] -- sequence.chain or 0 -        local typ       = sequence.type -        local subtables = sequence.subtables -        if chain < 0 then +        local dataset      = datasets[s] +              featurevalue = dataset[1] -- todo: pass to function instead of using a global +        local attribute    = dataset[2] +        local sequence     = dataset[3] -- sequences[s] -- also dataset[5] +        local kind         = dataset[4] +        ----- chain        = dataset[5] -- sequence.chain or 0 +        local rlparmode    = 0 +        local topstack     = 0 +        local success      = false +        local typ          = sequence.type +        local gpossing     = typ == "gpos_single" or typ == "gpos_pair" -- 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 -            local handler = handlers[typ]              -- we need to get rid of this slide! probably no longer needed in latest luatex              local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo              while start do @@ -2225,13 +3108,15 @@ local function featuresprocessor(head,font,attr)                              a = true                          end                          if a then +                            local char = getchar(start)                              for i=1,#subtables do                                  local lookupname = subtables[i]                                  local lookupcache = lookuphash[lookupname]                                  if lookupcache then -                                    local lookupmatch = lookupcache[getchar(start)] +                                    local lookupmatch = lookupcache[char]                                      if lookupmatch then -                                        head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                                        -- todo: disc? +                                        head, start, success = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)                                          if success then                                              break                                          end @@ -2252,24 +3137,30 @@ local function featuresprocessor(head,font,attr)                  end              end          else -            local handler = handlers[typ]              local ns = #subtables              local start = head -- local ?              rlmode = 0 -- to be checked ?              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)                  else -                    local function subrun(start) -                        -- mostly for gsub, gpos would demand a more clever approach -                        local head = start -                        local done = false +                    local function c_run(head) -- no need to check for 256 and attr probably also the same +                        local done  = false +                        local start = sweephead[head] +                        if start then +                            sweephead[head] = nil +                        else +                            start = head +                        end                          while start do                              local id = getid(start) -                            if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then +                            if id ~= glyph_code then +                                -- very unlikely +                                start = getnext(start) +                            elseif getfont(start) == font and getsubtype(start) < 256 then                                  local a = getattr(start,0)                                  if a then                                      a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) @@ -2281,7 +3172,7 @@ local function featuresprocessor(head,font,attr)                                      if lookupmatch then                                          -- sequence kan weg                                          local ok -                                        head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) +                                        head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)                                          if ok then                                              done = true                                          end @@ -2291,48 +3182,106 @@ local function featuresprocessor(head,font,attr)                                      start = getnext(start)                                  end                              else -                                start = getnext(start) +                                return head, false                              end                          end                          if done then -                            success = true -                            return head +                            success = true -- needed in this subrun?                          end +                        return head, done                      end -                    local function kerndisc(disc) -- we can assume that prev and next are glyphs -                        local prev = getprev(disc) -                        local next = getnext(disc) -                        if prev and next then -                            setfield(prev,"next",next) -                         -- setfield(next,"prev",prev) -                            local a = getattr(prev,0) -                            if a then -                                a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute) +                    local function t_run(start,stop) +                        while start ~= stop do +                            local id = getid(start) +                            if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then +                                local a = getattr(start,0) +                                if a then +                                    a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) +                                else +                                    a = not attribute or getprop(start,a_state) == attribute +                                end +                                if a then +                                    local lookupmatch = lookupcache[getchar(start)] +                                    if lookupmatch then -- hm, hyphens can match (tlig) so we need to really check +                                        -- if we need more than ligatures we can outline the code and use functions +                                        local s = getnext(start) +                                        local l = nil +                                        while s do +                                            local lg = lookupmatch[getchar(s)] +                                            if lg then +                                                l = lg +                                                s = getnext(s) +                                            else +                                                break +                                            end +                                        end +                                        if l and l.ligature then +                                            return true +                                        end +                                    end +                                end +                                start = getnext(start)                              else -                                a = not attribute or getprop(prev,a_state) == attribute +                                break                              end -                            if a then -                                local lookupmatch = lookupcache[getchar(prev)] -                                if lookupmatch then -                                    -- sequence kan weg -                                    local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) -                                    if ok then -                                        done = true -                                        success = true +                        end +                    end + +                    local function d_run(prev) -- we can assume that prev and next are glyphs +                        local a = getattr(prev,0) +                        if a then +                            a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute) +                        else +                            a = not attribute or getprop(prev,a_state) == attribute +                        end +                        if a then +                            local lookupmatch = lookupcache[getchar(prev)] +                            if lookupmatch then +                                -- sequence kan weg +                                local h, d, ok = handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1) +                                if ok then +                                    done    = true +                                    success = true +                                end +                            end +                        end +                    end + +                    local function k_run(sub,injection,last) +                        local a = getattr(sub,0) +                        if a then +                            a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute) +                        else +                            a = not attribute or getprop(sub,a_state) == attribute +                        end +                        if a then +                            -- sequence kan weg +                            for n in traverse_nodes(sub) do -- only gpos +                                if n == last then +                                    break +                                end +                                local id = getid(n) +                                if id == glyph_code then +                                    local lookupmatch = lookupcache[getchar(n)] +                                    if lookupmatch then +                                        local h, d, ok = handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection) +                                        if ok then +                                            done    = true +                                            success = true +                                        end                                      end +                                else +                                    -- message                                  end                              end -                            setfield(prev,"next",disc) -                         -- setfield(next,"prev",disc)                          end -                        return next                      end                      while start do                          local id = getid(start)                          if id == glyph_code then -                            if getfont(start) == font and getsubtype(start) < 256 then +                            if getfont(start) == font and getsubtype(start) < 256 then -- why a 256 test ...                                  local a = getattr(start,0)                                  if a then                                      a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) @@ -2340,59 +3289,52 @@ local function featuresprocessor(head,font,attr)                                      a = not attribute or getprop(start,a_state) == attribute                                  end                                  if a then -                                    local lookupmatch = lookupcache[getchar(start)] +                                    local char        = getchar(start) +                                    local lookupmatch = lookupcache[char]                                      if lookupmatch then                                          -- sequence kan weg                                          local ok -                                        head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) +                                        head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)                                          if ok then                                              success = true +                                        elseif gpossing and zwnjruns and char == zwnj then +                                            discrun(start,d_run)                                          end +                                    elseif gpossing and zwnjruns and char == zwnj then +                                        discrun(start,d_run)                                      end                                      if start then start = getnext(start) end                                  else -                                    start = getnext(start) +                                   start = getnext(start)                                  end                              else                                  start = getnext(start)                              end                          elseif id == disc_code then -                            -- mostly for gsub -                            if getsubtype(start) == discretionary_code then -                                local pre = getfield(start,"pre") -                                if pre then -                                    local new = subrun(pre) -                                    if new then setfield(start,"pre",new) end -                                end -                                local post = getfield(start,"post") -                                if post then -                                    local new = subrun(post) -                                    if new then setfield(start,"post",new) end -                                end -                                local replace = getfield(start,"replace") -                                if replace then -                                    local new = subrun(replace) -                                    if new then setfield(start,"replace",new) end -                                end -elseif typ == "gpos_single" or typ == "gpos_pair" then -    kerndisc(start) +                            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 -                            start = getnext(start)                          elseif id == whatsit_code then -- will be function                              local subtype = getsubtype(start)                              if subtype == dir_code then                                  local dir = getfield(start,"dir") -                                if     dir == "+TRT" or dir == "+TLT" then +                                if dir == "+TLT" then                                      topstack = topstack + 1                                      dirstack[topstack] = dir -                                elseif dir == "-TRT" or dir == "-TLT" then -                                    topstack = topstack - 1 -                                end -                                local newdir = dirstack[topstack] -                                if newdir == "+TRT" then -                                    rlmode = -1 -                                elseif newdir == "+TLT" then                                      rlmode = 1 +                                elseif dir == "+TRT" then +                                    topstack = topstack + 1 +                                    dirstack[topstack] = dir +                                    rlmode = -1 +                                elseif dir == "-TLT" or dir == "-TRT" then +                                    topstack = topstack - 1 +                                    rlmode = dirstack[topstack] == "+TRT" and -1 or 1                                  else                                      rlmode = rlparmode                                  end @@ -2422,15 +3364,23 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                          end                      end                  end +              else -                local function subrun(start) -                    -- mostly for gsub, gpos would demand a more clever approach -                    local head = start -                    local done = false +                local function c_run(head) +                    local done  = false +                    local start = sweephead[head] +                    if start then +                        sweephead[head] = nil +                    else +                        start = head +                    end                      while start do                          local id = getid(start) -                        if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then +                        if id ~= glyph_code then +                            -- very unlikely +                            start = getnext(start) +                        elseif getfont(start) == font and getsubtype(start) < 256 then                              local a = getattr(start,0)                              if a then                                  a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) @@ -2438,15 +3388,16 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                                  a = not attribute or getprop(start,a_state) == attribute                              end                              if a then +                                local char = getchar(start)                                  for i=1,ns do                                      local lookupname = subtables[i]                                      local lookupcache = lookuphash[lookupname]                                      if lookupcache then -                                        local lookupmatch = lookupcache[getchar(start)] +                                        local lookupmatch = lookupcache[char]                                          if lookupmatch then                                              -- we could move all code inline but that makes things even more unreadable                                              local ok -                                            head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                                            head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)                                              if ok then                                                  done = true                                                  break @@ -2464,50 +3415,127 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                                  start = getnext(start)                              end                          else -                            start = getnext(start) +                            return head, false                          end                      end                      if done then                          success = true -                        return head                      end +                    return head, done                  end -                local function kerndisc(disc) -- we can assume that prev and next are glyphs -                    local prev = getprev(disc) -                    local next = getnext(disc) -                    if prev and next then -                        setfield(prev,"next",next) -                     -- setfield(next,"prev",prev) -                        local a = getattr(prev,0) -                        if a then -                            a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute) -                        else -                            a = not attribute or getprop(prev,a_state) == attribute +                local function d_run(prev) +                    local a = getattr(prev,0) +                    if a then +                        a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute) +                    else +                        a = not attribute or getprop(prev,a_state) == attribute +                    end +                    if a then +                        -- brr prev can be disc +                        local char = getchar(prev) +                        for i=1,ns do +                            local lookupname  = subtables[i] +                            local lookupcache = lookuphash[lookupname] +                            if lookupcache then +                                local lookupmatch = lookupcache[char] +                                if lookupmatch then +                                    -- we could move all code inline but that makes things even more unreadable +                                    local h, d, ok = handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,i) +                                    if ok then +                                        done = true +                                        break +                                    end +                                end +                            else +                                report_missing_cache(typ,lookupname) +                            end                          end -                        if a then -                            for i=1,ns do -                                local lookupname = subtables[i] -                                local lookupcache = lookuphash[lookupname] -                                if lookupcache then -                                    local lookupmatch = lookupcache[getchar(prev)] -                                    if lookupmatch then -                                        -- we could move all code inline but that makes things even more unreadable -                                        local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) -                                        if ok then -                                            done = true -                                            break +                    end +                end + +               local function k_run(sub,injection,last) +                    local a = getattr(sub,0) +                    if a then +                        a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute) +                    else +                        a = not attribute or getprop(sub,a_state) == attribute +                    end +                    if a then +                        for n in traverse_nodes(sub) do -- only gpos +                            if n == last then +                                break +                            end +                            local id = getid(n) +                            if id == glyph_code then +                                local char = getchar(n) +                                for i=1,ns do +                                    local lookupname  = subtables[i] +                                    local lookupcache = lookuphash[lookupname] +                                    if lookupcache then +                                        local lookupmatch = lookupcache[char] +                                        if lookupmatch then +                                            local h, d, ok = handler(head,n,kind,lookupname,lookupmatch,sequence,lookuphash,i,injection) +                                            if ok then +                                                done = true +                                                break +                                            end                                          end +                                    else +                                        report_missing_cache(typ,lookupname) +                                    end +                                end +                            else +                                -- message +                            end +                        end +                    end +                end + +                local function t_run(start,stop) +                    while start ~= stop do +                        local id = getid(start) +                        if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then +                            local a = getattr(start,0) +                            if a then +                                a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) +                            else +                                a = not attribute or getprop(start,a_state) == attribute +                            end +                            if a then +                                local char = getchar(start) +                                for i=1,ns do +                                    local lookupname  = subtables[i] +                                    local lookupcache = lookuphash[lookupname] +                                    if lookupcache then +                                        local lookupmatch = lookupcache[char] +                                        if lookupmatch then +                                            -- if we need more than ligatures we can outline the code and use functions +                                            local s = getnext(start) +                                            local l = nil +                                            while s do +                                                local lg = lookupmatch[getchar(s)] +                                                if lg then +                                                    l = lg +                                                    s = getnext(s) +                                                else +                                                    break +                                                end +                                            end +                                            if l and l.ligature then +                                                return true +                                            end +                                        end +                                    else +                                        report_missing_cache(typ,lookupname)                                      end -                                else -                                    report_missing_cache(typ,lookupname)                                  end                              end +                            start = getnext(start) +                        else +                            break                          end -                        setfield(prev,"next",disc) -                     -- setfield(next,"prev",disc)                      end -                    return next                  end                  while start do @@ -2522,21 +3550,26 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                              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 lookupmatch = lookupcache[getchar(start)] +                                        local char = getchar(start) +                                        local lookupmatch = lookupcache[char]                                          if lookupmatch then                                              -- we could move all code inline but that makes things even more unreadable                                              local ok -                                            head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                                            head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)                                              if ok then                                                  success = true                                                  break                                              elseif not start then                                                  -- don't ask why ... shouldn't happen                                                  break +                                            elseif gpossing and zwnjruns and char == zwnj then +                                                discrun(start,d_run)                                              end +                                        elseif gpossing and zwnjruns and char == zwnj then +                                            discrun(start,d_run)                                          end                                      else                                          report_missing_cache(typ,lookupname) @@ -2550,42 +3583,30 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                              start = getnext(start)                          end                      elseif id == disc_code then -                        -- mostly for gsub -                        if getsubtype(start) == discretionary_code then -                            local pre = getfield(start,"pre") -                            if pre then -                                local new = subrun(pre) -                                if new then setfield(start,"pre",new) end -                            end -                            local post = getfield(start,"post") -                            if post then -                                local new = subrun(post) -                                if new then setfield(start,"post",new) end -                            end -                            local replace = getfield(start,"replace") -                            if replace then -                                local new = subrun(replace) -                                if new then setfield(start,"replace",new) end -                            end -elseif typ == "gpos_single" or typ == "gpos_pair" then -    kerndisc(start) +                        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 -                        start = getnext(start)                      elseif id == whatsit_code then                          local subtype = getsubtype(start)                          if subtype == dir_code then                              local dir = getfield(start,"dir") -                            if     dir == "+TRT" or dir == "+TLT" then +                            if dir == "+TLT" then                                  topstack = topstack + 1                                  dirstack[topstack] = dir -                            elseif dir == "-TRT" or dir == "-TLT" then -                                topstack = topstack - 1 -                            end -                            local newdir = dirstack[topstack] -                            if newdir == "+TRT" then -                                rlmode = -1 -                            elseif newdir == "+TLT" then                                  rlmode = 1 +                            elseif dir == "+TRT" then +                                topstack = topstack + 1 +                                dirstack[topstack] = dir +                                rlmode = -1 +                            elseif dir == "-TLT" or dir == "-TRT" then +                                topstack = topstack - 1 +                                rlmode = dirstack[topstack] == "+TRT" and -1 or 1                              else                                  rlmode = rlparmode                              end @@ -2629,6 +3650,8 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then      return head, done  end +-- this might move to the loader +  local function generic(lookupdata,lookupname,unicode,lookuphash)      local target = lookuphash[lookupname]      if target then @@ -2638,47 +3661,48 @@ local function generic(lookupdata,lookupname,unicode,lookuphash)      end  end -local action = { +local function ligature(lookupdata,lookupname,unicode,lookuphash) +    local target = lookuphash[lookupname] +    if not target then +        target = { } +        lookuphash[lookupname] = target +    end +    for i=1,#lookupdata do +        local li = lookupdata[i] +        local tu = target[li] +        if not tu then +            tu = { } +            target[li] = tu +        end +        target = tu +    end +    target.ligature = unicode +end + +local function pair(lookupdata,lookupname,unicode,lookuphash) +    local target = lookuphash[lookupname] +    if not target then +        target = { } +        lookuphash[lookupname] = target +    end +    local others = target[unicode] +    local paired = lookupdata[1] +    if others then +        others[paired] = lookupdata +    else +        others = { [paired] = lookupdata } +        target[unicode] = others +    end +end +local action = {      substitution = generic,      multiple     = generic,      alternate    = generic,      position     = generic, - -    ligature = function(lookupdata,lookupname,unicode,lookuphash) -        local target = lookuphash[lookupname] -        if not target then -            target = { } -            lookuphash[lookupname] = target -        end -        for i=1,#lookupdata do -            local li = lookupdata[i] -            local tu = target[li] -            if not tu then -                tu = { } -                target[li] = tu -            end -            target = tu -        end -        target.ligature = unicode -    end, - -    pair = function(lookupdata,lookupname,unicode,lookuphash) -        local target = lookuphash[lookupname] -        if not target then -            target = { } -            lookuphash[lookupname] = target -        end -        local others = target[unicode] -        local paired = lookupdata[1] -        if others then -            others[paired] = lookupdata -        else -            others = { [paired] = lookupdata } -            target[unicode] = others -        end -    end, - +    ligature     = ligature, +    pair         = pair, +    kern         = pair,  }  local function prepare_lookups(tfmdata) @@ -2691,12 +3715,17 @@ local function prepare_lookups(tfmdata)      local lookuptypes      = resources.lookuptypes      local characters       = tfmdata.characters      local descriptions     = tfmdata.descriptions +    local duplicates       = resources.duplicates      -- we cannot free the entries in the descriptions as sometimes we access      -- then directly (for instance anchors) ... selectively freeing does save      -- much memory as it's only a reference to a table and the slot in the      -- description hash is not freed anyway +    -- we can delay this using metatables so that we don't make the hashes for +    -- features we don't use but then we need to loop over the characters +    -- many times so we gain nothing +      for unicode, character in next, characters do -- we cannot loop over descriptions !          local description = descriptions[unicode] @@ -2706,7 +3735,7 @@ local function prepare_lookups(tfmdata)              local lookups = description.slookups              if lookups then                  for lookupname, lookupdata in next, lookups do -                    action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) +                    action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash,duplicates)                  end              end @@ -2716,7 +3745,7 @@ local function prepare_lookups(tfmdata)                      local lookuptype = lookuptypes[lookupname]                      for l=1,#lookuplist do                          local lookupdata = lookuplist[l] -                        action[lookuptype](lookupdata,lookupname,unicode,lookuphash) +                        action[lookuptype](lookupdata,lookupname,unicode,lookuphash,duplicates)                      end                  end              end @@ -2740,7 +3769,7 @@ local function prepare_lookups(tfmdata)                          for name, anchor in next, anchors do                              local lookups = anchor_to_lookup[name]                              if lookups then -                                for lookup, _ in next, lookups do +                                for lookup in next, lookups do                                      local target = lookuphash[lookup]                                      if target then                                          target[unicode] = anchors @@ -2760,6 +3789,8 @@ local function prepare_lookups(tfmdata)  end +-- so far +  local function split(replacement,original)      local result = { }      for i=1,#replacement do @@ -2835,7 +3866,7 @@ local function prepare_contextchains(tfmdata)                                  -- use sequence[start] instead but it's somewhat ugly.                                  nt = nt + 1                                  t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } -                                for unic, _  in next, sequence[start] do +                                for unic in next, sequence[start] do                                      local cu = contexts[unic]                                      if not cu then                                          contexts[unic] = t diff --git a/src/fontloader/misc/fontloader-fonts.lua b/src/fontloader/misc/fontloader-fonts.lua index c81e8cd..f18ba35 100644 --- a/src/fontloader/misc/fontloader-fonts.lua +++ b/src/fontloader/misc/fontloader-fonts.lua @@ -27,16 +27,10 @@ if not modules then modules = { } end modules ['luatex-fonts'] = {  -- also add more helper code here, but that depends to what extend metatex (sidetrack of context)  -- evolves into a low level layer (depends on time, as usual). -texio.write_nl("") -texio.write_nl("--------------------------------------------------------------------------------") -texio.write_nl("The font code has been brought in sync with the context version of 2014.12.21 so") -texio.write_nl("if things don't work out as expected the interfacing needs to be checked. When") -texio.write_nl("this works as expected a second upgrade will happen that gives a more complete") -texio.write_nl("support and another sync with the context code (that new code is currently being") -texio.write_nl("tested. The base pass is now integrated in the main pass. The results can differ") -texio.write_nl("from those in context because there we integrate some mechanisms differently.") -texio.write_nl("--------------------------------------------------------------------------------") -texio.write_nl("") +-- The code here is the same as in context version 2015.09.11 but the rendering in context can be +-- different from generic. This can be a side effect of additional callbacks, additional features +-- and interferences between mechanisms between macro packages. We use the rendering in context +-- and luatex-plain as reference for issues.  utf = utf or unicode.utf8 @@ -221,9 +215,9 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then          loadmodule('font-oti.lua')          loadmodule('font-otf.lua')          loadmodule('font-otb.lua') -        loadmodule('luatex-fonts-inj.lua') +        loadmodule('luatex-fonts-inj.lua') -- normally the same as font-inj.lua          loadmodule('luatex-fonts-ota.lua') -        loadmodule('luatex-fonts-otn.lua') +        loadmodule('luatex-fonts-otn.lua') -- normally the same as font-otn.lua          loadmodule('font-otp.lua')          loadmodule('luatex-fonts-lua.lua')          loadmodule('font-def.lua')         -- this code (stripped) might end up in luatex-fonts-def.lua diff --git a/src/fontloader/misc/fontloader-l-lpeg.lua b/src/fontloader/misc/fontloader-l-lpeg.lua index 55a0d89..5be1246 100644 --- a/src/fontloader/misc/fontloader-l-lpeg.lua +++ b/src/fontloader/misc/fontloader-l-lpeg.lua @@ -82,7 +82,7 @@ local lpegtype, lpegmatch, lpegprint = lpeg.type, lpeg.match, lpeg.print  -- let's start with an inspector:  if setinspector then -    setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +    setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)  end  -- Beware, we predefine a bunch of patterns here and one reason for doing so diff --git a/src/fontloader/misc/fontloader-l-lua.lua b/src/fontloader/misc/fontloader-l-lua.lua index 1a2a987..cb61829 100644 --- a/src/fontloader/misc/fontloader-l-lua.lua +++ b/src/fontloader/misc/fontloader-l-lua.lua @@ -129,22 +129,36 @@ local print, select, tostring = print, select, tostring  local inspectors = { } -function setinspector(inspector) -- global function -    inspectors[#inspectors+1] = inspector +function setinspector(kind,inspector) -- global function +    inspectors[kind] = inspector  end  function inspect(...) -- global function      for s=1,select("#",...) do          local value = select(s,...) -        local done = false -        for i=1,#inspectors do -            done = inspectors[i](value) -            if done then -                break +        if value == nil then +            print("nil") +        else +            local done  = false +            -- type driven (table) +            local kind      = type(value) +            local inspector = inspectors[kind] +            if inspector then +                done = inspector(value) +                if done then +                    break +                end +            end +            -- whatever driven (token, node, ...) +            for kind, inspector in next, inspectors do +                done = inspector(value) +                if done then +                    break +                end +            end +            if not done then +                print(tostring(value))              end -        end -        if not done then -            print(tostring(value))          end      end  end diff --git a/src/fontloader/misc/fontloader-l-string.lua b/src/fontloader/misc/fontloader-l-string.lua index 70c66f6..e9dc2bb 100644 --- a/src/fontloader/misc/fontloader-l-string.lua +++ b/src/fontloader/misc/fontloader-l-string.lua @@ -192,10 +192,11 @@ string.itself  = function(s) return s end  -- also handy (see utf variant) -local pattern = Ct(C(1)^0) -- string and not utf ! +local pattern_c = Ct( C(1)      ^0) -- string and not utf ! +local pattern_b = Ct((C(1)/byte)^0) -function string.totable(str) -    return lpegmatch(pattern,str) +function string.totable(str,bytes) +    return lpegmatch(bytes and pattern_b or pattern_c,str)  end  -- handy from within tex: diff --git a/src/fontloader/misc/fontloader-l-table.lua b/src/fontloader/misc/fontloader-l-table.lua index b02f210..552097e 100644 --- a/src/fontloader/misc/fontloader-l-table.lua +++ b/src/fontloader/misc/fontloader-l-table.lua @@ -1144,7 +1144,7 @@ function table.print(t,...)  end  if setinspector then -    setinspector(function(v) if type(v) == "table" then serialize(print,v,"table") return true end end) +    setinspector("table",function(v) if type(v) == "table" then serialize(print,v,"table") return true end end)  end  -- -- -- obsolete but we keep them for a while and might comment them later -- -- -- diff --git a/src/fontloader/misc/fontloader-mplib.lua b/src/fontloader/misc/fontloader-mplib.lua index c6628ac..fd6eb97 100644 --- a/src/fontloader/misc/fontloader-mplib.lua +++ b/src/fontloader/misc/fontloader-mplib.lua @@ -22,7 +22,9 @@ if metapost and metapost.version then  else -    local format, concat, abs, match = string.format, table.concat, math.abs, string.match +    local format, match, gsub = string.format, string.match, string.gsub +    local concat = table.concat +    local abs = math.abs      local mplib = require ('mplib')      local kpse  = require ('kpse') @@ -144,10 +146,101 @@ else          metapost.make = metapost.make or function()          end +        local template = [[ +            \pdfoutput=1 +            \pdfpkresolution600 +            \pdfcompresslevel=9 +            %s\relax +            \hsize=100in +            \vsize=\hsize +            \hoffset=-1in +            \voffset=\hoffset +            \topskip=0pt +            \setbox0=\hbox{%s}\relax +            \pageheight=\ht0 +            \pagewidth=\wd0 +            \box0 +            \bye +        ]] + +        metapost.texrunner = "mtxrun --script plain" + +        local texruns = 0   -- per document +        local texhash = { } -- per document + +        function metapost.maketext(mpd,str,what) +            -- inefficient but one can always use metafun .. it's more a test +            -- feature +            local verbatimtex = mpd.verbatimtex +            if not verbatimtex then +                verbatimtex = { } +                mpd.verbatimtex = verbatimtex +            end +            if what == 1 then +                table.insert(verbatimtex,str) +            else +                local texcode = format(template,concat(verbatimtex,"\n"),str) +                local texdone = texhash[texcode] +                local jobname = tex.jobname +                if not texdone then +                    texruns = texruns + 1 +                    texdone = texruns +                    texhash[texcode] = texdone +                    local texname = format("%s-mplib-%s.tmp",jobname,texdone) +                    local logname = format("%s-mplib-%s.log",jobname,texdone) +                    local pdfname = format("%s-mplib-%s.pdf",jobname,texdone) +                    io.savedata(texname,texcode) +                    os.execute(format("%s %s",metapost.texrunner,texname)) +                    os.remove(texname) +                    os.remove(logname) +                end +                return format('"image::%s-mplib-%s.pdf" infont defaultfont',jobname,texdone) +            end +        end + +        local function mpprint(buffer,...) +            for i=1,select("#",...) do +                local value = select(i,...) +                if value ~= nil then +                    local t = type(value) +                    if t == "number" then +                        buffer[#buffer+1] = format("%.16f",value) +                    elseif t == "string" then +                        buffer[#buffer+1] = value +                    elseif t == "table" then +                        buffer[#buffer+1] = "(" .. concat(value,",") .. ")" +                    else -- boolean or whatever +                        buffer[#buffer+1] = tostring(value) +                    end +                end +            end +        end + +        function metapost.runscript(mpd,code) +            local code = loadstring(code) +            if type(code) == "function" then +                local buffer = { } +                function metapost.print(...) +                    mpprint(buffer,...) +                end +                code() +             -- mpd.buffer = buffer -- for tracing +                return concat(buffer,"") +            end +            return "" +        end +          function metapost.load(name) +            local mpd = { +                buffer   = { }, +                verbatim = { } +            }              local mpx = mplib.new {                  ini_version = true, -                find_file = metapost.finder, +                find_file   = metapost.finder, +                make_text   = function(...) return metapost.maketext (mpd,...) end, +                run_script  = function(...) return metapost.runscript(mpd,...) end, +                extensions  = 1,              }              local result              if not mpx then @@ -217,8 +310,8 @@ else          return figure:objects()      end -    function metapost.convert(result, flusher) -        metapost.flush(result, flusher) +    function metapost.convert(result,flusher) +        metapost.flush(result,flusher)          return true -- done      end @@ -239,8 +332,13 @@ else      end      function pdf_textfigure(font,size,text,width,height,depth) -        text = text:gsub(".","\\hbox{%1}") -- kerning happens in metapost -        tex.sprint(format("\\MPLIBtextext{%s}{%s}{%s}{%s}{%s}",font,size,text,0,-( 7200/ 7227)/65536*depth)) +        local how, what = match(text,"^(.-)::(.+)$") +        if how == "image" then +            tex.sprint(format("\\MPLIBpdftext{%s}{%s}",what,depth)) +        else +            text = gsub(text,".","\\hbox{%1}") -- kerning happens in metapost +            tex.sprint(format("\\MPLIBtextext{%s}{%s}{%s}{%s}",font,size,text,depth)) +        end      end      local bend_tolerance = 131/65536 @@ -375,8 +473,10 @@ else                                      pdf_literalcode("Q")                                  else                                      local cs = object.color +                                    local cr = false                                      if cs and #cs > 0 then -                                        pdf_literalcode(metapost.colorconverter(cs)) +                                        cs, cr = metapost.colorconverter(cs) +                                        pdf_literalcode(cs)                                      end                                      local ml = object.miterlimit                                      if ml and ml ~= miterlimit then diff --git a/src/fontloader/misc/fontloader-mplib.tex b/src/fontloader/misc/fontloader-mplib.tex index 09dd179..f9de4b2 100644 --- a/src/fontloader/misc/fontloader-mplib.tex +++ b/src/fontloader/misc/fontloader-mplib.tex @@ -106,15 +106,14 @@  %D Text items have a special handler: -\def\MPLIBtextext#1#2#3#4#5% +\def\MPLIBtextext#1#2#3#4%    {\begingroup     \setbox\mplibscratchbox\hbox       {\font\temp=#1 at #2bp%        \temp        #3}%     \setbox\mplibscratchbox\hbox -     {\hskip#4 bp% -      \raise#5 bp% +     {\raise#4sp%        \box\mplibscratchbox}%     \wd\mplibscratchbox0pt%     \ht\mplibscratchbox0pt% @@ -122,4 +121,20 @@     \box\mplibscratchbox     \endgroup} +\def\MPLIBpdftext#1#2% +  {\ifcsname mplib::#1\endcsname +     % already done, forgotten outside convert group +     \message{<reusing mplib: #1>}% +   \else +     \message{<embedding mplib: #1>}% +     \immediate\pdfximage{#1}% we cannot remove the file as it is included last +     \expandafter\edef\csname mplib::#1\endcsname{\the\pdflastximage}% +   \fi +   \setbox\mplibscratchbox\hbox +     {\raise#2sp\hbox{\pdfrefximage\csname mplib::#1\endcsname}}% +   \wd\mplibscratchbox0pt% +   \ht\mplibscratchbox0pt% +   \dp\mplibscratchbox0pt% +   \box\mplibscratchbox} +  \endinput diff --git a/src/fontloader/misc/fontloader-plain.tex b/src/fontloader/misc/fontloader-plain.tex index c9a9e36..9902c49 100644 --- a/src/fontloader/misc/fontloader-plain.tex +++ b/src/fontloader/misc/fontloader-plain.tex @@ -11,7 +11,26 @@  \directlua {tex.enableprimitives('', tex.extraprimitives())} -\pdfoutput=1 +% We assume that pdf is used. + +\pdfoutput 1 + +% We set the page dimensions because otherwise the backend does weird things +% when we have for instance this on a line of its own: +% +%   \hbox to 100cm {\hss wide indeed\hss} +% +% The page dimension calculation is a fuzzy one as there are some compensations +% for the \hoffset and \voffset and such. I remember long discussions and much +% trial and error in figuring this out during pdftex development times. Where +% a dvi driver will project on a papersize (and thereby clip) the pdf backend +% has to deal with the lack of a page concept on tex by some guessing. Normally +% a macro package will set the dimensions to something reasonable anyway. + +\pagewidth   8.5in +\pageheight 11.0in + +% We load some code at runtime:  \everyjob \expandafter {%      \the\everyjob @@ -20,9 +39,11 @@      \input {luatex-math}%      \input {luatex-languages}%      \input {luatex-mplib}% -  % \input {luatex-gadgets}% +    \input {luatex-gadgets}%  } +% We also patch the version number: +  \edef\fmtversion{\fmtversion+luatex}  \dump diff --git a/src/fontloader/misc/fontloader-test.tex b/src/fontloader/misc/fontloader-test.tex index 6f48e0c..f851aab 100644 --- a/src/fontloader/misc/fontloader-test.tex +++ b/src/fontloader/misc/fontloader-test.tex @@ -1,3 +1,5 @@ +% texformat=luatex-plain +  %D \module  %D   [       file=luatex-test,  %D        version=2009.12.01, @@ -33,12 +35,12 @@  \font\mathtest=cambria(math) {\mathtest 123} -\font\gothic=msgothic(ms-gothic) {\gothic whatever} +% \font\gothic=msgothic(ms-gothic) {\gothic whatever} % no longer in windows 10  \bgroup -    \pdfprotrudechars2 -    \pdfadjustspacing2 +    \ifdefined\pdfprotrudechars \pdfprotrudechars \else \protrudechars \fi 2 \relax +    \ifdefined\pdfadjustspacing \pdfadjustspacing \else \adjustspacing \fi 2 \relax      \font\testb=file:lmroman12-regular:+liga;extend=1.5         at 12pt \testb \input tufte \par      \font\testb=file:lmroman12-regular:+liga;slant=0.8          at 12pt \testb \input tufte \par @@ -48,12 +50,30 @@  \setmplibformat{plain} +\directlua { +    function MpTest() +        metapost.print("fullcircle scaled 3cm") +    end +} +  \mplibcode      beginfig(1) ;          draw fullcircle              scaled 10cm              withcolor red              withpen pencircle xscaled 4mm yscaled 2mm rotated 30 ; +        draw "test" infont defaultfont scaled 4 ; +        verbatimtex \sl etex; +        draw btex some more test etex scaled 2 ; +        currentpicture := currentpicture shifted (0,1cm) ; +        verbatimtex \bf etex; +        draw btex another test etex scaled 2 ; +        currentpicture := currentpicture shifted (0,1cm) ; +        draw btex another test etex scaled 2 ; +        draw +            runscript("MpTest()") +            withcolor green +            withpen pencircle xscaled 2mm yscaled 1mm rotated 20 ;      endfig ;  \endmplibcode diff --git a/src/fontloader/misc/fontloader-util-str.lua b/src/fontloader/misc/fontloader-util-str.lua index c2139b1..95534c8 100644 --- a/src/fontloader/misc/fontloader-util-str.lua +++ b/src/fontloader/misc/fontloader-util-str.lua @@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['util-str'] = {      license   = "see context related readme files"  } -utilities         = utilities or {} +utilities         = utilities or { }  utilities.strings = utilities.strings or { }  local strings     = utilities.strings @@ -354,7 +354,16 @@ function string.autosingle(s,sep)      return ("'" .. tostring(s) .. "'")  end -local tracedchars  = { } +local tracedchars  = { [0] = +    -- the regular bunch +    "[null]", "[soh]", "[stx]", "[etx]", "[eot]", "[enq]", "[ack]", "[bel]", +    "[bs]",   "[ht]",  "[lf]",  "[vt]",  "[ff]",  "[cr]",  "[so]",  "[si]", +    "[dle]",  "[dc1]", "[dc2]", "[dc3]", "[dc4]", "[nak]", "[syn]", "[etb]", +    "[can]",  "[em]",  "[sub]", "[esc]", "[fs]",  "[gs]",  "[rs]",  "[us]", +    -- plus space +    "[space]", -- 0x20 +} +  string.tracedchars = tracedchars  strings.tracers    = tracedchars diff --git a/src/fontloader/runtime/fontloader-reference.lua b/src/fontloader/runtime/fontloader-reference.lua index d8095a2..a2a598b 100644 --- a/src/fontloader/runtime/fontloader-reference.lua +++ b/src/fontloader/runtime/fontloader-reference.lua @@ -1,6 +1,6 @@  -- merged file : luatex-fonts-merged.lua  -- parent file : luatex-fonts.lua --- merge date  : 05/24/15 12:42:55 +-- merge date  : 10/09/15 21:28:28  do -- begin closure to overcome local limits and interference @@ -57,21 +57,33 @@ if not package.loaders then  end  local print,select,tostring=print,select,tostring  local inspectors={} -function setinspector(inspector)  -  inspectors[#inspectors+1]=inspector +function setinspector(kind,inspector)  +  inspectors[kind]=inspector  end  function inspect(...)     for s=1,select("#",...) do      local value=select(s,...) -    local done=false -    for i=1,#inspectors do -      done=inspectors[i](value) -      if done then -        break +    if value==nil then +      print("nil") +    else +      local done=false +      local kind=type(value) +      local inspector=inspectors[kind] +      if inspector then +        done=inspector(value) +        if done then +          break +        end +      end +      for kind,inspector in next,inspectors do +        done=inspector(value) +        if done then +          break +        end +      end +      if not done then +        print(tostring(value))        end -    end -    if not done then -      print(tostring(value))      end    end  end @@ -112,7 +124,7 @@ local floor=math.floor  local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt  local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print  if setinspector then -  setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end) +  setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)  end  lpeg.patterns=lpeg.patterns or {}   local patterns=lpeg.patterns @@ -995,9 +1007,10 @@ function string.valid(str,default)    return (type(str)=="string" and str~="" and str) or default or nil  end  string.itself=function(s) return s end -local pattern=Ct(C(1)^0)  -function string.totable(str) -  return lpegmatch(pattern,str) +local pattern_c=Ct(C(1)^0)  +local pattern_b=Ct((C(1)/byte)^0) +function string.totable(str,bytes) +  return lpegmatch(bytes and pattern_b or pattern_c,str)  end  local replacer=lpeg.replacer("@","%%")   function string.tformat(fmt,...) @@ -1884,7 +1897,7 @@ function table.print(t,...)    end  end  if setinspector then -  setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +  setinspector("table",function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)  end  function table.sub(t,i,j)    return { unpack(t,i,j) } @@ -2937,7 +2950,13 @@ function string.autosingle(s,sep)    end    return ("'"..tostring(s).."'")  end -local tracedchars={} +local tracedchars={ [0]= +  "[null]","[soh]","[stx]","[etx]","[eot]","[enq]","[ack]","[bel]", +  "[bs]","[ht]","[lf]","[vt]","[ff]","[cr]","[so]","[si]", +  "[dle]","[dc1]","[dc2]","[dc3]","[dc4]","[nak]","[syn]","[etb]", +  "[can]","[em]","[sub]","[esc]","[fs]","[gs]","[rs]","[us]", +  "[space]", +}  string.tracedchars=tracedchars  strings.tracers=tracedchars  function string.tracedchar(b) @@ -3886,6 +3905,8 @@ local nodecodes={} for k,v in next,node.types  () do nodecodes[string.gsub(v,"_"  local whatcodes={} for k,v in next,node.whatsits() do whatcodes[string.gsub(v,"_","")]=k end  local glyphcodes={ [0]="character","glyph","ligature","ghost","left","right" }  local disccodes={ [0]="discretionary","explicit","automatic","regular","first","second" } +for i=0,#glyphcodes do glyphcodes[glyphcodes[i]]=i end +for i=0,#disccodes do disccodes [disccodes [i]]=i end  nodes.nodecodes=nodecodes  nodes.whatcodes=whatcodes  nodes.whatsitcodes=whatcodes @@ -4361,6 +4382,7 @@ function constructors.scale(tfmdata,specification)    local hdelta=delta    local vdelta=delta    target.designsize=parameters.designsize  +  target.units=units    target.units_per_em=units    local direction=properties.direction or tfmdata.direction or 0     target.direction=direction @@ -4472,21 +4494,28 @@ function constructors.scale(tfmdata,specification)      target.nomath=true      target.mathparameters=nil     end -  local italickey="italic" -  local useitalics=true    if hasmath then -    autoitalicamount=false  -  elseif properties.textitalics then -    italickey="italic_correction" -    useitalics=false -    if properties.delaytextitalics then +    local mathitalics=properties.mathitalics +    if mathitalics==false then +      if trace_defining then +        report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename) +      end +      hasitalics=false +      autoitalicamount=false +    end +  else +    local textitalics=properties.textitalics +    if textitalics==false then +      if trace_defining then +        report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename) +      end +      hasitalics=false        autoitalicamount=false      end    end    if trace_defining then      report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a", -      name,fullname,filename,hdelta,vdelta, -      hasmath and "enabled" or "disabled",useitalics and "enabled" or "disabled") +      name,fullname,filename,hdelta,vdelta,hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")    end    constructors.beforecopyingcharacters(target,tfmdata)    local sharedkerns={} @@ -4584,22 +4613,6 @@ function constructors.scale(tfmdata,specification)          chr.right_protruding=protrusionfactor*width*vr        end      end -    if autoitalicamount then -      local vi=description.italic -      if not vi then -        local vi=description.boundingbox[3]-description.width+autoitalicamount -        if vi>0 then  -          chr[italickey]=vi*hdelta -        end -      elseif vi~=0 then -        chr[italickey]=vi*hdelta -      end -    elseif hasitalics then -      local vi=description.italic -      if vi and vi~=0 then -        chr[italickey]=vi*hdelta -      end -    end      if hasmath then        local vn=character.next        if vn then @@ -4637,7 +4650,7 @@ function constructors.scale(tfmdata,specification)            end          end        end -      local va=character.top_accent +      local va=character.accent        if va then          chr.top_accent=vdelta*va        end @@ -4660,6 +4673,27 @@ function constructors.scale(tfmdata,specification)            chr.mathkern=kerns           end        end +      if hasitalics then +        local vi=character.italic +        if vi and vi~=0 then +          chr.italic=vi*hdelta +        end +      end +    elseif autoitalicamount then  +      local vi=description.italic +      if not vi then +        local vi=description.boundingbox[3]-description.width+autoitalicamount +        if vi>0 then  +          chr.italic=vi*hdelta +        end +      elseif vi~=0 then +        chr.italic=vi*hdelta +      end +    elseif hasitalics then  +      local vi=character.italic +      if vi and vi~=0 then +        chr.italic=vi*hdelta +      end      end      if haskerns then        local vk=character.kerns @@ -4722,6 +4756,7 @@ function constructors.scale(tfmdata,specification)      end      targetcharacters[unicode]=chr    end +  properties.setitalics=hasitalics    constructors.aftercopyingcharacters(target,tfmdata)    constructors.trytosharefont(target,tfmdata)    return target @@ -4762,11 +4797,20 @@ function constructors.finalize(tfmdata)    if not parameters.slantfactor then      parameters.slantfactor=tfmdata.slant or 0    end -  if not parameters.designsize then -    parameters.designsize=tfmdata.designsize or (factors.pt*10) +  local designsize=parameters.designsize +  if designsize then +    parameters.minsize=tfmdata.minsize or designsize +    parameters.maxsize=tfmdata.maxsize or designsize +  else +    designsize=factors.pt*10 +    parameters.designsize=designsize +    parameters.minsize=designsize +    parameters.maxsize=designsize    end +  parameters.minsize=tfmdata.minsize or parameters.designsize +  parameters.maxsize=tfmdata.maxsize or parameters.designsize    if not parameters.units then -    parameters.units=tfmdata.units_per_em or 1000 +    parameters.units=tfmdata.units or tfmdata.units_per_em or 1000    end    if not tfmdata.descriptions then      local descriptions={}  @@ -4829,6 +4873,7 @@ function constructors.finalize(tfmdata)    tfmdata.auto_protrude=nil    tfmdata.extend=nil    tfmdata.slant=nil +  tfmdata.units=nil    tfmdata.units_per_em=nil    tfmdata.cache=nil    properties.finalized=true @@ -5393,24 +5438,13 @@ local fonts=fonts or {}  local mappings=fonts.mappings or {}  fonts.mappings=mappings  local allocate=utilities.storage.allocate -local function loadlumtable(filename)  -  local lumname=file.replacesuffix(file.basename(filename),"lum") -  local lumfile=resolvers.findfile(lumname,"map") or "" -  if lumfile~="" and lfs.isfile(lumfile) then -    if trace_loading or trace_mapping then -      report_fonts("loading map table %a",lumfile) -    end -    lumunic=dofile(lumfile) -    return lumunic,lumfile -  end -end  local hex=R("AF","09") -local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end -local hexsix=(hex*hex*hex*hex*hex*hex)/function(s) return tonumber(s,16) end +local hexfour=(hex*hex*hex^-2)/function(s) return tonumber(s,16) end +local hexsix=(hex*hex*hex^-4)/function(s) return tonumber(s,16) end  local dec=(R("09")^1)/tonumber  local period=P(".") -local unicode=P("uni")*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true)) -local ucode=P("u")*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true)) +local unicode=(P("uni")+P("UNI"))*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true))  +local ucode=(P("u")+P("U") )*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true))   local index=P("index")*dec*Cc(false)  local parser=unicode+ucode+index  local parsers={} @@ -5485,7 +5519,6 @@ local function fromunicode16(str)      return (tonumber(l,16))*0x400+tonumber(r,16)-0xDC00    end  end -mappings.loadlumtable=loadlumtable  mappings.makenameparser=makenameparser  mappings.tounicode=tounicode  mappings.tounicode16=tounicode16 @@ -5516,244 +5549,162 @@ for k,v in next,overloads do    end  end  mappings.overloads=overloads -function mappings.addtounicode(data,filename) +function mappings.addtounicode(data,filename,checklookups)    local resources=data.resources -  local properties=data.properties -  local descriptions=data.descriptions    local unicodes=resources.unicodes -  local lookuptypes=resources.lookuptypes    if not unicodes then      return    end +  local properties=data.properties +  local descriptions=data.descriptions    unicodes['space']=unicodes['space'] or 32    unicodes['hyphen']=unicodes['hyphen'] or 45    unicodes['zwj']=unicodes['zwj']  or 0x200D    unicodes['zwnj']=unicodes['zwnj']  or 0x200C -  local private=fonts.constructors.privateoffset -  local unicodevector=fonts.encodings.agl.unicodes +  local private=fonts.constructors and fonts.constructors.privateoffset or 0xF0000  +  local unicodevector=fonts.encodings.agl.unicodes or {}  +  local contextvector=fonts.encodings.agl.ctxcodes or {}     local missing={} -  local lumunic,uparser,oparser -  local cidinfo,cidnames,cidcodes,usedmap -  cidinfo=properties.cidinfo -  usedmap=cidinfo and fonts.cid.getmap(cidinfo) +  local nofmissing=0 +  local oparser=nil +  local cidnames=nil +  local cidcodes=nil +  local cidinfo=properties.cidinfo +  local usedmap=cidinfo and fonts.cid.getmap(cidinfo) +  local uparser=makenameparser()     if usedmap then -    oparser=usedmap and makenameparser(cidinfo.ordering) -    cidnames=usedmap.names -    cidcodes=usedmap.unicodes +     oparser=usedmap and makenameparser(cidinfo.ordering) +     cidnames=usedmap.names +     cidcodes=usedmap.unicodes    end -  uparser=makenameparser() -  local ns,nl=0,0 +  local ns=0 +  local nl=0    for unic,glyph in next,descriptions do -    local index=glyph.index      local name=glyph.name -    local r=overloads[name] -    if r then -      glyph.unicode=r.unicode -    elseif unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then -      local unicode=lumunic and lumunic[name] or unicodevector[name] -      if unicode then -        glyph.unicode=unicode -        ns=ns+1 -      end -      if (not unicode) and usedmap then -        local foundindex=lpegmatch(oparser,name) -        if foundindex then -          unicode=cidcodes[foundindex]  -          if unicode then -            glyph.unicode=unicode -            ns=ns+1 -          else -            local reference=cidnames[foundindex]  -            if reference then -              local foundindex=lpegmatch(oparser,reference) -              if foundindex then -                unicode=cidcodes[foundindex] -                if unicode then -                  glyph.unicode=unicode -                  ns=ns+1 -                end -              end -              if not unicode or unicode=="" then -                local foundcodes,multiple=lpegmatch(uparser,reference) -                if foundcodes then -                  glyph.unicode=foundcodes -                  if multiple then -                    nl=nl+1 -                    unicode=true -                  else +    if name then +      local index=glyph.index +      local r=overloads[name] +      if r then +        glyph.unicode=r.unicode +      elseif not unic or unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then +        local unicode=unicodevector[name] or contextvector[name] +        if unicode then +          glyph.unicode=unicode +          ns=ns+1 +        end +        if (not unicode) and usedmap then +          local foundindex=lpegmatch(oparser,name) +          if foundindex then +            unicode=cidcodes[foundindex]  +            if unicode then +              glyph.unicode=unicode +              ns=ns+1 +            else +              local reference=cidnames[foundindex]  +              if reference then +                local foundindex=lpegmatch(oparser,reference) +                if foundindex then +                  unicode=cidcodes[foundindex] +                  if unicode then +                    glyph.unicode=unicode                      ns=ns+1 -                    unicode=foundcodes +                  end +                end +                if not unicode or unicode=="" then +                  local foundcodes,multiple=lpegmatch(uparser,reference) +                  if foundcodes then +                    glyph.unicode=foundcodes +                    if multiple then +                      nl=nl+1 +                      unicode=true +                    else +                      ns=ns+1 +                      unicode=foundcodes +                    end                    end                  end                end              end            end          end -      end -      if not unicode or unicode=="" then -        local split=lpegmatch(namesplitter,name) -        local nsplit=split and #split or 0 -        local t,n={},0 -        unicode=true -        for l=1,nsplit do -          local base=split[l] -          local u=unicodes[base] or unicodevector[base] -          if not u then -            break -          elseif type(u)=="table" then -            if u[1]>=private then -              unicode=false -              break +        if not unicode or unicode=="" then +          local split=lpegmatch(namesplitter,name) +          local nsplit=split and #split or 0  +          if nsplit==0 then +          elseif nsplit==1 then +            local base=split[1] +            local u=unicodes[base] or unicodevector[base] or contextvector[name] +            if not u then +            elseif type(u)=="table" then +              if u[1]<private then +                unicode=u +                glyph.unicode=unicode +              end +            elseif u<private then +              unicode=u +              glyph.unicode=unicode              end -            n=n+1 -            t[n]=u[1]            else -            if u>=private then -              unicode=false -              break -            end -            n=n+1 -            t[n]=u -          end -        end -        if n==0 then -        elseif n==1 then -          glyph.unicode=t[1] -        else -          glyph.unicode=t -        end -        nl=nl+1 -      end -      if not unicode or unicode=="" then -        local foundcodes,multiple=lpegmatch(uparser,name) -        if foundcodes then -          glyph.unicode=foundcodes -          if multiple then -            nl=nl+1 -            unicode=true -          else -            ns=ns+1 -            unicode=foundcodes -          end -        end -      end -      local r=overloads[unicode] -      if r then -        unicode=r.unicode -        glyph.unicode=unicode -      end -      if not unicode then -        missing[name]=true -      end -    end -  end -  if next(missing) then -    local guess={} -    local function check(gname,code,unicode) -      local description=descriptions[code] -      local variant=description.name -      if variant==gname then -        return -      end -      local unic=unicodes[variant] -      if unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then -      else -        return -      end -      if descriptions[code].unicode then -        return -      end -      local g=guess[variant] -      if g then -        g[gname]=unicode -      else -        guess[variant]={ [gname]=unicode } -      end -    end -    for unicode,description in next,descriptions do -      local slookups=description.slookups -      if slookups then -        local gname=description.name -        for tag,data in next,slookups do -          local lookuptype=lookuptypes[tag] -          if lookuptype=="alternate" then -            for i=1,#data do -              check(gname,data[i],unicode) -            end -          elseif lookuptype=="substitution" then -            check(gname,data,unicode) -          end -        end -      end -      local mlookups=description.mlookups -      if mlookups then -        local gname=description.name -        for tag,list in next,mlookups do -          local lookuptype=lookuptypes[tag] -          if lookuptype=="alternate" then -            for i=1,#list do -              local data=list[i] -              for i=1,#data do -                check(gname,data[i],unicode) +            local t,n={},0 +            for l=1,nsplit do +              local base=split[l] +              local u=unicodes[base] or unicodevector[base] or contextvector[name] +              if not u then +                break +              elseif type(u)=="table" then +                if u[1]>=private then +                  break +                end +                n=n+1 +                t[n]=u[1] +              else +                if u>=private then +                  break +                end +                n=n+1 +                t[n]=u                end              end -          elseif lookuptype=="substitution" then -            for i=1,#list do -              check(gname,list[i],unicode) +            if n>0 then +              if n==1 then +                unicode=t[1] +              else +                unicode=t +              end +              glyph.unicode=unicode              end            end -        end -      end -    end -    local done=true -    while done do -      done=false -      for k,v in next,guess do -        if type(v)~="number" then -          for kk,vv in next,v do -            if vv==-1 or vv>=private or (vv>=0xE000 and vv<=0xF8FF) or vv==0xFFFE or vv==0xFFFF then -              local uu=guess[kk] -              if type(uu)=="number" then -                guess[k]=uu -                done=true -              end +          nl=nl+1 +        end +        if not unicode or unicode=="" then +          local foundcodes,multiple=lpegmatch(uparser,name) +          if foundcodes then +            glyph.unicode=foundcodes +            if multiple then +              nl=nl+1 +              unicode=true              else -              guess[k]=vv -              done=true +              ns=ns+1 +              unicode=foundcodes              end            end          end -      end -    end -    local orphans=0 -    local guessed=0 -    for k,v in next,guess do -      if type(v)=="number" then -        descriptions[unicodes[k]].unicode=descriptions[v].unicode or v  -        guessed=guessed+1 -      else -        local t=nil -        local l=lower(k) -        local u=unicodes[l] -        if not u then -          orphans=orphans+1 -        elseif u==-1 or u>=private or (u>=0xE000 and u<=0xF8FF) or u==0xFFFE or u==0xFFFF then -          local unicode=descriptions[u].unicode -          if unicode then -            descriptions[unicodes[k]].unicode=unicode -            guessed=guessed+1 -          else -            orphans=orphans+1 -          end -        else -          orphans=orphans+1 +        local r=overloads[unicode] +        if r then +          unicode=r.unicode +          glyph.unicode=unicode +        end +        if not unicode then +          missing[unic]=true +          nofmissing=nofmissing+1          end        end -    end -    if trace_loading and orphans>0 or guessed>0 then -      report_fonts("%s glyphs with no related unicode, %s guessed, %s orphans",guessed+orphans,guessed,orphans) +    else      end    end +  if type(checklookups)=="function" then +    checklookups(data,missing,nofmissing) +  end    if trace_mapping then      for unic,glyph in table.sortedhash(descriptions) do        local name=glyph.name @@ -5881,6 +5832,7 @@ local readers=fonts.readers  local constructors=fonts.constructors  local encodings=fonts.encodings  local tfm=constructors.newhandler("tfm") +tfm.version=1.000  local tfmfeatures=constructors.newfeatures("tfm")  local registertfmfeature=tfmfeatures.register  constructors.resolvevirtualtoo=false  @@ -6067,7 +6019,7 @@ local keys={}  function keys.FontName  (data,line) data.metadata.fontname=strip  (line)                      data.metadata.fullname=strip  (line) end  function keys.ItalicAngle (data,line) data.metadata.italicangle=tonumber (line) end -function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch=toboolean(line,true) end +function keys.IsFixedPitch(data,line) data.metadata.monospaced=toboolean(line,true) end  function keys.CharWidth  (data,line) data.metadata.charwidth=tonumber (line) end  function keys.XHeight   (data,line) data.metadata.xheight=tonumber (line) end  function keys.Descender  (data,line) data.metadata.descender=tonumber (line) end @@ -6489,7 +6441,7 @@ local function copytotfm(data)      local emdash=0x2014      local spacer="space"      local spaceunits=500 -    local monospaced=metadata.isfixedpitch +    local monospaced=metadata.monospaced      local charwidth=metadata.charwidth      local italicangle=metadata.italicangle      local charxheight=metadata.xheight and metadata.xheight>0 and metadata.xheight @@ -7144,12 +7096,10 @@ if not modules then modules={} end modules ['font-otf']={    license="see context related readme files"  }  local utfbyte=utf.byte -local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip +local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip  local type,next,tonumber,tostring=type,next,tonumber,tostring  local abs=math.abs -local insert=table.insert -local lpegmatch=lpeg.match -local reversed,concat,remove,sortedkeys=table.reversed,table.concat,table.remove,table.sortedkeys +local reversed,concat,insert,remove,sortedkeys=table.reversed,table.concat,table.insert,table.remove,table.sortedkeys  local ioflush=io.flush  local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive  local formatters=string.formatters @@ -7176,7 +7126,7 @@ local report_otf=logs.reporter("fonts","otf loading")  local fonts=fonts  local otf=fonts.handlers.otf  otf.glists={ "gsub","gpos" } -otf.version=2.812  +otf.version=2.819   otf.cache=containers.define("fonts","otf",otf.version,true)  local hashes=fonts.hashes  local definers=fonts.definers @@ -7353,10 +7303,10 @@ local ordered_enhancers={    "reorganize subtables",    "check glyphs",    "check metadata", -  "check extra features",    "prepare tounicode",    "check encoding",    "add duplicates", +  "expand lookups",    "cleanup tables",    "compact lookups",    "purge names", @@ -7493,6 +7443,7 @@ function otf.load(filename,sub,featurefile)      end     end     if reload then +    starttiming("fontloader")      report_otf("loading %a, hash %a",filename,hash)      local fontdata,messages      if sub then @@ -7526,6 +7477,7 @@ function otf.load(filename,sub,featurefile)        data={          size=size,          time=time, +        subfont=sub,          format=otf_format(filename),          featuredata=featurefiles,          resources={ @@ -7553,7 +7505,6 @@ function otf.load(filename,sub,featurefile)            tounicodetable=Ct(splitter),          },        } -      starttiming(data)        report_otf("file size: %s",size)        enhancers.apply(data,filename,fontdata)        local packtime={} @@ -7570,10 +7521,10 @@ function otf.load(filename,sub,featurefile)        if cleanup>1 then          collectgarbage("collect")        end -      stoptiming(data) +      stoptiming("fontloader")        if elapsedtime then  -        report_otf("preprocessing and caching time %s, packtime %s", -          elapsedtime(data),packdata and elapsedtime(packtime) or 0) +        report_otf("loading, optimizing, packing and caching time %s, pack time %s", +          elapsedtime("fontloader"),packdata and elapsedtime(packtime) or 0)        end        close_font(fontdata)         if cleanup>3 then @@ -7584,6 +7535,7 @@ function otf.load(filename,sub,featurefile)          collectgarbage("collect")        end      else +      stoptiming("fontloader")        data=nil        report_otf("loading failed due to read error")      end @@ -7625,6 +7577,7 @@ function otf.load(filename,sub,featurefile)        applyruntimefixes(filename,data)      end      enhance("add dimensions",data,filename,nil,false) +enhance("check extra features",data,filename)      if trace_sequences then        showfeatureorder(data,filename)      end @@ -7785,7 +7738,7 @@ actions["prepare glyphs"]=function(data,filename,raw)                  end                  if not unicode or unicode==-1 then                     if not name then -                    name=format("u%06X.ctx",private) +                    name=formatters["u%06X.ctx"](private)                    end                    unicode=private                    unicodes[name]=private @@ -7796,7 +7749,7 @@ actions["prepare glyphs"]=function(data,filename,raw)                    nofnames=nofnames+1                  else                    if not name then -                    name=format("u%06X.ctx",unicode) +                    name=formatters["u%06X.ctx"](unicode)                    end                    unicodes[name]=unicode                    nofunicodes=nofunicodes+1 @@ -7810,25 +7763,25 @@ actions["prepare glyphs"]=function(data,filename,raw)                    glyph=glyph,                  }                  descriptions[unicode]=description -local altuni=glyph.altuni -if altuni then -  for i=1,#altuni do -    local a=altuni[i] -    local u=a.unicode -    if u~=unicode then -      local v=a.variant -      if v then -        local vv=variants[v] -        if vv then -          vv[u]=unicode -        else  -          vv={ [u]=unicode } -          variants[v]=vv -        end -      end -    end -  end -end +                local altuni=glyph.altuni +                if altuni then +                  for i=1,#altuni do +                    local a=altuni[i] +                    local u=a.unicode +                    if u~=unicode then +                      local v=a.variant +                      if v then +                        local vv=variants[v] +                        if vv then +                          vv[u]=unicode +                        else  +                          vv={ [u]=unicode } +                          variants[v]=vv +                        end +                      end +                    end +                  end +                end                end              end            else @@ -8014,7 +7967,7 @@ actions["add duplicates"]=function(data,filename,raw)            end            if u>0 then               local duplicate=table.copy(description)  -            duplicate.comment=format("copy of U+%05X",unicode) +            duplicate.comment=formatters["copy of %U"](unicode)              descriptions[u]=duplicate              if trace_loading then                report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n) @@ -8035,7 +7988,7 @@ actions["analyze glyphs"]=function(data,filename,raw)    local marks={}     for unicode,description in next,descriptions do      local glyph=description.glyph -    local italic=glyph.italic_correction +    local italic=glyph.italic_correction       if not italic then      elseif italic==0 then      else @@ -8096,7 +8049,8 @@ end  actions["reorganize features"]=function(data,filename,raw)     local features={}    data.resources.features=features -  for k,what in next,otf.glists do +  for k=1,#otf.glists do +    local what=otf.glists[k]      local dw=raw[what]      if dw then        local f={} @@ -8178,8 +8132,9 @@ actions["reorganize subtables"]=function(data,filename,raw)    local lookups={}    local chainedfeatures={}    resources.sequences=sequences -  resources.lookups=lookups -  for _,what in next,otf.glists do +  resources.lookups=lookups  +  for k=1,#otf.glists do +    local what=otf.glists[k]      local dw=raw[what]      if dw then        for k=1,#dw do @@ -8353,12 +8308,15 @@ local function r_uncover(splitter,cache,cover,replacements)  end  actions["reorganize lookups"]=function(data,filename,raw)    if data.lookups then -    local splitter=data.helpers.tounicodetable +    local helpers=data.helpers +    local duplicates=data.resources.duplicates +    local splitter=helpers.tounicodetable      local t_u_cache={}      local s_u_cache=t_u_cache       local t_h_cache={}      local s_h_cache=t_h_cache       local r_u_cache={}  +    helpers.matchcache=t_h_cache      for _,lookup in next,data.lookups do        local rules=lookup.rules        if rules then @@ -8504,6 +8462,44 @@ actions["reorganize lookups"]=function(data,filename,raw)      end    end  end +actions["expand lookups"]=function(data,filename,raw)  +  if data.lookups then +    local cache=data.helpers.matchcache +    if cache then +      local duplicates=data.resources.duplicates +      for key,hash in next,cache do +        local done=nil +        for key in next,hash do +          local unicode=duplicates[key] +          if not unicode then +          elseif type(unicode)=="table" then +            for i=1,#unicode do +              local u=unicode[i] +              if hash[u] then +              elseif done then +                done[u]=key +              else +                done={ [u]=key } +              end +            end +          else +            if hash[unicode] then +            elseif done then +              done[unicode]=key +            else +              done={ [unicode]=key } +            end +          end +        end +        if done then +          for u in next,done do +            hash[u]=true +          end +        end +      end +    end +  end +end  local function check_variants(unicode,the_variants,splitter,unicodes)    local variants=the_variants.variants    if variants then  @@ -8544,11 +8540,11 @@ local function check_variants(unicode,the_variants,splitter,unicodes)        parts=nil      end    end -  local italic_correction=the_variants.italic_correction -  if italic_correction and italic_correction==0 then -    italic_correction=nil +  local italic=the_variants.italic +  if italic and italic==0 then +    italic=nil    end -  return variants,parts,italic_correction +  return variants,parts,italic  end  actions["analyze math"]=function(data,filename,raw)    if raw.math then @@ -8558,13 +8554,14 @@ actions["analyze math"]=function(data,filename,raw)      for unicode,description in next,data.descriptions do        local glyph=description.glyph        local mathkerns=glyph.mathkern  -      local horiz_variants=glyph.horiz_variants -      local vert_variants=glyph.vert_variants -      local top_accent=glyph.top_accent -      if mathkerns or horiz_variants or vert_variants or top_accent then +      local hvariants=glyph.horiz_variants +      local vvariants=glyph.vert_variants +      local accent=glyph.top_accent +      local italic=glyph.italic_correction +      if mathkerns or hvariants or vvariants or accent or italic then          local math={} -        if top_accent then -          math.top_accent=top_accent +        if accent then +          math.accent=accent          end          if mathkerns then            for k,v in next,mathkerns do @@ -8580,15 +8577,14 @@ actions["analyze math"]=function(data,filename,raw)            end            math.kerns=mathkerns          end -        if horiz_variants then -          math.horiz_variants,math.horiz_parts,math.horiz_italic_correction=check_variants(unicode,horiz_variants,splitter,unicodes) +        if hvariants then +          math.hvariants,math.hparts,math.hitalic=check_variants(unicode,hvariants,splitter,unicodes)          end -        if vert_variants then -          math.vert_variants,math.vert_parts,math.vert_italic_correction=check_variants(unicode,vert_variants,splitter,unicodes) +        if vvariants then +          math.vvariants,math.vparts,math.vitalic=check_variants(unicode,vvariants,splitter,unicodes)          end -        local italic_correction=description.italic -        if italic_correction and italic_correction~=0 then -          math.italic_correction=italic_correction +        if italic and italic~=0 then +          math.italic=italic          end          description.math=math        end @@ -8745,7 +8741,7 @@ actions["merge kern classes"]=function(data,filename,raw)        report_otf("%s kern overloads ignored",ignored)      end      if blocked>0 then -      report_otf("%s succesive kerns blocked",blocked) +      report_otf("%s successive kerns blocked",blocked)      end    end  end @@ -8774,16 +8770,18 @@ actions["check metadata"]=function(data,filename,raw)        ttftables[i].data="deleted"      end    end +  local names=raw.names    if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then      local function valid(what) -      local names=raw.names -      for i=1,#names do -        local list=names[i] -        local names=list.names -        if names then -          local name=names[what] -          if name and valid_ps_name(name) then -            return name +      if names then +        for i=1,#names do +          local list=names[i] +          local names=list.names +          if names then +            local name=names[what] +            if name and valid_ps_name(name) then +              return name +            end            end          end        end @@ -8806,6 +8804,28 @@ actions["check metadata"]=function(data,filename,raw)      check("fontname")      check("fullname")    end +  if names then +    local psname=metadata.psname +    if not psname or psname=="" then +      for i=1,#names do +        local name=names[i] +        if lower(name.lang)=="english (us)" then +          local specification=name.names +          if specification then +            local postscriptname=specification.postscriptname +            if postscriptname then +              psname=postscriptname +            end +          end +        end +        break +      end +    end +    if psname~=metadata.fontname then +      report_otf("fontname %a, fullname %a, psname %a",metadata.fontname,metadata.fullname,psname) +    end +    metadata.psname=psname +  end  end  actions["cleanup tables"]=function(data,filename,raw)    local duplicates=data.resources.duplicates @@ -8901,7 +8921,7 @@ actions["reorganize glyph lookups"]=function(data,filename,raw)    end  end  local zero={ 0,0 } -actions["reorganize glyph anchors"]=function(data,filename,raw)  +actions["reorganize glyph anchors"]=function(data,filename,raw)    local descriptions=data.descriptions    for unicode,description in next,descriptions do      local anchors=description.glyph.anchors @@ -9103,9 +9123,13 @@ local function copytotfm(data,cache_id)      local spaceunits=500      local spacer="space"      local designsize=metadata.designsize or metadata.design_size or 100 +    local minsize=metadata.minsize or metadata.design_range_bottom or designsize +    local maxsize=metadata.maxsize or metadata.design_range_top  or designsize      local mathspecs=metadata.math      if designsize==0 then        designsize=100 +      minsize=100 +      maxsize=100      end      if mathspecs then        for name,value in next,mathspecs do @@ -9120,8 +9144,9 @@ local function copytotfm(data,cache_id)          local d=descriptions[unicode]          local m=d.math          if m then -          local variants=m.horiz_variants -          local parts=m.horiz_parts +          local italic=m.italic +          local variants=m.hvariants +          local parts=m.hparts            if variants then              local c=character              for i=1,#variants do @@ -9132,9 +9157,10 @@ local function copytotfm(data,cache_id)              c.horiz_variants=parts            elseif parts then              character.horiz_variants=parts +            italic=m.hitalic            end -          local variants=m.vert_variants -          local parts=m.vert_parts +          local variants=m.vvariants +          local parts=m.vparts            if variants then              local c=character              for i=1,#variants do @@ -9145,14 +9171,14 @@ local function copytotfm(data,cache_id)              c.vert_variants=parts            elseif parts then              character.vert_variants=parts +            italic=m.vitalic            end -          local italic_correction=m.vert_italic_correction -          if italic_correction then -            character.vert_italic_correction=italic_correction  +          if italic and italic~=0 then +            character.italic=italic             end -          local top_accent=m.top_accent -          if top_accent then -            character.top_accent=top_accent +          local accent=m.accent +          if accent then +            character.accent=accent            end            local kerns=m.kerns            if kerns then @@ -9164,14 +9190,14 @@ local function copytotfm(data,cache_id)      local filename=constructors.checkedfilename(resources)      local fontname=metadata.fontname      local fullname=metadata.fullname or fontname -    local psname=fontname or fullname -    local units=metadata.units_per_em or 1000 +    local psname=metadata.psname or fontname or fullname +    local units=metadata.units or metadata.units_per_em or 1000      if units==0 then         units=1000  -      metadata.units_per_em=1000 +      metadata.units=1000        report_otf("changing %a units to %a",0,units)      end -    local monospaced=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced") +    local monospaced=metadata.monospaced or metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced")      local charwidth=pfminfo.avgwidth       local charxheight=pfminfo.os2_xheight and pfminfo.os2_xheight>0 and pfminfo.os2_xheight      local italicangle=metadata.italicangle @@ -9236,8 +9262,10 @@ local function copytotfm(data,cache_id)        end      end      parameters.designsize=(designsize/10)*65536 -    parameters.ascender=abs(metadata.ascent or 0) -    parameters.descender=abs(metadata.descent or 0) +    parameters.minsize=(minsize/10)*65536 +    parameters.maxsize=(maxsize/10)*65536 +    parameters.ascender=abs(metadata.ascender or metadata.ascent or 0) +    parameters.descender=abs(metadata.descender or metadata.descent or 0)      parameters.units=units      properties.space=spacer      properties.encodingbytes=2 @@ -9416,6 +9444,99 @@ function otf.scriptandlanguage(tfmdata,attr)    local properties=tfmdata.properties    return properties.script or "dflt",properties.language or "dflt"  end +local function justset(coverage,unicode,replacement) +  coverage[unicode]=replacement +end +otf.coverup={ +  stepkey="subtables", +  actions={ +    substitution=justset, +    alternate=justset, +    multiple=justset, +    ligature=justset, +    kern=justset, +  }, +  register=function(coverage,lookuptype,format,feature,n,descriptions,resources) +    local name=formatters["ctx_%s_%s"](feature,n) +    if lookuptype=="kern" then +      resources.lookuptypes[name]="position" +    else +      resources.lookuptypes[name]=lookuptype +    end +    for u,c in next,coverage do +      local description=descriptions[u] +      local slookups=description.slookups +      if slookups then +        slookups[name]=c +      else +        description.slookups={ [name]=c } +      end +    end +    return name +  end +} +local function getgsub(tfmdata,k,kind) +  local description=tfmdata.descriptions[k] +  if description then +    local slookups=description.slookups  +    if slookups then +      local shared=tfmdata.shared +      local rawdata=shared and shared.rawdata +      if rawdata then +        local lookuptypes=rawdata.resources.lookuptypes +        if lookuptypes then +          local properties=tfmdata.properties +          local validlookups,lookuplist=otf.collectlookups(rawdata,kind,properties.script,properties.language) +          if validlookups then +            for l=1,#lookuplist do +              local lookup=lookuplist[l] +              local found=slookups[lookup] +              if found then +                return found,lookuptypes[lookup] +              end +            end +          end +        end +      end +    end +  end +end +otf.getgsub=getgsub  +function otf.getsubstitution(tfmdata,k,kind,value) +  local found,kind=getgsub(tfmdata,k,kind) +  if not found then +  elseif kind=="substitution" then +    return found +  elseif kind=="alternate" then +    local choice=tonumber(value) or 1  +    return found[choice] or found[1] or k +  end +  return k +end +otf.getalternate=otf.getsubstitution +function otf.getmultiple(tfmdata,k,kind) +  local found,kind=getgsub(tfmdata,k,kind) +  if found and kind=="multiple" then +    return found +  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 @@ -9946,8 +10067,8 @@ local function featuresinitializer(tfmdata,value)        local collectlookups=otf.collectlookups        local rawdata=tfmdata.shared.rawdata        local properties=tfmdata.properties -      local script=properties.script -      local language=properties.language +      local script=properties.script   +      local language=properties.language         local basesubstitutions=rawdata.resources.features.gsub        local basepositionings=rawdata.resources.features.gpos        if basesubstitutions or basepositionings then @@ -10125,7 +10246,8 @@ end  function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext)     local dx=factor*(exit[1]-entry[1])    local dy=-factor*(exit[2]-entry[2]) -  local ws,wn=tfmstart.width,tfmnext.width +  local ws=tfmstart.width +  local wn=tfmnext.width    nofregisteredcursives=nofregisteredcursives+1    if rlmode<0 then      dx=-(dx+wn) @@ -10172,7 +10294,10 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne    return dx,dy,nofregisteredcursives  end  function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)  -  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       local yoffset=y-h      local leftkern=x    @@ -10182,9 +10307,12 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)        if rlmode and rlmode<0 then          leftkern,rightkern=rightkern,leftkern        end +      if not injection then +        injection="injections" +      end        local p=rawget(properties,current)        if p then -        local i=rawget(p,"injections") +        local i=rawget(p,injection)          if i then            if leftkern~=0 then              i.leftkern=(i.leftkern or 0)+leftkern @@ -10196,19 +10324,19 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)              i.yoffset=(i.yoffset or 0)+yoffset            end          elseif leftkern~=0 or rightkern~=0 then -          p.injections={ +          p[injection]={              leftkern=leftkern,              rightkern=rightkern,              yoffset=yoffset,            }          else -          p.injections={ +          p[injection]={              yoffset=yoffset,            }          end        elseif leftkern~=0 or rightkern~=0 then          properties[current]={ -          injections={ +          [injection]={              leftkern=leftkern,              rightkern=rightkern,              yoffset=yoffset, @@ -10216,7 +10344,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)          }        else          properties[current]={ -          injections={ +          [injection]={              yoffset=yoffset,            },          } @@ -10255,7 +10383,7 @@ function injections.setkern(current,factor,rlmode,x,injection)      return 0,0    end  end -function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)  +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk)     local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])    nofregisteredmarks=nofregisteredmarks+1    if rlmode>=0 then @@ -10265,11 +10393,15 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)    if p then      local i=rawget(p,"injections")      if i then -      i.markx=dx -      i.marky=dy -      i.markdir=rlmode or 0 -      i.markbase=nofregisteredmarks -      i.markbasenode=base +      if i.markmark then +      else +        i.markx=dx +        i.marky=dy +        i.markdir=rlmode or 0 +        i.markbase=nofregisteredmarks +        i.markbasenode=base +        i.markmark=mkmk +      end      else        p.injections={          markx=dx, @@ -10277,6 +10409,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)          markdir=rlmode or 0,          markbase=nofregisteredmarks,          markbasenode=base, +        markmark=mkmk,        }      end    else @@ -10287,6 +10420,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)          markdir=rlmode or 0,          markbase=nofregisteredmarks,          markbasenode=base, +        markmark=mkmk,        },      }    end @@ -10391,27 +10525,33 @@ local function show_result(head)      current=getnext(current)    end  end -local function collect_glyphs_1(head) -  local glyphs,nofglyphs={},0 -  local marks,nofmarks={},0 +local function collect_glyphs(head,offsets) +  local glyphs,glyphi,nofglyphs={},{},0 +  local marks,marki,nofmarks={},{},0    local nf,tm=nil,nil -  for n in traverse_id(glyph_code,head) do  -    if getsubtype(n)<256 then -      local f=getfont(n) -      if f~=nf then -        nf=f -        tm=fontdata[nf].resources.marks  -      end -      if tm and tm[getchar(n)] then -        nofmarks=nofmarks+1 -        marks[nofmarks]=n -      else -        nofglyphs=nofglyphs+1 -        glyphs[nofglyphs]=n -      end +  local n=head +  local function identify(n,what) +    local f=getfont(n) +    if f~=nf then +      nf=f +      tm=fontdata[nf].resources +      if tm then +        tm=tm.marks +      end +    end +    if tm and tm[getchar(n)] then +      nofmarks=nofmarks+1 +      marks[nofmarks]=n +      marki[nofmarks]="injections" +    else +      nofglyphs=nofglyphs+1 +      glyphs[nofglyphs]=n +      glyphi[nofglyphs]=what +    end +    if offsets then        local p=rawget(properties,n)        if p then -        local i=rawget(p,"injections") +        local i=rawget(p,what)          if i then            local yoffset=i.yoffset            if yoffset and yoffset~=0 then @@ -10421,36 +10561,47 @@ local function collect_glyphs_1(head)        end      end    end -  return glyphs,nofglyphs,marks,nofmarks -end -local function collect_glyphs_2(head) -  local glyphs,nofglyphs={},0 -  local marks,nofmarks={},0 -  local nf,tm=nil,nil -  for n in traverse_id(glyph_code,head) do -    if getsubtype(n)<256 then -      local f=getfont(n) -      if f~=nf then -        nf=f -        tm=fontdata[nf].resources.marks  -      end -      if tm and tm[getchar(n)] then -        nofmarks=nofmarks+1 -        marks[nofmarks]=n -      else -        nofglyphs=nofglyphs+1 -        glyphs[nofglyphs]=n -      end +  while n do  +    local id=getid(n) +    if id==glyph_code then +      identify(n,"injections") +    elseif id==disc_code then +      local d=getfield(n,"pre") +      if d then +        for n in traverse_id(glyph_code,d) do +          if getsubtype(n)<256 then +            identify(n,"preinjections") +          end +        end +			end +      local d=getfield(n,"post") +      if d then +        for n in traverse_id(glyph_code,d) do +          if getsubtype(n)<256 then +            identify(n,"postinjections") +          end +        end +			end +      local d=getfield(n,"replace") +      if d then +        for n in traverse_id(glyph_code,d) do +          if getsubtype(n)<256 then +            identify(n,"replaceinjections") +          end +        end +			end      end +		n=getnext(n)    end -  return glyphs,nofglyphs,marks,nofmarks +  return glyphs,glyphi,nofglyphs,marks,marki,nofmarks  end -local function inject_marks(marks,nofmarks) +local function inject_marks(marks,marki,nofmarks)    for i=1,nofmarks do      local n=marks[i]      local pn=rawget(properties,n)      if pn then -      pn=rawget(pn,"injections") +      local ni=marki[i] +      local pn=rawget(pn,ni)        if pn then          local p=pn.markbasenode          if p then @@ -10459,7 +10610,7 @@ local function inject_marks(marks,nofmarks)            local rightkern=nil            local pp=rawget(properties,p)            if pp then -            pp=rawget(pp,"injections") +            pp=rawget(pp,ni)              if pp then                rightkern=pp.rightkern              end @@ -10468,11 +10619,17 @@ local function inject_marks(marks,nofmarks)              if pn.markdir<0 then                ox=px-pn.markx-rightkern              else -              local leftkern=pp.leftkern -              if leftkern then -                ox=px-pn.markx +							 +	 +							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-leftkern +                ox=px-pn.markx                end              end            else @@ -10485,12 +10642,7 @@ local function inject_marks(marks,nofmarks)            end            setfield(n,"xoffset",ox)            local py=getfield(p,"yoffset") -          local oy=0 -          if marks[p] then -            oy=py+pn.marky -          else -            oy=getfield(n,"yoffset")+py+pn.marky -          end +          local oy=getfield(n,"yoffset")+py+pn.marky            setfield(n,"yoffset",oy)          else          end @@ -10498,14 +10650,14 @@ local function inject_marks(marks,nofmarks)      end    end  end -local function inject_cursives(glyphs,nofglyphs) +local function inject_cursives(glyphs,glyphi,nofglyphs)    local cursiveanchor,lastanchor=nil,nil    local minc,maxc,last=0,0,nil    for i=1,nofglyphs do      local n=glyphs[i]      local pn=rawget(properties,n)      if pn then -      pn=rawget(pn,"injections") +      pn=rawget(pn,glyphi[i])      end      if pn then        local cursivex=pn.cursivex @@ -10571,22 +10723,59 @@ local function inject_cursives(glyphs,nofglyphs)      end    end  end -local function inject_kerns(head,list,length) +local function inject_kerns(head,glist,ilist,length)     for i=1,length do -    local n=list[i] +    local n=glist[i]      local pn=rawget(properties,n)      if pn then -      local i=rawget(pn,"injections") -      if i then -        local leftkern=i.leftkern -        if leftkern and leftkern~=0 then -          insert_node_before(head,n,newkern(leftkern))  -        end -        local rightkern=i.rightkern -        if rightkern and rightkern~=0 then -          insert_node_after(head,n,newkern(rightkern))  -        end -      end +			local dp=nil +			local dr=nil +      local ni=ilist[i] +      local p=nil +			if ni=="injections" then +				p=getprev(n) +				if p then +					local id=getid(p) +					if id==disc_code then +						dp=getfield(p,"post") +						dr=getfield(p,"replace") +					end +				end +			end +			if dp then +				local i=rawget(pn,"postinjections") +				if i then +					local leftkern=i.leftkern +					if leftkern and leftkern~=0 then +						local t=find_tail(dp) +						insert_node_after(dp,t,newkern(leftkern)) +            setfield(p,"post",dp)  +					end +				end +			end +			if dr then +				local i=rawget(pn,"replaceinjections") +				if i then +					local leftkern=i.leftkern +					if leftkern and leftkern~=0 then +						local t=find_tail(dr) +						insert_node_after(dr,t,newkern(leftkern)) +            setfield(p,"replace",dr)  +					end +				end +			else +				local i=rawget(pn,ni) +				if i then +					local leftkern=i.leftkern +					if leftkern and leftkern~=0 then +						insert_node_before(head,n,newkern(leftkern))  +					end +					local rightkern=i.rightkern +					if rightkern and rightkern~=0 then +						insert_node_after(head,n,newkern(rightkern))  +					end +				end +			end      end    end  end @@ -10595,23 +10784,18 @@ local function inject_everything(head,where)    if trace_injections then      trace(head,"everything")    end -  local glyphs,nofglyphs,marks,nofmarks -  if nofregisteredpairs>0 then -    glyphs,nofglyphs,marks,nofmarks=collect_glyphs_1(head) -  else -    glyphs,nofglyphs,marks,nofmarks=collect_glyphs_2(head) -  end +  local glyphs,glyphi,nofglyphs,marks,marki,nofmarks=collect_glyphs(head,nofregisteredpairs>0)    if nofglyphs>0 then      if nofregisteredcursives>0 then -      inject_cursives(glyphs,nofglyphs) +      inject_cursives(glyphs,glyphi,nofglyphs)      end      if nofregisteredmarks>0 then  -      inject_marks(marks,nofmarks) +      inject_marks(marks,marki,nofmarks)      end -    inject_kerns(head,glyphs,nofglyphs) +    inject_kerns(head,glyphs,glyphi,nofglyphs)    end    if nofmarks>0 then -    inject_kerns(head,marks,nofmarks) +    inject_kerns(head,marks,marki,nofmarks)  	end    if keepregisteredcounts then      keepregisteredcounts=false @@ -10629,7 +10813,7 @@ local function inject_kerns_only(head,where)      trace(head,"kerns")    end    local n=head -  local p=nil +  local p=nil     while n do      local id=getid(n)      if id==glyph_code then @@ -10645,6 +10829,7 @@ local function inject_kerns_only(head,where)                  if leftkern and leftkern~=0 then                    local t=find_tail(d)                    insert_node_after(d,t,newkern(leftkern)) +                  setfield(p,"post",d)                   end                end              end @@ -10656,6 +10841,7 @@ local function inject_kerns_only(head,where)                  if leftkern and leftkern~=0 then                    local t=find_tail(d)                    insert_node_after(d,t,newkern(leftkern)) +                  setfield(p,"replace",d)                   end                end              else @@ -10677,8 +10863,6 @@ local function inject_kerns_only(head,where)              end            end          end -      else -        break        end        p=nil      elseif id==disc_code then @@ -10733,7 +10917,7 @@ local function inject_kerns_only(head,where)          local h=d          for n in traverse_id(glyph_code,d) do            if getsubtype(n)<256 then -            local pn=rawget(properties,n)  +            local pn=rawget(properties,n)              if pn then                local i=rawget(pn,"replaceinjections")                if i then @@ -10770,7 +10954,7 @@ local function inject_pairs_only(head,where)      trace(head,"pairs")    end    local n=head -  local p=nil +  local p=nil     while n do      local id=getid(n)      if id==glyph_code then @@ -10786,6 +10970,7 @@ local function inject_pairs_only(head,where)                  if leftkern and leftkern~=0 then                    local t=find_tail(d)                    insert_node_after(d,t,newkern(leftkern)) +                  setfield(p,"post",d)                   end                end              end @@ -10797,6 +10982,7 @@ local function inject_pairs_only(head,where)                  if leftkern and leftkern~=0 then                    local t=find_tail(d)                    insert_node_after(d,t,newkern(leftkern)) +                  setfield(p,"replace",d)                   end                end              else @@ -10811,24 +10997,22 @@ local function inject_pairs_only(head,where)            else              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)                 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 @@ -10837,16 +11021,12 @@ local function inject_pairs_only(head,where)          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 @@ -10854,6 +11034,10 @@ local function inject_pairs_only(head,where)                    insert_node_after(head,n,newkern(rightkern))                    n=getnext(n)                   end +                local yoffset=i.yoffset +                if yoffset and yoffset~=0 then +                  setfield(n,"yoffset",yoffset) +                end                end              end            else @@ -10869,14 +11053,10 @@ local function inject_pairs_only(head,where)          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)) @@ -10886,6 +11066,10 @@ local function inject_pairs_only(head,where)                    insert_node_after(head,n,newkern(rightkern))                    n=getnext(n)                   end +                local yoffset=i.yoffset +                if yoffset and yoffset~=0 then +                  setfield(n,"yoffset",yoffset) +                end                end              end            else @@ -10901,14 +11085,10 @@ local function inject_pairs_only(head,where)          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)) @@ -10918,6 +11098,10 @@ local function inject_pairs_only(head,where)                    insert_node_after(head,n,newkern(rightkern))                    n=getnext(n)                   end +                local yoffset=i.yoffset +                if yoffset and yoffset~=0 then +                  setfield(n,"yoffset",yoffset) +                end                end              end            else @@ -10942,7 +11126,7 @@ local function inject_pairs_only(head,where)    end    return tonode(head),true  end -function injections.handler(head,where)  +function injections.handler(head,where)    if nofregisteredmarks>0 or nofregisteredcursives>0 then      return inject_everything(head,where)    elseif nofregisteredpairs>0 then @@ -11342,14 +11526,12 @@ if not modules then modules={} end modules ['font-otn']={    copyright="PRAGMA ADE / ConTeXt Development Team",    license="see context related readme files",  } -local concat,insert,remove=table.concat,table.insert,table.remove -local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip -local type,next,tonumber,tostring=type,next,tonumber,tostring -local lpegmatch=lpeg.match +local type,next,tonumber=type,next,tonumber  local random=math.random  local formatters=string.formatters  local logs,trackers,nodes,attributes=logs,trackers,nodes,attributes  local registertracker=trackers.register +local registerdirective=directives.register  local fonts=fonts  local otf=fonts.handlers.otf  local trace_lookups=false registertracker("otf.lookups",function(v) trace_lookups=v end) @@ -11368,6 +11550,13 @@ local trace_applied=false registertracker("otf.applied",function(v) trace_applie  local trace_steps=false registertracker("otf.steps",function(v) trace_steps=v end)  local trace_skips=false registertracker("otf.skips",function(v) trace_skips=v end)  local trace_directions=false registertracker("otf.directions",function(v) trace_directions=v end) +local trace_kernruns=false registertracker("otf.kernruns",function(v) trace_kernruns=v end) +local trace_discruns=false registertracker("otf.discruns",function(v) trace_discruns=v end) +local trace_compruns=false registertracker("otf.compruns",function(v) trace_compruns=v end) +local quit_on_no_replacement=true  +local zwnjruns=true +registerdirective("otf.zwnjruns",function(v) zwnjruns=v end) +registerdirective("otf.chain.quitonnoreplacement",function(value) quit_on_no_replacement=value end)  local report_direct=logs.reporter("fonts","otf direct")  local report_subchain=logs.reporter("fonts","otf subchain")  local report_chain=logs.reporter("fonts","otf chain") @@ -11426,8 +11615,6 @@ 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 -local automatic_code=disccodes.automatic  local ligature_code=glyphcodes.ligature  local privateattribute=attributes.private  local a_state=privateattribute('state') @@ -11461,6 +11648,13 @@ local lookuptags=false  local handlers={}  local rlmode=0  local featurevalue=false +local sweephead={} +local sweepnode=nil +local sweepprev=nil +local sweepnext=nil +local notmatchpre={} +local notmatchpost={} +local notmatchreplace={}  local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check)  or function() end  local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end  local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end @@ -11530,6 +11724,65 @@ local function copy_glyph(g)      return n    end  end +local function flattendisk(head,disc) +  local replace=getfield(disc,"replace") +  setfield(disc,"replace",nil) +  free_node(disc) +  if head==disc then +    local next=getnext(disc) +    if replace then +      if next then +        local tail=find_node_tail(replace) +        setfield(tail,"next",next) +        setfield(next,"prev",tail) +      end +      return replace,replace +    elseif next then +      return next,next +    else +      return  +    end +  else +    local next=getnext(disc) +    local prev=getprev(disc) +    if replace then +      local tail=find_node_tail(replace) +      if next then +        setfield(tail,"next",next) +        setfield(next,"prev",tail) +      end +      setfield(prev,"next",replace) +      setfield(replace,"prev",prev) +      return head,replace +    else +      if next then +        setfield(next,"prev",prev) +      end +      setfield(prev,"next",next) +      return head,next +    end +  end +end +local function appenddisc(disc,list) +  local post=getfield(disc,"post") +  local replace=getfield(disc,"replace") +  local phead=list +  local rhead=copy_node_list(list) +  local ptail=find_node_tail(post) +  local rtail=find_node_tail(replace) +  if post then +    setfield(ptail,"next",phead) +    setfield(phead,"prev",ptail) +  else +    setfield(disc,"post",phead) +  end +  if replace then +    setfield(rtail,"next",rhead) +    setfield(rhead,"prev",rtail) +  else +    setfield(disc,"replace",rhead) +  end +end  local function markstoligature(kind,lookupname,head,start,stop,char)    if start==stop and getchar(start)==char then      return head,start @@ -11557,8 +11810,8 @@ local function markstoligature(kind,lookupname,head,start,stop,char)      return head,base    end  end -local function getcomponentindex(start) -  if getid(start)~=glyph_code then +local function getcomponentindex(start)  +  if getid(start)~=glyph_code then       return 0    elseif getsubtype(start)==ligature_code then      local i=0 @@ -11574,14 +11827,22 @@ local function getcomponentindex(start)      return 0    end  end +local a_noligature=attributes.private("noligature")  local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound)  +  if getattr(start,a_noligature)==1 then +    return head,start +  end    if start==stop and getchar(start)==char then      resetinjection(start)      setfield(start,"char",char)      return head,start    end +  local components=getfield(start,"components") +  if components then +  end    local prev=getprev(start)    local next=getnext(stop) +  local comp=start    setfield(start,"prev",nil)    setfield(stop,"next",nil)    local base=copy_glyph(start) @@ -11591,15 +11852,15 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun    resetinjection(base)    setfield(base,"char",char)    setfield(base,"subtype",ligature_code) -  setfield(base,"components",start)  +  setfield(base,"components",comp)     if prev then      setfield(prev,"next",base)    end    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 @@ -11617,7 +11878,9 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun          if trace_marks then            logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start))          end -        head,current=insert_node_after(head,current,copy_node(start))  +        local n=copy_node(start) +        copyinjection(n,start) +        head,current=insert_node_after(head,current,n)         elseif trace_marks then          logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))        end @@ -11636,16 +11899,75 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun        end        start=getnext(start)      end +  else +    local discprev=getfield(discfound,"prev") +    local discnext=getfield(discfound,"next") +    if discprev and discnext then +      local pre=getfield(discfound,"pre") +      local post=getfield(discfound,"post") +      local replace=getfield(discfound,"replace") +      if not replace then  +        local prev=getfield(base,"prev") +        local copied=copy_node_list(comp) +        setfield(discnext,"prev",nil)  +        setfield(discprev,"next",nil)  +        if pre then +          setfield(discprev,"next",pre) +          setfield(pre,"prev",discprev) +        end +        pre=comp +        if post then +          local tail=find_node_tail(post) +          setfield(tail,"next",discnext) +          setfield(discnext,"prev",tail) +          setfield(post,"prev",nil) +        else +          post=discnext +        end +        setfield(prev,"next",discfound) +        setfield(discfound,"prev",prev) +        setfield(discfound,"next",next) +        setfield(next,"prev",discfound) +        setfield(base,"next",nil) +        setfield(base,"prev",nil) +        setfield(base,"components",copied) +        setfield(discfound,"pre",pre) +        setfield(discfound,"post",post) +        setfield(discfound,"replace",base) +        setfield(discfound,"subtype",discretionary_code) +        base=prev  +      end +    end    end    return head,base  end -function handlers.gsub_single(head,start,kind,lookupname,replacement) -  if trace_singles then -    logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement)) +local function multiple_glyphs(head,start,multiple,ignoremarks) +  local nofmultiples=#multiple +  if nofmultiples>0 then +    resetinjection(start) +    setfield(start,"char",multiple[1]) +    if nofmultiples>1 then +      local sn=getnext(start) +      for k=2,nofmultiples do +        local n=copy_node(start)  +        resetinjection(n) +        setfield(n,"char",multiple[k]) +        setfield(n,"prev",start) +        setfield(n,"next",sn) +        if sn then +          setfield(sn,"prev",n) +        end +        setfield(start,"next",n) +        start=n +      end +    end +    return head,start,true +  else +    if trace_multiples then +      logprocess("no multiple for %s",gref(getchar(start))) +    end +    return head,start,false    end -  resetinjection(start) -  setfield(start,"char",replacement) -  return head,start,true  end  local function get_alternative_glyph(start,alternatives,value,trace_alternatives)    local n=#alternatives @@ -11678,33 +12000,13 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives      end    end  end -local function multiple_glyphs(head,start,multiple,ignoremarks) -  local nofmultiples=#multiple -  if nofmultiples>0 then -    resetinjection(start) -    setfield(start,"char",multiple[1]) -    if nofmultiples>1 then -      local sn=getnext(start) -      for k=2,nofmultiples do -        local n=copy_node(start)  -        resetinjection(n) -        setfield(n,"char",multiple[k]) -        setfield(n,"next",sn) -        setfield(n,"prev",start) -        if sn then -          setfield(sn,"prev",n) -        end -        setfield(start,"next",n) -        start=n -      end -    end -    return head,start,true -  else -    if trace_multiples then -      logprocess("no multiple for %s",gref(getchar(start))) -    end -    return head,start,false +function handlers.gsub_single(head,start,kind,lookupname,replacement) +  if trace_singles then +    logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))    end +  resetinjection(start) +  setfield(start,"char",replacement) +  return head,start,true  end  function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence)    local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue @@ -11729,7 +12031,7 @@ function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)    return multiple_glyphs(head,start,multiple,sequence.flags[1])  end  function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) -  local s,stop,discfound=getnext(start),nil,false +  local s,stop=getnext(start),nil    local startchar=getchar(start)    if marks[startchar] then      while s do @@ -11757,23 +12059,29 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)          else            head,start=markstoligature(kind,lookupname,head,start,stop,lig)          end -        return head,start,true +        return head,start,true,false        else        end      end    else      local skipmark=sequence.flags[1] +    local discfound=false +    local lastdisc=nil      while s do        local id=getid(s) -      if id==glyph_code and getsubtype(s)<256 then -        if getfont(s)==currentfont then +      if id==glyph_code and getsubtype(s)<256 then  +        if getfont(s)==currentfont then                 local char=getchar(s)            if skipmark and marks[char] then              s=getnext(s) -          else -            local lg=ligature[char] +          else  +            local lg=ligature[char]               if lg then -              stop=s +              if not discfound and lastdisc then +                discfound=lastdisc +                lastdisc=nil +              end +              stop=s                 ligature=lg                s=getnext(s)              else @@ -11784,13 +12092,13 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)            break          end        elseif id==disc_code then -        discfound=true +        lastdisc=s          s=getnext(s)        else          break        end      end -    local lig=ligature.ligature +    local lig=ligature.ligature       if lig then        if stop then          if trace_ligatures then @@ -11807,12 +12115,71 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)            logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))          end        end -      return head,start,true +      return head,start,true,discfound      else      end    end +  return head,start,false,discfound +end +function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence,injection) +  local startchar=getchar(start) +  local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection)  +  if trace_kerns then +    logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) +  end    return head,start,false  end +function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection) +  local snext=getnext(start) +  if not snext then +    return head,start,false +  else +    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 +      local nextchar=getchar(snext) +      local krn=kerns[nextchar] +      if not krn and marks[nextchar] then +        prev=snext +        snext=getnext(snext) +      else +        if not krn then +        elseif type(krn)=="table" then +          if lookuptype=="pair" then  +            local a,b=krn[2],krn[3] +            if a and #a>0 then +              local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,injection)  +              if trace_kerns then +                local startchar=getchar(start) +                logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) +              end +            end +            if b and #b>0 then +              local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection)  +              if trace_kerns then +                local startchar=getchar(start) +                logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) +              end +            end +          else  +            report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) +          end +          done=true +        elseif krn~=0 then +          local k=setkern(snext,factor,rlmode,krn,injection) +          if trace_kerns then +            logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))  +          end +          done=true +        end +        break +      end +    end +    return head,start,done +  end +end  function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence)    local markchar=getchar(start)    if marks[markchar] then @@ -11965,7 +12332,7 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence                if al[anchor] then                  local ma=markanchors[anchor]                  if ma then -                  local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar]) +                  local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)                    if trace_marks then                      logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",                        pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -12043,65 +12410,6 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)      return head,start,false    end  end -function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) -  local startchar=getchar(start) -  local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) -  if trace_kerns then -    logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) -  end -  return head,start,false -end -function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) -  local snext=getnext(start) -  if not snext then -    return head,start,false -  else -    local prev,done=start,false -    local factor=tfmdata.parameters.factor -    local lookuptype=lookuptypes[lookupname] -    while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do -      local nextchar=getchar(snext) -      local krn=kerns[nextchar] -      if not krn and marks[nextchar] then -        prev=snext -        snext=getnext(snext) -      else -        if not krn then -        elseif type(krn)=="table" then -          if lookuptype=="pair" then  -            local a,b=krn[2],krn[3] -            if a and #a>0 then -              local startchar=getchar(start) -              local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) -              if trace_kerns then -                logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) -              end -            end -            if b and #b>0 then -              local startchar=getchar(start) -              local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) -              if trace_kerns then -                logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) -              end -            end -          else  -            report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) -          end -          done=true -        elseif krn~=0 then -          local k=setkern(snext,factor,rlmode,krn) -          if trace_kerns then -            logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) -          end -          done=true -        end -        break -      end -    end -    return head,start,done -  end -end -local chainmores={}  local chainprocs={}  local function logprocess(...)    if trace_steps then @@ -12121,10 +12429,6 @@ function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,looku    logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))    return head,start,false  end -function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) -  logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) -  return head,start,false -end  function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements)    local char=getchar(start)    local replacement=replacements[char] @@ -12143,7 +12447,7 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo    local current=start    local subtables=currentlookup.subtables    if #subtables>1 then -    logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) +    logwarning("todo: check if we need to loop over the replacements: % t",subtables)    end    while current do      if getid(current)==glyph_code then @@ -12177,7 +12481,6 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo    end    return head,start,false  end -chainmores.gsub_single=chainprocs.gsub_single  function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)    local startchar=getchar(start)    local subtables=currentlookup.subtables @@ -12202,7 +12505,6 @@ function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,    end    return head,start,false  end -chainmores.gsub_multiple=chainprocs.gsub_multiple  function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)    local current=start    local subtables=currentlookup.subtables @@ -12244,7 +12546,6 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext    end    return head,start,false  end -chainmores.gsub_alternate=chainprocs.gsub_alternate  function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)    local startchar=getchar(start)    local subtables=currentlookup.subtables @@ -12264,13 +12565,19 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,        local s=getnext(start)        local discfound=false        local last=stop -      local nofreplacements=0 +      local nofreplacements=1        local skipmark=currentlookup.flags[1]        while s do          local id=getid(s)          if id==disc_code then -          s=getnext(s) -          discfound=true +          if not discfound then +            discfound=s +          end +          if s==stop then +            break  +          else +            s=getnext(s) +          end          else            local schar=getchar(s)            if skipmark and marks[schar] then  @@ -12303,7 +12610,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,            end          end          head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) -        return head,start,true,nofreplacements +        return head,start,true,nofreplacements,discfound        elseif trace_bugs then          if start==stop then            logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) @@ -12313,9 +12620,82 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,        end      end    end -  return head,start,false,0 +  return head,start,false,0,false +end +function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) +  local startchar=getchar(start) +  local subtables=currentlookup.subtables +  local lookupname=subtables[1] +  local kerns=lookuphash[lookupname] +  if kerns then +    kerns=kerns[startchar]  +    if kerns then +      local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns)  +      if trace_kerns then +        logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) +      end +    end +  end +  return head,start,false +end +function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) +  local snext=getnext(start) +  if snext then +    local startchar=getchar(start) +    local subtables=currentlookup.subtables +    local lookupname=subtables[1] +    local kerns=lookuphash[lookupname] +    if kerns then +      kerns=kerns[startchar] +      if kerns then +        local lookuptype=lookuptypes[lookupname] +        local prev,done=start,false +        local factor=tfmdata.parameters.factor +        while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do +          local nextchar=getchar(snext) +          local krn=kerns[nextchar] +          if not krn and marks[nextchar] then +            prev=snext +            snext=getnext(snext) +          else +            if not krn then +            elseif type(krn)=="table" then +              if lookuptype=="pair" then +                local a,b=krn[2],krn[3] +                if a and #a>0 then +                  local startchar=getchar(start) +                  local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a)  +                  if trace_kerns then +                    logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) +                  end +                end +                if b and #b>0 then +                  local startchar=getchar(start) +                  local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b)  +                  if trace_kerns then +                    logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) +                  end +                end +              else +                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) +              end +              done=true +            elseif krn~=0 then +              local k=setkern(snext,factor,rlmode,krn) +              if trace_kerns then +                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) +              end +              done=true +            end +            break +          end +        end +        return head,start,done +      end +    end +  end +  return head,start,false  end -chainmores.gsub_ligature=chainprocs.gsub_ligature  function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)    local markchar=getchar(start)    if marks[markchar] then @@ -12479,7 +12859,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext                if al[anchor] then                  local ma=markanchors[anchor]                  if ma then -                  local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar]) +                  local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)                    if trace_marks then                      logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",                        cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -12566,113 +12946,286 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l    end    return head,start,false  end -function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -  local startchar=getchar(start) -  local subtables=currentlookup.subtables -  local lookupname=subtables[1] -  local kerns=lookuphash[lookupname] -  if kerns then -    kerns=kerns[startchar]  -    if kerns then -      local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) -      if trace_kerns then -        logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) +local function show_skip(kind,chainname,char,ck,class) +  if ck[9] then +    logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) +  else +    logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) +  end +end +local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,chainindex,sequence,chainproc) +  if not start then +    return head,start,false +  end +  local startishead=start==head +  local seq=ck[3] +  local f=ck[4] +  local l=ck[5] +  local s=#seq +  local done=false +  local sweepnode=sweepnode +  local sweeptype=sweeptype +  local sweepoverflow=false +  local checkdisc=getprev(head)  +  local keepdisc=not sweepnode +  local lookaheaddisc=nil +  local backtrackdisc=nil +  local current=start +  local last=start +  local prev=getprev(start) +  local i=f +  while i<=l do +    local id=getid(current) +    if id==glyph_code then +      i=i+1 +      last=current +      current=getnext(current) +    elseif id==disc_code then +      if keepdisc then +        keepdisc=false +        if notmatchpre[current]~=notmatchreplace[current] then +          lookaheaddisc=current +        end +        local replace=getfield(current,"replace") +        while replace and i<=l do +          if getid(replace)==glyph_code then +            i=i+1 +          end +          replace=getnext(replace) +        end +        last=current +        current=getnext(c) +      else +        head,current=flattendisk(head,current) +      end +    else +      last=current +      current=getnext(current) +    end +    if current then +    elseif sweepoverflow then +      break +    elseif sweeptype=="post" or sweeptype=="replace" then +      current=getnext(sweepnode) +      if current then +        sweeptype=nil +        sweepoverflow=true +      else +        break        end      end    end -  return head,start,false -end -chainmores.gpos_single=chainprocs.gpos_single -function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -  local snext=getnext(start) -  if snext then -    local startchar=getchar(start) -    local subtables=currentlookup.subtables -    local lookupname=subtables[1] -    local kerns=lookuphash[lookupname] -    if kerns then -      kerns=kerns[startchar] -      if kerns then -        local lookuptype=lookuptypes[lookupname] -        local prev,done=start,false -        local factor=tfmdata.parameters.factor -        while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do -          local nextchar=getchar(snext) -          local krn=kerns[nextchar] -          if not krn and marks[nextchar] then -            prev=snext -            snext=getnext(snext) -          else -            if not krn then -            elseif type(krn)=="table" then -              if lookuptype=="pair" then -                local a,b=krn[2],krn[3] -                if a and #a>0 then -                  local startchar=getchar(start) -                  local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) -                  if trace_kerns then -                    logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) -                  end -                end -                if b and #b>0 then -                  local startchar=getchar(start) -                  local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) -                  if trace_kerns then -                    logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) -                  end -                end -              else -                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) -                local a,b=krn[2],krn[6] -                if a and a~=0 then -                  local k=setkern(snext,factor,rlmode,a) -                  if trace_kerns then -                    logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) -                  end -                end -                if b and b~=0 then -                  logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) -                end -              end -              done=true -            elseif krn~=0 then -              local k=setkern(snext,factor,rlmode,krn) -              if trace_kerns then -                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) -              end -              done=true +  if sweepoverflow then +    local prev=current and getprev(current) +    if not current or prev~=sweepnode then +      local head=getnext(sweepnode) +      local tail=nil +      if prev then +        tail=prev +        setfield(current,"prev",sweepnode) +      else +        tail=find_node_tail(head) +      end +      setfield(sweepnode,"next",current) +      setfield(head,"prev",nil) +      setfield(tail,"next",nil) +      appenddisc(sweepnode,head) +    end +  end +  if l<s then +    local i=l +    local t=sweeptype=="post" or sweeptype=="replace" +    while current and i<s do +      local id=getid(current) +      if id==glyph_code then +        i=i+1 +        current=getnext(current) +      elseif id==disc_code then +        if keepdisc then +          keepdisc=false +          if notmatchpre[current]~=notmatchreplace[current] then +            lookaheaddisc=current +          end +          local replace=getfield(c,"replace") +          while replace and i<s do +            if getid(replace)==glyph_code then +              i=i+1              end -            break +            replace=getnext(replace)            end +          current=getnext(current) +        elseif notmatchpre[current]~=notmatchreplace[current] then +          head,current=flattendisk(head,current) +        else +          current=getnext(current)  +        end +      else +        current=getnext(current) +      end +      if not current and t then +        current=getnext(sweepnode) +        if current then +          sweeptype=nil          end -        return head,start,done        end      end    end -  return head,start,false -end -chainmores.gpos_pair=chainprocs.gpos_pair -local function show_skip(kind,chainname,char,ck,class) -  if ck[9] then -    logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) +  if f>1 then +    local current=prev +    local i=f +    local t=sweeptype=="pre" or sweeptype=="replace" +    if not current and t and current==checkdisk then +      current=getprev(sweepnode) +    end +    while current and i>1 do  +      local id=getid(current) +      if id==glyph_code then +        i=i-1 +      elseif id==disc_code then +        if keepdisc then +          keepdisc=false +          if notmatchpost[current]~=notmatchreplace[current] then +            backtrackdisc=current +          end +          local replace=getfield(current,"replace") +          while replace and i>1 do +            if getid(replace)==glyph_code then +              i=i-1 +            end +            replace=getnext(replace) +          end +        elseif notmatchpost[current]~=notmatchreplace[current] then +          head,current=flattendisk(head,current) +        end +      end +      current=getprev(current) +      if t and current==checkdisk then +        current=getprev(sweepnode) +      end +    end +  end +  local ok=false +  if lookaheaddisc then +    local cf=start +    local cl=getprev(lookaheaddisc) +    local cprev=getprev(start) +    local insertedmarks=0 +    while cprev and getid(cf)==glyph_code and getfont(cf)==currentfont and getsubtype(cf)<256 and marks[getchar(cf)] do +      insertedmarks=insertedmarks+1 +      cf=cprev +      startishead=cf==head +      cprev=getprev(cprev) +    end +    setfield(lookaheaddisc,"prev",cprev) +    if cprev then +      setfield(cprev,"next",lookaheaddisc) +    end +    setfield(cf,"prev",nil) +    setfield(cl,"next",nil) +    if startishead then +      head=lookaheaddisc +    end +    local replace=getfield(lookaheaddisc,"replace") +    local pre=getfield(lookaheaddisc,"pre") +    local new=copy_node_list(cf) +    local cnew=new +    for i=1,insertedmarks do +      cnew=getnext(cnew) +    end +    local clast=cnew +    for i=f,l do +      clast=getnext(clast) +    end +    if not notmatchpre[lookaheaddisc] then +      cf,start,ok=chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +    end +    if not notmatchreplace[lookaheaddisc] then +      new,cnew,ok=chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +    end +    if pre then +      setfield(cl,"next",pre) +      setfield(pre,"prev",cl) +    end +    if replace then +      local tail=find_node_tail(new) +      setfield(tail,"next",replace) +      setfield(replace,"prev",tail) +    end +    setfield(lookaheaddisc,"pre",cf)    +    setfield(lookaheaddisc,"replace",new)  +    start=getprev(lookaheaddisc) +    sweephead[cf]=getnext(clast) +    sweephead[new]=getnext(last) +  elseif backtrackdisc then +    local cf=getnext(backtrackdisc) +    local cl=start +    local cnext=getnext(start) +    local insertedmarks=0 +    while cnext and getid(cnext)==glyph_code and getfont(cnext)==currentfont and getsubtype(cnext)<256 and marks[getchar(cnext)] do +      insertedmarks=insertedmarks+1 +      cl=cnext +      cnext=getnext(cnext) +    end +    if cnext then +      setfield(cnext,"prev",backtrackdisc) +    end +    setfield(backtrackdisc,"next",cnext) +    setfield(cf,"prev",nil) +    setfield(cl,"next",nil) +    local replace=getfield(backtrackdisc,"replace") +    local post=getfield(backtrackdisc,"post") +    local new=copy_node_list(cf) +    local cnew=find_node_tail(new) +    for i=1,insertedmarks do +      cnew=getprev(cnew) +    end +    local clast=cnew +    for i=f,l do +      clast=getnext(clast) +    end +    if not notmatchpost[backtrackdisc] then +      cf,start,ok=chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +    end +    if not notmatchreplace[backtrackdisc] then +      new,cnew,ok=chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +    end +    if post then +      local tail=find_node_tail(post) +      setfield(tail,"next",cf) +      setfield(cf,"prev",tail) +    else +      post=cf +    end +    if replace then +      local tail=find_node_tail(replace) +      setfield(tail,"next",new) +      setfield(new,"prev",tail) +    else +      replace=new +    end +    setfield(backtrackdisc,"post",post)     +    setfield(backtrackdisc,"replace",replace)  +    start=getprev(backtrackdisc) +    sweephead[post]=getnext(clast) +    sweephead[replace]=getnext(last)    else -    logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) +    head,start,ok=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)    end +  return head,start,ok  end -local quit_on_no_replacement=true -directives.register("otf.chain.quitonnoreplacement",function(value)  -  quit_on_no_replacement=value -end)  local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) +  local sweepnode=sweepnode +  local sweeptype=sweeptype +  local diskseen=false +  local checkdisc=getprev(head)    local flags=sequence.flags    local done=false    local skipmark=flags[1]    local skipligature=flags[2]    local skipbase=flags[3] -  local someskip=skipmark or skipligature or skipbase  -  local markclass=sequence.markclass           +  local markclass=sequence.markclass    local skipped=false -  for k=1,#contexts do +  for k=1,#contexts do       local match=true      local current=start      local last=start @@ -12682,14 +13235,20 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq      if s==1 then        match=getid(current)==glyph_code and getfont(current)==currentfont and getsubtype(current)<256 and seq[1][getchar(current)]      else -      local f,l=ck[4],ck[5] +      local f=ck[4] +      local l=ck[5]        if f==1 and f==l then        else          if f==l then          else +          local discfound=nil            local n=f+1            last=getnext(last)            while n<=l do +            if not last and (sweeptype=="post" or sweeptype=="replace") then +              last=getnext(sweepnode) +              sweeptype=nil +            end              if last then                local id=getid(last)                if id==glyph_code then @@ -12697,7 +13256,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                    local char=getchar(last)                    local ccd=descriptions[char]                    if ccd then -                    local class=ccd.class +                    local class=ccd.class or "base"                      if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then                        skipped=true                        if trace_skips then @@ -12710,18 +13269,76 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                        end                        n=n+1                      else -                      match=false +                      if discfound then +                        notmatchreplace[discfound]=true +                        match=not notmatchpre[discfound] +                      else +                        match=false +                      end                        break                      end                    else -                    match=false +                    if discfound then +                      notmatchreplace[discfound]=true +                      match=not notmatchpre[discfound] +                    else +                      match=false +                    end                      break                    end                  else -                  match=false +                  if discfound then +                    notmatchreplace[discfound]=true +                    match=not notmatchpre[discfound] +                  else +                    match=false +                  end                    break                  end                elseif id==disc_code then +                diskseen=true +                discfound=last +                notmatchpre[last]=nil +                notmatchpost[last]=true +                notmatchreplace[last]=nil +                local pre=getfield(last,"pre") +                local replace=getfield(last,"replace") +                if pre then +                  local n=n +                  while pre do +                    if seq[n][getchar(pre)] then +                      n=n+1 +                      pre=getnext(pre) +                      if n>l then +                        break +                      end +                    else +                      notmatchpre[last]=true +                      break +                    end +                  end +                  if n<=l then +                    notmatchpre[last]=true +                  end +                else +                  notmatchpre[last]=true +                end +                if replace then +                  while replace do +                    if seq[n][getchar(replace)] then +                      n=n+1 +                      replace=getnext(replace) +                      if n>l then +                        break +                      end +                    else +                      notmatchreplace[last]=true +                      match=not notmatchpre[last] +                      break +                    end +                  end +                  match=not notmatchpre[last] +                end                  last=getnext(last)                else                  match=false @@ -12737,49 +13354,132 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq        if match and f>1 then          local prev=getprev(start)          if prev then -          local n=f-1 -          while n>=1 do -            if prev then -              local id=getid(prev) -              if id==glyph_code then -                if getfont(prev)==currentfont and getsubtype(prev)<256 then  -                  local char=getchar(prev) -                  local ccd=descriptions[char] -                  if ccd then -                    local class=ccd.class -                    if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then -                      skipped=true -                      if trace_skips then -                        show_skip(kind,chainname,char,ck,class) +          if prev==checkdisc and (sweeptype=="pre" or sweeptype=="replace") then +            prev=getprev(sweepnode) +          end +          if prev then +            local discfound=nil +            local n=f-1 +            while n>=1 do +              if prev then +                local id=getid(prev) +                if id==glyph_code then +                  if getfont(prev)==currentfont and getsubtype(prev)<256 then  +                    local char=getchar(prev) +                    local ccd=descriptions[char] +                    if ccd then +                      local class=ccd.class +                      if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then +                        skipped=true +                        if trace_skips then +                          show_skip(kind,chainname,char,ck,class) +                        end +                      elseif seq[n][char] then +                        n=n -1 +                      else +                        if discfound then +                          notmatchreplace[discfound]=true +                          match=not notmatchpost[discfound] +                        else +                          match=false +                        end +                        break                        end -                    elseif seq[n][char] then -                      n=n -1                      else -                      match=false +                      if discfound then +                        notmatchreplace[discfound]=true +                        match=not notmatchpost[discfound] +                      else +                        match=false +                      end                        break                      end                    else -                    match=false +                    if discfound then +                      notmatchreplace[discfound]=true +                      match=not notmatchpost[discfound] +                    else +                      match=false +                    end                      break                    end +                elseif id==disc_code then +                  diskseen=true +                  discfound=prev +                  notmatchpre[prev]=true +                  notmatchpost[prev]=nil +                  notmatchreplace[prev]=nil +                  local pre=getfield(prev,"pre") +                  local post=getfield(prev,"post") +                  local replace=getfield(prev,"replace") +                  if pre~=start and post~=start and replace~=start then +                    if post then +                      local n=n +                      local posttail=find_node_tail(post) +                      while posttail do +                        if seq[n][getchar(posttail)] then +                          n=n-1 +                          if posttail==post then +                            break +                          else +                            posttail=getprev(posttail) +                            if n<1 then +                              break +                            end +                          end +                        else +                          notmatchpost[prev]=true +                          break +                        end +                      end +                      if n>=1 then +                        notmatchpost[prev]=true +                      end +                    else +                      notmatchpost[prev]=true +                    end +                    if replace then +                      local replacetail=find_node_tail(replace) +                      while replacetail do +                        if seq[n][getchar(replacetail)] then +                          n=n-1 +                          if replacetail==replace then +                            break +                          else +                            replacetail=getprev(replacetail) +                            if n<1 then +                              break +                            end +                          end +                        else +                          notmatchreplace[prev]=true +                          match=not notmatchpost[prev] +                          break +                        end +                      end +                      if not match then +                        break +                      end +                    else +                    end +                  else +                  end +                elseif seq[n][32] then +                  n=n -1                  else                    match=false                    break                  end -              elseif id==disc_code then -              elseif seq[n][32] then -                n=n -1 +                prev=getprev(prev) +              elseif seq[n][32] then  +                n=n-1                else                  match=false                  break                end -              prev=getprev(prev) -            elseif seq[n][32] then  -              n=n -1 -            else -              match=false -              break              end +          else +            match=false            end          else            match=false @@ -12787,7 +13487,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq        end        if match and s>l then          local current=last and getnext(last) +        if not current then +          if sweeptype=="post" or sweeptype=="replace" then +            current=getnext(sweepnode) +          end +        end          if current then +          local discfound=nil            local n=l+1            while n<=s do              if current then @@ -12806,18 +13512,79 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                      elseif seq[n][char] then                        n=n+1                      else -                      match=false +                      if discfound then +                        notmatchreplace[discfound]=true +                        match=not notmatchpre[discfound] +                      else +                        match=false +                      end                        break                      end                    else -                    match=false +                    if discfound then +                      notmatchreplace[discfound]=true +                      match=not notmatchpre[discfound] +                    else +                      match=false +                    end                      break                    end                  else -                  match=false +                  if discfound then +                    notmatchreplace[discfound]=true +                    match=not notmatchpre[discfound] +                  else +                    match=false +                  end                    break                  end                elseif id==disc_code then +                diskseen=true +                discfound=current +                notmatchpre[current]=nil +                notmatchpost[current]=true +                notmatchreplace[current]=nil +                local pre=getfield(current,"pre") +                local replace=getfield(current,"replace") +                if pre then +                  local n=n +                  while pre do +                    if seq[n][getchar(pre)] then +                      n=n+1 +                      pre=getnext(pre) +                      if n>s then +                        break +                      end +                    else +                      notmatchpre[current]=true +                      break +                    end +                  end +                  if n<=s then +                    notmatchpre[current]=true +                  end +                else +                  notmatchpre[current]=true +                end +                if replace then +                  while replace do +                    if seq[n][getchar(replace)] then +                      n=n+1 +                      replace=getnext(replace) +                      if n>s then +                        break +                      end +                    else +                      notmatchreplace[current]=true +                      match=notmatchpre[current] +                      break +                    end +                  end +                  if not match then +                    break +                  end +                else +                end                elseif seq[n][32] then                   n=n+1                else @@ -12838,6 +13605,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq        end      end      if match then +      local diskchain=diskseen or sweepnode        if trace_contexts then          local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5]          local char=getchar(start) @@ -12856,10 +13624,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq            local chainlookupname=chainlookups[1]            local chainlookup=lookuptable[chainlookupname]            if chainlookup then -            local cp=chainprocs[chainlookup.type] -            if cp then +            local chainproc=chainprocs[chainlookup.type] +            if chainproc then                local ok -              head,start,ok=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +              if diskchain then +                head,start,ok=chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc) +              else +                head,start,ok=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +              end                if ok then                  done=true                end @@ -12871,13 +13643,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq            end           else            local i=1 -          while true do +          while start and true do              if skipped then -              while true do +              while true do                   local char=getchar(start)                  local ccd=descriptions[char]                  if ccd then -                  local class=ccd.class +                  local class=ccd.class or "base"                    if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then                      start=getnext(start)                    else @@ -12893,26 +13665,33 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq              if not chainlookup then                i=i+1              else -              local cp=chainmores[chainlookup.type] -              if not cp then +              local chainproc=chainprocs[chainlookup.type] +              if not chainproc then                  logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)                  i=i+1                else                  local ok,n -                head,start,ok,n=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) +                if diskchain then +                  head,start,ok=chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc) +                else +                  head,start,ok,n=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) +                end                  if ok then                    done=true -                  i=i+(n or 1) -                else -                  i=i+1 +                  if n and n>1 then +                    if i+n>nofchainlookups then +                      break +                    else +                    end +                  end                  end +                i=i+1                end              end -            if i>nofchainlookups then +            if i>nofchainlookups or not start then                break              elseif start then                start=getnext(start) -            else              end            end          end @@ -12927,8 +13706,16 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq            end          end        end +      if done then +        break  +      end      end    end +  if diskseen then  +    notmatchpre={} +    notmatchpost={} +    notmatchreplace={} +  end    return head,start,done  end  local verbose_handle_contextchain=function(font,...) @@ -12999,7 +13786,7 @@ local function initialize(sequence,script,language,enabled)            local scripts=features[kind]             local languages=scripts[script] or scripts[wildcard]            if languages and (languages[language] or languages[wildcard]) then -            return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence } +            return { valid,autofeatures[kind] or false,sequence,kind }            end          end        end @@ -13039,6 +13826,175 @@ function otf.dataset(tfmdata,font)    end    return rl  end +local function kernrun(disc,run) +  if trace_kernruns then +    report_run("kern")  +  end +  local prev=getprev(disc)  +  local next=getnext(disc) +  local pre=getfield(disc,"pre") +  local post=getfield(disc,"post") +  local replace=getfield(disc,"replace") +  local prevmarks=prev +  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) and not (getid(prev)==glyph_code and getfont(prev)==currentfont and getsubtype(prev)<256) then +    prev=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 +  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") +  end +  if not post then +  elseif next then +    local tail=find_node_tail(post) +    setfield(tail,"next",next) +    setfield(next,"prev",tail) +    run(post,"postinjections",next) +    setfield(tail,"next",nil) +    setfield(next,"prev",disc) +  else +    run(post,"postinjections") +  end +  if not replace and 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) +    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 +local function comprun(disc,run) +  if trace_compruns then +    report_run("comp: %s",languages.serializediscretionary(disc)) +  end +  local pre=getfield(disc,"pre") +  if pre then +    sweepnode=disc +    sweeptype="pre"  +    local new,done=run(pre) +    if done then +      setfield(disc,"pre",new) +    end +  end +  local post=getfield(disc,"post") +  if post then +    sweepnode=disc +    sweeptype="post" +    local new,done=run(post) +    if done then +      setfield(disc,"post",new) +    end +  end +  local replace=getfield(disc,"replace") +  if replace then +    sweepnode=disc +    sweeptype="replace" +    local new,done=run(replace) +    if done then +      setfield(disc,"replace",new) +    end +  end +  sweepnode=nil +  sweeptype=nil +end +local function testrun(disc,trun,crun)  +  local next=getnext(disc) +  if next then +    local replace=getfield(disc,"replace") +    if replace then +      local prev=getprev(disc) +      if prev then +        local tail=find_node_tail(replace) +        setfield(tail,"next",next) +        setfield(next,"prev",tail) +        if trun(replace,next) then +          setfield(disc,"replace",nil)  +          setfield(prev,"next",replace) +          setfield(replace,"prev",prev) +          setfield(next,"prev",tail) +          setfield(tail,"next",next) +          setfield(disc,"prev",nil) +          setfield(disc,"next",nil) +          flush_node_list(disc) +          return replace  +        else +          setfield(tail,"next",nil) +          setfield(next,"prev",disc) +        end +      else +      end +    else +    end +  else +  end +  comprun(disc,crun) +  return next +end +local function discrun(disc,drun,krun) +  local next=getnext(disc) +  local prev=getprev(disc) +  if trace_discruns then +    report_run("disc")  +  end +  if next and prev then +    setfield(prev,"next",next) +    drun(prev) +    setfield(prev,"next",disc) +  end +  local pre=getfield(disc,"pre") +  if not pre then +  elseif prev then +    local nest=getprev(pre) +    setfield(pre,"prev",prev) +    setfield(prev,"next",pre) +    krun(prev,"preinjections") +    setfield(pre,"prev",nest) +    setfield(prev,"next",disc) +  else +    krun(pre,"preinjections") +  end +  return next +end  local function featuresprocessor(head,font,attr)    local lookuphash=lookuphashes[font]     if not lookuphash then @@ -13059,23 +14015,25 @@ local function featuresprocessor(head,font,attr)    lookuptags=resources.lookuptags    currentfont=font    rlmode=0 +  sweephead={}    local sequences=resources.sequences    local done=false    local datasets=otf.dataset(tfmdata,font,attr)    local dirstack={}    for s=1,#datasets do      local dataset=datasets[s] -    featurevalue=dataset[1]  -    local sequence=dataset[5]  +       featurevalue=dataset[1]  +    local attribute=dataset[2] +    local sequence=dataset[3]  +    local kind=dataset[4]      local rlparmode=0      local topstack=0      local success=false -    local attribute=dataset[2] -    local chain=dataset[3]       local typ=sequence.type +    local gpossing=typ=="gpos_single" or typ=="gpos_pair"       local subtables=sequence.subtables -    if chain<0 then -      local handler=handlers[typ] +    local handler=handlers[typ] +    if typ=="gsub_reversecontextchain" then        local start=find_node_tail(head)         while start do          local id=getid(start) @@ -13088,13 +14046,14 @@ local function featuresprocessor(head,font,attr)                a=true              end              if a then +              local char=getchar(start)                for i=1,#subtables do                  local lookupname=subtables[i]                  local lookupcache=lookuphash[lookupname]                  if lookupcache then -                  local lookupmatch=lookupcache[getchar(start)] +                  local lookupmatch=lookupcache[char]                    if lookupmatch then -                    head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                    head,start,success=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)                      if success then                        break                      end @@ -13115,7 +14074,6 @@ local function featuresprocessor(head,font,attr)          end        end      else -      local handler=handlers[typ]        local ns=#subtables        local start=head         rlmode=0  @@ -13125,12 +14083,19 @@ local function featuresprocessor(head,font,attr)          if not lookupcache then             report_missing_cache(typ,lookupname)          else -          local function subrun(start) -            local head=start +          local function c_run(head)               local done=false +            local start=sweephead[head] +            if start then +              sweephead[head]=nil +            else +              start=head +            end              while start do                local id=getid(start) -              if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then +              if id~=glyph_code then +                start=getnext(start) +              elseif getfont(start)==font and getsubtype(start)<256 then                  local a=getattr(start,0)                  if a then                    a=(a==attr) and (not attribute or getprop(start,a_state)==attribute) @@ -13141,7 +14106,7 @@ local function featuresprocessor(head,font,attr)                    local lookupmatch=lookupcache[getchar(start)]                    if lookupmatch then                      local ok -                    head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) +                    head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)                      if ok then                        done=true                      end @@ -13151,43 +14116,98 @@ local function featuresprocessor(head,font,attr)                    start=getnext(start)                  end                else -                start=getnext(start) +                return head,false                end              end              if done then -              success=true -              return head +              success=true               end +            return head,done            end -          local function kerndisc(disc)  -            local prev=getprev(disc) -            local next=getnext(disc) -            if prev and next then -              setfield(prev,"next",next) -              local a=getattr(prev,0) -              if a then -                a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute) +          local function t_run(start,stop) +            while start~=stop do +              local id=getid(start) +              if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then +                local a=getattr(start,0) +                if a then +                  a=(a==attr) and (not attribute or getprop(start,a_state)==attribute) +                else +                  a=not attribute or getprop(start,a_state)==attribute +                end +                if a then +                  local lookupmatch=lookupcache[getchar(start)] +                  if lookupmatch then +                    local s=getnext(start) +                    local l=nil +                    while s do +                      local lg=lookupmatch[getchar(s)] +                      if lg then +                        l=lg +                        s=getnext(s) +                      else +                        break +                      end +                    end +                    if l and l.ligature then +                      return true +                    end +                  end +                end +                start=getnext(start)                else -                a=not attribute or getprop(prev,a_state)==attribute +                break                end -              if a then -                local lookupmatch=lookupcache[getchar(prev)] -                if lookupmatch then -                  local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) -                  if ok then -                    done=true -                    success=true +            end +          end +          local function d_run(prev)  +            local a=getattr(prev,0) +            if a then +              a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute) +            else +              a=not attribute or getprop(prev,a_state)==attribute +            end +            if a then +              local lookupmatch=lookupcache[getchar(prev)] +              if lookupmatch then +                local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1) +                if ok then +                  done=true +                  success=true +                end +              end +            end +          end +          local function k_run(sub,injection,last) +            local a=getattr(sub,0) +            if a then +              a=(a==attr) and (not attribute or getprop(sub,a_state)==attribute) +            else +              a=not attribute or getprop(sub,a_state)==attribute +            end +            if a then +              for n in traverse_nodes(sub) do  +                if n==last then +                  break +                end +                local id=getid(n) +                if id==glyph_code then +                  local lookupmatch=lookupcache[getchar(n)] +                  if lookupmatch then +                    local h,d,ok=handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection) +                    if ok then +                      done=true +                      success=true +                    end                    end +                else                  end                end -              setfield(prev,"next",disc)              end -            return next            end            while start do              local id=getid(start)              if id==glyph_code then -              if getfont(start)==font and getsubtype(start)<256 then +              if getfont(start)==font and getsubtype(start)<256 then                   local a=getattr(start,0)                  if a then                    a=(a==attr) and (not attribute or getprop(start,a_state)==attribute) @@ -13195,13 +14215,18 @@ local function featuresprocessor(head,font,attr)                    a=not attribute or getprop(start,a_state)==attribute                  end                  if a then -                  local lookupmatch=lookupcache[getchar(start)] +                  local char=getchar(start) +                  local lookupmatch=lookupcache[char]                    if lookupmatch then                      local ok -                    head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) +                    head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)                      if ok then                        success=true +                    elseif gpossing and zwnjruns and char==zwnj then +                      discrun(start,d_run)                      end +                  elseif gpossing and zwnjruns and char==zwnj then +                    discrun(start,d_run)                    end                    if start then start=getnext(start) end                  else @@ -13211,41 +14236,30 @@ local function featuresprocessor(head,font,attr)                  start=getnext(start)                end              elseif id==disc_code then -              if getsubtype(start)==discretionary_code then -                local pre=getfield(start,"pre") -                if pre then -                  local new=subrun(pre) -                  if new then setfield(start,"pre",new) end -                end -                local post=getfield(start,"post") -                if post then -                  local new=subrun(post) -                  if new then setfield(start,"post",new) end -                end -                local replace=getfield(start,"replace") -                if replace then -                  local new=subrun(replace) -                  if new then setfield(start,"replace",new) end -                end -elseif typ=="gpos_single" or typ=="gpos_pair" then -  kerndisc(start) +              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 -              start=getnext(start)              elseif id==whatsit_code then                 local subtype=getsubtype(start)                if subtype==dir_code then                  local dir=getfield(start,"dir") -                if   dir=="+TRT" or dir=="+TLT" then +                if dir=="+TLT" then                    topstack=topstack+1                    dirstack[topstack]=dir -                elseif dir=="-TRT" or dir=="-TLT" then -                  topstack=topstack-1 -                end -                local newdir=dirstack[topstack] -                if newdir=="+TRT" then -                  rlmode=-1 -                elseif newdir=="+TLT" then                    rlmode=1 +                elseif dir=="+TRT" then +                  topstack=topstack+1 +                  dirstack[topstack]=dir +                  rlmode=-1 +                elseif dir=="-TLT" or dir=="-TRT" then +                  topstack=topstack-1 +                  rlmode=dirstack[topstack]=="+TRT" and -1 or 1                  else                    rlmode=rlparmode                  end @@ -13275,12 +14289,19 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then            end          end        else -        local function subrun(start) -          local head=start +        local function c_run(head)            local done=false +          local start=sweephead[head] +          if start then +            sweephead[head]=nil +          else +            start=head +          end            while start do              local id=getid(start) -            if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then +            if id~=glyph_code then +              start=getnext(start) +            elseif getfont(start)==font and getsubtype(start)<256 then                local a=getattr(start,0)                if a then                  a=(a==attr) and (not attribute or getprop(start,a_state)==attribute) @@ -13288,14 +14309,15 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                  a=not attribute or getprop(start,a_state)==attribute                end                if a then +                local char=getchar(start)                  for i=1,ns do                    local lookupname=subtables[i]                    local lookupcache=lookuphash[lookupname]                    if lookupcache then -                    local lookupmatch=lookupcache[getchar(start)] +                    local lookupmatch=lookupcache[char]                      if lookupmatch then                        local ok -                      head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                      head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)                        if ok then                          done=true                          break @@ -13312,46 +14334,120 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                  start=getnext(start)                end              else -              start=getnext(start) +              return head,false              end            end            if done then              success=true -            return head            end +          return head,done          end -        local function kerndisc(disc)  -          local prev=getprev(disc) -          local next=getnext(disc) -          if prev and next then -            setfield(prev,"next",next) -            local a=getattr(prev,0) -            if a then -              a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute) -            else -              a=not attribute or getprop(prev,a_state)==attribute +        local function d_run(prev) +          local a=getattr(prev,0) +          if a then +            a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute) +          else +            a=not attribute or getprop(prev,a_state)==attribute +          end +          if a then +            local char=getchar(prev) +            for i=1,ns do +              local lookupname=subtables[i] +              local lookupcache=lookuphash[lookupname] +              if lookupcache then +                local lookupmatch=lookupcache[char] +                if lookupmatch then +                  local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,i) +                  if ok then +                    done=true +                    break +                  end +                end +              else +                report_missing_cache(typ,lookupname) +              end              end -            if a then -              for i=1,ns do -                local lookupname=subtables[i] -                local lookupcache=lookuphash[lookupname] -                if lookupcache then -                  local lookupmatch=lookupcache[getchar(prev)] -                  if lookupmatch then -                    local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) -                    if ok then -                      done=true -                      break +          end +        end +        local function k_run(sub,injection,last) +          local a=getattr(sub,0) +          if a then +            a=(a==attr) and (not attribute or getprop(sub,a_state)==attribute) +          else +            a=not attribute or getprop(sub,a_state)==attribute +          end +          if a then +            for n in traverse_nodes(sub) do  +              if n==last then +                break +              end +              local id=getid(n) +              if id==glyph_code then +                local char=getchar(n) +                for i=1,ns do +                  local lookupname=subtables[i] +                  local lookupcache=lookuphash[lookupname] +                  if lookupcache then +                    local lookupmatch=lookupcache[char] +                    if lookupmatch then +                      local h,d,ok=handler(head,n,kind,lookupname,lookupmatch,sequence,lookuphash,i,injection) +                      if ok then +                        done=true +                        break +                      end                      end +                  else +                    report_missing_cache(typ,lookupname) +                  end +                end +              else +              end +            end +          end +        end +        local function t_run(start,stop) +          while start~=stop do +            local id=getid(start) +            if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then +              local a=getattr(start,0) +              if a then +                a=(a==attr) and (not attribute or getprop(start,a_state)==attribute) +              else +                a=not attribute or getprop(start,a_state)==attribute +              end +              if a then +                local char=getchar(start) +                for i=1,ns do +                  local lookupname=subtables[i] +                  local lookupcache=lookuphash[lookupname] +                  if lookupcache then +                    local lookupmatch=lookupcache[char] +                    if lookupmatch then +                      local s=getnext(start) +                      local l=nil +                      while s do +                        local lg=lookupmatch[getchar(s)] +                        if lg then +                          l=lg +                          s=getnext(s) +                        else +                          break +                        end +                      end +                      if l and l.ligature then +                        return true +                      end +                    end +                  else +                    report_missing_cache(typ,lookupname)                    end -                else -                  report_missing_cache(typ,lookupname)                  end                end +              start=getnext(start) +            else +              break              end -            setfield(prev,"next",disc)            end -          return next          end          while start do            local id=getid(start) @@ -13368,16 +14464,21 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                    local lookupname=subtables[i]                    local lookupcache=lookuphash[lookupname]                    if lookupcache then -                    local lookupmatch=lookupcache[getchar(start)] +                    local char=getchar(start) +                    local lookupmatch=lookupcache[char]                      if lookupmatch then                        local ok -                      head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                      head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)                        if ok then                          success=true                          break                        elseif not start then                          break +                      elseif gpossing and zwnjruns and char==zwnj then +                        discrun(start,d_run)                        end +                    elseif gpossing and zwnjruns and char==zwnj then +                      discrun(start,d_run)                      end                    else                      report_missing_cache(typ,lookupname) @@ -13391,41 +14492,30 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                start=getnext(start)              end            elseif id==disc_code then -            if getsubtype(start)==discretionary_code then -              local pre=getfield(start,"pre") -              if pre then -                local new=subrun(pre) -                if new then setfield(start,"pre",new) end -              end -              local post=getfield(start,"post") -              if post then -                local new=subrun(post) -                if new then setfield(start,"post",new) end -              end -              local replace=getfield(start,"replace") -              if replace then -                local new=subrun(replace) -                if new then setfield(start,"replace",new) end -              end -elseif typ=="gpos_single" or typ=="gpos_pair" then -  kerndisc(start) +            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 -            start=getnext(start)            elseif id==whatsit_code then              local subtype=getsubtype(start)              if subtype==dir_code then                local dir=getfield(start,"dir") -              if   dir=="+TRT" or dir=="+TLT" then +              if dir=="+TLT" then                  topstack=topstack+1                  dirstack[topstack]=dir -              elseif dir=="-TRT" or dir=="-TLT" then -                topstack=topstack-1 -              end -              local newdir=dirstack[topstack] -              if newdir=="+TRT" then -                rlmode=-1 -              elseif newdir=="+TLT" then                  rlmode=1 +              elseif dir=="+TRT" then +                topstack=topstack+1 +                dirstack[topstack]=dir +                rlmode=-1 +              elseif dir=="-TLT" or dir=="-TRT" then +                topstack=topstack-1 +                rlmode=dirstack[topstack]=="+TRT" and -1 or 1                else                  rlmode=rlparmode                end @@ -13473,43 +14563,46 @@ local function generic(lookupdata,lookupname,unicode,lookuphash)      lookuphash[lookupname]={ [unicode]=lookupdata }    end  end +local function ligature(lookupdata,lookupname,unicode,lookuphash) +  local target=lookuphash[lookupname] +  if not target then +    target={} +    lookuphash[lookupname]=target +  end +  for i=1,#lookupdata do +    local li=lookupdata[i] +    local tu=target[li] +    if not tu then +      tu={} +      target[li]=tu +    end +    target=tu +  end +  target.ligature=unicode +end +local function pair(lookupdata,lookupname,unicode,lookuphash) +  local target=lookuphash[lookupname] +  if not target then +    target={} +    lookuphash[lookupname]=target +  end +  local others=target[unicode] +  local paired=lookupdata[1] +  if others then +    others[paired]=lookupdata +  else +    others={ [paired]=lookupdata } +    target[unicode]=others +  end +end  local action={    substitution=generic,    multiple=generic,    alternate=generic,    position=generic, -  ligature=function(lookupdata,lookupname,unicode,lookuphash) -    local target=lookuphash[lookupname] -    if not target then -      target={} -      lookuphash[lookupname]=target -    end -    for i=1,#lookupdata do -      local li=lookupdata[i] -      local tu=target[li] -      if not tu then -        tu={} -        target[li]=tu -      end -      target=tu -    end -    target.ligature=unicode -  end, -  pair=function(lookupdata,lookupname,unicode,lookuphash) -    local target=lookuphash[lookupname] -    if not target then -      target={} -      lookuphash[lookupname]=target -    end -    local others=target[unicode] -    local paired=lookupdata[1] -    if others then -      others[paired]=lookupdata -    else -      others={ [paired]=lookupdata } -      target[unicode]=others -    end -  end, +  ligature=ligature, +  pair=pair, +  kern=pair,  }  local function prepare_lookups(tfmdata)    local rawdata=tfmdata.shared.rawdata @@ -13520,13 +14613,14 @@ local function prepare_lookups(tfmdata)    local lookuptypes=resources.lookuptypes    local characters=tfmdata.characters    local descriptions=tfmdata.descriptions +  local duplicates=resources.duplicates    for unicode,character in next,characters do       local description=descriptions[unicode]      if description then        local lookups=description.slookups        if lookups then          for lookupname,lookupdata in next,lookups do -          action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) +          action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash,duplicates)          end        end        local lookups=description.mlookups @@ -13535,7 +14629,7 @@ local function prepare_lookups(tfmdata)            local lookuptype=lookuptypes[lookupname]            for l=1,#lookuplist do              local lookupdata=lookuplist[l] -            action[lookuptype](lookupdata,lookupname,unicode,lookuphash) +            action[lookuptype](lookupdata,lookupname,unicode,lookuphash,duplicates)            end          end        end @@ -13557,7 +14651,7 @@ local function prepare_lookups(tfmdata)              for name,anchor in next,anchors do                local lookups=anchor_to_lookup[name]                if lookups then -                for lookup,_ in next,lookups do +                for lookup in next,lookups do                    local target=lookuphash[lookup]                    if target then                      target[unicode]=anchors @@ -13639,7 +14733,7 @@ local function prepare_contextchains(tfmdata)                if sequence[1] then                  nt=nt+1                  t[nt]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements } -                for unic,_ in next,sequence[start] do +                for unic in next,sequence[start] do                    local cu=contexts[unic]                    if not cu then                      contexts[unic]=t @@ -13698,9 +14792,8 @@ if not modules then modules={} end modules ['font-otp']={    copyright="PRAGMA ADE / ConTeXt Development Team",    license="see context related readme files"  } -local next,type=next,type +local next,type,tostring=next,type,tostring  local sort,concat=table.sort,table.concat -local sortedhash=table.sortedhash  local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end)  local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)  local report_otf=logs.reporter("fonts","otf loading") @@ -14676,10 +15769,11 @@ end  function resolvers.name(specification)    local resolve=fonts.names.resolve    if resolve then -    local resolved,sub=resolve(specification.name,specification.sub,specification)  +    local resolved,sub,subindex=resolve(specification.name,specification.sub,specification)       if resolved then        specification.resolved=resolved        specification.sub=sub +      specification.subindex=subindex        local suffix=lower(suffixonly(resolved))        if fonts.formats[suffix] then          specification.forced=suffix @@ -14696,10 +15790,11 @@ end  function resolvers.spec(specification)    local resolvespec=fonts.names.resolvespec    if resolvespec then -    local resolved,sub=resolvespec(specification.name,specification.sub,specification)  +    local resolved,sub,subindex=resolvespec(specification.name,specification.sub,specification)       if resolved then        specification.resolved=resolved        specification.sub=sub +      specification.subindex=subindex        specification.forced=lower(suffixonly(resolved))        specification.forcedname=resolved        specification.name=removesuffix(resolved) @@ -15341,12 +16436,30 @@ function nodes.handlers.nodepass(head)          local range=basefonts[i]          local start=range[1]          local stop=range[2] -        if stop then -          start,stop=ligaturing(start,stop) -          start,stop=kerning(start,stop) -        elseif start then -          start=ligaturing(start) -          start=kerning(start) +        if start or stop then +          local prev=nil +          local next=nil +          local front=start==head +          if stop then +            next=stop.next +            start,stop=ligaturing(start,stop) +            start,stop=kerning(start,stop) +          elseif start then +            prev=start.prev +            start=ligaturing(start) +            start=kerning(start) +          end +          if prev then +            start.prev=prev +            prev.next=start +          end +          if next then +            stop.next=next +            next.prev=stop +          end +          if front then +            head=start +          end          end        end      end @@ -15356,7 +16469,7 @@ function nodes.handlers.nodepass(head)    end  end  function nodes.handlers.basepass(head) -  if not basepass then +  if basepass then      head=ligaturing(head)      head=kerning(head)    end diff --git a/src/luaotfload-auxiliary.lua b/src/luaotfload-auxiliary.lua index 1ef581e..15541af 100644 --- a/src/luaotfload-auxiliary.lua +++ b/src/luaotfload-auxiliary.lua @@ -4,8 +4,6 @@  --  DESCRIPTION:  part of luaotfload  -- REQUIREMENTS:  luaotfload 2.6  --       AUTHOR:  Khaled Hosny, Élie Roux, Philipp Gesang ---      VERSION:  2.6 ---     MODIFIED:  2015-03-29 12:43:26+0200  -----------------------------------------------------------------------  -- @@ -19,6 +17,7 @@ local aux                   = luaotfload.aux  local log                   = luaotfload.log  local report                = log.report  local fonthashes            = fonts.hashes +local encodings             = fonts.encodings  local identifiers           = fonthashes.identifiers  local fontnames             = fonts.names @@ -214,8 +213,6 @@ luatexbase.add_to_callback(  ---                      glyphs and characters  ----------------------------------------------------------------------- -local agl = fonts.encodings.agl -  --- int -> int -> bool  local font_has_glyph = function (font_id, codepoint)    local fontdata = fonts.hashes.identifiers[font_id] @@ -232,7 +229,7 @@ aux.font_has_glyph = font_has_glyph  local raw_slot_of_name = function (font_id, glyphname)    local fontdata = font.fonts[font_id]    if fontdata.type == "virtual" then --- get base font for glyph idx -    local codepoint  = agl.unicodes[glyphname] +    local codepoint  = encodings.agl.unicodes[glyphname]      local glyph      = fontdata.characters[codepoint]      if fontdata.characters[codepoint] then        return codepoint @@ -293,7 +290,7 @@ local indices  --- int -> (string | false)  local name_of_slot = function (codepoint)    if not indices then --- this will load the glyph list -    local unicodes = agl.unicodes +    local unicodes = encodings.agl.unicodes      indices = table.swapped(unicodes)    end    local glyphname = indices[codepoint] diff --git a/src/luaotfload-colors.lua b/src/luaotfload-colors.lua index 89884b6..98549c8 100644 --- a/src/luaotfload-colors.lua +++ b/src/luaotfload-colors.lua @@ -19,8 +19,7 @@ explanation: http://tug.org/pipermail/luatex/2013-May/004305.html  --doc]]-- -local log                   = luaotfload.log -local logreport             = log.report +local logreport             = luaotfload and luaotfload.log.report or print  local nodedirect            = node.direct  local newnode               = nodedirect.new @@ -44,10 +43,7 @@ local texsettoks            = tex.settoks  local texgettoks            = tex.gettoks  local stringformat          = string.format - -local otffeatures           = fonts.constructors.newfeatures("otf")  local identifiers           = fonts.hashes.identifiers -local registerotffeature    = otffeatures.register  local add_color_callback --[[ this used to be a global‽ ]] @@ -101,36 +97,6 @@ local sanitize_color_expression = function (digits)      return sanitized  end ---[[doc-- -``setcolor`` modifies tfmdata.properties.color in place ---doc]]-- - ---- fontobj -> string -> unit ---- ----         (where “string” is a rgb value as three octet ----         hexadecimal, with an optional fourth transparency ----         value) ---- -local setcolor = function (tfmdata, value) -    local sanitized  = sanitize_color_expression(value) -    local properties = tfmdata.properties - -    if sanitized then -        properties.color = sanitized -        add_color_callback() -    end -end - -registerotffeature { -    name        = "color", -    description = "color", -    initializers = { -        base = setcolor, -        node = setcolor, -    } -} - -  --- something is carried around in ``res``  --- for later use by color_handler() --- but what? @@ -192,6 +158,7 @@ local whatsit_t         = nodetype("whatsit")  local disc_t            = nodetype("disc")  local pdfliteral_t      = node.subtype("pdf_literal")  local colorstack_t      = node.subtype("pdf_colorstack") +local mlist_to_hlist    = node.mlist_to_hlist  local color_callback  local color_attr        = luatexbase.new_attribute("luaotfload_color_attribute") @@ -230,8 +197,6 @@ local get_font_color = function (font_id)      return font_color  end -local cnt = 0 -  --[[doc--  While the second argument and second returned value are apparently  always nil when the function is called, they temporarily take string @@ -240,26 +205,24 @@ values during the node list traversal.  --- (node * (string | nil)) -> (node * (string | nil))  local node_colorize -node_colorize = function (head, current_color) +node_colorize = function (head, toplevel, current_color)      local n = head      while n do          local n_id = getid(n)          if n_id == hlist_t or n_id == vlist_t then -            cnt = cnt + 1              local n_list = getlist(n)              if getattribute(n_list, color_attr) then                  if current_color then                      head, n, current_color = color_whatsit(head, n, current_color, false)                  end              else -                n_list, current_color = node_colorize(n_list, current_color) +                n_list, current_color = node_colorize(n_list, false, current_color)                  if current_color and getsubtype(n) == 1 then -- created by linebreak                      n_list, _, current_color = color_whatsit(n_list, nodetail(n_list), current_color, false, true)                  end                  setfield(n, "head", n_list)              end -            cnt = cnt - 1          elseif n_id == glyph_t then              --- colorization is restricted to those fonts @@ -303,7 +266,7 @@ node_colorize = function (head, current_color)          n = getnext(n)      end -    if cnt == 0 and current_color then +    if toplevel and current_color then          head, _, current_color = color_whatsit(head, nodetail(head), current_color, false, true)      end @@ -314,7 +277,7 @@ end  --- node -> node  local color_handler = function (head)      head = todirect(head) -    head = node_colorize(head) +    head = node_colorize(head, true)      head = tonode(head)      -- now append our page resources @@ -351,6 +314,8 @@ local color_handler = function (head)  end  local color_callback_activated = 0 +local add_to_callback          = luatexbase.add_to_callback +local priority_in_callback     = luatexbase.priority_in_callback  --- unit -> unit  add_color_callback = function ( ) @@ -360,22 +325,75 @@ add_color_callback = function ( )      end      if color_callback_activated == 0 then -        luatexbase.add_to_callback(color_callback, -                                   color_handler, -                                   "luaotfload.color_handler") -        luatexbase.add_to_callback("hpack_filter", -                                   function (head, groupcode) -                                       if  groupcode == "hbox"          or -                                           groupcode == "adjusted_hbox" or -                                           groupcode == "align_set"     then -                                           head = color_handler(head) -                                       end -                                       return head -                                   end, -                                   "luaotfload.color_handler") +        add_to_callback(color_callback, +                        color_handler, +                        "luaotfload.color_handler") +        add_to_callback("hpack_filter", +                        function (head, groupcode) +                            if  groupcode == "hbox"          or +                                groupcode == "adjusted_hbox" or +                                groupcode == "align_set"     then +                                head = color_handler(head) +                            end +                            return head +                        end, +                        "luaotfload.color_handler") +        add_to_callback("mlist_to_hlist", +                        function (head, display_type, need_penalties) +                            if priority_in_callback("mlist_to_hlist","luaotfload.color_handler") == 1 then +                                head = mlist_to_hlist(head, display_type, need_penalties) +                            end +                            if display_type == "text" then +                                return head +                            end +                            return color_handler(head) +                        end, +                        "luaotfload.color_handler")          color_callback_activated = 1      end  end +--[[doc-- +``setcolor`` modifies tfmdata.properties.color in place +--doc]]-- + +--- fontobj -> string -> unit +--- +---         (where “string” is a rgb value as three octet +---         hexadecimal, with an optional fourth transparency +---         value) +--- +local setcolor = function (tfmdata, value) +    local sanitized  = sanitize_color_expression(value) +    local properties = tfmdata.properties + +    if sanitized then +        properties.color = sanitized +        add_color_callback() +    end +end + +return { +    init = function () +        logreport = luaotfload.log.report +        if not fonts then +            logreport ("log", 0, "color", +                       "OTF mechanisms missing -- did you forget to \z +                       load a font loader?") +            return false +        end +        fonts.handlers.otf.features.register { +            name        = "color", +            description = "color", +            initializers = { +                base = setcolor, +                node = setcolor, +            } +        } +        return true +    end +} + +  -- vim:tw=71:sw=4:ts=4:expandtab diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua index e2cfbd8..57311dc 100644 --- a/src/luaotfload-configuration.lua +++ b/src/luaotfload-configuration.lua @@ -5,8 +5,6 @@  -- REQUIREMENTS:  Luaotfload 2.6 or above  --       AUTHOR:  Philipp Gesang (Phg), <phg42.2a@gmail.com>  --       AUTHOR:  Dohyun Kim <nomosnomos@gmail.com> ---      VERSION:  same as Luaotfload ---     MODIFIED:  2015-05-05  -------------------------------------------------------------------------------  -- @@ -18,17 +16,12 @@ if not modules then modules = { } end modules ["luaotfload-configuration"] = {    license   = "GNU GPL v2.0"  } -luaotfload                    = luaotfload or { } -config                        = config or { } -config.luaotfload             = { } -  local status_file             = "luaotfload-status"  local luaotfloadstatus        = require (status_file) -local stringexplode           = string.explode +local string                  = string  local stringfind              = string.find  local stringformat            = string.format -local string                  = string  local stringstrip             = string.strip  local stringsub               = string.sub @@ -55,9 +48,7 @@ local lpegmatch               = lpeg.match  local commasplitter           = lpeg.splitat ","  local equalssplitter          = lpeg.splitat "=" -local kpse                    = kpse  local kpseexpand_path         = kpse.expand_path -local kpselookup              = kpse.lookup  local lfs                     = lfs  local lfsisfile               = lfs.isfile @@ -67,16 +58,12 @@ local file                    = file  local filejoin                = file.join  local filereplacesuffix       = file.replacesuffix +local logreport               = print -- overloaded later +local getwritablepath         = caches.getwritablepath -local parsers                 = luaotfload.parsers - -local log                     = luaotfload.log -local logreport               = log.report - -local config_parser           = parsers.config -local stripslashes            = parsers.stripslashes -local getwritablepath         = caches.getwritablepath +local config_parser -- set later during init +local stripslashes  -- set later during init  -------------------------------------------------------------------------------  ---                                SETTINGS @@ -140,11 +127,40 @@ local feature_presets = {  --doc]]--  local registered_loaders = { -  default   = luaotfloadstatus and luaotfloadstatus.notes.loader or "reference", -  reference = "reference", -  tl2014    = "tl2014", +  default    = luaotfloadstatus and luaotfloadstatus.notes.loader or "reference", +  reference  = "reference", +  unpackaged = "unpackaged", +  context    = "context", +  tl2014     = "tl2014",  } +local pick_fontloader = function (s) +  local ldr = registered_loaders[s] +  if ldr ~= nil and type (ldr) == "string" then +    logreport ("log", 2, "conf", "Using predefined fontloader \"%s\".", ldr) +    return ldr +  end +  local idx = stringfind (s, ":") +  if idx and idx > 2 then +    if stringsub (s, 1, idx - 1) == "context" then +      local pth = stringsub (s, idx + 1) +      pth = stringstrip (pth) +      logreport ("log", 2, "conf", "Context base path specified at \"%s\".", pth) +      if lfsisdir (pth) then +        logreport ("log", 5, "conf", "Context base path exists at \"%s\".", pth) +        return pth +      end +      pth = kpseexpand_path (pth) +      if lfsisdir (pth) then +        logreport ("log", 5, "conf", "Context base path exists at \"%s\".", pth) +        return pth +      end +      logreport ("both", 0, "conf", "Context base path not found at \"%s\".", pth) +    end +  end +  return nil +end +  --[[doc--    The ``post_linebreak_filter`` has been made the default callback for @@ -182,7 +198,7 @@ local default_config = {      definer        = "patch",      log_level      = 0,      color_callback = "post_linebreak_filter", -    live           = true, +    fontloader     = "default",    },    misc = {      bisect         = false, @@ -292,17 +308,20 @@ local set_name_resolver = function ()      --- replace the resolver from luatex-fonts      if config.luaotfload.db.resolver == "cached" then          logreport ("both", 2, "cache", "Caching of name: lookups active.") -        names.resolvespec  = names.resolve_cached +        names.resolvespec  = fonts.names.lookup_font_name_cached      else -        names.resolvespec  = names.resolve_name +        names.resolvespec  = fonts.names.lookup_font_name      end    end    return true  end  local set_loglevel = function () -  log.set_loglevel (config.luaotfload.run.log_level) -  return true +  if luaotfload then +    luaotfload.log.set_loglevel (config.luaotfload.run.log_level) +    return true +  end +  return false  end  local build_cache_paths = function () @@ -476,10 +495,8 @@ local option_spec = {        in_t      = string_t,        out_t     = string_t,        transform = function (id) -        local ldr = registered_loaders[id] +        local ldr = pick_fontloader (id)          if ldr ~= nil then -          logreport ("log", 2, "conf", -                     "Using predefined fontloader \"%s\".", ldr)            return ldr          end          logreport ("log", 0, "conf", @@ -846,7 +863,7 @@ local read = function (extra)      return false    end -  local parsed = lpegmatch (parsers.config, raw) +  local parsed = lpegmatch (config_parser, raw)    if not parsed then      logreport ("both", 2, "conf", "Error parsing configuration file %q.", readme)      return false @@ -912,13 +929,27 @@ end  ---                                 EXPORTS  ------------------------------------------------------------------------------- -luaotfload.default_config = default_config - -config.actions = { -  read             = read, -  apply            = apply, -  apply_defaults   = apply_defaults, -  reconfigure      = reconfigure, -  dump             = dump, +return { +  init = function () +    config.luaotfload = { } +    logreport         = luaotfload.log.report +    local parsers     = luaotfload.parsers +    config_parser     = parsers.config +    stripslashes      = parsers.stripslashes + +    luaotfload.default_config = default_config +    config.actions = { +      read             = read, +      apply            = apply, +      apply_defaults   = apply_defaults, +      reconfigure      = reconfigure, +      dump             = dump, +    } +    if not apply_defaults () then +      logreport ("log", 0, "load", +                 "Configuration unsuccessful: error loading default settings.") +    end +    return true +  end  } diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua index f4aab16..1bc2768 100644 --- a/src/luaotfload-database.lua +++ b/src/luaotfload-database.lua @@ -39,13 +39,8 @@ if not modules then modules = { } end modules ['luaotfload-database'] = {  local lpeg                     = require "lpeg"  local P, Cc, lpegmatch         = lpeg.P, lpeg.Cc, lpeg.match -local parsers                  = luaotfload.parsers -local read_fonts_conf          = parsers.read_fonts_conf -local stripslashes             = parsers.stripslashes -local splitcomma               = parsers.splitcomma -  local log                      = luaotfload.log -local report                   = log.report +local logreport                = log and log.report or print -- overriden later on  local report_status            = log.names_status  local report_status_start      = log.names_status_start  local report_status_stop       = log.names_status_stop @@ -119,19 +114,10 @@ local tablefastcopy            = table.fastcopy  local tabletofile              = table.tofile  local tabletohash              = table.tohash  local tableserialize           = table.serialize ---- the font loader namespace is “fonts”, same as in Context ---- we need to put some fallbacks into place for when running ---- as a script -fonts                          = fonts          or { } -fonts.names                    = fonts.names    or { } -fonts.definers                 = fonts.definers or { } - -local names                    = fonts.names +local names                    = fonts and fonts.names or { } +  local name_index               = nil --> upvalue for names.data  local lookup_cache             = nil --> for names.lookups -names.version                  = 2.51 -names.data                     = nil      --- contains the loaded database -names.lookups                  = nil      --- contains the lookup cache  --- string -> (string * string)  local make_luanames = function (path) @@ -363,7 +349,7 @@ local initialize_namedata = function (formats, created)          status          = { }, -- was: status; map abspath -> mapping          mappings        = { }, -- TODO: check if still necessary after rewrite          names           = { }, ---      files           = { }, -- created later +        files           = { }, -- created later          meta            = {              created    = created or now,              formats    = formats, @@ -485,12 +471,12 @@ load_names = function (dry_run, no_rebuild)      local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua)      if data then -        report ("log", 0, "db", -                "Font names database loaded from %s", foundname) -        report ("term", 3, "db", -                "Font names database loaded from %s", foundname) -        report ("info", 3, "db", "Loading took %0.f ms.", -                1000 * (osgettimeofday () - starttime)) +        logreport ("log", 0, "db", +                   "Font names database loaded from %s", foundname) +        logreport ("term", 3, "db", +                   "Font names database loaded from %s", foundname) +        logreport ("info", 3, "db", "Loading took %0.f ms.", +                   1000 * (osgettimeofday () - starttime))          local db_version, names_version          if data.meta then @@ -503,32 +489,32 @@ load_names = function (dry_run, no_rebuild)          end          names_version = names.version          if db_version ~= names_version then -            report ("both", 0, "db", -                    [[Version mismatch; expected %4.3f, got %4.3f.]], -                    names_version, db_version) +            logreport ("both", 0, "db", +                       [[Version mismatch; expected %4.3f, got %4.3f.]], +                       names_version, db_version)              if not fonts_reloaded then -                report ("both", 0, "db", [[Force rebuild.]]) +                logreport ("both", 0, "db", [[Force rebuild.]])                  data = update_names ({ }, true, false)                  if not data then -                    report ("both", 0, "db", -                            "Database creation unsuccessful.") +                    logreport ("both", 0, "db", +                               "Database creation unsuccessful.")                  end              end          end      else          if no_rebuild == true then -            report ("both", 2, "db", -                    [[Database does not exist, skipping rebuild though.]]) +            logreport ("both", 2, "db", +                       [[Database does not exist, skipping rebuild though.]])              return false          end -        report ("both", 0, "db", -                [[Font names database not found, generating new one.]]) -        report ("both", 0, "db", -                [[This can take several minutes; please be patient.]]) +        logreport ("both", 0, "db", +                   [[Font names database not found, generating new one.]]) +        logreport ("both", 0, "db", +                   [[This can take several minutes; please be patient.]])          data = update_names (initialize_namedata (get_font_filter ()),                               nil, dry_run)          if not data then -            report ("both", 0, "db", "Database creation unsuccessful.") +            logreport ("both", 0, "db", "Database creation unsuccessful.")          end      end      return data @@ -559,12 +545,12 @@ local load_lookups  load_lookups = function ( )      local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua)      if data then -        report("log", 0, "cache", "Lookup cache loaded from %s.", foundname) -        report("term", 3, "cache", -               "Lookup cache loaded from %s.", foundname) +        logreport ("log", 0, "cache", "Lookup cache loaded from %s.", foundname) +        logreport ("term", 3, "cache", +                   "Lookup cache loaded from %s.", foundname)      else -        report("both", 1, "cache", -               "No lookup cache, creating empty.") +        logreport ("both", 1, "cache", +                   "No lookup cache, creating empty.")          data = { }      end      lookup_cache = data @@ -780,23 +766,24 @@ local lookup_font_name_cached  lookup_font_name_cached = function (specification)      if not lookup_cache then load_lookups () end      local request = hash_request(specification) -    report("both", 4, "cache", "Looking for %q in cache ...", -           request) +    logreport ("both", 4, "cache", "Looking for %q in cache ...", +               request)      local found = lookup_cache [request]      --- case 1) cache positive ----------------------------------------      if found then --- replay fields from cache hit -        report("info", 4, "cache", "Found!") +        logreport ("info", 4, "cache", "Found!")          local basename = found[1]          --- check the presence of the file in case it’s been removed          local success = verify_font_file (basename)          if success == true then              return basename, found[2], true          end -        report("both", 4, "cache", "Cached file not found; resolving again.") +        logreport ("both", 4, "cache", +                   "Cached file not found; resolving again.")      else -        report("both", 4, "cache", "Not cached; resolving.") +        logreport ("both", 4, "cache", "Not cached; resolving.")      end      --- case 2) cache negative ---------------------------------------- @@ -807,16 +794,16 @@ lookup_font_name_cached = function (specification)      end      --- ... then we add the fields to the cache ... ...      local entry = { filename, subfont } -    report("both", 4, "cache", "New entry: %s.", request) +    logreport ("both", 4, "cache", "New entry: %s.", request)      lookup_cache [request] = entry      --- obviously, the updated cache needs to be stored.      --- TODO this should trigger a save only once the      ---      document is compiled (finish_pdffile callback?) -    report("both", 5, "cache", "Saving updated cache.") +    logreport ("both", 5, "cache", "Saving updated cache.")      local success = save_lookups ()      if not success then --- sad, but not critical -        report("both", 0, "cache", "Error writing cache.") +        logreport ("both", 0, "cache", "Error writing cache.")      end      return filename, subfont  end @@ -973,8 +960,8 @@ local lookup_familyname = function (specification, name, style, askedsize)      if not success then          return nil, nil      end -    report ("info", 2, "db", "Match found: %s(%d).", -            resolved, subfont or 0) +    logreport ("info", 2, "db", "Match found: %s(%d).", +               resolved, subfont or 0)      return resolved, subfont  end @@ -1140,9 +1127,9 @@ reload_db = function (why, caller, ...)      local namedata  = name_index      local formats   = tableconcat (namedata.meta.formats, ",") -    report ("both", 0, "db", -            "Reload initiated (formats: %s); reason: %q.", -            formats, why) +    logreport ("both", 0, "db", +               "Reload initiated (formats: %s); reason: %q.", +               formats, why)      set_font_filter (formats)      namedata = update_names (namedata, false, false) @@ -1153,7 +1140,7 @@ reload_db = function (why, caller, ...)          return caller (...)      end -    report ("both", 0, "db", "Database update unsuccessful.") +    logreport ("both", 0, "db", "Database update unsuccessful.")  end  --- string -> string -> int @@ -1184,6 +1171,25 @@ local iterative_levenshtein = function (s1, s2)    return costs[len2]--- lower right has the distance  end +--- string list -> string list +local delete_dupes = function (lst) +    local n0 = #lst +    if n0 == 0 then return lst end +    tablesort (lst) +    local ret = { } +    local last +    for i = 1, n0 do +        local cur = lst[i] +        if cur ~= last then +            last = cur +            ret[#ret + 1] = cur +        end +    end +    logreport (false, 8, "query", +               "Removed %d duplicate names.", n0 - #ret) +    return ret +end +  --- string -> int -> bool  find_closest = function (name, limit)      local name     = sanitize_fontname (name) @@ -1229,6 +1235,7 @@ find_closest = function (name, limit)          else --- append              namelst[#namelst+1] = fullname          end +          by_distance[dist] = namelst      end @@ -1237,16 +1244,16 @@ find_closest = function (name, limit)      if n_distances > 0 then --- got some data          tablesort(distances)          limit = mathmin(n_distances, limit) -        report(false, 1, "query", -               "Displaying %d distance levels.", limit) +        logreport (false, 1, "query", +                   "Displaying %d distance levels.", limit)          for i = 1, limit do              local dist     = distances[i] -            local namelst  = by_distance[dist] -            report(false, 0, "query", -                   "Distance from \"%s\": %s\n    " -                   .. tableconcat (namelst, "\n    "), -                   name, dist) +            local namelst  = delete_dupes (by_distance[dist]) +            logreport (false, 0, "query", +                       "Distance from \"%s\": %s\n    " +                       .. tableconcat (namelst, "\n    "), +                       name, dist)          end          return true @@ -1273,7 +1280,7 @@ local load_font_file = function (filename, subfont)      local rawfont, _msg = fontloaderopen (filename, subfont)      --local rawfont, _msg = fontloaderinfo (filename, subfont)      if not rawfont then -        report ("log", 1, "db", "ERROR: failed to open %s.", filename) +        logreport ("log", 1, "db", "ERROR: failed to open %s.", filename)          return      end      return rawfont @@ -1316,8 +1323,8 @@ local get_english_names = function (metadata)      end      -- no (English) names table, probably a broken font -    report("both", 3, "db", -            "%s: missing or broken English names table.", basename) +    logreport ("both", 3, "db", +               "%s: missing or broken English names table.", basename)      return { fontname = metadata.fontname,               fullname = metadata.fullname, }  end @@ -1343,9 +1350,9 @@ local get_raw_info = function (metadata, basename)          --- Broken names table, e.g. avkv.ttf with UTF-16 strings;          --- we put some dummies in place like the fontloader          --- (font-otf.lua) does. -        report("both", 3, "db", -               "%s has invalid postscript font names, using dummies.", -               basename) +        logreport ("both", 3, "db", +                   "%s has invalid postscript font names, using dummies.", +                   basename)          fontname = "bad-fontname-" .. basename          fullname = "bad-fullname-" .. basename      end @@ -1619,7 +1626,7 @@ local compare_timestamps = function (fullname,      if targetentrystatus ~= nil      and targetentrystatus.timestamp == targettimestamp then -        report ("log", 3, "db", "Font %q already read.", fullname) +        logreport ("log", 3, "db", "Font %q already read.", fullname)          return false      end @@ -1641,7 +1648,7 @@ local compare_timestamps = function (fullname,              targetentrystatus.index [targetindex + 1] = location          end -        report ("log", 3, "db", "Font %q already indexed.", fullname) +        logreport ("log", 3, "db", "Font %q already indexed.", fullname)          return false      end @@ -1721,8 +1728,8 @@ local read_font_names = function (fullname,      --- 1) skip if blacklisted      if names.blacklist[fullname] or names.blacklist[basename] then -        report("log", 2, "db", -               "Ignoring blacklisted font %q.", fullname) +        logreport ("log", 2, "db", +                   "Ignoring blacklisted font %q.", fullname)          return false      end @@ -1745,8 +1752,8 @@ local read_font_names = function (fullname,      local loader    = loaders [format] --- ot_fullinfo, t1_fullinfo      if not loader then -        report ("both", 0, "db", -                "Unknown format: %q, skipping.", format) +        logreport ("both", 0, "db", +                   "Unknown format: %q, skipping.", format)          return false      end @@ -1755,8 +1762,8 @@ local read_font_names = function (fullname,      local info = fontloaderinfo (fullname)      if not info then -        report ("log", 1, "db", -                "Failed to read basic information from %q", basename) +        logreport ("log", 1, "db", +                   "Failed to read basic information from %q", basename)          return false      end @@ -1828,11 +1835,7 @@ do      end  end -fonts.path_normalize = path_normalize - -names.blacklist = { } - -local blacklist   = names.blacklist +local blacklist = { }  local p_blacklist --- prefixes of dirs  --- string list -> string list @@ -1861,8 +1864,8 @@ local create_blacklist = function (blacklist, whitelist)      local result = { }      local dirs   = { } -    report("info", 2, "db", "Blacklisting %d files and directories.", -           #blacklist) +    logreport ("info", 2, "db", "Blacklisting %d files and directories.", +               #blacklist)      for i=1, #blacklist do          local entry = blacklist[i]          if lfsisdir(entry) then @@ -1872,7 +1875,7 @@ local create_blacklist = function (blacklist, whitelist)          end      end -    report("info", 2, "db", "Whitelisting %d files.", #whitelist) +    logreport ("info", 2, "db", "Whitelisting %d files.", #whitelist)      for i=1, #whitelist do          result[whitelist[i]] = nil      end @@ -1914,9 +1917,9 @@ read_blacklist = function ()                  if first_chr == "%" or stringis_empty(line) then                      -- comment or empty line                  elseif first_chr == "-" then -                    report ("both", 3, "db", -                            "Whitelisted file %q via %q.", -                            line, path) +                    logreport ("both", 3, "db", +                               "Whitelisted file %q via %q.", +                               line, path)                      whitelist[#whitelist+1] = stringsub(line, 2, -1)                  else                      local cmt = stringfind(line, "%%") @@ -1924,9 +1927,9 @@ read_blacklist = function ()                          line = stringsub(line, 1, cmt - 1)                      end                      line = stringstrip(line) -                    report ("both", 3, "db", -                            "Blacklisted file %q via %q.", -                            line, path) +                    logreport ("both", 3, "db", +                               "Blacklisted file %q via %q.", +                               line, path)                      blacklist[#blacklist+1] = line                  end              end @@ -1938,9 +1941,8 @@ end  local p_font_filter  do -    local current_formats = { } -      local extension_pattern = function (list) +        if type (list) ~= "table" or #list == 0 then return P(-1) end          local pat          for i=#list, 1, -1 do              local e = list[i] @@ -1957,12 +1959,17 @@ do      --- small helper to adjust the font filter pattern (--formats      --- option) +    local current_formats = { } +      set_font_filter = function (formats)          if not formats or type (formats) ~= "string" then              return          end +        if splitcomma == nil then +            splitcomma = luaotfload.parsers and luaotfload.parsers.splitcomma +        end          if stringsub (formats, 1, 1) == "+" then -- add              formats = lpegmatch (splitcomma, stringsub (formats, 2))              if formats then @@ -2123,24 +2130,24 @@ end  --- string -> string -> string * string list  local collect_font_filenames_dir = function (dirname, location)      if lpegmatch (p_blacklist, dirname) then -        report ("both", 4, "db", -                "Skipping blacklisted directory %s.", dirname) +        logreport ("both", 4, "db", +                   "Skipping blacklisted directory %s.", dirname)          --- ignore          return { }      end      local found = find_font_files (dirname, location ~= "texmf" and location ~= "local")      if not found then -        report ("both", 4, "db", -                "No such directory: %q; skipping.", dirname) +        logreport ("both", 4, "db", +                   "No such directory: %q; skipping.", dirname)          return { }      end      local nfound = #found      local files  = { } -    report ("both", 4, "db", -            "%d font files detected in %s.", -            nfound, dirname) +    logreport ("both", 4, "db", +               "%d font files detected in %s.", +               nfound, dirname)      for j = 1, nfound do          local fullname = found[j]          files[#files + 1] = { path_normalize (fullname), location } @@ -2148,10 +2155,12 @@ local collect_font_filenames_dir = function (dirname, location)      return files  end -  --- string list -> string list  local filter_out_pwd = function (dirs)      local result = { } +    if stripslashes == nil then +        stripslashes = luaotfload.parsers and luaotfload.parsers.stripslashes +    end      local pwd = path_normalize (lpegmatch (stripslashes,                                             lfscurrentdir ()))      for i = 1, #dirs do @@ -2184,14 +2193,14 @@ local collect_font_filenames_texmf = function ()      local osfontdir = kpseexpand_path "$OSFONTDIR"      if stringis_empty (osfontdir) then -        report ("info", 1, "db", "Scanning TEXMF for fonts...") +        logreport ("info", 1, "db", "Scanning TEXMF for fonts...")      else -        report ("info", 1, "db", "Scanning TEXMF and $OSFONTDIR for fonts...") +        logreport ("info", 1, "db", "Scanning TEXMF and $OSFONTDIR for fonts...")          if log.get_loglevel () > 3 then              local osdirs = filesplitpath (osfontdir) -            report ("info", 0, "db", "$OSFONTDIR has %d entries:", #osdirs) +            logreport ("info", 0, "db", "$OSFONTDIR has %d entries:", #osdirs)              for i = 1, #osdirs do -                report ("info", 0, "db", "[%d] %s", i, osdirs[i]) +                logreport ("info", 0, "db", "[%d] %s", i, osdirs[i])              end          end      end @@ -2205,14 +2214,14 @@ local collect_font_filenames_texmf = function ()      end      local tasks = filter_out_pwd (filesplitpath (fontdirs)) -    report ("info", 3, "db", -            "Initiating scan of %d directories.", #tasks) +    logreport ("info", 3, "db", +               "Initiating scan of %d directories.", #tasks)      local files = { }      for _, dir in next, tasks do          files = tableappend (files, collect_font_filenames_dir (dir, "texmf"))      end -    report ("term", 3, "db", "Collected %d files.", #files) +    logreport ("term", 3, "db", "Collected %d files.", #files)      return files  end @@ -2233,7 +2242,10 @@ local function get_os_dirs ()              "/usr/local/etc/fonts/fonts.conf",              "/etc/fonts/fonts.conf",          } -        local os_dirs = read_fonts_conf(fonts_conves, find_files) +        if not luaotfload.parsers then +            logreport ("log", 0, "db", "Fatal: no fonts.conf parser.") +        end +        local os_dirs = luaotfload.parsers.read_fonts_conf(fonts_conves, find_files)          return os_dirs      end      return {} @@ -2246,15 +2258,20 @@ end  --doc]]--  --- string list -> size_t -local count_removed = function (old) -    report("log", 4, "db", "Checking removed files.") +local count_removed = function (files) +    if not files or not files.full then +        logreport ("log", 4, "db", "Empty file store; no data to work with.") +        return 0 +    end +    local old = files.full +    logreport ("log", 4, "db", "Checking removed files.")      local nrem = 0      local nold = #old      for i = 1, nold do          local f = old[i]          if not kpsereadable_file (f) then -            report("log", 2, "db", -                   "File %s does not exist in file system.") +            logreport ("log", 2, "db", +                      "File %s does not exist in file system.")              nrem = nrem + 1          end      end @@ -2281,7 +2298,7 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run)      local nfiles    = #files      local nnew      = 0 -    report ("info", 1, "db", "Scanning %d collected font files ...", nfiles) +    logreport ("info", 1, "db", "Scanning %d collected font files ...", nfiles)      local bylocation = { texmf     = { 0, 0 }                         , ["local"] = { 0, 0 } @@ -2294,12 +2311,12 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run)          count[1]                   = count[1] + 1          if dry_run == true then              local truncated = truncate_string (fullname, 43) -            report ("log", 2, "db", "Would have been loading %s.", fullname) +            logreport ("log", 2, "db", "Would have been loading %s.", fullname)              report_status ("term", "db", "Would have been loading %s", truncated)              --- skip the read_font_names part          else              local truncated = truncate_string (fullname, 32) -            report ("log", 2, "db", "Loading font %s.", fullname) +            logreport ("log", 2, "db", "Loading font %s.", fullname)              report_status ("term", "db", "Loading font %s", truncated)              local new = read_font_names (fullname, currentnames,                                           targetnames, location) @@ -2311,8 +2328,8 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run)      end      report_status_stop ("term", "db", "Scanned %d files, %d new.", nfiles, nnew)      for location, count in next, bylocation do -        report ("term", 4, "db", "   * %s: %d files, %d new", -                location, count[1], count[2]) +        logreport ("term", 4, "db", "   * %s: %d files, %d new", +                   location, count[1], count[2])      end      return nnew  end @@ -2321,15 +2338,15 @@ end  local collect_font_filenames_system = function ()      local n_scanned, n_new = 0, 0 -    report ("info", 1, "db", "Scanning system fonts...") -    report ("info", 2, "db", -            "Searching in static system directories...") +    logreport ("info", 1, "db", "Scanning system fonts...") +    logreport ("info", 2, "db", +               "Searching in static system directories...")      local files = { }      for _, dir in next, get_os_dirs () do          tableappend (files, collect_font_filenames_dir (dir, "system"))      end -    report ("term", 3, "db", "Collected %d files.", #files) +    logreport ("term", 3, "db", "Collected %d files.", #files)      return files  end @@ -2355,25 +2372,25 @@ end  --- unit -> string * string list  local collect_font_filenames_local = function ()      local pwd = lfscurrentdir () -    report ("both", 1, "db", "Scanning for fonts in $PWD (%q) ...", pwd) +    logreport ("both", 1, "db", "Scanning for fonts in $PWD (%q) ...", pwd)      local files  = collect_font_filenames_dir (pwd, "local")      local nfiles = #files      if nfiles > 0 then          targetnames.meta["local"] = true --- prevent saving to disk -        report ("term", 1, "db", "Found %d files.", pwd) +        logreport ("term", 1, "db", "Found %d files.", pwd)      else -        report ("term", 1, "db", -                "Couldn’t find a thing here. What a waste.", pwd) +        logreport ("term", 1, "db", +                   "Couldn’t find a thing here. What a waste.", pwd)      end -    report ("term", 3, "db", "Collected %d files.", #files) +    logreport ("term", 3, "db", "Collected %d files.", #files)      return files  end  --- fontentry list -> filemap  generate_filedata = function (mappings) -    report ("both", 2, "db", "Creating filename map.") +    logreport ("both", 2, "db", "Creating filename map.")      local nmappings  = #mappings @@ -2435,10 +2452,10 @@ generate_filedata = function (mappings)          if inbase then              local present = inbase [basename]              if present then -                report ("both", 4, "db", -                        "Conflicting basename: %q already indexed \z -                         in category %s, ignoring.", -                        barename, location) +                logreport ("both", 4, "db", +                           "Conflicting basename: %q already indexed \z +                            in category %s, ignoring.", +                           barename, location)                  conflicts.basenames = conflicts.basenames + 1                  --- track conflicts per font @@ -2465,10 +2482,10 @@ generate_filedata = function (mappings)          if inbare then              local present = inbare [barename]              if present then -                report ("both", 4, "db", -                        "Conflicting barename: %q already indexed \z -                         in category %s/%s, ignoring.", -                        barename, location, format) +                logreport ("both", 4, "db", +                           "Conflicting barename: %q already indexed \z +                            in category %s/%s, ignoring.", +                           barename, location, format)                  conflicts.barenames = conflicts.barenames + 1                  --- track conflicts per font @@ -2650,7 +2667,7 @@ end  collect_families = function (mappings) -    report ("info", 2, "db", "Analyzing families.") +    logreport ("info", 2, "db", "Analyzing families.")      local families = {          ["local"]  = { }, @@ -2746,7 +2763,7 @@ local style_categories   = { "r", "b", "i", "bi" }  local bold_categories    = {      "b",      "bi" }  group_modifiers = function (mappings, families) -    report ("info", 2, "db", "Analyzing shapes, weights, and styles.") +    logreport ("info", 2, "db", "Analyzing shapes, weights, and styles.")      for location, location_data in next, families do          for format, format_data in next, location_data do              for familyname, collected in next, format_data do @@ -2845,7 +2862,7 @@ end  order_design_sizes = function (families) -    report ("info", 2, "db", "Ordering design sizes.") +    logreport ("info", 2, "db", "Ordering design sizes.")      for location, data in next, families do          for format, data in next, data do @@ -2870,7 +2887,7 @@ end  --- unit -> string * string list  local collect_font_filenames = function () -    report ("info", 4, "db", "Scanning the filesystem for font files.") +    logreport ("info", 4, "db", "Scanning the filesystem for font files.")      local filenames = { }      local bisect    = config.luaotfload.misc.bisect @@ -2900,7 +2917,7 @@ end  --- int -> string  local nth_font_filename = function (n) -    report ("info", 4, "db", "Picking font file no. %d.", n) +    logreport ("info", 4, "db", "Picking font file no. %d.", n)      if not p_blacklist then          read_blacklist ()      end @@ -2915,7 +2932,7 @@ end  --doc]]--  local font_slice = function (lo, hi) -    report ("info", 4, "db", "Retrieving font files nos. %d--%d.", lo, hi) +    logreport ("info", 4, "db", "Retrieving font files nos. %d--%d.", lo, hi)      if not p_blacklist then          read_blacklist ()      end @@ -2937,7 +2954,7 @@ end  --- unit -> int  local count_font_files = function () -    report ("info", 4, "db", "Counting font files.") +    logreport ("info", 4, "db", "Counting font files.")      if not p_blacklist then          read_blacklist ()      end @@ -3063,31 +3080,31 @@ local collect_statistics = function (mappings)                      itemlist = tableconcat (itemlist, ", ")                  end -                report ("both", 0, "db", -                        "       · %4d × %s.", -                        freq, itemlist) +                logreport ("both", 0, "db", +                           "       · %4d × %s.", +                           freq, itemlist)              end          end -        report ("both", 0, "", "~~~~ font index statistics ~~~~") -        report ("both", 0, "db", -                "   · Collected %d fonts (%d names) in %d families.", -                #mappings, n_fullname, n_family) +        logreport ("both", 0, "", "~~~~ font index statistics ~~~~") +        logreport ("both", 0, "db", +                   "   · Collected %d fonts (%d names) in %d families.", +                   #mappings, n_fullname, n_family)          pprint_top (families, 4, true) -        report ("both", 0, "db", -                "   · %d different “subfamily” kinds.", -                setsize (subfamily)) +        logreport ("both", 0, "db", +                   "   · %d different “subfamily” kinds.", +                   setsize (subfamily))          pprint_top (subfamily, 4) -        report ("both", 0, "db", -                "   · %d different “prefmodifiers” kinds.", -                setsize (prefmodifiers)) +        logreport ("both", 0, "db", +                   "   · %d different “prefmodifiers” kinds.", +                   setsize (prefmodifiers))          pprint_top (prefmodifiers, 4) -        report ("both", 0, "db", -                "   · %d different “fontstyle_name” kinds.", -                setsize (fontstyle_name)) +        logreport ("both", 0, "db", +                   "   · %d different “fontstyle_name” kinds.", +                   setsize (fontstyle_name))          pprint_top (fontstyle_name, 4)      end @@ -3121,7 +3138,7 @@ update_names = function (currentnames, force, dry_run)      local conf = config.luaotfload      if conf.run.live ~= false and conf.db.update_live == false then -        report ("info", 2, "db", "Skipping database update.") +        logreport ("info", 2, "db", "Skipping database update.")          --- skip all db updates          return currentnames or name_index      end @@ -3133,15 +3150,16 @@ update_names = function (currentnames, force, dry_run)      - “targetnames” is the final table to return      - force is whether we rebuild it from scratch or not      ]] -    report("both", 1, "db", "Updating the font names database" -                         .. (force and " forcefully." or ".")) +    logreport ("both", 1, "db", +               "Updating the font names database" +               .. (force and " forcefully." or "."))      if config.luaotfload.db.skip_read == true then          --- the difference to a “dry run” is that we don’t search          --- for font files entirely. we also ignore the “force”          --- parameter since it concerns only the font files. -        report ("info", 2, "db", -                "Ignoring font files, reusing old data.") +        logreport ("info", 2, "db", +                   "Ignoring font files, reusing old data.")          currentnames = load_names (false)          targetnames  = currentnames      else @@ -3152,8 +3170,9 @@ update_names = function (currentnames, force, dry_run)                  currentnames = load_names (dry_run)              end              if currentnames.meta.version ~= names.version then -                report ("both", 1, "db", "No font names database or old " -                                    .. "one found; generating new one.") +                logreport ("both", 1, "db", +                           "No font names database or old \z +                            one found; generating new one.")                  currentnames = initialize_namedata (get_font_filter ())              end          end @@ -3169,16 +3188,16 @@ update_names = function (currentnames, force, dry_run)          --- pass 2: read font files (normal case) or reuse information          --- present in index -        n_rem = count_removed (currentnames.files.full) +        n_rem = count_removed (currentnames.files)          n_new = retrieve_namedata (font_filenames,                                     currentnames,                                     targetnames,                                     dry_run) -        report ("info", 3, "db", -                "Found %d font files; %d new, %d stale entries.", -                #font_filenames, n_new, n_rem) +        logreport ("info", 3, "db", +                   "Found %d font files; %d new, %d stale entries.", +                   #font_filenames, n_new, n_rem)      end      --- pass 3 (optional): collect some stats about the raw font info @@ -3204,27 +3223,27 @@ update_names = function (currentnames, force, dry_run)      --- pass 7: order design size tables      targetnames.families    = order_design_sizes (targetnames.families) -    report ("info", 3, "db", -            "Rebuilt in %0.f ms.", -            1000 * (osgettimeofday () - starttime)) +    logreport ("info", 3, "db", +               "Rebuilt in %0.f ms.", +               1000 * (osgettimeofday () - starttime))      name_index = targetnames      if dry_run ~= true then          if n_new + n_rem == 0 then -            report ("info", 2, "db", -                    "No new or removed fonts, skip saving to disk.") +            logreport ("info", 2, "db", +                       "No new or removed fonts, skip saving to disk.")          else              local success, reason = save_names ()              if not success then -                report ("both", 0, "db", -                        "Failed to save database to disk: %s", -                        reason) +                logreport ("both", 0, "db", +                           "Failed to save database to disk: %s", +                           reason)              end          end          if flush_lookup_cache () and save_lookups () then -            report ("both", 2, "cache", "Lookup cache emptied.") +            logreport ("both", 2, "cache", "Lookup cache emptied.")              return targetnames          end      end @@ -3241,18 +3260,18 @@ save_lookups = function ( )          caches.compile (lookup_cache, luaname, lucname)          --- double check ...          if lfsisfile (luaname) and lfsisfile (lucname) then -            report ("both", 3, "cache", "Lookup cache saved.") +            logreport ("both", 3, "cache", "Lookup cache saved.")              return true          end -        report ("info", 0, "cache", "Could not compile lookup cache.") +        logreport ("info", 0, "cache", "Could not compile lookup cache.")          return false      end -    report ("info", 0, "cache", "Lookup cache file not writable.") +    logreport ("info", 0, "cache", "Lookup cache file not writable.")      if not fileiswritable (luaname) then -        report ("info", 0, "cache", "Failed to write %s.", luaname) +        logreport ("info", 0, "cache", "Failed to write %s.", luaname)      end      if not fileiswritable (lucname) then -        report ("info", 0, "cache", "Failed to write %s.", lucname) +        logreport ("info", 0, "cache", "Failed to write %s.", lucname)      end      return false  end @@ -3281,33 +3300,33 @@ save_names = function (currentnames)              tabletofile (luaname, currentnames, true)              caches.compile (currentnames, luaname, lucname)          end -        report ("info", 2, "db", "Font index saved at ...") +        logreport ("info", 2, "db", "Font index saved at ...")          local success = false          if lfsisfile (luaname) then -            report ("info", 2, "db", "Text: " .. luaname) +            logreport ("info", 2, "db", "Text: " .. luaname)              success = true          end          if lfsisfile (gzname) then -            report ("info", 2, "db", "Gzip: " .. gzname) +            logreport ("info", 2, "db", "Gzip: " .. gzname)              success = true          end          if lfsisfile (lucname) then -            report ("info", 2, "db", "Byte: " .. lucname) +            logreport ("info", 2, "db", "Byte: " .. lucname)              success = true          end          if success then              return true          else -            report ("info", 0, "db", "Could not compile font index.") +            logreport ("info", 0, "db", "Could not compile font index.")              return false          end      end -    report ("info", 0, "db", "Index file not writable") +    logreport ("info", 0, "db", "Index file not writable")      if not fileiswritable (luaname) then -        report ("info", 0, "db", "Failed to write %s.", luaname) +        logreport ("info", 0, "db", "Failed to write %s.", luaname)      end      if not fileiswritable (lucname) then -        report ("info", 0, "db", "Failed to write %s.", lucname) +        logreport ("info", 0, "db", "Failed to write %s.", lucname)      end      return false  end @@ -3321,7 +3340,7 @@ end  --- string -> string -> string list -> string list -> string list -> unit  local print_cache = function (category, path, luanames, lucnames, rest)      local report_indeed = function (...) -        report("info", 0, "cache", ...) +        logreport ("info", 0, "cache", ...)      end      report_indeed("Luaotfload cache: %s", category)      report_indeed("location: %s", path) @@ -3333,15 +3352,15 @@ end  --- string -> string -> string list -> bool -> bool  local purge_from_cache = function (category, path, list, all) -    report("info", 1, "cache", "Luaotfload cache: %s %s", -        (all and "erase" or "purge"), category) -    report("info", 1, "cache", "location: %s",path) +    logreport ("info", 1, "cache", "Luaotfload cache: %s %s", +               (all and "erase" or "purge"), category) +    logreport ("info", 1, "cache", "location: %s", path)      local n = 0      for i=1,#list do          local filename = list[i]          if stringfind(filename,"luatex%-cache") then -- safeguard              if all then -                report("info", 5, "cache", "Removing %s.", filename) +                logreport ("info", 5, "cache", "Removing %s.", filename)                  osremove(filename)                  n = n + 1              else @@ -3350,7 +3369,7 @@ local purge_from_cache = function (category, path, list, all)                      local checkname = file.replacesuffix(                          filename, "lua", "luc")                      if lfsisfile(checkname) then -                        report("info", 5, "cache", "Removing %s.", filename) +                        logreport ("info", 5, "cache", "Removing %s.", filename)                          osremove(filename)                          n = n + 1                      end @@ -3358,7 +3377,7 @@ local purge_from_cache = function (category, path, list, all)              end          end      end -    report("info", 1, "cache", "Removed lua files : %i", n) +    logreport ("info", 1, "cache", "Removed lua files : %i", n)      return true  end @@ -3435,7 +3454,7 @@ local erase_cache = function ( )  end  local separator = function ( ) -    report("info", 0, string.rep("-", 67)) +    logreport ("info", 0, string.rep("-", 67))  end  --- unit -> unit @@ -3464,35 +3483,55 @@ end  --- export functionality to the namespace “fonts.names”  ----------------------------------------------------------------------- -names.set_font_filter             = set_font_filter -names.flush_lookup_cache          = flush_lookup_cache -names.save_lookups                = save_lookups -names.load                        = load_names -names.access_font_index           = access_font_index -names.data                        = function () return name_index end -names.save                        = save_names -names.update                      = update_names -names.lookup_font_file            = lookup_font_file -names.lookup_font_name            = lookup_font_name -names.lookup_font_name_cached     = lookup_font_name_cached -names.getfilename                 = lookup_fullpath -names.lookup_fullpath             = lookup_fullpath -names.read_blacklist              = read_blacklist -names.sanitize_fontname           = sanitize_fontname -names.getmetadata                 = getmetadata -names.set_location_precedence     = set_location_precedence -names.count_font_files            = count_font_files -names.nth_font_filename           = nth_font_filename -names.font_slice                  = font_slice - ---- font cache -names.purge_cache    = purge_cache -names.erase_cache    = erase_cache -names.show_cache     = show_cache - -names.find_closest = find_closest - --- for testing purpose -names.read_fonts_conf = read_fonts_conf +local export = { +    set_font_filter             = set_font_filter, +    flush_lookup_cache          = flush_lookup_cache, +    save_lookups                = save_lookups, +    load                        = load_names, +    access_font_index           = access_font_index, +    data                        = function () return name_index end, +    save                        = save_names, +    update                      = update_names, +    lookup_font_file            = lookup_font_file, +    lookup_font_name            = lookup_font_name, +    lookup_font_name_cached     = lookup_font_name_cached, +    getfilename                 = lookup_fullpath, +    lookup_fullpath             = lookup_fullpath, +    read_blacklist              = read_blacklist, +    sanitize_fontname           = sanitize_fontname, +    getmetadata                 = getmetadata, +    set_location_precedence     = set_location_precedence, +    count_font_files            = count_font_files, +    nth_font_filename           = nth_font_filename, +    font_slice                  = font_slice, +    --- font cache +    purge_cache                 = purge_cache, +    erase_cache                 = erase_cache, +    show_cache                  = show_cache, +    find_closest                = find_closest, +    -- for testing purpose +} + +return { +    init = function () +        --- the font loader namespace is “fonts”, same as in Context +        --- we need to put some fallbacks into place for when running +        --- as a script +        if not fonts then return false end +        logreport       = luaotfload.log.report +        local fonts     = fonts +        fonts.names     = fonts.names or names +        fonts.formats   = fonts.formats or { } +        fonts.definers  = fonts.definers or { resolvers = { } } + +        names.blacklist = blacklist +        names.version   = 2.51 +        names.data      = nil      --- contains the loaded database +        names.lookups   = nil      --- contains the lookup cache + +        for sym, ref in next, export do names[sym] = ref end +        return true +    end +}  -- vim:tw=71:sw=4:ts=4:expandtab diff --git a/src/luaotfload-diagnostics.lua b/src/luaotfload-diagnostics.lua index 80e461c..582105a 100644 --- a/src/luaotfload-diagnostics.lua +++ b/src/luaotfload-diagnostics.lua @@ -4,8 +4,6 @@  --  DESCRIPTION:  functionality accessible by the --diagnose option  -- REQUIREMENTS:  luaotfload-tool.lua  --       AUTHOR:  Philipp Gesang (Phg), <phg42.2a@gmail.com> ---      VERSION:  2.5 ---     MODIFIED:  2014-01-02 21:23:06+0100  -----------------------------------------------------------------------  --  local names                    = fonts.names @@ -655,27 +653,27 @@ local diagnose = function (job)                you may sleep well.")          return true, false      end -    out (         [[=============================================== -                                        WARNING -                    =============================================== +    out (             [[=============================================== +                                            WARNING +                        =============================================== -                    The diagnostic detected %d errors. +                        The diagnostic detected %d errors. -                    This version of luaotfload may have been -                    tampered with. Modified versions of the -                    luaotfload source are unsupported. Read the log -                    carefully and get a clean version from CTAN or -                    github: +                        This version of luaotfload may have been +                        tampered with. Modified versions of the +                        luaotfload source are unsupported. Read the log +                        carefully and get a clean version from CTAN or +                        github: -                        × http://www.ctan.org/pkg/luaotfload  -                        × https://github.com/lualatex/luaotfload/releases +                            × http://www.ctan.org/pkg/luaotfload  +                            × https://github.com/lualatex/luaotfload/releases -                    If you are uncertain as to how to proceed, then -                    ask on the lualatex mailing list: +                        If you are uncertain as to how to proceed, then +                        ask on the lualatex mailing list: -                        http://www.tug.org/mailman/listinfo/lualatex-dev +                            http://www.tug.org/mailman/listinfo/lualatex-dev -                    =============================================== +                        ===============================================  ]],          errcnt)      return true, false  end diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua index 9b895ce..6fb2114 100644 --- a/src/luaotfload-features.lua +++ b/src/luaotfload-features.lua @@ -921,24 +921,13 @@ end  ---[[ end included font-ltx.lua ]] ---[[doc-- -This uses the code from luatex-fonts-merged (<- font-otc.lua) instead -of the removed luaotfload-font-otc.lua. - -TODO find out how far we get setting features without these lines, -relying on luatex-fonts only (it *does* handle features somehow, after -all). ---doc]]-- - --- we assume that the other otf stuff is loaded already +-- We assume that the other otf stuff is loaded already; though there’s +-- another check below during the initialization phase.  ---[[ begin snippet from font-otc.lua ]]  local trace_loading       = false  trackers.register("otf.loading", function(v) trace_loading = v end)  local report_otf          = logs.reporter("fonts","otf loading") -local otf                 = fonts.handlers.otf -local registerotffeature  = otf.features.register -  --[[HH--     In the userdata interface we can not longer tweak the loaded font as @@ -960,7 +949,7 @@ setmetatableindex(types, function(t,k) t[k] = k return k end) -- "key"  local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } }  local noflags    = { } -local function addfeature(data,feature,specifications) +local function addfeature (data, feature, specifications)      local descriptions = data.descriptions      local resources    = data.resources      local lookups      = resources.lookups @@ -1100,26 +1089,9 @@ local function addfeature(data,feature,specifications)      end  end - -otf.enhancers.addfeature = addfeature - -local extrafeatures = { } - -function otf.addfeature(name,specification) -    extrafeatures[name] = specification -end - -local function enhance(data,filename,raw) -    for feature, specification in next, extrafeatures do -        addfeature(data,feature,specification) -    end -end - -otf.enhancers.register("check extra features",enhance) -  ---[[ end snippet from font-otc.lua ]] -local tlig = { +local tlig_specification = {      {          type      = "substitution",          features  = everywhere, @@ -1167,9 +1139,6 @@ local tlig = {      },  } -otf.addfeature ("tlig", tlig) -otf.addfeature ("trep", { }) -  local anum_arabic = { --- these are the same as in font-otc      [0x0030] = 0x0660,      [0x0031] = 0x0661, @@ -1228,11 +1197,45 @@ local anum_specification = {      },  } -otf.addfeature ("anum", anum_specification) +return { +    init = function () + +        if not fonts and fonts.handlers then +            logreport ("log", 0, "color", +                       "OTF mechanisms missing -- did you forget to \z +                       load a font loader?") +            return false +        end + +        local otf = fonts.handlers.otf -registerotffeature { -    name        = "anum", -    description = "arabic digits", +        local extrafeatures = { +            tlig = tlig_specification, +            trep = { }, +            anum = anum_specification, +        } + +        otf.enhancers.register ("check extra features", +                                function (data,filename, raw) +                                    for feature, specification in next, extrafeatures do +                                        addfeature (data, feature, specification) +                                    end +                                end) + +        logreport = luaotfload.log.report +        if not fonts then +            logreport ("log", 0, "color", +                       "OTF mechanisms missing -- did you forget to \z +                       load a font loader?") +            return false +        end + +        otf.features.register { +            name        = "anum", +            description = "arabic digits", +        } +        return true +    end  }  -- vim:tw=71:sw=4:ts=4:expandtab diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua index a493cc1..0f7464a 100644 --- a/src/luaotfload-init.lua +++ b/src/luaotfload-init.lua @@ -4,12 +4,11 @@  --  DESCRIPTION:  Luaotfload font loader initialization  -- REQUIREMENTS:  luatex v.0.80 or later; packages lualibs, luatexbase  --       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net> ---      VERSION:  1.0 ---      CREATED:  2015-05-26 07:50:54+0200  -----------------------------------------------------------------------  --  local setmetatable = setmetatable +local kpselookup   = kpse.lookup  --[[doc-- @@ -38,8 +37,8 @@ local logreport  --- filled in after loading the log module  --[[doc--      \subsection{Preparing the Font Loader} -    We treat the fontloader as a black box so behavior is consistent -    between formats. +    We treat the fontloader as a semi-black box so behavior is +    consistent between formats.      We load the fontloader code directly in the same fashion as the      Plain format \identifier{luatex-fonts} that is part of Context.      How this is executed depends on the presence on the @@ -183,6 +182,78 @@ local pop_namespaces = function (normalglobal,      end  end +--- below paths are relative to the texmf-context +local ltx = "tex/generic/context/luatex" +local ctx = "tex/context/base" + +local context_modules = { + +  --- Since 2.6 those are directly provided by the Lualibs package. +  { false, "l-lua"      }, +  { false, "l-lpeg"     }, +  { false, "l-function" }, +  { false, "l-string"   }, +  { false, "l-table"    }, +  { false, "l-io"       }, +  { false, "l-file"     }, +  { false, "l-boolean"  }, +  { false, "l-math"     }, +  { false, "util-str"   }, + +  --- These constitute the fontloader proper. +  { ltx,   "luatex-basics-gen" }, +  { ctx,   "data-con"          }, +  { ltx,   "luatex-basics-nod" }, +  { ctx,   "font-ini"          }, +  { ctx,   "font-con"          }, +  { ltx,   "luatex-fonts-enc"  }, +  { ctx,   "font-cid"          }, +  { ctx,   "font-map"          }, +  { ltx,   "luatex-fonts-syn"  }, +  { ltx,   "luatex-fonts-tfm"  }, +  { ctx,   "font-oti"          }, +  { ctx,   "font-otf"          }, +  { ctx,   "font-otb"          }, +  { ltx,   "luatex-fonts-inj"  }, --> since 2014-01-07, replaces node-inj.lua +  { ltx,   "luatex-fonts-ota"  }, +  { ltx,   "luatex-fonts-otn"  }, --> since 2014-01-07, replaces font-otn.lua +  { ctx,   "font-otp"          }, --> since 2013-04-23 +  { ltx,   "luatex-fonts-lua"  }, +  { ctx,   "font-def"          }, +  { ltx,   "luatex-fonts-def"  }, +  { ltx,   "luatex-fonts-ext"  }, +  { ltx,   "luatex-fonts-cbk"  }, + +} --[[context_modules]] + +local load_context_modules = function (pth) + +  local load_module   = luaotfload.loaders.context +  local ignore_module = luaotfload.loaders.ignore + +  logreport ("both", 2, "init", +             "Loading fontloader components from context.") +  local n = #context_modules +  for i = 1, n do +    local sub, spec = unpack (context_modules [i]) +    if sub == false then +      ignore_module (spec) +    elseif type (sub) == "string" then +      if pth then +        load_module (spec, file.join (pth, sub)) +      else +        load_module (spec) +      end +    else +      logreport ("both", 0, "init", +                 "Internal error, please report. \z +                  This is not your fault.") +      os.exit (-1) +    end +  end + +end +  local init_adapt = function ()    local context_environment  = { } @@ -205,6 +276,7 @@ end --- [init_adapt]  local init_main = function ()    local load_fontloader_module = luaotfload.loaders.fontloader +  local ignore_module          = luaotfload.loaders.ignore    --[[doc-- @@ -216,49 +288,103 @@ local init_main = function ()    --doc]]-- -  load_fontloader_module (luaotfload.fontloader_package) +  local fontloader = config.luaotfload and config.luaotfload.run.fontloader +                                        or "reference" +  fontloader = tostring (fontloader) -  ---load_fontloader_module "font-odv.lua" --- <= Devanagari support from Context +  if fontloader == "reference" or fontloader == "default" then +    logreport ("log", 4, "init", "Using reference fontloader.") +    load_fontloader_module (luaotfload.fontloader_package) -  if not fonts then -    --- the loading sequence is known to change, so this might have to -    --- be updated with future updates! -    --- do not modify it though unless there is a change to the merged -    --- package! -    load_fontloader_module "l-lua" -    load_fontloader_module "l-lpeg" -    load_fontloader_module "l-function" -    load_fontloader_module "l-string" -    load_fontloader_module "l-table" -    load_fontloader_module "l-io" -    load_fontloader_module "l-file" -    load_fontloader_module "l-boolean" -    load_fontloader_module "l-math" -    load_fontloader_module "util-str" -    load_fontloader_module "luatex-basics-gen" +  elseif fontloader == "unpackaged" then +    logreport ("both", 4, "init", +               "Loading fontloader components individually.") +    --- The loading sequence is known to change, so this might have to be +    --- updated with future updates. Do not modify it though unless there is +    --- a change to the upstream package! + +    --- Since 2.6 those are directly provided by the Lualibs package. +    ignore_module "l-lua" +    ignore_module "l-lpeg" +    ignore_module "l-function" +    ignore_module "l-string" +    ignore_module "l-table" +    ignore_module "l-io" +    ignore_module "l-file" +    ignore_module "l-boolean" +    ignore_module "l-math" +    ignore_module "util-str" +    ignore_module "luatex-basics-gen" + +    --- These constitute the fontloader proper.      load_fontloader_module "data-con" -    load_fontloader_module "luatex-basics-nod" +    load_fontloader_module "basics-nod"      load_fontloader_module "font-ini"      load_fontloader_module "font-con" -    load_fontloader_module "luatex-fonts-enc" +    load_fontloader_module "fonts-enc"      load_fontloader_module "font-cid"      load_fontloader_module "font-map" -    load_fontloader_module "luatex-fonts-syn" -    load_fontloader_module "luatex-fonts-tfm" +    load_fontloader_module "fonts-syn" +    load_fontloader_module "fonts-tfm"      load_fontloader_module "font-oti"      load_fontloader_module "font-otf"      load_fontloader_module "font-otb" -    load_fontloader_module "luatex-fonts-inj"  --> since 2014-01-07, replaces node-inj.lua -    load_fontloader_module "luatex-fonts-ota" -    load_fontloader_module "luatex-fonts-otn"  --> since 2014-01-07, replaces font-otn.lua -    load_fontloader_module "font-otp"          --> since 2013-04-23 -    load_fontloader_module "luatex-fonts-lua" +    load_fontloader_module "fonts-inj"  --> since 2014-01-07, replaces node-inj.lua +    load_fontloader_module "fonts-ota" +    load_fontloader_module "fonts-otn"  --> since 2014-01-07, replaces font-otn.lua +    load_fontloader_module "font-otp"   --> since 2013-04-23 +    load_fontloader_module "fonts-lua"      load_fontloader_module "font-def" -    load_fontloader_module "luatex-fonts-def" -    load_fontloader_module "luatex-fonts-ext" -    load_fontloader_module "luatex-fonts-cbk" -  end --- non-merge fallback scope +    load_fontloader_module "fonts-def" +    load_fontloader_module "fonts-ext" +    load_fontloader_module "fonts-cbk" + +  elseif fontloader == "context" then +    logreport ("both", 2, "init", +               "Attempting to load Context modules in lookup path.") +    load_context_modules () + +  elseif lfs.isdir (fontloader) then +    logreport ("both", 2, "init", +               "Attempting to load Context files under prefix “%s”.", +               fontloader) +    load_context_modules (fontloader) + +  elseif lfs.isfile (fontloader) then +    logreport ("both", 2, "init", +               "Attempting to load fontloader from absolute path “%s”.", +               fontloader) +    local _void = require (fontloader) + +  elseif kpselookup (fontloader) then +    local pth = kpselookup (fontloader) +    logreport ("both", 2, "init", +               "Attempting to load fontloader “%s” from kpse-resolved path “%s”.", +               fontloader, path) +    local _void = require (path) + +  elseif fontloader then +    logreport ("log", 4, "init", +               "Using predefined fontloader “%s”.", +               fontloader) +    load_fontloader_module (fontloader) + +  else +    logreport ("log", 4, "init", +               "No match for requested fontloader “%s”.", +               fontloader) +    fontloader = luaotfload.fontloader_package +    logreport ("log", 4, "init", +               "Defaulting to predefined fontloader “%s”.", +               fontloader) +    load_fontloader_module (fontloader) +  end + +  ---load_fontloader_module "font-odv.lua" --- <= Devanagari support from Context +  logreport ("both", 0, "init", +             "Context OpenType loader version “%s”", +             fonts.handlers.otf.version)  end --- [init_main]  local init_cleanup = function (store) @@ -431,6 +557,8 @@ return {                 os.gettimeofday() - starttime)      local n = init_post ()      logreport ("both", 5, "init", "post hook terminated, %d actions performed", n) +    return true    end  } +-- vim:tw=79:sw=2:ts=2:expandtab diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua index 901d4d8..38062f6 100644 --- a/src/luaotfload-loaders.lua +++ b/src/luaotfload-loaders.lua @@ -1,34 +1,141 @@ -if not modules then modules = { } end modules ["loaders"] = { -    version   = "2.5", -    comment   = "companion to luaotfload-main.lua", -    author    = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", -    copyright = "PRAGMA ADE / ConTeXt Development Team", -    license   = "see context related readme files" -} +#!/usr/bin/env texlua +----------------------------------------------------------------------- +--         FILE:  luaotfload-loaders.lua +--  DESCRIPTION:  Luaotfload callback handling +-- REQUIREMENTS:  luatex v.0.80 or later; packages lualibs, luatexbase +--       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net>, Hans Hagen, Khaled Hosny, Elie Roux +----------------------------------------------------------------------- +-- +--- Contains parts of the earlier main script. + +if not lualibs    then error "this module requires Luaotfload" end +if not luaotfload then error "this module requires Luaotfload" end + +local logreport = luaotfload.log and luaotfload.log.report or print + +local install_formats = function () +  local fonts = fonts +  if not fonts then return false end + +  local readers  = fonts.readers +  local handlers = fonts.handlers +  local formats  = fonts.formats +  if not readers or not handlers or not formats then return false end -local fonts           = fonts -local readers         = fonts.readers -local handlers        = fonts.handlers -local formats         = fonts.formats - -local pfb_reader = function (specification) -  return readers.opentype (specification, "pfb", "type1") -end  -  -local pfa_reader = function (specification) -  return readers.opentype (specification, "pfa", "type1") +  local aux = function (which, reader) +    if   not which  or type (which) ~= "string" +      or not reader or type (reader) ~= "function" then +      logreport ("both", 2, "loaders", "Error installing reader for “%s”.", which) +      return false +    end +    formats  [which] = "type1" +    readers  [which] = reader +    handlers [which] = { } +    return true +  end + +  return aux ("pfa", function (spec) return readers.opentype (spec, "pfa", "type1") end) +     and aux ("pfb", function (spec) return readers.opentype (spec, "pfb", "type1") end) +     and aux ("ofm", readers.tfm)  end -formats.pfa  = "type1" -readers.pfa  = pfa_reader -handlers.pfa = { } +--[[doc-- + +    \subsection{\CONTEXT override} +    \label{define-font} +    We provide a simplified version of the original font definition +    callback. + +--doc]]-- + + +local definers --- (string, spec -> size -> id -> tmfdata) hash_t +do +  local read = fonts.definers.read -formats.pfb  = "type1" -readers.pfb  = pfb_reader -handlers.pfb = { } +  local patch = function (specification, size, id) +    local fontdata = read (specification, size, id) +    if type (fontdata) == "table" and fontdata.shared then +      --- We need to test for the “shared” field here +      --- or else the fontspec capheight callback will +      --- operate on tfm fonts. +      luatexbase.call_callback ("luaotfload.patch_font", fontdata, specification) +    else +      luatexbase.call_callback ("luaotfload.patch_font_unsafe", fontdata, specification) +    end +    return fontdata +  end -formats.ofm  = "type1" -readers.ofm  = readers.tfm -handlers.ofm = { } +  local mk_info = function (name) +    local definer = name == "patch" and patch or read +    return function (specification, size, id) +      logreport ("both", 0, "loaders", "defining font no. %d", id) +      logreport ("both", 0, "loaders", "   > active font definer: %q", name) +      logreport ("both", 0, "loaders", "   > spec %q", specification) +      logreport ("both", 0, "loaders", "   > at size %.2f pt", size / 2^16) +      local result = definer (specification, size, id) +      if not result then +        logreport ("both", 0, "loaders", "   > font definition failed") +        return +      elseif type (result) == "number" then +        logreport ("both", 0, "loaders", "   > font definition yielded id %d", result) +        return result +      end +      logreport ("both", 0, "loaders", "   > font definition successful") +      logreport ("both", 0, "loaders", "   > name %q",     result.name     or "<nil>") +      logreport ("both", 0, "loaders", "   > fontname %q", result.fontname or "<nil>") +      logreport ("both", 0, "loaders", "   > fullname %q", result.fullname or "<nil>") +      return result +    end +  end --- vim:tw=71:sw=2:ts=2:expandtab +  definers = { +    patch          = patch, +    generic        = read, +    info_patch     = mk_info "patch", +    info_generic   = mk_info "generic", +  } +end + +--[[doc-- + +  We create callbacks for patching fonts on the fly, to be used by +  other packages. In addition to the regular \identifier{patch_font} +  callback there is an unsafe variant \identifier{patch_font_unsafe} +  that will be invoked even if the target font lacks certain essential +  tfmdata tables. + +  The callbacks initially contain the empty function that we are going +  to override below. + +--doc]]-- + +local install_callbacks = function () +  local create_callback  = luatexbase.create_callback +  local dummy_function   = function () end +  create_callback ("luaotfload.patch_font",        "simple", dummy_function) +  create_callback ("luaotfload.patch_font_unsafe", "simple", dummy_function) +  luatexbase.reset_callback "define_font" +  local definer = config.luaotfload.run.definer +  luatexbase.add_to_callback ("define_font", +                              definers[definer or "patch"], +                              "luaotfload.define_font", +                              1) +  return true +end + +return { +  init = function () +    local ret = true +    if not install_formats () then +      logreport ("log", 0, "loaders", "Error initializing OFM/PF{A,B} loaders.") +      ret = false +    end +    if not install_callbacks () then +      logreport ("log", 0, "loaders", "Error installing font loader callbacks.") +      ret = false +    end +    return ret +  end +} +-- vim:tw=79:sw=2:ts=2:expandtab diff --git a/src/luaotfload-log.lua b/src/luaotfload-log.lua index 7c012f4..e5db310 100644 --- a/src/luaotfload-log.lua +++ b/src/luaotfload-log.lua @@ -91,7 +91,7 @@ local set_logout = function (s, finalizers)          logout = "redirect"          local chan = choose_logfile ()          chan:write (stringformat ("logging initiated at %s", -                                  osdate ("%Y-%m-%d %h:%m:%s", --- i. e. osdate "%F %T" +                                  osdate ("%Y-%m-%d %H:%M:%S", --- i. e. osdate "%F %T"                                            ostime ())))          local writefile = function (...)              if select ("#", ...) == 2 then @@ -118,7 +118,7 @@ local set_logout = function (s, finalizers)          finalizers[#finalizers+1] = function ()              chan:write (stringformat ("\nlogging finished at %s\n", -                                      osdate ("%Y-%m-%d %h:%m:%s", --- i. e. osdate "%F %T" +                                      osdate ("%Y-%m-%d %H:%M:%S", --- i. e. osdate "%F %T"                                                ostime ())))              chan:close ()              texiowrite    = texio.write diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua index b633ed7..62765e4 100644 --- a/src/luaotfload-main.lua +++ b/src/luaotfload-main.lua @@ -3,23 +3,17 @@  --  DESCRIPTION:  Luaotfload entry point  -- REQUIREMENTS:  luatex v.0.80 or later; packages lualibs, luatexbase  --       AUTHOR:  Élie Roux, Khaled Hosny, Philipp Gesang ---      VERSION:  same as Luaotfload ---     MODIFIED:  2015-06-09 23:08:18+0200  -----------------------------------------------------------------------  -- ---- Note: ---- This file was part of the original luaotfload.dtx and has been ---- converted to a pure Lua file during the transition from Luaotfload ---- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex) ---- markup. -local initial_log_level           = 0 +local osgettimeofday              = os.gettimeofday +config                            = config     or { }  luaotfload                        = luaotfload or { }  local luaotfload                  = luaotfload  luaotfload.log                    = luaotfload.log or { }  luaotfload.version                = "2.6"  luaotfload.loaders                = { } -luaotfload.min_luatex_version     = 79             --- i. e. 0.79 +luaotfload.min_luatex_version     = 80             --- i. e. 0.79  luaotfload.fontloader_package     = "reference"    --- default: from current Context  local authors = "\z @@ -36,7 +30,7 @@ local authors = "\z  luaotfload.module = {      name          = "luaotfload-main",      version       = 2.60001, -    date          = "2015/05/26", +    date          = "2015/11/05",      description   = "OpenType layout system.",      author        = authors,      copyright     = authors, @@ -67,23 +61,10 @@ luaotfload.module = {  local luatexbase       = luatexbase  local require          = require  local type             = type -local add_to_callback  = luatexbase.add_to_callback -local create_callback  = luatexbase.create_callback -local reset_callback   = luatexbase.reset_callback -local call_callback    = luatexbase.call_callback -local dummy_function = function () end --- XXX this will be moved to the luaotfload namespace when we have the init module - -local error, warning, info, log = +local _error, _warning, _info, _log =      luatexbase.provides_module(luaotfload.module) -luaotfload.log.tex        = { -    error        = error, -    warning      = warning, -    info         = info, -    log          = log, -} -  --[[doc--       We set the minimum version requirement for \LUATEX to v0.76, @@ -117,153 +98,166 @@ end  --doc]]--  local make_loader_name = function (prefix, name) -    local msg = luaotfload.log and luaotfload.log.report or print -    if prefix then -        msg ("log", 7, "load", -             "Composing fontloader name from constitutents %s, %s", -             prefix, name) -        return prefix .. "-" .. name .. ".lua" +    local msg = luaotfload.log and luaotfload.log.report +             or function (...) texio.write_nl ("log", ...) end +    if not name then +        msg ("both", 0, "load", +             "Fatal error: make_loader_name (“%s”, “%s”).", +             tostring (prefix), tostring (name)) +        return "dummy-name"      end -    msg ("log", 7, "load", -         "Loading fontloader file %s literally.", -         name) -    return name +    name = tostring (name) +    if prefix == false then +        msg ("log", 9, "load", +             "No prefix requested, passing module name “%s” unmodified.", +             name) +        return tostring (name) .. ".lua" +    end +    prefix = tostring (prefix) +    msg ("log", 9, "load", +         "Composing module name from constituents %s, %s.", +         prefix, name) +    return prefix .. "-" .. name .. ".lua"  end +local timing_info = { +    t_load = { }, +    t_init = { }, +} +  local make_loader = function (prefix)      return function (name) +        local t_0 = osgettimeofday ()          local modname = make_loader_name (prefix, name) -        return require (modname) +        local data = require (modname) +        local t_end = osgettimeofday () +        timing_info.t_load [name] = t_end - t_0 +        return data      end  end -local load_luaotfload_module = make_loader "luaotfload" ------ load_luaotfload_module = make_loader "luatex" --=> for Luatex-Plain -local load_fontloader_module = make_loader "fontloader" - -luaotfload.loaders.luaotfload = load_luaotfload_module -luaotfload.loaders.fontloader = load_fontloader_module - -luaotfload.init = load_luaotfload_module "init" --- fontloader initialization - -local store           = luaotfload.init.early () -local log             = luaotfload.log -local logreport       = log.report -  --[[doc-- - -    Now we load the modules written for \identifier{luaotfload}. - +    Certain files are kept around that aren’t loaded because they are part of +    the imported fontloader. In order to keep the initialization structure +    intact we also provide a no-op version of the module loader that can be +    called in the expected places.  --doc]]-- -load_luaotfload_module "parsers"         --- fonts.conf and syntax -load_luaotfload_module "configuration"   --- configuration options - -if not config.actions.apply_defaults () then -    logreport ("log", 0, "load", "Configuration unsuccessful.") +local dummy_loader = function (name) +    luaotfload.log.report ("log", 3, "load", +                           "Skipping module “%s” on purpose.", +                           name)  end -luaotfload.init.main (store) - -load_luaotfload_module "loaders"         --- Type1 font wrappers -load_luaotfload_module "database"        --- Font management. -load_luaotfload_module "colors"          --- Per-font colors. - -luaotfload.resolvers = load_luaotfload_module "resolvers" --- Font lookup - -luaotfload.resolvers.install () - -if not config.actions.reconfigure () then -    logreport ("log", 0, "load", "Post-configuration hooks failed.") -end - ---[[doc-- - -    We create callbacks for patching fonts on the fly, to be used by -    other packages. In addition to the regular \identifier{patch_font} -    callback there is an unsafe variant \identifier{patch_font_unsafe} -    that will be invoked even if the target font lacks certain essential -    tfmdata tables. - -    The callbacks initially contain the empty function that we are going to -    override below. - ---doc]]-- - -create_callback("luaotfload.patch_font",        "simple", dummy_function) -create_callback("luaotfload.patch_font_unsafe", "simple", dummy_function) - ---[[doc-- - -    \subsection{\CONTEXT override} -    \label{define-font} -    We provide a simplified version of the original font definition -    callback. - ---doc]]-- - - -local definers = { } --- (string, spec -> size -> id -> tmfdata) hash_t -do -    local read = fonts.definers.read - -    local patch = function (specification, size, id) -        local fontdata = read (specification, size, id) -        if type (fontdata) == "table" and fontdata.shared then -            --- We need to test for the “shared” field here -            --- or else the fontspec capheight callback will -            --- operate on tfm fonts. -            call_callback ("luaotfload.patch_font", fontdata, specification) +local context_loader = function (name, path) +    luaotfload.log.report ("log", 3, "load", +                           "Loading module “%s” from Context.", +                           name) +    local t_0 = osgettimeofday () +    local modname = make_loader_name (false, name) +    local modpath = modname +    if path then +        if lfs.isdir (path) then +            luaotfload.log.report ("log", 3, "load", +                                   "Prepending path “%s”.", +                                   path) +            modpath = file.join (path, modname)          else -            call_callback ("luaotfload.patch_font_unsafe", fontdata, specification) +            luaotfload.log.report ("both", 0, "load", +                                   "Non-existant path “%s” specified, ignoring.", +                                   path)          end -        return fontdata      end +    local ret = require (modpath) +    local t_end = osgettimeofday () +    timing_info.t_load [name] = t_end - t_0 + +    if ret ~= true then +        --- require () returns “true” upon success unless the loaded file +        --- yields a non-zero exit code. This isn’t per se indicating that +        --- something isn’t right, but against HH’s coding practices. We’ll +        --- silently ignore this ever happening on lower log levels. +        luaotfload.log.report ("log", 4, "load", +                               "Module “%s” returned “%s”.", ret) +    end +    return ret +end -    local mk_info = function (name) -        local definer = name == "patch" and patch or read -        return function (specification, size, id) -            logreport ("both", 0, "main", "defining font no. %d", id) -            logreport ("both", 0, "main", "   > active font definer: %q", name) -            logreport ("both", 0, "main", "   > spec %q", specification) -            logreport ("both", 0, "main", "   > at size %.2f pt", size / 2^16) -            local result = definer (specification, size, id) -            if not result then -                logreport ("both", 0, "main", "   > font definition failed") -                return -            elseif type (result) == "number" then -                logreport ("both", 0, "main", "   > font definition yielded id %d", result) -                return result +local install_loaders = function () +    local loaders      = { } +    local loadmodule   = make_loader "luaotfload" +    loaders.luaotfload = loadmodule +    loaders.fontloader = make_loader "fontloader" +    loaders.context    = context_loader +    loaders.ignore     = dummy_loader +----loaders.plaintex   = make_loader "luatex" --=> for Luatex-Plain + +    loaders.initialize = function (name) +        local tmp       = loadmodule (name) +        local logreport = luaotfload.log.report +        if type (tmp) == "table" then +            local init = tmp.init +            if init and type (init) == "function" then +                local t_0 = osgettimeofday () +                if not init () then +                    logreport ("log", 0, "load", +                               "Failed to load module “%s”.", name) +                    return +                end +                local t_end = osgettimeofday () +                local d_t = t_end - t_0 +                logreport ("log", 4, "load", +                           "Module “%s” loaded in %d ms.", +                           name, d_t) +                timing_info.t_init [name] = d_t              end -            logreport ("both", 0, "main", "   > font definition successful") -            logreport ("both", 0, "main", "   > name %q",     result.name     or "<nil>") -            logreport ("both", 0, "main", "   > fontname %q", result.fontname or "<nil>") -            logreport ("both", 0, "main", "   > fullname %q", result.fullname or "<nil>") -            return result          end      end -    definers.patch          = patch -    definers.generic        = read -    definers.info_patch     = mk_info "patch" -    definers.info_generic   = mk_info "generic" +    return loaders  end -reset_callback "define_font" ---[[doc-- +luaotfload.main = function () -    Finally we register the callbacks. +    luaotfload.loaders = install_loaders () +    local loaders    = luaotfload.loaders +    local loadmodule = loaders.luaotfload +    local initialize = loaders.initialize ---doc]]-- +    local starttime = osgettimeofday () +    local init      = loadmodule "init" --- fontloader initialization +    local store     = init.early ()     --- injects the log module too +    local logreport = luaotfload.log.report + +    initialize "parsers"         --- fonts.conf and syntax +    initialize "configuration"   --- configuration options + +    if not init.main (store) then +        logreport ("log", 0, "load", "Main fontloader initialization failed.") +    end -local definer = config.luaotfload.run.definer -add_to_callback ("define_font", definers[definer], "luaotfload.define_font", 1) +    initialize "loaders"         --- Font loading; callbacks +    initialize "database"        --- Font management. +    initialize "colors"          --- Per-font colors. -load_luaotfload_module "features"     --- font request and feature handling -load_luaotfload_module "letterspace"  --- extra character kerning -load_luaotfload_module "auxiliary"    --- additional high-level functionality +    luaotfload.resolvers = loadmodule "resolvers" --- Font lookup +    luaotfload.resolvers.init () -luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec +    if not config.actions.reconfigure () then +        logreport ("log", 0, "load", "Post-configuration hooks failed.") +    end + +    initialize "features"     --- font request and feature handling +    loadmodule "letterspace"  --- extra character kerning +    loadmodule "auxiliary"    --- additional high-level functionality + +    luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec + +    logreport ("both", 0, "main", +               "initialization completed in %0.3f seconds", +               osgettimeofday() - starttime) +----inspect (timing_info) +end  -- vim:tw=79:sw=4:ts=4:et diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua index a52b5d4..0349cdc 100644 --- a/src/luaotfload-parsers.lua +++ b/src/luaotfload-parsers.lua @@ -2,26 +2,12 @@  -------------------------------------------------------------------------------  --         FILE:  luaotfload-parsers.lua  --  DESCRIPTION:  various lpeg-based parsers used in Luaotfload --- REQUIREMENTS:  Luaotfload > 2.4 ---       AUTHOR:  Philipp Gesang (Phg), <phg42.2a@gmail.com> ---      VERSION:  same as Luaotfload ---      CREATED:  2014-01-14 10:15:20+0100 +-- REQUIREMENTS:  Luaotfload >= 2.6 +--       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net>  -------------------------------------------------------------------------------  -- -if not modules then modules = { } end modules ['luaotfload-parsers'] = { -  version   = "2.5", -  comment   = "companion to luaotfload-main.lua", -  author    = "Philipp Gesang", -  copyright = "Luaotfload Development Team", -  license   = "GNU GPL v2.0" -} - -luaotfload                 = luaotfload or { } -luaotfload.parsers         = luaotfload.parsers or { } -local parsers              = luaotfload.parsers -parsers.traversal_maxdepth = 42 --- prevent stack overflows -local traversal_maxdepth   = parsers.traversal_maxdepth --- TODO could be an option +local traversal_maxdepth  = 42 --- prevent stack overflows  local rawset            = rawset @@ -42,8 +28,7 @@ local filedirname       = file.dirname  local io                = io  local ioopen            = io.open -local log               = luaotfload.log -local logreport         = log.report +local logreport         = print  local string            = string  local stringsub         = string.sub @@ -399,10 +384,6 @@ local read_fonts_conf = function (path_list, find_files)    return acc  end -luaotfload.parsers.read_fonts_conf = read_fonts_conf - - -  -------------------------------------------------------------------------------  ---                               MISC PARSERS  ------------------------------------------------------------------------------- @@ -410,10 +391,8 @@ luaotfload.parsers.read_fonts_conf = read_fonts_conf  local trailingslashes   = slash^1 * P(-1)  local stripslashes      = C((1 - trailingslashes)^0) -parsers.stripslashes    = stripslashes  local splitcomma        = Ct((C(noncomma^1) + comma)^1) -parsers.splitcomma      = splitcomma @@ -653,8 +632,6 @@ local font_request      = Ct(path_lookup   * (colon^-1 * features)^-1  --- v2.5 parser: 1065 rules  --- v1.2 parser:  230 rules -luaotfload.parsers.font_request = font_request -  -------------------------------------------------------------------------------  ---                                INI FILES  ------------------------------------------------------------------------------- @@ -731,7 +708,7 @@ local ini_variables     = Cg (Cf (Ct "" * ini_variable^0, rawset), "variables")  local ini_section       = Ct (ini_heading * ini_variables)  local ini_sections      = skip_line^0 * ini_section^0 -local config            = Ct (ini_sections) +local parse_config      = Ct (ini_sections)  --[=[doc-- @@ -763,6 +740,22 @@ local config            = Ct (ini_sections)  --doc]=]-- -luaotfload.parsers.config = config +return { +  init = function () +    logreport = luaotfload.log.report +    luaotfload.parsers = { +      --- parameters +      traversal_maxdepth    = traversal_maxdepth, +      --- main parsers +      read_fonts_conf       = read_fonts_conf, +      font_request          = font_request, +      config                = parse_config, +      --- common patterns +      stripslashes          = stripslashes, +      splitcomma            = splitcomma, +    } +    return true +  end +}  -- vim:ft=lua:tw=71:et:sw=2:sts=4:ts=8 diff --git a/src/luaotfload-resolvers.lua b/src/luaotfload-resolvers.lua index 42ea2fd..3d7f6b0 100644 --- a/src/luaotfload-resolvers.lua +++ b/src/luaotfload-resolvers.lua @@ -5,8 +5,6 @@  --  DESCRIPTION:  Resolvers for hooking into the fontloader  -- REQUIREMENTS:  Luaotfload and a decent bit of courage  --       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net> ---      VERSION:  1.0 ---      CREATED:  2015-07-23 07:31:50+0200  -----------------------------------------------------------------------  --  --- The bare fontloader uses a set of simplistic file name resolvers @@ -37,15 +35,9 @@ local stringlower         = string.lower  local stringformat        = string.format  local filesuffix          = file.suffix  local fileremovesuffix    = file.removesuffix -local formats             = fonts.formats -local names               = fonts.names -local encodings           = fonts.encodings  local luatexbase          = luatexbase  local logreport           = luaotfload.log.report -formats.ofm               = "type1" -encodings.known           = encodings.known or { } -  --[[doc--      \identifier{luaotfload} promises easy access to system fonts. @@ -70,9 +62,9 @@ encodings.known           = encodings.known or { }  local resolve_file  resolve_file = function (specification) -    local name   = names.lookup_font_file (specification.name) +    local name   = fonts.names.lookup_font_file (specification.name)      local suffix = filesuffix (name) -    if formats[suffix] then +    if fonts.formats[suffix] then          specification.forced      = stringlower (suffix)          specification.forcedname  = fileremovesuffix(name)      else @@ -101,7 +93,7 @@ resolve_path = function (specification)          resolve_file (specification)      else          local suffix = filesuffix (name) -        if formats[suffix] then +        if fonts.formats[suffix] then              specification.forced      = stringlower (suffix)              specification.name        = fileremovesuffix(name)              specification.forcedname  = name @@ -122,9 +114,9 @@ end  local resolve_name  resolve_name = function (specification) -    local resolver = names.lookup_font_name_cached +    local resolver = fonts.names.lookup_font_name_cached      if config.luaotfload.run.resolver == "normal" then -        resolver = names.lookup_font_name +        resolver = fonts.names.lookup_font_name      end      local resolved, subfont = resolver (specification)      if resolved then @@ -210,7 +202,7 @@ local resolve_kpse  resolve_kpse = function (specification)      local name       = specification.name      local suffix     = filesuffix (name) -    if suffix and formats[suffix] then +    if suffix and fonts.formats[suffix] then          name = fileremovesuffix (name)          if resolvers.findfile (name, suffix) then              specification.forced       = stringlower (suffix) @@ -218,7 +210,7 @@ resolve_kpse = function (specification)              return          end      end -    for t, format in next, formats do --- brute force +    for t, format in next, fonts.formats do --- brute force          if kpsefind_file (name, format) then              specification.forced = t              specification.name   = name @@ -238,8 +230,11 @@ local resolve_my = function (specification)  end  return { -    install = function ( ) -        luatexbase.create_callback ("luaotfload.resolve_font", "simple", function () end) +    init = function ( ) +        if luatexbase and luatexbase.create_callback then +            luatexbase.create_callback ("luaotfload.resolve_font", +                                        "simple", function () end) +        end          logreport ("log", 5, "resolvers", "installing font resolvers", name)          local request_resolvers = fonts.definers.resolvers          request_resolvers.file = resolve_file @@ -248,8 +243,11 @@ return {          request_resolvers.path = resolve_path          request_resolvers.kpse = resolve_kpse          request_resolvers.my   = resolve_my +        fonts.formats.ofm      = "type1" +        fonts.encodings        = fonts.encodings       or { } +        fonts.encodings.known  = fonts.encodings.known or { }          return true -    end, --- [.install] +    end, --- [.init]  }  --- vim:ft=lua:ts=8:sw=4:et diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua index 4376e90..e9a434a 100755 --- a/src/luaotfload-tool.lua +++ b/src/luaotfload-tool.lua @@ -4,9 +4,7 @@  --  DESCRIPTION:  database functionality  -- REQUIREMENTS:  luaotfload 2.6  --       AUTHOR:  Khaled Hosny, Élie Roux, Philipp Gesang ---      VERSION:  2.6  --      LICENSE:  GPL v2.0 ---     MODIFIED:  2015-03-29 12:43:00+0200  -----------------------------------------------------------------------  luaotfload          = luaotfload or { } @@ -76,7 +74,6 @@ else -- 5.2      runtime = { "stock", _VERSION }  end -  local C, Ct, P, S  = lpeg.C, lpeg.Ct, lpeg.P, lpeg.S  local lpegmatch    = lpeg.match @@ -102,6 +99,7 @@ config.lualibs.prefer_merged    = true  config.lualibs.load_extended    = true  require "lualibs" +  local iosavedata                = io.savedata  local lfsisdir                  = lfs.isdir  local lfsisfile                 = lfs.isfile @@ -131,22 +129,69 @@ local backup = {  }  texio.write, texio.write_nl          = dummy_function, dummy_function -require"luaotfload-basics-gen.lua" +require "luaotfload-basics-gen.lua"  texio.write, texio.write_nl          = backup.write, backup.write_nl  utilities                            = backup.utilities -require "luaotfload-log.lua"       --- this populates the luaotfload.log.* namespace -require "luaotfload-parsers"       --- fonts.conf, configuration, and request syntax -require "luaotfload-configuration" --- configuration file handling -require "luaotfload-database" +fonts = { names = { } } -- for db; normally provided by the fontloaders + +local require_init = { } + +local loadmodule = function (name) +    local v = require ("luaotfload-" .. name) +    if v then +        local mod = { } +        local tv  = type (v) +        if tv == "table" then +            mod.name = name +            mod.init = v.init +            require_init [#require_init + 1] = mod +        elseif tv == "function" then +            mod.name = name +            mod.init = v +            require_init [#require_init + 1] = mod +        end +    end +end +  require "alt_getopt" -local names                          = fonts.names -local sanitize_fontname              = names.sanitize_fontname +loadmodule "log.lua"       --- this populates the luaotfload.log.* namespace +loadmodule "parsers"       --- fonts.conf, configuration, and request syntax +loadmodule "configuration" --- configuration file handling +loadmodule "database" +loadmodule "resolvers"     --- Font lookup + +local logreport + +local init_modules = function () +    --- NB we don’t command the logger at this point. +    local todo = #require_init +    local ret  = true +    for i = 1, todo do +        local mod  = require_init[i] +        local name = mod.name +        local init = mod.init +        if type (init) ~= "function" then +            error ("luaotfload broken; module " +                   .. name .. " missing initializers!") +        end +        local v = mod.init () +        if v == true then +            --- evaluated well +        elseif type (v) == "table" then +            luaotfload[name] = v +        else +            error ("luaotfload broken; initialization of module " +                   .. name .. " returned " .. tostring (v) .. ".") +            return false +        end +    end +    logreport = luaotfload.log.report +    return ret +end -local log                            = luaotfload.log -local report                         = log.report  local help_messages = {      ["luaotfload-tool"] = [[ @@ -163,8 +208,7 @@ Usage: %s [OPTIONS...]    -q --quiet                   don't output anything    -v --verbose=LEVEL           be more verbose (print the searched directories) -  -vv                          print the loaded fonts -  -vvv                         print all steps of directory searching +  -v, -vv .. -vvvvvvvvv        set loglevel in unary    --log=stdout                 redirect log output to stdout    -V --version                 print version and exit @@ -184,7 +228,7 @@ Usage: %s [OPTIONS...]    -c --no-compress             do not gzip index file (text version only)    -l --flush-lookups           empty lookup cache of font requests    -D --dry-run                 skip loading of fonts, just scan -  --formats=[+|-]EXTENSIONS    set, add, or subtract formats to index +  --formats=[+|-]EXTENSIONS    set, add, or subtract file formats    -p --prefer-texmf            prefer fonts in the TEXMF over system fonts    --max-fonts=N                process at most N font files @@ -265,7 +309,7 @@ local about = [[  local version_msg = function ( )      local out   = function (...) texiowrite_nl (stringformat (...)) end      local uname = os.uname () -    local meta  = names.getmetadata () +    local meta  = fonts.names.getmetadata ()      out (about, luaotfload.self)      out ("%s version: %q", luaotfload.self, version)      out ("Revision: %q", config.luaotfload.status.notes.revision) @@ -666,7 +710,7 @@ subfont_by_name = function (lst, askedname, n)      local font = lst[n]      if font then -        if sanitize_fontname (font.fullname) == askedname then +        if fonts.names.sanitize_fontname (font.fullname) == askedname then              return font          end          return subfont_by_name (lst, askedname, n+1) @@ -683,10 +727,10 @@ The font info knows two levels of detail:  --doc]]--  local show_font_info = function (basename, askedname, detail, warnings) -    local filenames = names.data().files +    local filenames = fonts.names.data().files      local index     = filenames.base[basename]      local fullname  = filenames.full[index] -    askedname = sanitize_fontname (askedname) +    askedname = fonts.names.sanitize_fontname (askedname)      if not fullname then -- texmf          fullname = resolvers.findfile(basename)      end @@ -696,9 +740,9 @@ local show_font_info = function (basename, askedname, detail, warnings)          if nfonts > 0 then -- true type collection              local subfont              if askedname then -                report (true, 1, "resolve", -                        [[%s is part of the font collection %s]], -                        askedname, basename) +                logreport (true, 1, "resolve", +                           [[%s is part of the font collection %s]], +                           askedname, basename)                  subfont = subfont_by_name(shortinfo, askedname)              end              if subfont then @@ -707,11 +751,11 @@ local show_font_info = function (basename, askedname, detail, warnings)                      show_full_info(fullname, subfont, warnings)                  end              else -- list all subfonts -                report (true, 1, "resolve", -                        [[%s is a font collection]], basename) +                logreport (true, 1, "resolve", +                           [[%s is a font collection]], basename)                  for subfont = 1, nfonts do -                    report (true, 1, "resolve", -                            [[Showing info for font no. %d]], n) +                    logreport (true, 1, "resolve", +                               [[Showing info for font no. %d]], n)                      show_info_items(shortinfo[subfont])                      if detail == true then                          show_full_info(fullname, subfont, warnings) @@ -725,7 +769,7 @@ local show_font_info = function (basename, askedname, detail, warnings)              end          end      else -        report (true, 1, "resolve", "Font %s not found", filename) +        logreport (true, 1, "resolve", "Font %s not found", filename)      end  end @@ -753,9 +797,9 @@ local actions = { } --- (jobspec -> (bool * bool)) list  actions.loglevel = function (job)      local lvl = job.log_level      if lvl then -        log.set_loglevel(lvl) -        report ("info", 3, "util", "Setting the log level to %d.", lvl) -        report ("log", 2, "util", "Lua=%q", _VERSION) +        luaotfload.log.set_loglevel(lvl) +        logreport ("info", 3, "util", "Setting the log level to %d.", lvl) +        logreport ("log", 2, "util", "Lua=%q", _VERSION)      end      return true, true  end @@ -790,19 +834,21 @@ actions.help = function (job)  end  actions.blacklist = function (job) -    names.read_blacklist() +    fonts.names.read_blacklist()      local n = 0 -    for n, entry in next, tablesortedkeys(names.blacklist) do +    for n, entry in next, tablesortedkeys(fonts.names.blacklist) do          iowrite (stringformat("(%d %s)\n", n, entry))      end      return true, false  end  actions.generate = function (job) -    local _ = names.update (fontnames, job.force_reload, job.dry_run) -    local namedata = names.data () +    local _ = fonts.names.update (fontnames, job.force_reload, job.dry_run) +    local namedata = fonts.names.data ()      if namedata then -        report ("info", 2, "db", "Fonts in the database: %i", #namedata.mappings) +        logreport ("info", 2, "db", +                   "Fonts in the database: %i", +                   #namedata.mappings)          return true, true      end      return false, false @@ -838,12 +884,14 @@ local write_bisect_status = function (data)                                    osdate ("%Y-%m-d %H:%M:%S", os.time ()),                                    payload)      if status and iosavedata (bisect_status_file, status) then -        report ("info", 4, "bisect", -                "Bisection state written to %s.", bisect_status_file) +        logreport ("info", 4, "bisect", +                   "Bisection state written to %s.", +                   bisect_status_file)          return true      end -    report ("info", 0, "bisect", -            "Failed to write bisection state to %s.", bisect_status_file) +    logreport ("info", 0, "bisect", +               "Failed to write bisection state to %s.", +               bisect_status_file)      return false  end @@ -855,16 +903,22 @@ end  --- unit -> state list  local read_bisect_status = function () -    report ("info", 4, "bisect", "Testing for status file: %q.", bisect_status_file) +    logreport ("info", 4, "bisect", +               "Testing for status file: %q.", +               bisect_status_file)      if not lfsisfile (bisect_status_file) then -        report ("info", 2, "bisect", "No such file: %q.", bisect_status_file) -        report ("info", 0, "bisect", "Not in bisect mode.") +        logreport ("info", 2, "bisect", +                   "No such file: %q.", bisect_status_file) +        logreport ("info", 0, "bisect", +                   "Not in bisect mode.")          return false      end -    report ("info", 4, "bisect", "Reading status file: %q.", bisect_status_file) +    logreport ("info", 4, "bisect", +               "Reading status file: %q.", bisect_status_file)      local success, status = pcall (dofile, bisect_status_file)      if not success then -        report ("info", 0, "bisect", "Could not read status file.") +        logreport ("info", 0, "bisect", +                   "Could not read status file.")          return false      end      return status @@ -879,19 +933,21 @@ end  local bisect_start = function ()      if lfsisfile (bisect_status_file) then -        report ("info", 0, "bisect", -                "Bisect session in progress.", -                bisect_status_file) -        report ("info", 0, "bisect", -                "Use --bisect=stop to erase it before starting over.") +        logreport ("info", 0, "bisect", +                   "Bisect session in progress.", +                   bisect_status_file) +        logreport ("info", 0, "bisect", +                   "Use --bisect=stop to erase it before starting over.")          return false, false      end -    report ("info", 2, "bisect", -            "Starting bisection of font database %q.", bisect_status_file) -    local n     = names.count_font_files () +    logreport ("info", 2, "bisect", +               "Starting bisection of font database %q.", +               bisect_status_file) +    local n     = fonts.names.count_font_files ()      local pivot = mathfloor (n / 2)      local data  = { { 1, n, pivot } } -    report ("info", 0, "bisect", "Initializing pivot to %d.", pivot) +    logreport ("info", 0, "bisect", +               "Initializing pivot to %d.", pivot)      if write_bisect_status (data) then          return true, false      end @@ -905,21 +961,23 @@ end  --doc]]--  local bisect_stop = function () -    report ("info", 3, "bisect", "Erasing bisection state at %s.", bisect_status_file) +    logreport ("info", 3, "bisect", +               "Erasing bisection state at %s.", +               bisect_status_file)      if lfsisfile (bisect_status_file) then          local success, msg = os.remove (bisect_status_file)          if not success then -            report ("info", 2, "bisect", -                    "Failed to erase file %s (%s).", -                     bisect_status_file, msg) +            logreport ("info", 2, "bisect", +                       "Failed to erase file %s (%s).", +                        bisect_status_file, msg)          end      end      if lfsisdir (bisect_status_path) then          local success, msg = os.remove (bisect_status_path)          if not success then -            report ("info", 2, "bisect", -                    "Failed to erase directory %s (%s).", -                     bisect_status_path, msg) +            logreport ("info", 2, "bisect", +                       "Failed to erase directory %s (%s).", +                       bisect_status_path, msg)          end      end      if lfsisfile (bisect_status_file) then @@ -936,12 +994,12 @@ end  --doc]]--  local bisect_terminate = function (nsteps, culprit) -    report ("info", 1, "bisect", -            "Bisection completed after %d steps.", nsteps) -    report ("info", 0, "bisect", -            "Bad file: %s.", names.nth_font_filename (culprit)) -    report ("info", 0, "bisect", -            "Run with --bisect=stop to finish bisection.") +    logreport ("info", 1, "bisect", +               "Bisection completed after %d steps.", nsteps) +    logreport ("info", 0, "bisect", +               "Bad file: %s.", fonts.names.nth_font_filename (culprit)) +    logreport ("info", 0, "bisect", +               "Run with --bisect=stop to finish bisection.")      return true, false  end @@ -952,10 +1010,10 @@ end  --doc]]--  local list_remainder = function (lo, hi) -    local fonts = names.font_slice (lo, hi) -    report ("info", 0, "bisect", "%d fonts left.", hi - lo + 1) +    local fonts = fonts.names.font_slice (lo, hi) +    logreport ("info", 0, "bisect", "%d fonts left.", hi - lo + 1)      for i = 1, #fonts do -        report ("info", 1, "bisect", "   · %2d: %s", lo, fonts[i]) +        logreport ("info", 1, "bisect", "   · %2d: %s", lo, fonts[i])          lo = lo + 1      end  end @@ -988,8 +1046,9 @@ local bisect_set = function (outcome)      local lo, hi, pivot = unpack (previous) -    report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.", -            nsteps, lo, hi, pivot) +    logreport ("info", 3, "bisect", +               "Previous step %d: lo=%d, hi=%d, pivot=%d.", +               nsteps, lo, hi, pivot)      if outcome == "bad" then          hi = pivot @@ -1000,9 +1059,9 @@ local bisect_set = function (outcome)              return bisect_terminate (nsteps, lo)          end          pivot = mathfloor ((lo + hi) / 2) -        report ("info", 0, "bisect", -                "Continuing with the lower segment: lo=%d, hi=%d, pivot=%d.", -                lo, hi, pivot) +        logreport ("info", 0, "bisect", +                   "Continuing with the lower segment: lo=%d, hi=%d, pivot=%d.", +                   lo, hi, pivot)      elseif outcome == "good" then          lo = pivot + 1          if lo >= hi then --- complete @@ -1012,11 +1071,12 @@ local bisect_set = function (outcome)              return bisect_terminate (nsteps, lo)          end          pivot = mathfloor ((lo + hi) / 2) -        report ("info", 0, "bisect", -                "Continuing with the upper segment: lo=%d, hi=%d, pivot=%d.", -                lo, hi, pivot) +        logreport ("info", 0, "bisect", +                   "Continuing with the upper segment: lo=%d, hi=%d, pivot=%d.", +                   lo, hi, pivot)      else -- can’t happen -        report ("info", 0, "bisect", "What the hell?", lo, hi, pivot) +        logreport ("info", 0, "bisect", +                   "What the hell?", lo, hi, pivot)          return false, false      end @@ -1043,13 +1103,13 @@ local bisect_status = function ()      if nsteps > 1 then          for i = nsteps - 1, 1, -1 do              local step = status[i] -            report ("info", 2, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.", -                    i, unpack (step)) +            logreport ("info", 2, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.", +                       i, unpack (step))          end      end      local current = status[nsteps] -    report ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.", -            nsteps, unpack (current)) +    logreport ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.", +               nsteps, unpack (current))      return true, false  end @@ -1075,10 +1135,10 @@ local bisect_run = function ()          current = status[nsteps - 1]      end      local lo, hi, pivot = unpack (current) -    report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.", -            nsteps, lo, hi, pivot) -    report ("info", 1, "bisect", "Step %d: Testing fonts from %d to %d.", -            currentstep, lo, pivot) +    logreport ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.", +               nsteps, lo, hi, pivot) +    logreport ("info", 1, "bisect", "Step %d: Testing fonts from %d to %d.", +               currentstep, lo, pivot)      config.luaotfload.misc.bisect = { lo, pivot }      return true, true  end @@ -1096,18 +1156,18 @@ actions.bisect = function (job)      local mode   = job.bisect      local runner = bisect_modes[mode]      if not runner then -        report ("info", 0, "bisect", "Unknown directive %q.", mode) +        logreport ("info", 0, "bisect", "Unknown directive %q.", mode)          return false, false      end      return runner (job)  end  actions.flush = function (job) -    local success = names.flush_lookup_cache() +    local success = fonts.names.flush_lookup_cache()      if success then -        local success = names.save_lookups() +        local success = fonts.names.save_lookups()          if success then -            report ("info", 2, "cache", "Lookup cache emptied") +            logreport ("info", 2, "cache", "Lookup cache emptied")              return true, true          end      end @@ -1115,16 +1175,16 @@ actions.flush = function (job)  end  local cache_directives = { -    ["purge"] = names.purge_cache, -    ["erase"] = names.erase_cache, -    ["show"]  = names.show_cache, +    ["purge"] = fonts.names.purge_cache, +    ["erase"] = fonts.names.erase_cache, +    ["show"]  = fonts.names.show_cache,  }  actions.cache = function (job)      local directive = cache_directives[job.cache]      if not directive or type(directive) ~= "function" then -        report ("info", 2, "cache", -                "Invalid font cache directive %s.", job.cache) +        logreport ("info", 2, "cache", +                   "Invalid font cache directive %s.", job.cache)          return false, false      end      if directive() then @@ -1147,7 +1207,7 @@ actions.query = function (job)          features      = { },      } -    tmpspec = names.handle_request (tmpspec) +    tmpspec = fonts.names.handle_request (tmpspec)      if not tmpspec.size then          tmpspec.size = 655360 --- assume 10pt @@ -1158,38 +1218,38 @@ actions.query = function (job)      if tmpspec.lookup == "name"      or tmpspec.lookup == "anon" --- not *exactly* as resolvers.anon      then -        foundname, subfont = names.resolve_name (tmpspec) +        foundname, _, success = fonts.names.lookup_font_name (tmpspec)          if foundname then -            foundname, _, success = names.font_file_lookup (foundname) +            foundname, _, success = fonts.names.lookup_font_file (foundname)          end      elseif tmpspec.lookup == "file" then -        foundname, _, success = -            names.font_file_lookup (tmpspec.name) +        foundname, _, success = fonts.names.lookup_font_file (tmpspec.name)      end      if success then -        report (false, 0, "resolve", "Font %q found!", query) +        logreport (false, 0, "resolve", "Font %q found!", query)          if subfont then -            report (false, 0, "resolve", -                    "Resolved file name %q, subfont nr. %q", -                foundname, subfont) +            logreport (false, 0, "resolve", +                       "Resolved file name %q, subfont nr. %q", +                       foundname, subfont)          else -            report (false, 0, "resolve", -                    "Resolved file name %q", foundname) +            logreport (false, 0, "resolve", +                       "Resolved file name %q", foundname)          end          if job.show_info then              show_font_info (foundname, query, job.full_info, job.warnings)              iowrite "\n"          end      else -        report (false, 0, "resolve", "Cannot find %q in index.", query) -        report (false, 0, "resolve", -                "Hint: use the --fuzzy option to display suggestions.", -                query) +        logreport (false, 0, "resolve", "Cannot find %q in index.", query)          if job.fuzzy == true then -            report (false, 0, "resolve", -                    "Looking for close matches, this may take a while ...") -            local _success = names.find_closest(query, job.fuzzy_limit) +            logreport (false, 0, "resolve", +                       "Looking for close matches, this may take a while ...") +            local _success = fonts.names.find_closest(query, job.fuzzy_limit) +        else +            logreport (false, 0, "resolve", +                       "Hint: use the --fuzzy option to display suggestions.", +                       query)          end      end      return true, true @@ -1262,14 +1322,13 @@ set_primary_field = function (fields, addme, acc, n)      return acc  end -local splitcomma = luaotfload.parsers.splitcomma -  actions.list = function (job)      local criterion     = job.criterion      local asked_fields  = job.asked_fields -    local name_index    = names.data () +    local name_index    = fonts.names.data ()      if asked_fields then +        local splitcomma = luaotfload.parsers.splitcomma          asked_fields = lpegmatch(splitcomma, asked_fields)      end @@ -1279,14 +1338,14 @@ actions.list = function (job)      end      if not name_index then -        name_index = names.load() +        name_index = fonts.names.load()      end      local mappings  = name_index.mappings      local nmappings = #mappings      if criterion == "*" then -        report (false, 1, "list", "All %d entries", nmappings) +        logreport (false, 1, "list", "All %d entries", nmappings)          for i=1, nmappings do              local entry     = mappings[i]              local fields    = get_fields(entry, asked_fields) @@ -1301,12 +1360,12 @@ actions.list = function (job)          criterion          = criterion[1]          asked_fields       = set_primary_field(asked_fields, criterion) -        report (false, 1, "list", "By %s", criterion) +        logreport (false, 1, "list", "By %s", criterion)          --- firstly, build a list of fonts to operate on          local targets = { }          if asked_value then --- only those whose value matches -            report (false, 2, "list", "Restricting to value %s", asked_value) +            logreport (false, 2, "list", "Restricting to value %s", asked_value)              for i=1, nmappings do                  local entry = mappings[i]                  if  entry[criterion] @@ -1351,7 +1410,7 @@ actions.list = function (job)              end          end          local ntargets = #targets -        report (false, 2, "list", "%d entries", ntargets) +        logreport (false, 2, "list", "%d entries", ntargets)          --- now, output the collection          for i=1, ntargets do @@ -1488,7 +1547,7 @@ local process_cmdline = function ( ) -- unit -> jobspec          elseif v == "log" then              local str = optarg[n]              if str then -                finalizers = log.set_logout(str, finalizers) +                finalizers = luaotfload.log.set_logout(str, finalizers)              end          elseif v == "find" then              action_pending["query"] = true @@ -1521,7 +1580,7 @@ local process_cmdline = function ( ) -- unit -> jobspec          elseif v == "D" then              result.dry_run = true          elseif v == "p" then -            names.set_location_precedence { +            fonts.names.set_location_precedence {                  "local", "texmf", "system"              }          elseif v == "b" then @@ -1582,6 +1641,8 @@ local process_cmdline = function ( ) -- unit -> jobspec  end  local main = function ( ) -- unit -> int +    if init_modules () == false then return -42 end +      local retval    = 0      local job       = process_cmdline() @@ -1592,23 +1653,23 @@ local main = function ( ) -- unit -> int          local actionname = action_sequence[i]          local exit       = false          if action_pending[actionname] then -            report ("log", 3, "util", "Preparing for task", "%s", actionname) +            logreport ("log", 3, "util", "Preparing for task", "%s", actionname)              local action             = actions[actionname]              local success, continue  = action(job)              if not success then -                report (false, 0, "util", -                        "Failed to execute task.", "%s", actionname) +                logreport (false, 0, "util", +                           "Failed to execute task.", "%s", actionname)                  retval = -1                  exit   = true              elseif not continue then -                report (false, 3, "util", -                        "Task completed, exiting.", "%s", actionname) +                logreport (false, 3, "util", +                           "Task completed, exiting.", "%s", actionname)                  exit = true              else -                report (false, 3, "util", -                        "Task completed successfully.", "%s", actionname) +                logreport (false, 3, "util", +                           "Task completed successfully.", "%s", actionname)              end          end          if exit then break end diff --git a/src/luaotfload.sty b/src/luaotfload.sty index c9c9864..ec62dad 100644 --- a/src/luaotfload.sty +++ b/src/luaotfload.sty @@ -45,4 +45,5 @@    \RequirePackage{luatexbase}  \fi  \RequireLuaModule{luaotfload-main} +\directlua{local _void = luaotfload.main ()} | 
