diff options
Diffstat (limited to 'src/fontloader/misc')
22 files changed, 1566 insertions, 869 deletions
| diff --git a/src/fontloader/misc/fontloader-font-con.lua b/src/fontloader/misc/fontloader-font-con.lua index b118535..1a0daff 100644 --- a/src/fontloader/misc/fontloader-font-con.lua +++ b/src/fontloader/misc/fontloader-font-con.lua @@ -10,7 +10,6 @@ if not modules then modules = { } end modules ['font-con'] = {  local next, tostring, rawget = next, tostring, rawget  local format, match, lower, gsub = string.format, string.match, string.lower, string.gsub -local utfbyte = utf.byte  local sort, insert, concat, sortedkeys, serialize, fastcopy = table.sort, table.insert, table.concat, table.sortedkeys, table.serialize, table.fastcopy  local derivetable = table.derive @@ -338,6 +337,20 @@ function constructors.enhanceparameters(parameters)      }  end +local function mathkerns(v,vdelta) +    local k = { } +    for i=1,#v do +        local entry  = v[i] +        local height = entry.height +        local kern   = entry.kern +        k[i] = { +            height = height and vdelta*height or 0, +            kern   = kern   and vdelta*kern   or 0, +        } +    end +    return k +end +  function constructors.scale(tfmdata,specification)      local target         = { } -- the new table      -- @@ -749,22 +762,15 @@ function constructors.scale(tfmdata,specification)                  chr.top_accent = vdelta*va              end              if stackmath then -                local mk = character.mathkerns -- not in math ? +                local mk = character.mathkerns                  if mk then -                    local kerns = { } -                    local v = mk.top_right    if v then local k = { } for i=1,#v do local vi = v[i] -                        k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } -                    end     kerns.top_right    = k end -                    local v = mk.top_left     if v then local k = { } for i=1,#v do local vi = v[i] -                        k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } -                    end     kerns.top_left     = k end -                    local v = mk.bottom_left  if v then local k = { } for i=1,#v do local vi = v[i] -                        k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } -                    end     kerns.bottom_left  = k end -                    local v = mk.bottom_right if v then local k = { } for i=1,#v do local vi = v[i] -                        k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } -                    end     kerns.bottom_right = k end -                    chr.mathkern = kerns -- singular -> should be patched in luatex ! +                    local tr, tl, br, bl = mk.topright, mk.topleft, mk.bottomright, mk.bottomleft +                    chr.mathkern = { -- singular -> should be patched in luatex ! +                        top_right    = tr and mathkerns(tr,vdelta) or nil, +                        top_left     = tl and mathkerns(tl,vdelta) or nil, +                        bottom_right = br and mathkerns(br,vdelta) or nil, +                        bottom_left  = bl and mathkerns(bl,vdelta) or nil, +                    }                  end              end              if hasitalics then @@ -1043,6 +1049,29 @@ function constructors.hashfeatures(specification) -- will be overloaded      return "unknown"  end +-- hashmethods.normal = function(list) +--     local s = { } +--     local n = 0 +--     for k, v in next, list do +--         if not k then +--             -- no need to add to hash +--         elseif k == "number" or k == "features" then +--             -- no need to add to hash (maybe we need a skip list) +--         else +--             n = n + 1 +--             s[n] = k +--         end +--     end +--     if n > 0 then +--         sort(s) +--         for i=1,n do +--             local k = s[i] +--             s[i] = k .. '=' .. tostring(list[k]) +--         end +--         return concat(s,"+") +--     end +-- end +  hashmethods.normal = function(list)      local s = { }      local n = 0 @@ -1053,15 +1082,11 @@ hashmethods.normal = function(list)              -- no need to add to hash (maybe we need a skip list)          else              n = n + 1 -            s[n] = k +            s[n] = k .. '=' .. tostring(v)          end      end      if n > 0 then          sort(s) -        for i=1,n do -            local k = s[i] -            s[i] = k .. '=' .. tostring(list[k]) -        end          return concat(s,"+")      end  end @@ -1230,7 +1255,11 @@ function constructors.getfeatureaction(what,where,mode,name)      end  end -function constructors.newhandler(what) -- could be a metatable newindex +local newhandler        = { } +constructors.handlers   = newhandler -- downward compatible +constructors.newhandler = newhandler + +local function setnewhandler(what) -- could be a metatable newindex      local handler = handlers[what]      if not handler then          handler = { } @@ -1239,7 +1268,16 @@ function constructors.newhandler(what) -- could be a metatable newindex      return handler  end -function constructors.newfeatures(what) -- could be a metatable newindex +setmetatable(newhandler, { +    __call  = function(t,k) local v = t[k] return v end, +    __index = function(t,k) local v = setnewhandler(k) t[k] = v return v end, +}) + +local newfeatures        = { } +constructors.newfeatures = newfeatures -- downward compatible +constructors.features    = newfeatures + +local function setnewfeatures(what)      local handler = handlers[what]      local features = handler.features      if not features then @@ -1259,6 +1297,11 @@ function constructors.newfeatures(what) -- could be a metatable newindex      return features  end +setmetatable(newfeatures, { +    __call  = function(t,k) local v = t[k] return v end, +    __index = function(t,k) local v = setnewfeatures(k) t[k] = v return v end, +}) +  --[[ldx--  <p>We need to check for default features. For this we provide  a helper function.</p> @@ -1286,7 +1329,6 @@ function constructors.initializefeatures(what,tfmdata,features,trace,report)          local properties       = tfmdata.properties or { } -- brrr          local whathandler      = handlers[what]          local whatfeatures     = whathandler.features -        local whatinitializers = whatfeatures.initializers          local whatmodechecker  = whatfeatures.modechecker          -- properties.mode can be enforces (for instance in font-otd)          local mode             = properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base" diff --git a/src/fontloader/misc/fontloader-font-dsp.lua b/src/fontloader/misc/fontloader-font-dsp.lua index 330a940..a1ae17f 100644 --- a/src/fontloader/misc/fontloader-font-dsp.lua +++ b/src/fontloader/misc/fontloader-font-dsp.lua @@ -69,7 +69,6 @@ local readers           = fonts.handlers.otf.readers  local streamreader      = readers.streamreader  local setposition       = streamreader.setposition -local skipbytes         = streamreader.skip  local skipshort         = streamreader.skipshort  local readushort        = streamreader.readcardinal2  -- 16-bit unsigned integer  local readulong         = streamreader.readcardinal4  -- 24-bit unsigned integer @@ -77,6 +76,7 @@ local readshort         = streamreader.readinteger2   -- 16-bit   signed integer  local readfword         = readshort  local readstring        = streamreader.readstring  local readtag           = streamreader.readtag +local readbytes         = streamreader.readbytes  local gsubhandlers      = { }  local gposhandlers      = { } @@ -716,6 +716,8 @@ function gsubhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg      end  end +-- we see coverage format 0x300 in some old ms fonts +  local function sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)      local tableoffset = lookupoffset + offset      setposition(f,tableoffset) @@ -1599,6 +1601,14 @@ do          local reported = { } +        local function report_issue(i,what,sequence,kind) +            local name = sequence.name +            if not reported[name] then +                report("rule %i in %s lookup %a has %s lookups",i,what,name,kind) +                reported[name] = true +            end +        end +          for i=lastsequence+1,nofsequences do              local sequence = sequences[i]              local steps    = sequence.steps @@ -1610,18 +1620,10 @@ do                          local rule     = rules[i]                          local rlookups = rule.lookups                          if not rlookups then -                            local name = sequence.name -                            if not reported[name] then -                                report("rule %i in %s lookup %a has %s lookups",i,what,name,"no") -                                reported[name] = true -                            end +                            report_issue(i,what,sequence,"no")                          elseif not next(rlookups) then -                            local name = sequence.name -                            if not reported[name] then -                                -- can be ok as it aborts a chain sequence -                                report("rule %i in %s lookup %a has %s lookups",i,what,name,"empty") -                                reported[name] = true -                            end +                            -- can be ok as it aborts a chain sequence +                            report_issue(i,what,sequence,"empty")                              rule.lookups = nil                          else                              for index, lookupid in sortedhash(rlookups) do -- nicer @@ -1629,23 +1631,36 @@ do                                  if not h then                                      -- here we have a lookup that is used independent as well                                      -- as in another one -                                    nofsublookups = nofsublookups + 1 -                                 -- report("registering %i as sublookup %i",lookupid,nofsublookups) -                                    local d = lookups[lookupid].done -                                    h = { -                                        index     = nofsublookups, -- handy for tracing -                                        name      = f_lookupname(lookupprefix,"d",lookupid+lookupidoffset), -                                        derived   = true,          -- handy for tracing -                                        steps     = d.steps, -                                        nofsteps  = d.nofsteps, -                                        type      = d.lookuptype, -                                        markclass = d.markclass or nil, -                                        flags     = d.flags, -                                     -- chain     = d.chain, -                                    } -                                    sublookuplist[nofsublookups] = h -                                    sublookuphash[lookupid] = nofsublookups -                                    sublookupcheck[lookupid] = 1 +                                    local lookup = lookups[lookupid] +                                    if lookup then +                                        local d = lookup.done +                                        if d then +                                            nofsublookups = nofsublookups + 1 +                                         -- report("registering %i as sublookup %i",lookupid,nofsublookups) +                                            h = { +                                                index     = nofsublookups, -- handy for tracing +                                                name      = f_lookupname(lookupprefix,"d",lookupid+lookupidoffset), +                                                derived   = true,          -- handy for tracing +                                                steps     = d.steps, +                                                nofsteps  = d.nofsteps, +                                                type      = d.lookuptype, +                                                markclass = d.markclass or nil, +                                                flags     = d.flags, +                                             -- chain     = d.chain, +                                            } +                                            sublookuplist[nofsublookups] = h +                                            sublookuphash[lookupid] = nofsublookups +                                            sublookupcheck[lookupid] = 1 +                                        else +                                            report_issue(i,what,sequence,"missing") +                                            rule.lookups = nil +                                            break +                                        end +                                    else +                                        report_issue(i,what,sequence,"bad") +                                        rule.lookups = nil +                                        break +                                    end                                  else                                      sublookupcheck[lookupid] = sublookupcheck[lookupid] + 1                                  end @@ -2015,16 +2030,15 @@ local function readmathglyphinfo(f,fontdata,offset)              local function get(offset)                  setposition(f,kernoffset+offset)                  local n = readushort(f) -                if n > 0 then +                if n == 0 then +                    local k = readmathvalue(f) +                    if k == 0 then +                        -- no need for it (happens sometimes) +                    else +                        return { { kern = k } } +                    end +                else                      local l = { } -                 -- for i=1,n do -                 --     l[i] = { readushort(f), 0 } -- height, kern -                 --     skipshort(f) -                 -- end -                 -- for i=1,n do -                 --     l[i][2] = readushort(f) -                 --     skipshort(f) -                 -- end                      for i=1,n do                          l[i] = { height = readmathvalue(f) }                      end @@ -2059,10 +2073,10 @@ local function readmathglyphinfo(f,fontdata,offset)                      if next(kernset) then                          local glyph = glyphs[coverage[i]]                          local math  = glyph.math -                        if not math then -                            glyph.math = { kerns = kernset } -                        else +                        if math then                              math.kerns = kernset +                        else +                            glyph.math = { kerns = kernset }                          end                      end                  end @@ -2178,7 +2192,7 @@ function readers.math(f,fontdata,specification)              setposition(f,tableoffset)              local version = readulong(f)              if version ~= 0x00010000 then -                report("table version %a of %a is not supported (yet), maybe font %s is bad",version,what,fontdata.filename) +                report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"math",fontdata.filename)                  return              end              local constants = readushort(f) @@ -2198,3 +2212,146 @@ function readers.math(f,fontdata,specification)          end      end  end + +function readers.colr(f,fontdata,specification) +    local datatable = fontdata.tables.colr +    if datatable then +        if specification.glyphs then +            local tableoffset = datatable.offset +            setposition(f,tableoffset) +            local version = readushort(f) +            if version ~= 0 then +                report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"colr",fontdata.filename) +                return +            end +            if not fontdata.tables.cpal then +                report("color table %a in font %a has no mandate %a table","colr",fontdata.filename,"cpal") +                fontdata.colorpalettes = { } +            end +            local glyphs       = fontdata.glyphs +            local nofglyphs    = readushort(f) +            local baseoffset   = readulong(f) +            local layeroffset  = readulong(f) +            local noflayers    = readushort(f) +            local layerrecords = { } +            local maxclass     = 0 +            -- The special value 0xFFFF is foreground (but we index from 1). It +            -- more looks like indices into a palette so 'class' is a better name +            -- than 'palette'. +            setposition(f,tableoffset + layeroffset) +            for i=1,noflayers do +                local slot    = readushort(f) +                local class = readushort(f) +                if class < 0xFFFF then +                    class = class + 1 +                    if class > maxclass then +                        maxclass = class +                    end +                end +                layerrecords[i] = { +                    slot  = slot, +                    class = class, +                } +            end +            fontdata.maxcolorclass = maxclass +            setposition(f,tableoffset + baseoffset) +            for i=0,nofglyphs-1 do +                local glyphindex = readushort(f) +                local firstlayer = readushort(f) +                local noflayers  = readushort(f) +                local t = { } +                for i=1,noflayers do +                    t[i] = layerrecords[firstlayer+i] +                end +                glyphs[glyphindex].colors = t +            end +        end +        fontdata.hascolor = true +    end +end + +function readers.cpal(f,fontdata,specification) +    if specification.glyphs then +        local datatable = fontdata.tables.cpal +        if datatable then +            local tableoffset = datatable.offset +            setposition(f,tableoffset) +            local version = readushort(f) +            if version > 1 then +                report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"cpal",fontdata.filename) +                return +            end +            local nofpaletteentries  = readushort(f) +            local nofpalettes        = readushort(f) +            local nofcolorrecords    = readushort(f) +            local firstcoloroffset   = readulong(f) +            local colorrecords       = { } +            local palettes           = { } +            for i=1,nofpalettes do +                palettes[i] = readushort(f) +            end +            if version == 1 then +                -- used for guis +                local palettettypesoffset = readulong(f) +                local palettelabelsoffset = readulong(f) +                local paletteentryoffset  = readulong(f) +            end +            setposition(f,tableoffset+firstcoloroffset) +            for i=1,nofcolorrecords do +                local b, g, r, a = readbytes(f,4) +                colorrecords[i] = { +                    r, g, b, a ~= 255 and a or nil, +                } +            end +            for i=1,nofpalettes do +                local p = { } +                local o = palettes[i] +                for j=1,nofpaletteentries do +                    p[j] = colorrecords[o+j] +                end +                palettes[i] = p +            end +            fontdata.colorpalettes = palettes +        end +    end +end + +function readers.svg(f,fontdata,specification) +    local datatable = fontdata.tables.svg +    if datatable then +        if specification.glyphs then +            local tableoffset = datatable.offset +            setposition(f,tableoffset) +            local version = readushort(f) +            if version ~= 0 then +                report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"svg",fontdata.filename) +                return +            end +            local glyphs      = fontdata.glyphs +            local indexoffset = tableoffset + readulong(f) +            local reserved    = readulong(f) +            setposition(f,indexoffset) +            local nofentries  = readushort(f) +            local entries     = { } +            for i=1,nofentries do +                entries[i] = { +                    first  = readushort(f), +                    last   = readushort(f), +                    offset = indexoffset + readulong(f), +                    length = readulong(f), +                } +            end +            for i=1,nofentries do +                local entry = entries[i] +                setposition(f,entry.offset) +                entries[i] = { +                    first = entry.first, +                    last  = entry.last, +                    data  = readstring(f,entry.length) +                } +            end +            fontdata.svgshapes = entries +        end +        fontdata.hascolor = true +    end +end diff --git a/src/fontloader/misc/fontloader-font-gbn.lua b/src/fontloader/misc/fontloader-font-gbn.lua index daa072b..1ae817d 100644 --- a/src/fontloader/misc/fontloader-font-gbn.lua +++ b/src/fontloader/misc/fontloader-font-gbn.lua @@ -19,7 +19,6 @@ local nodes = nodes  local nuts        = nodes.nuts -- context abstraction of direct nodes  local traverse_id = nuts.traverse_id -local remove_node = nuts.remove  local free_node   = nuts.free  local glyph_code  = nodes.nodecodes.glyph @@ -126,17 +125,19 @@ function nodes.handlers.nodepass(head)                              local variant = hash[getchar(p)]                              if variant then                                  setchar(p,variant) -                                if not redundant then -                                    redundant = { n } -                                else -                                    redundant[#redundant+1] = n -                                end                              end                          end                      end +                    -- per generic user request we always remove selectors +                    if not redundant then +                        redundant = { n } +                    else +                        redundant[#redundant+1] = n +                    end                  end              end          end +        local nofbasefonts = #basefonts          if redundant then              for i=1,#redundant do                  local r = redundant[i] @@ -147,8 +148,8 @@ function nodes.handlers.nodepass(head)                  else                      setlink(p,n)                  end -                if b > 0 then -                    for i=1,b do +                if nofbasefonts > 0 then +                    for i=1,nofbasefonts do                          local bi = basefonts[i]                          if r == bi[1] then                              bi[1] = n @@ -192,8 +193,8 @@ function nodes.handlers.nodepass(head)                  end              end          end -        if basemodepass and #basefonts > 0 then -            for i=1,#basefonts do +        if basemodepass and nofbasefonts > 0 then +            for i=1,nofbasefonts do                  local range = basefonts[i]                  local start = range[1]                  local stop  = range[2] diff --git a/src/fontloader/misc/fontloader-font-ini.lua b/src/fontloader/misc/fontloader-font-ini.lua index c547f89..abc3194 100644 --- a/src/fontloader/misc/fontloader-font-ini.lua +++ b/src/fontloader/misc/fontloader-font-ini.lua @@ -12,8 +12,6 @@ if not modules then modules = { } end modules ['font-ini'] = {  local allocate = utilities.storage.allocate -local report_defining = logs.reporter("fonts","defining") -  fonts               = fonts or { }  local fonts         = fonts diff --git a/src/fontloader/misc/fontloader-font-map.lua b/src/fontloader/misc/fontloader-font-map.lua index 509e751..6151b37 100644 --- a/src/fontloader/misc/fontloader-font-map.lua +++ b/src/fontloader/misc/fontloader-font-map.lua @@ -10,7 +10,6 @@ local tonumber, next, type = tonumber, next, type  local match, format, find, concat, gsub, lower = string.match, string.format, string.find, table.concat, string.gsub, string.lower  local P, R, S, C, Ct, Cc, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.match -local utfbyte = utf.byte  local floor = math.floor  local formatters = string.formatters @@ -45,7 +44,7 @@ of obsolete. Some code may move to runtime or auxiliary modules.</p>  --     end  -- end -local hex     = R("AF","09") +local hex     = R("AF","af","09")  ----- 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 @@ -140,7 +139,7 @@ local f_double = formatters["%04X%04X"]  --     end  -- end -local function tounicode16(unicode,name) +local function tounicode16(unicode)      if unicode < 0xD7FF or (unicode > 0xDFFF and unicode <= 0xFFFF) then          return f_single(unicode)      else @@ -149,7 +148,7 @@ local function tounicode16(unicode,name)      end  end -local function tounicode16sequence(unicodes,name) +local function tounicode16sequence(unicodes)      local t = { }      for l=1,#unicodes do          local u = unicodes[l] @@ -295,145 +294,145 @@ function mappings.addtounicode(data,filename,checklookups)      local ns            = 0      local nl            = 0      -- -    for unic, glyph in next, descriptions do +    for du, glyph in next, descriptions do          local name = glyph.name          if name then -            local index = glyph.index -            local r = overloads[name] -            if r then +            local overload = overloads[name] +            if overload 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 +             -- glyph.name    = overload.name +                glyph.unicode = overload.unicode +            else +                local gu = glyph.unicode -- can already be set (number or table) +                if not gu or gu == -1 or du >= private or (du >= 0xE000 and du <= 0xF8FF) or du == 0xFFFE or du == 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. -                -- -                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 +                    -- 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 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 -                    else -                        local t, n = { }, 0 -                        for l=1,nsplit do -                            local base = split[l] +                        elseif nsplit == 1 then +                            local base = split[1]                              local u = unicodes[base] or unicodevector[base] or contextvector[name]                              if not u then -                                break +                                -- skip                              elseif type(u) == "table" then -                                if u[1] >= private then -                                    break +                                -- unlikely +                                if u[1] < private then +                                    unicode = u +                                    glyph.unicode = unicode                                  end -                                n = n + 1 -                                t[n] = u[1] -                            else -                                if u >= private then +                            elseif u < private then +                                unicode = u +                                glyph.unicode = unicode +                            end +                        else +                            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 +                            if n > 0 then +                                if n == 1 then +                                    unicode = t[1] +                                else +                                    unicode = t                                  end -                                n = n + 1 -                                t[n] = u +                                glyph.unicode = unicode                              end                          end -                        if n > 0 then -                            if n == 1 then -                                unicode = t[1] +                        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 -                                unicode = t +                                ns      = ns + 1 +                                unicode = foundcodes                              end -                            glyph.unicode = unicode                          end                      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 +                    -- check using substitutes and alternates +                    local r = overloads[unicode] +                    if r then +                        unicode = r.unicode +                        glyph.unicode = unicode +                    end +                    -- +                    if not unicode then +                        missing[du] = true +                        nofmissing  = nofmissing + 1                      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[unic] = true -                    nofmissing    = nofmissing + 1                  end              end -        else -            -- no name          end      end      if type(checklookups) == "function" then diff --git a/src/fontloader/misc/fontloader-font-ocl.lua b/src/fontloader/misc/fontloader-font-ocl.lua new file mode 100644 index 0000000..9661083 --- /dev/null +++ b/src/fontloader/misc/fontloader-font-ocl.lua @@ -0,0 +1,297 @@ +if not modules then modules = { } end modules ['font-ocl'] = { +    version   = 1.001, +    comment   = "companion to font-otf.lua (context)", +    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", +    copyright = "PRAGMA ADE / ConTeXt Development Team", +    license   = "see context related readme files" +} + +-- todo : user list of colors + +local formatters = string.formatters + +local otf = fonts.handlers.otf + +local f_color_start = formatters["pdf:direct: %f %f %f rg"] +local s_color_stop  = "pdf:direct:" + +if context then + +    local startactualtext = nil +    local stopactualtext  = nil + +    function otf.getactualtext(n) +        if not startactualtext then +            startactualtext = backends.codeinjections.startunicodetoactualtext +            stopactualtext  = backends.codeinjections.stopunicodetoactualtext +        end +        return startactualtext(n), stopactualtext() +    end + +else + +    local tounicode = fonts.mappings.tounicode16 + +    function otf.getactualtext(n) +        return "/Span << /ActualText <feff" .. tounicode(n) .. "> >> BDC", "EMC" +    end + +end + +local function initializecolr(tfmdata,kind,value) -- hm, always value +    if value then +        local palettes = tfmdata.resources.colorpalettes +        if palettes then +            -- +            local palette = palettes[tonumber(value) or 1] or palettes[1] or { } +            local classes = #palette +            if classes == 0 then +                return +            end +            -- +            local characters   = tfmdata.characters +            local descriptions = tfmdata.descriptions +            local properties   = tfmdata.properties +            local colorvalues  = { } +            -- +            properties.virtualized = true +            tfmdata.fonts = { +                { id = 0 } +            } +            -- +            for i=1,classes do +                local p = palette[i] +                colorvalues[i] = { "special", f_color_start(p[1]/255,p[2]/255,p[3]/255) } +            end +            -- +            local getactualtext = otf.getactualtext +            -- +            for unicode, character in next, characters do +                local description = descriptions[unicode] +                if description then +                    local colorlist = description.colors +                    if colorlist then +                        local b, e = getactualtext(unicode) +                        local w = character.width or 0 +                        local s = #colorlist +                        local n = 1 +                        local t = { +                            { "special", "pdf:direct: q " .. b } +                        } +                        for i=1,s do +                            local entry = colorlist[i] +                            n = n + 1 t[n] = colorvalues[entry.class] +                            n = n + 1 t[n] = { "char", entry.slot } +                            if s > 1 and i < s and w ~= 0 then +                                n = n + 1 t[n] = { "right", -w } +                            end +                        end +                        n = n + 1 t[n] = { "special", "pdf:direct:" .. e .. " Q" } +                        character.commands = t +                    end +                end +            end +        end +    end +end + +fonts.handlers.otf.features.register { +    name         = "colr", +    description  = "color glyphs", +    manipulators = { +        base = initializecolr, +        node = initializecolr, +    } +} + +local otfsvg   = otf.svg or { } +otf.svg        = otfsvg +otf.svgenabled = true + +do + +    local nofstreams = 0 + + -- local f_setstream = formatters[ [[io.savedata("svg-glyph-%05i",%q)]] ] + -- local f_getstream = formatters[ [[svg-glyph-%05i]] ] + + -- function otfsvg.storepdfdata(pdf) + --     nofstreams = nofstreams + 1 + --     storepdfdata = function(pdf) + --         nofstreams = nofstreams + 1 + --         return f_setstream(nofstreams,pdf), f_getstream(nofstreams) + --     end + -- end + +    local f_name = formatters[ [[svg-glyph-%05i]] ] +    local f_used = context and formatters[ [[original:///%s]] ] or formatters[ [[%s]] ] + +    local cache = { } + +    function otfsvg.storepdfdata(pdf) +        nofstreams = nofstreams + 1 +        local o, n = epdf.openMemStream(pdf,#pdf,f_name(nofstreams)) +        cache[n] = o -- we need to keep in mem +        return nil, f_used(n), nil +    end + +    if context then + +        local storepdfdata = otfsvg.storepdfdata +        local initialized  = false + +        function otfsvg.storepdfdata(pdf) +            if not initialized then +                if resolvers.setmemstream then +                    local f_setstream = formatters[ [[resolvers.setmemstream("svg-glyph-%05i",%q,true)]] ] +                    local f_getstream = formatters[ [[memstream:///svg-glyph-%05i]] ] +                    local f_nilstream = formatters[ [[resolvers.resetmemstream("svg-glyph-%05i",true)]] ] +                    storepdfdata = function(pdf) +                        nofstreams = nofstreams + 1 +                        return +                            f_setstream(nofstreams,pdf), +                            f_getstream(nofstreams), +                            f_nilstream(nofstreams) +                    end +                    otfsvg.storepdfdata = storepdfdata +                end +                initialized = true +            end +            return storepdfdata(pdf) +        end + +    end + +end + +if context and xml.convert then + +    local report_svg = logs.reporter("fonts","svg conversion") + +    function otfsvg.topdf(svgshapes) +        local svgfile   = "temp-otf-svg-shape.svg" +        local pdffile   = "temp-otf-svg-shape.pdf" +        local command   = "inkscape " .. svgfile .. " --export-pdf=" .. pdffile +     -- local command   = [[python "c:\Users\Hans Hagen\AppData\Roaming\Python\Scripts\cairosvg" -f pdf ]] .. svgfile .. " -o " .. pdffile +        local testrun   = false +        local pdfshapes = { } +        local nofshapes = #svgshapes +        report_svg("processing %i svg containers",nofshapes) +        for i=1,nofshapes do +            local entry = svgshapes[i] +            for j=entry.first,entry.last do +                local svg  = xml.convert(entry.data) +                local data = xml.first(svg,"/svg[@id='glyph"..j.."']") +                io.savedata(svgfile,tostring(data)) +                report_svg("processing svg shape of glyph %i in container %i",j,i) +                os.execute(command) +                pdfshapes[j] = io.loaddata(pdffile) +            end +            if testrun and i > testrun then +                report_svg("quiting test run") +                break +            end +        end +        os.remove(svgfile) +        return pdfshapes +    end + +else + +    function otfsvg.topdf(svgshapes) +        local svgfile   = "temp-otf-svg-shape.svg" +        local pdffile   = "temp-otf-svg-shape.pdf" +        local command   = "inkscape " .. svgfile .. " --export-pdf=" .. pdffile +        local pdfshapes = { } +        local nofshapes = #svgshapes +        texio.write(formatters["[converting %i svg glyphs to pdf using command %q : "](nofshapes,command)) +        for i=1,nofshapes do +            local entry = svgshapes[i] +            for j=entry.first,entry.last do +                -- cross our fingers .. some, day i will filter +                texio.write(formatters["%i "](j)) +                io.savedata(svgfile,tostring(entry.data)) +                os.execute(command) +                pdfshapes[j] = io.loaddata(pdffile) +            end +        end +        os.remove(svgfile) +        texio.write("done]") +        return pdfshapes +    end + +end + +local function initializesvg(tfmdata,kind,value) -- hm, always value +    if value and otf.svgenabled then +        local characters   = tfmdata.characters +        local descriptions = tfmdata.descriptions +        local properties   = tfmdata.properties +        -- +        local svg       = properties.svg +        local hash      = svg and svg.hash +        local timestamp = svg and svg.timestamp +        if not hash then +            return +        end +        -- +        local pdffile   = containers.read(otf.pdfcache,hash) +        local pdfshapes = pdffile and pdffile.pdfshapes +        if not pdfshapes or pdffile.timestamp ~= timestamp then +            local svgfile   = containers.read(otf.svgcache,hash) +            local svgshapes = svgfile and svgfile.svgshapes +            pdfshapes = svgshapes and otfsvg.topdf(svgshapes) or { } +            containers.write(otf.pdfcache, hash, { +                pdfshapes = pdfshapes, +                timestamp = timestamp, +            }) +        end +        if not pdfshapes or not next(pdfshapes) then +            return +        end +        -- +        properties.virtualized = true +        tfmdata.fonts = { +            { id = 0 } +        } +        -- +        local getactualtext = otf.getactualtext +        local storepdfdata  = otfsvg.storepdfdata +        -- +        local nop = { "nop" } +        -- +        for unicode, character in next, characters do +            local index = character.index +            if index then +                local pdf = pdfshapes[index] +                if pdf then +                    local setcode, name, nilcode = storepdfdata(pdf) +                    if name then +                        local bt, et = getactualtext(unicode) +                        local wd = character.width  or 0 +                        local ht = character.height or 0 +                        local dp = character.depth  or 0 +                        character.commands = { +                            { "special", "pdf:direct:" .. bt }, +                            { "down", dp }, +                            setcode and { "lua", setcode } or nop, +                            { "image", { filename = name, width = wd, height = ht, depth = dp } }, +                            nilcode and { "lua", nilcode } or nop, +                            { "special", "pdf:direct:" .. et }, +                        } +                        character.svg = true +                    end +                end +            end +        end +    end +end + +fonts.handlers.otf.features.register { +    name         = "svg", +    description  = "svg glyphs", +    manipulators = { +        base = initializesvg, +        node = initializesvg, +    } +} diff --git a/src/fontloader/misc/fontloader-font-afm.lua b/src/fontloader/misc/fontloader-font-one.lua index 0d6b7cb..a6f47e8 100644 --- a/src/fontloader/misc/fontloader-font-afm.lua +++ b/src/fontloader/misc/fontloader-font-one.lua @@ -1,4 +1,4 @@ -if not modules then modules = { } end modules ['font-afm'] = { +if not modules then modules = { } end modules ['font-one'] = {      version   = 1.001,      comment   = "companion to font-ini.mkiv",      author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", @@ -7,33 +7,27 @@ if not modules then modules = { } end modules ['font-afm'] = {  }  --[[ldx-- -<p>Some code may look a bit obscure but this has to do with the -fact that we also use this code for testing and much code evolved -in the transition from <l n='tfm'/> to <l n='afm'/> to <l -n='otf'/>.</p> +<p>Some code may look a bit obscure but this has to do with the fact that we also use +this code for testing and much code evolved in the transition from <l n='tfm'/> to +<l n='afm'/> to <l n='otf'/>.</p> -<p>The following code still has traces of intermediate font support -where we handles font encodings. Eventually font encoding goes -away.</p> - -<p>The embedding of a font involves creating temporary files and -depending on your system setup that can fail. It took more than a -day to figure out why sometimes embedding failed in mingw luatex -where running on a real path like c:\... failed while running on -say e:\... being a link worked well. The native windows binaries -don't have this issue.</p> +<p>The following code still has traces of intermediate font support where we handles +font encodings. Eventually font encoding went away but we kept some code around in +other modules.</p> +<p>This version implements a node mode approach so that users can also more easily +add features.</p>  --ldx]]--  local fonts, logs, trackers, containers, resolvers = fonts, logs, trackers, containers, resolvers -local next, type, tonumber = next, type, tonumber +local next, type, tonumber, rawget = next, type, tonumber, rawget  local match, gmatch, lower, gsub, strip, find = string.match, string.gmatch, string.lower, string.gsub, string.strip, string.find  local char, byte, sub = string.char, string.byte, string.sub  local abs = math.abs  local bxor, rshift = bit32.bxor, bit32.rshift -local P, S, R, Cmt, C, Ct, Cs, lpegmatch, patterns = lpeg.P, lpeg.S, lpeg.R, lpeg.Cmt, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.match, lpeg.patterns -local derivetable = table.derive +local P, S, R, Cmt, C, Ct, Cs, Carg = lpeg.P, lpeg.S, lpeg.R, lpeg.Cmt, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.Carg +local lpegmatch, patterns = lpeg.match, lpeg.patterns  local trace_features     = false  trackers.register("afm.features",   function(v) trace_features = v end)  local trace_indexing     = false  trackers.register("afm.indexing",   function(v) trace_indexing = v end) @@ -43,6 +37,7 @@ local trace_defining     = false  trackers.register("fonts.defining", function(v  local report_afm         = logs.reporter("fonts","afm loading")  local setmetatableindex  = table.setmetatableindex +local derivetable        = table.derive  local findbinfile        = resolvers.findbinfile @@ -50,434 +45,67 @@ local definers           = fonts.definers  local readers            = fonts.readers  local constructors       = fonts.constructors -local afm                = constructors.newhandler("afm") -local pfb                = constructors.newhandler("pfb") +local afm                = constructors.handlers.afm +local pfb                = constructors.handlers.pfb +local otf                = fonts.handlers.otf + +local otfreaders         = otf.readers +local otfenhancers       = otf.enhancers -local afmfeatures        = constructors.newfeatures("afm") +local afmfeatures        = constructors.features.afm  local registerafmfeature = afmfeatures.register -afm.version              = 1.501 -- incrementing this number one up will force a re-cache +afm.version              = 1.512 -- incrementing this number one up will force a re-cache  afm.cache                = containers.define("fonts", "afm", afm.version, true)  afm.autoprefixed         = true -- this will become false some day (catches texnansi-blabla.*)  afm.helpdata             = { }  -- set later on so no local for this  afm.syncspace            = true -- when true, nicer stretch values -afm.addligatures         = true -- best leave this set to true -afm.addtexligatures      = true -- best leave this set to true -afm.addkerns             = true -- best leave this set to true  local overloads          = fonts.mappings.overloads  local applyruntimefixes  = fonts.treatments and fonts.treatments.applyfixes -local function setmode(tfmdata,value) -    if value then -        tfmdata.properties.mode = lower(value) -    end -end - -registerafmfeature { -    name         = "mode", -    description  = "mode", -    initializers = { -        base = setmode, -        node = setmode, -    } -} -  --[[ldx-- -<p>We start with the basic reader which we give a name similar to the -built in <l n='tfm'/> and <l n='otf'/> reader.</p> ---ldx]]-- - ---~ Comment FONTIDENTIFIER LMMATHSYMBOLS10 ---~ Comment CODINGSCHEME TEX MATH SYMBOLS ---~ Comment DESIGNSIZE 10.0 pt ---~ Comment CHECKSUM O 4261307036 ---~ Comment SPACE 0 plus 0 minus 0 ---~ Comment QUAD 1000 ---~ Comment EXTRASPACE 0 ---~ Comment NUM 676.508 393.732 443.731 ---~ Comment DENOM 685.951 344.841 ---~ Comment SUP 412.892 362.892 288.889 ---~ Comment SUB 150 247.217 ---~ Comment SUPDROP 386.108 ---~ Comment SUBDROP 50 ---~ Comment DELIM 2390 1010 ---~ Comment AXISHEIGHT 250 - -local comment = P("Comment") -local spacing = patterns.spacer  -- S(" \t")^1 -local lineend = patterns.newline -- S("\n\r") -local words   = C((1 - lineend)^1) -local number  = C((R("09") + S("."))^1) / tonumber * spacing^0 -local data    = lpeg.Carg(1) - -local pattern = ( -- needs testing ... not used anyway as we no longer need math afm's -    comment * spacing * -        ( -            data * ( -                ("CODINGSCHEME" * spacing * words                                      ) / function(fd,a)                                      end + -                ("DESIGNSIZE"   * spacing * number * words                             ) / function(fd,a)     fd[ 1]                 = a       end + -                ("CHECKSUM"     * spacing * number * words                             ) / function(fd,a)     fd[ 2]                 = a       end + -                ("SPACE"        * spacing * number * "plus" * number * "minus" * number) / function(fd,a,b,c) fd[ 3], fd[ 4], fd[ 5] = a, b, c end + -                ("QUAD"         * spacing * number                                     ) / function(fd,a)     fd[ 6]                 = a       end + -                ("EXTRASPACE"   * spacing * number                                     ) / function(fd,a)     fd[ 7]                 = a       end + -                ("NUM"          * spacing * number * number * number                   ) / function(fd,a,b,c) fd[ 8], fd[ 9], fd[10] = a, b, c end + -                ("DENOM"        * spacing * number * number                            ) / function(fd,a,b  ) fd[11], fd[12]         = a, b    end + -                ("SUP"          * spacing * number * number * number                   ) / function(fd,a,b,c) fd[13], fd[14], fd[15] = a, b, c end + -                ("SUB"          * spacing * number * number                            ) / function(fd,a,b)   fd[16], fd[17]         = a, b    end + -                ("SUPDROP"      * spacing * number                                     ) / function(fd,a)     fd[18]                 = a       end + -                ("SUBDROP"      * spacing * number                                     ) / function(fd,a)     fd[19]                 = a       end + -                ("DELIM"        * spacing * number * number                            ) / function(fd,a,b)   fd[20], fd[21]         = a, b    end + -                ("AXISHEIGHT"   * spacing * number                                     ) / function(fd,a)     fd[22]                 = a       end -            ) -          + (1-lineend)^0 -        ) -  + (1-comment)^1 -)^0 - -local function scan_comment(str) -    local fd = { } -    lpegmatch(pattern,str,1,fd) -    return fd -end - --- On a rainy day I will rewrite this in lpeg ... or we can use the (slower) fontloader --- as in now supports afm/pfb loading but it's not too bad to have different methods --- for testing approaches. - -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.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) -    line = lower(line) -    local designsize = match(line,"designsize[^%d]*(%d+)") -    if designsize then data.metadata.designsize = tonumber(designsize) end -end - -local function get_charmetrics(data,charmetrics,vector) -    local characters = data.characters -    local chr, ind = { }, 0 -    for k, v in gmatch(charmetrics,"([%a]+) +(.-) *;") do -        if k == 'C'  then -            v = tonumber(v) -            if v < 0 then -                ind = ind + 1 -- ? -            else -                ind = v -            end -            chr = { -                index = ind -            } -        elseif k == 'WX' then -            chr.width = tonumber(v) -        elseif k == 'N'  then -            characters[v] = chr -        elseif k == 'B'  then -            local llx, lly, urx, ury = match(v,"^ *(.-) +(.-) +(.-) +(.-)$") -            chr.boundingbox = { tonumber(llx), tonumber(lly), tonumber(urx), tonumber(ury) } -        elseif k == 'L'  then -            local plus, becomes = match(v,"^(.-) +(.-)$") -            local ligatures = chr.ligatures -            if ligatures then -                ligatures[plus] = becomes -            else -                chr.ligatures = { [plus] = becomes } -            end -        end -    end -end - -local function get_kernpairs(data,kernpairs) -    local characters = data.characters -    for one, two, value in gmatch(kernpairs,"KPX +(.-) +(.-) +(.-)\n") do -        local chr = characters[one] -        if chr then -            local kerns = chr.kerns -            if kerns then -                kerns[two] = tonumber(value) -            else -                chr.kerns = { [two] = tonumber(value) } -            end -        end -    end -end - -local function get_variables(data,fontmetrics) -    for key, rest in gmatch(fontmetrics,"(%a+) *(.-)[\n\r]") do -        local keyhandler = keys[key] -        if keyhandler then -            keyhandler(data,rest) -        end -    end -end - -local get_indexes - -do - -    -- old font loader - -    local fontloader      = fontloader -    local get_indexes_old = false - -    if fontloader then - -        local font_to_table = fontloader.to_table -        local open_font     = fontloader.open -        local close_font    = fontloader.close - -        get_indexes_old = function(data,pfbname) -            local pfbblob = open_font(pfbname) -            if pfbblob then -                local characters = data.characters -                local pfbdata = font_to_table(pfbblob) -                if pfbdata then -                    local glyphs = pfbdata.glyphs -                    if glyphs then -                        if trace_loading then -                            report_afm("getting index data from %a",pfbname) -                        end -                        for index, glyph in next, glyphs do -                            local name = glyph.name -                            if name then -                                local char = characters[name] -                                if char then -                                    if trace_indexing then -                                        report_afm("glyph %a has index %a",name,index) -                                    end -                                    char.index = index -                                end -                            end -                        end -                    elseif trace_loading then -                        report_afm("no glyph data in pfb file %a",pfbname) -                    end -                elseif trace_loading then -                    report_afm("no data in pfb file %a",pfbname) -                end -                close_font(pfbblob) -            elseif trace_loading then -                report_afm("invalid pfb file %a",pfbname) -            end -        end - -    end - -    -- new (unfinished) font loader but i see no differences between -    -- old and new (one bad vector with old) - -    local n, m - -    local progress = function(str,position,name,size) -        local forward = position + tonumber(size) + 3 + 2 -        n = n + 1 -        if n >= m then -            return #str, name -        elseif forward < #str then -            return forward, name -        else -            return #str, name -        end -    end - -    local initialize = function(str,position,size) -        n = 0 -        m = tonumber(size) -        return position + 1 -    end - -    local charstrings = P("/CharStrings") -    local name        = P("/") * C((R("az")+R("AZ")+R("09")+S("-_."))^1) -    local size        = C(R("09")^1) -    local spaces      = P(" ")^1 - -    local p_filternames = Ct ( -        (1-charstrings)^0 * charstrings * spaces * Cmt(size,initialize) -      * (Cmt(name * P(" ")^1 * C(R("09")^1), progress) + P(1))^1 -    ) - -    -- if one of first 4 not 0-9A-F then binary else hex - -    local decrypt - -    do - -        local r, c1, c2, n = 0, 0, 0, 0 - -        local function step(c) -            local cipher = byte(c) -            local plain  = bxor(cipher,rshift(r,8)) -            r = ((cipher + r) * c1 + c2) % 65536 -            return char(plain) -        end - -        decrypt = function(binary) -            r, c1, c2, n = 55665, 52845, 22719, 4 -            binary       = gsub(binary,".",step) -            return sub(binary,n+1) -        end - -     -- local pattern = Cs((P(1) / step)^1) -     -- -     -- decrypt = function(binary) -     --     r, c1, c2, n = 55665, 52845, 22719, 4 -     --     binary = lpegmatch(pattern,binary) -     --     return sub(binary,n+1) -     -- end - -    end - -    local function loadpfbvector(filename) -        -- for the moment limited to encoding only - -        local data = io.loaddata(resolvers.findfile(filename)) - -        if not find(data,"!PS%-AdobeFont%-") then -            print("no font",filename) -            return -        end - -        if not data then -            print("no data",filename) -            return -        end - -        local ascii, binary = match(data,"(.*)eexec%s+......(.*)") - -        if not binary then -            print("no binary",filename) -            return -        end - -        binary = decrypt(binary,4) - -        local vector = lpegmatch(p_filternames,binary) - -        vector[0] = table.remove(vector,1) - -        if not vector then -            print("no vector",filename) -            return -        end - -        return vector - -    end - -    get_indexes = function(data,pfbname) -        local vector = loadpfbvector(pfbname) -        if vector then -            local characters = data.characters -            if trace_loading then -                report_afm("getting index data from %a",pfbname) -            end -            for index=1,#vector do -                local name = vector[index] -                local char = characters[name] -                if char then -                    if trace_indexing then -                        report_afm("glyph %a has index %a",name,index) -                    end -                    char.index = index -                end -            end -        end -    end +<p>We cache files. Caching is taken care of in the loader. We cheat a bit by adding +ligatures and kern information to the afm derived data. That way we can set them faster +when defining a font.</p> -    if get_indexes_old then - -        afm.use_new_indexer = true -        get_indexes_new     = get_indexes - -        get_indexes = function(data,pfbname) -            if afm.use_new_indexer then -                return get_indexes_new(data,pfbname) -            else -                return get_indexes_old(data,pfbname) -            end -        end +<p>We still keep the loading two phased: first we load the data in a traditional +fashion and later we transform it to sequences. Then we apply some methods also +used in opentype fonts (like <t>tlig</t>).</p> +--ldx]]-- -    end +local enhancers = { +    -- It's cleaner to implement them after we've seen what we are +    -- dealing with. +} -end +local steps     = { +    "unify names", +    "add ligatures", +    "add extra kerns", +    "normalize features", +    "fix names", +--  "add tounicode data", +} -local function readafm(filename) -    local ok, afmblob, size = resolvers.loadbinfile(filename) -- has logging -    if ok and afmblob then -        local data = { -            resources = { -                filename = resolvers.unresolve(filename), -                version  = afm.version, -                creator  = "context mkiv", -            }, -            properties = { -                hasitalics = false, -            }, -            goodies = { -            }, -            metadata   = { -                filename = file.removesuffix(file.basename(filename)) -            }, -            characters = { -                -- a temporary store -            }, -            descriptions = { -                -- the final store -            }, -        } -        afmblob = gsub(afmblob,"StartCharMetrics(.-)EndCharMetrics", function(charmetrics) +local function applyenhancers(data,filename) +    for i=1,#steps do +        local step     = steps[i] +        local enhancer = enhancers[step] +        if enhancer then              if trace_loading then -                report_afm("loading char metrics") +                report_afm("applying enhancer %a",step)              end -            get_charmetrics(data,charmetrics,vector) -            return "" -        end) -        afmblob = gsub(afmblob,"StartKernPairs(.-)EndKernPairs", function(kernpairs) -            if trace_loading then -                report_afm("loading kern pairs") -            end -            get_kernpairs(data,kernpairs) -            return "" -        end) -        afmblob = gsub(afmblob,"StartFontMetrics%s+([%d%.]+)(.-)EndFontMetrics", function(version,fontmetrics) -            if trace_loading then -                report_afm("loading variables") -            end -            data.afmversion = version -            get_variables(data,fontmetrics) -            data.fontdimens = scan_comment(fontmetrics) -- todo: all lpeg, no time now -            return "" -        end) -        return data -    else -        if trace_loading then -            report_afm("no valid afm file %a",filename) +            enhancer(data,filename) +        else +            report_afm("invalid enhancer %a",step)          end -        return nil      end  end ---[[ldx-- -<p>We cache files. Caching is taken care of in the loader. We cheat a bit -by adding ligatures and kern information to the afm derived data. That -way we can set them faster when defining a font.</p> ---ldx]]-- - -local addkerns, addligatures, addtexligatures, unify, normalize, fixnames -- we will implement these later -  function afm.load(filename) -    -- hm, for some reasons not resolved yet      filename = resolvers.findfile(filename,'afm') or ""      if filename ~= "" and not fonts.names.ignoredfile(filename) then          local name = file.removesuffix(file.basename(filename)) @@ -498,55 +126,42 @@ function afm.load(filename)          end          if not data or data.size ~= size or data.time ~= time or data.pfbsize ~= pfbsize or data.pfbtime ~= pfbtime then              report_afm("reading %a",filename) -            data = readafm(filename) +            data = afm.readers.loadfont(filename,pfbname)              if data then -                if pfbname ~= "" then -                    data.resources.filename = resolvers.unresolve(pfbname) -                    get_indexes(data,pfbname) -               elseif trace_loading then -                    report_afm("no pfb file for %a",filename) -                 -- data.resources.filename = "unset" -- better than loading the afm file -                end -                report_afm("unifying %a",filename) -                unify(data,filename) -                if afm.addligatures then -                    report_afm("add ligatures") -                    addligatures(data) -                end -                if afm.addtexligatures then -                    report_afm("add tex ligatures") -                    addtexligatures(data) -                end -                if afm.addkerns then -                    report_afm("add extra kerns") -                    addkerns(data) -                end -                normalize(data) -                fixnames(data) -                report_afm("add tounicode data") +                applyenhancers(data,filename) +             -- otfreaders.addunicodetable(data) -- only when not done yet                  fonts.mappings.addtounicode(data,filename) +             -- otfreaders.extend(data) +                otfreaders.pack(data)                  data.size = size                  data.time = time                  data.pfbsize = pfbsize                  data.pfbtime = pfbtime                  report_afm("saving %a in cache",name) -                data.resources.unicodes = nil -- consistent with otf but here we save not much +             -- data.resources.unicodes = nil -- consistent with otf but here we save not much                  data = containers.write(afm.cache, name, data)                  data = containers.read(afm.cache,name)              end -            if applyruntimefixes and data then +        end +        if data then +         -- constructors.addcoreunicodes(unicodes) +            otfreaders.unpack(data) +            otfreaders.expand(data) -- inline tables +            otfreaders.addunicodetable(data) -- only when not done yet +            otfenhancers.apply(data,filename,data) +            if applyruntimefixes then                  applyruntimefixes(filename,data)              end          end          return data -    else -        return nil      end  end -local uparser = fonts.mappings.makenameparser() +-- we run a more advanced analyzer later on anyway + +local uparser = fonts.mappings.makenameparser() -- each time -unify = function(data, filename) +enhancers["unify names"] = function(data, filename)      local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context      local unicodes      = { }      local names         = { } @@ -556,7 +171,7 @@ unify = function(data, filename)          local code = unicodevector[name] -- or characters.name_to_unicode[name]          if not code then              code = lpegmatch(uparser,name) -            if not code then +            if type(code) ~= "number" then                  code = private                  private = private + 1                  report_afm("assigning private slot %U for unknown glyph name %a",code,name) @@ -602,15 +217,108 @@ end  local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } }  local noflags    = { false, false, false, false } -afm.experimental_normalize = false - -normalize = function(data) -    if type(afm.experimental_normalize) == "function" then -        afm.experimental_normalize(data) +enhancers["normalize features"] = function(data) +    local ligatures  = setmetatableindex("table") +    local kerns      = setmetatableindex("table") +    local extrakerns = setmetatableindex("table") +    for u, c in next, data.descriptions do +        local l = c.ligatures +        local k = c.kerns +        local e = c.extrakerns +        if l then +            ligatures[u] = l +            for u, v in next, l do +                l[u] = { ligature = v } +            end +            c.ligatures = nil +        end +        if k then +            kerns[u] = k +            for u, v in next, k do +                k[u] = v -- { v, 0 } +            end +            c.kerns = nil +        end +        if e then +            extrakerns[u] = e +            for u, v in next, e do +                e[u] = v -- { v, 0 } +            end +            c.extrakerns = nil +        end      end +    local features = { +        gpos = { }, +        gsub = { }, +    } +    local sequences = { +        -- only filled ones +    } +    if next(ligatures) then +        features.gsub.liga = everywhere +        data.properties.hasligatures = true +        sequences[#sequences+1] = { +            features = { +                liga = everywhere, +            }, +            flags    = noflags, +            name     = "s_s_0", +            nofsteps = 1, +            order    = { "liga" }, +            type     = "gsub_ligature", +            steps    = { +                { +                    coverage = ligatures, +                }, +            }, +        } +    end +    if next(kerns) then +        features.gpos.kern = everywhere +        data.properties.haskerns = true +        sequences[#sequences+1] = { +            features = { +                kern = everywhere, +            }, +            flags    = noflags, +            name     = "p_s_0", +            nofsteps = 1, +            order    = { "kern" }, +            type     = "gpos_pair", +            steps    = { +                { +                    format   = "kern", +                    coverage = kerns, +                }, +            }, +        } +    end +    if next(extrakerns) then +        features.gpos.extrakerns = everywhere +        data.properties.haskerns = true +        sequences[#sequences+1] = { +            features = { +                extrakerns = everywhere, +            }, +            flags    = noflags, +            name     = "p_s_1", +            nofsteps = 1, +            order    = { "extrakerns" }, +            type     = "gpos_pair", +            steps    = { +                { +                    format   = "kern", +                    coverage = extrakerns, +                }, +            }, +        } +    end +    -- todo: compress kerns +    data.resources.features  = features +    data.resources.sequences = sequences  end -fixnames = function(data) +enhancers["fix names"] = function(data)      for k, v in next, data.descriptions do          local n = v.name          local r = overloads[n] @@ -657,8 +365,13 @@ local addthem = function(rawdata,ligatures)      end  end -addligatures    = function(rawdata) addthem(rawdata,afm.helpdata.ligatures   ) end -addtexligatures = function(rawdata) addthem(rawdata,afm.helpdata.texligatures) end +enhancers["add ligatures"] = function(rawdata) +    addthem(rawdata,afm.helpdata.ligatures) +end + +-- enhancers["add tex ligatures"] = function(rawdata) +--     addthem(rawdata,afm.helpdata.texligatures) +-- end  --[[ldx--  <p>We keep the extra kerns in separate kerning tables so that we can use @@ -672,7 +385,7 @@ them selectively.</p>  -- we don't use the character database. (Ok, we can have a context specific  -- variant). -addkerns = function(rawdata) -- using shcodes is not robust here +enhancers["add extra kerns"] = function(rawdata) -- using shcodes is not robust here      local descriptions = rawdata.descriptions      local resources    = rawdata.resources      local unicodes     = resources.unicodes @@ -864,11 +577,31 @@ local function copytotfm(data)              end              --          end -        local fd = data.fontdimens -        if fd and fd[8] and fd[9] and fd[10] then -- math -            for k,v in next, fd do -                parameters[k] = v -            end +        -- +        if metadata.sup then +            local dummy    = { 0, 0, 0 } +            parameters[ 1] = metadata.designsize        or 0 +            parameters[ 2] = metadata.checksum          or 0 +            parameters[ 3], +            parameters[ 4], +            parameters[ 5] = unpack(metadata.space      or dummy) +            parameters[ 6] =        metadata.quad       or 0 +            parameters[ 7] =        metadata.extraspace or 0 +            parameters[ 8], +            parameters[ 9], +            parameters[10] = unpack(metadata.num        or dummy) +            parameters[11], +            parameters[12] = unpack(metadata.denom      or dummy) +            parameters[13], +            parameters[14], +            parameters[15] = unpack(metadata.sup        or dummy) +            parameters[16], +            parameters[17] = unpack(metadata.sub        or dummy) +            parameters[18] =        metadata.supdrop    or 0 +            parameters[19] =        metadata.subdrop    or 0 +            parameters[20], +            parameters[21] = unpack(metadata.delim      or dummy) +            parameters[22] =        metadata.axisheight or 0          end          --          parameters.designsize = (metadata.designsize or 10)*65536 @@ -900,10 +633,9 @@ local function copytotfm(data)  end  --[[ldx-- -<p>Originally we had features kind of hard coded for <l n='afm'/> -files but since I expect to support more font formats, I decided -to treat this fontformat like any other and handle features in a -more configurable way.</p> +<p>Originally we had features kind of hard coded for <l n='afm'/> files but since I +expect to support more font formats, I decided to treat this fontformat like any +other and handle features in a more configurable way.</p>  --ldx]]--  function afm.setfeatures(tfmdata,features) @@ -982,6 +714,8 @@ local function afmtotfm(specification)                          tfmdata.shared = shared                      end                      shared.rawdata   = rawdata +                    shared.dynamics  = { } +                    tfmdata.changed  = { }                      shared.features  = features                      shared.processes = afm.setfeatures(tfmdata,features)                  end @@ -1015,127 +749,34 @@ local function read_from_afm(specification)  end  --[[ldx-- -<p>Here comes the implementation of a few features. We only implement -those that make sense for this format.</p> +<p>We have the usual two modes and related features initializers and processors.</p>  --ldx]]-- -local function prepareligatures(tfmdata,ligatures,value) -    if value then -        local descriptions = tfmdata.descriptions -        local hasligatures = false -        for unicode, character in next, tfmdata.characters do -            local description = descriptions[unicode] -            local dligatures = description.ligatures -            if dligatures then -                local cligatures = character.ligatures -                if not cligatures then -                    cligatures = { } -                    character.ligatures = cligatures -                end -                for unicode, ligature in next, dligatures do -                    cligatures[unicode] = { -                        char = ligature, -                        type = 0 -                    } -                end -                hasligatures = true -            end -        end -        tfmdata.properties.hasligatures = hasligatures -    end -end - -local function preparekerns(tfmdata,kerns,value) +local function setmode(tfmdata,value)      if value then -        local rawdata      = tfmdata.shared.rawdata -        local resources    = rawdata.resources -        local unicodes     = resources.unicodes -        local descriptions = tfmdata.descriptions -        local haskerns     = false -        for u, chr in next, tfmdata.characters do -            local d = descriptions[u] -            local newkerns = d[kerns] -            if newkerns then -                local kerns = chr.kerns -                if not kerns then -                    kerns = { } -                    chr.kerns = kerns -                end -                for k,v in next, newkerns do -                    local uk = unicodes[k] -                    if uk then -                        kerns[uk] = v -                    end -                end -                haskerns = true -            end -        end -        tfmdata.properties.haskerns = haskerns -    end -end - -local list = { - -- [0x0022] = 0x201D, -    [0x0027] = 0x2019, - -- [0x0060] = 0x2018, -} - -local function texreplacements(tfmdata,value) -    local descriptions = tfmdata.descriptions -    local characters   = tfmdata.characters -    for k, v in next, list do -        characters  [k] = characters  [v] -- we forget about kerns -        descriptions[k] = descriptions[v] -- we forget about kerns +        tfmdata.properties.mode = lower(value)      end  end -local function ligatures   (tfmdata,value) prepareligatures(tfmdata,'ligatures',   value) end -local function texligatures(tfmdata,value) prepareligatures(tfmdata,'texligatures',value) end -local function kerns       (tfmdata,value) preparekerns    (tfmdata,'kerns',       value) end -local function extrakerns  (tfmdata,value) preparekerns    (tfmdata,'extrakerns',  value) end - -registerafmfeature { -    name         = "liga", -    description  = "traditional ligatures", -    initializers = { -        base = ligatures, -        node = ligatures, -    } -} - -registerafmfeature { -    name         = "kern", -    description  = "intercharacter kerning", -    initializers = { -        base = kerns, -        node = kerns, -    } -} - -registerafmfeature { -    name         = "extrakerns", -    description  = "additional intercharacter kerning", -    initializers = { -        base = extrakerns, -        node = extrakerns, -    } -} -  registerafmfeature { -    name         = 'tlig', -    description  = 'tex ligatures', +    name         = "mode", +    description  = "mode",      initializers = { -        base = texligatures, -        node = texligatures, +        base = setmode, +        node = setmode,      }  }  registerafmfeature { -    name         = 'trep', -    description  = 'tex replacements', +    name         = "features", +    description  = "features", +    default      = true,      initializers = { -        base = texreplacements, -        node = texreplacements, +        node     = otf.nodemodeinitializer, +        base     = otf.basemodeinitializer, +    }, +    processors   = { +        node     = otf.featuresprocessor,      }  } @@ -1171,7 +812,8 @@ local function check_afm(specification,fullname)  end  function readers.afm(specification,method) -    local fullname, tfmdata = specification.filename or "", nil +    local fullname = specification.filename or "" +    local tfmdata  = nil      if fullname == "" then          local forced = specification.forced or ""          if forced ~= "" then @@ -1200,7 +842,16 @@ function readers.pfb(specification,method) -- only called when forced      if trace_defining then          report_afm("using afm reader for %a",original)      end -    specification.specification = gsub(original,"%.pfb",".afm")      specification.forced = "afm" +    local function swap(name) +        local value = specification[swap] +        if value then +            specification[swap] = gsub("%.pfb",".afm",1) +        end +    end +    swap("filename") +    swap("fullname") +    swap("forcedname") +    swap("specification")      return readers.afm(specification,method)  end diff --git a/src/fontloader/misc/fontloader-font-onr.lua b/src/fontloader/misc/fontloader-font-onr.lua new file mode 100644 index 0000000..a4969ad --- /dev/null +++ b/src/fontloader/misc/fontloader-font-onr.lua @@ -0,0 +1,411 @@ +if not modules then modules = { } end modules ['font-onr'] = { +    version   = 1.001, +    comment   = "companion to font-ini.mkiv", +    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", +    copyright = "PRAGMA ADE / ConTeXt Development Team", +    license   = "see context related readme files" +} + +--[[ldx-- +<p>Some code may look a bit obscure but this has to do with the fact that we also use +this code for testing and much code evolved in the transition from <l n='tfm'/> to +<l n='afm'/> to <l n='otf'/>.</p> + +<p>The following code still has traces of intermediate font support where we handles +font encodings. Eventually font encoding went away but we kept some code around in +other modules.</p> + +<p>This version implements a node mode approach so that users can also more easily +add features.</p> +--ldx]]-- + +local fonts, logs, trackers, resolvers = fonts, logs, trackers, resolvers + +local next, type, tonumber, rawget = next, type, tonumber, rawget +local match, lower, gsub, strip, find = string.match, string.lower, string.gsub, string.strip, string.find +local char, byte, sub = string.char, string.byte, string.sub +local abs = math.abs +local bxor, rshift = bit32.bxor, bit32.rshift +local P, S, R, Cmt, C, Ct, Cs, Carg = lpeg.P, lpeg.S, lpeg.R, lpeg.Cmt, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.Carg +local lpegmatch, patterns = lpeg.match, lpeg.patterns + +local trace_indexing     = false  trackers.register("afm.indexing",   function(v) trace_indexing = v end) +local trace_loading      = false  trackers.register("afm.loading",    function(v) trace_loading  = v end) + +local report_afm         = logs.reporter("fonts","afm loading") +local report_afm         = logs.reporter("fonts","pfb loading") + +fonts                    = fonts or { } +local handlers           = fonts.handlers or { } +fonts.handlers           = handlers +local afm                = handlers.afm or { } +handlers.afm             = afm +local readers            = afm.readers or { } +afm.readers              = readers + +afm.version              = 1.512 -- incrementing this number one up will force a re-cache + +--[[ldx-- +<p>We start with the basic reader which we give a name similar to the built in <l n='tfm'/> +and <l n='otf'/> reader.</p> +<p>We use a new (unfinished) pfb loader but I see no differences between the old +and new vectors (we actually had one bad vector with the old loader).</p> +--ldx]]-- + +local get_indexes + +do + +    local n, m + +    local progress = function(str,position,name,size) +        local forward = position + tonumber(size) + 3 + 2 +        n = n + 1 +        if n >= m then +            return #str, name +        elseif forward < #str then +            return forward, name +        else +            return #str, name +        end +    end + +    local initialize = function(str,position,size) +        n = 0 +        m = tonumber(size) +        return position + 1 +    end + +    local charstrings = P("/CharStrings") +    local name        = P("/") * C((R("az")+R("AZ")+R("09")+S("-_."))^1) +    local size        = C(R("09")^1) +    local spaces      = P(" ")^1 + +    local p_filternames = Ct ( +        (1-charstrings)^0 * charstrings * spaces * Cmt(size,initialize) +      * (Cmt(name * P(" ")^1 * C(R("09")^1), progress) + P(1))^1 +    ) + +    -- if one of first 4 not 0-9A-F then binary else hex + +    local decrypt + +    do + +        local r, c1, c2, n = 0, 0, 0, 0 + +        local function step(c) +            local cipher = byte(c) +            local plain  = bxor(cipher,rshift(r,8)) +            r = ((cipher + r) * c1 + c2) % 65536 +            return char(plain) +        end + +        decrypt = function(binary) +            r, c1, c2, n = 55665, 52845, 22719, 4 +            binary       = gsub(binary,".",step) +            return sub(binary,n+1) +        end + +     -- local pattern = Cs((P(1) / step)^1) +     -- +     -- decrypt = function(binary) +     --     r, c1, c2, n = 55665, 52845, 22719, 4 +     --     binary = lpegmatch(pattern,binary) +     --     return sub(binary,n+1) +     -- end + +    end + +    local function loadpfbvector(filename) +        -- for the moment limited to encoding only + +        local data = io.loaddata(resolvers.findfile(filename)) + +        if not data then +            report_pfb("no data in %a",filename) +            return +        end + +        if not (find(data,"!PS%-AdobeFont%-") or find(data,"%%!FontType1")) then +            report_pfb("no font in %a",filename) +            return +        end + +        local ascii, binary = match(data,"(.*)eexec%s+......(.*)") + +        if not binary then +            report_pfb("no binary data in %a",filename) +            return +        end + +        binary = decrypt(binary,4) + +        local vector = lpegmatch(p_filternames,binary) + +        if vector[1] == ".notdef" then +            -- tricky +            vector[0] = table.remove(vector,1) +        end + +        if not vector then +            report_pfb("no vector in %a",filename) +            return +        end + +        return vector + +    end + +    get_indexes = function(data,pfbname) +        local vector = loadpfbvector(pfbname) +        if vector then +            local characters = data.characters +            if trace_loading then +                report_afm("getting index data from %a",pfbname) +            end +            for index=1,#vector do +                local name = vector[index] +                local char = characters[name] +                if char then +                    if trace_indexing then +                        report_afm("glyph %a has index %a",name,index) +                    end +                    char.index = index +                end +            end +        end +    end + +end + +--[[ldx-- +<p>We start with the basic reader which we give a name similar to the built in <l n='tfm'/> +and <l n='otf'/> reader. We only need data that is relevant for our use. We don't support +more complex arrangements like multiple master (obsolete), direction specific kerning, etc.</p> +--ldx]]-- + +local spacer     = patterns.spacer +local whitespace = patterns.whitespace +local lineend    = patterns.newline +local spacing    = spacer^0 +local number     = spacing * S("+-")^-1 * (R("09") + S("."))^1 / tonumber +local name       = spacing * C((1 - whitespace)^1) +local words      = spacing * ((1 - lineend)^1 / strip) +local rest       = (1 - lineend)^0 +local fontdata   = Carg(1) +local semicolon  = spacing * P(";") +local plus       = spacing * P("plus") * number +local minus      = spacing * P("minus") * number + +-- kern pairs + +local function addkernpair(data,one,two,value) +    local chr = data.characters[one] +    if chr then +        local kerns = chr.kerns +        if kerns then +            kerns[two] = tonumber(value) +        else +            chr.kerns = { [two] = tonumber(value) } +        end +    end +end + +local p_kernpair = (fontdata * P("KPX") * name * name * number) / addkernpair + +-- char metrics + +local chr = false +local ind = 0 + +local function start(data,version) +    data.metadata.afmversion = version +    ind = 0 +    chr = { } +end + +local function stop() +    ind = 0 +    chr = false +end + +local function setindex(i) +    if i < 0 then +        ind = ind + 1 -- ? +    else +        ind = i +    end +    chr = { +        index = ind +    } +end + +local function setwidth(width) +    chr.width = width +end + +local function setname(data,name) +    data.characters[name] = chr +end + +local function setboundingbox(boundingbox) +    chr.boundingbox = boundingbox +end + +local function setligature(plus,becomes) +    local ligatures = chr.ligatures +    if ligatures then +        ligatures[plus] = becomes +    else +        chr.ligatures = { [plus] = becomes } +    end +end + +local p_charmetric = ( ( +    P("C")  * number          / setindex +  + P("WX") * number          / setwidth +  + P("N")  * fontdata * name / setname +  + P("B")  * Ct((number)^4)  / setboundingbox +  + P("L")  * (name)^2        / setligature +  ) * semicolon )^1 + +local p_charmetrics = P("StartCharMetrics") * number * (p_charmetric + (1-P("EndCharMetrics")))^0 * P("EndCharMetrics") +local p_kernpairs   = P("StartKernPairs")   * number * (p_kernpair   + (1-P("EndKernPairs"  )))^0 * P("EndKernPairs"  ) + +local function set_1(data,key,a)     data.metadata[lower(key)] = a           end +local function set_2(data,key,a,b)   data.metadata[lower(key)] = { a, b }    end +local function set_3(data,key,a,b,c) data.metadata[lower(key)] = { a, b, c } end + +-- Notice         string +-- EncodingScheme string +-- MappingScheme  integer +-- EscChar        integer +-- CharacterSet   string +-- Characters     integer +-- IsBaseFont     boolean +-- VVector        number number +-- IsFixedV       boolean + +local p_parameters = P(false) +  + fontdata +  * ((P("FontName") + P("FullName") + P("FamilyName"))/lower) +  * words / function(data,key,value) +        data.metadata[key] = value +    end +  + fontdata +  * ((P("Weight") + P("Version"))/lower) +  * name / function(data,key,value) +        data.metadata[key] = value +    end +  + fontdata +  * P("IsFixedPitch") +  * name / function(data,pitch) +        data.metadata.monospaced = toboolean(pitch,true) +    end +  + fontdata +  * P("FontBBox") +  * Ct(number^4) / function(data,boundingbox) +        data.metadata.boundingbox = boundingbox +  end +  + fontdata +  * ((P("CharWidth") + P("CapHeight") + P("XHeight") + P("Descender") + P("Ascender") + P("ItalicAngle"))/lower) +  * number / function(data,key,value) +        data.metadata[key] = value +    end +  + P("Comment") * spacing * ( P(false) +      + (fontdata * C("DESIGNSIZE")     * number                   * rest) / set_1 -- 1 +      + (fontdata * C("TFM designsize") * number                   * rest) / set_1 +      + (fontdata * C("DesignSize")     * number                   * rest) / set_1 +      + (fontdata * C("CODINGSCHEME")   * words                    * rest) / set_1 -- +      + (fontdata * C("CHECKSUM")       * number * words           * rest) / set_1 -- 2 +      + (fontdata * C("SPACE")          * number * plus * minus    * rest) / set_3 -- 3 4 5 +      + (fontdata * C("QUAD")           * number                   * rest) / set_1 -- 6 +      + (fontdata * C("EXTRASPACE")     * number                   * rest) / set_1 -- 7 +      + (fontdata * C("NUM")            * number * number * number * rest) / set_3 -- 8 9 10 +      + (fontdata * C("DENOM")          * number * number          * rest) / set_2 -- 11 12 +      + (fontdata * C("SUP")            * number * number * number * rest) / set_3 -- 13 14 15 +      + (fontdata * C("SUB")            * number * number          * rest) / set_2 -- 16 17 +      + (fontdata * C("SUPDROP")        * number                   * rest) / set_1 -- 18 +      + (fontdata * C("SUBDROP")        * number                   * rest) / set_1 -- 19 +      + (fontdata * C("DELIM")          * number * number          * rest) / set_2 -- 20 21 +      + (fontdata * C("AXISHEIGHT")     * number                   * rest) / set_1 -- 22 +    ) + +local fullparser = ( P("StartFontMetrics") * fontdata * name / start ) +                 * ( p_charmetrics + p_kernpairs + p_parameters + (1-P("EndFontMetrics")) )^0 +                 * ( P("EndFontMetrics") / stop ) + +local fullparser = ( P("StartFontMetrics") * fontdata * name / start ) +                 * ( p_charmetrics + p_kernpairs + p_parameters + (1-P("EndFontMetrics")) )^0 +                 * ( P("EndFontMetrics") / stop ) + +local infoparser = ( P("StartFontMetrics") * fontdata * name / start ) +                 * ( p_parameters + (1-P("EndFontMetrics")) )^0 +                 * ( P("EndFontMetrics") / stop ) + +--    infoparser = ( P("StartFontMetrics") * fontdata * name / start ) +--               * ( p_parameters + (1-P("EndFontMetrics") - P("StartCharMetrics")) )^0 +--               * ( (P("EndFontMetrics") + P("StartCharMetrics")) / stop ) + +local function read(filename,parser) +    local afmblob = io.loaddata(filename) +    if afmblob then +        local data = { +            resources = { +                filename = resolvers.unresolve(filename), +                version  = afm.version, +                creator  = "context mkiv", +            }, +            properties = { +                hasitalics = false, +            }, +            goodies = { +            }, +            metadata   = { +                filename = file.removesuffix(file.basename(filename)) +            }, +            characters = { +                -- a temporary store +            }, +            descriptions = { +                -- the final store +            }, +        } +        if trace_loading then +            report_afm("parsing afm file %a",filename) +        end +        lpegmatch(parser,afmblob,1,data) +        return data +    else +        if trace_loading then +            report_afm("no valid afm file %a",filename) +        end +        return nil +    end +end + +function readers.loadfont(afmname,pfbname) +    local data = read(resolvers.findfile(afmname),fullparser) +    if data then +        if not pfbname or pfbname == "" then +            pfbname = file.replacesuffix(file.nameonly(afmname),"pfb") +            pfbname = resolvers.findfile(pfbname) +        end +        if pfbname and pfbname ~= "" then +            data.resources.filename = resolvers.unresolve(pfbname) +            get_indexes(data,pfbname) +        elseif trace_loading then +            report_afm("no pfb file for %a",afmname) +         -- data.resources.filename = "unset" -- better than loading the afm file +        end +        return data +    end +end + +function readers.getinfo(filename) +    local data = read(resolvers.findfile(filename),infoparser) +    if data then +        return data.metadata +    end +end diff --git a/src/fontloader/misc/fontloader-font-osd.lua b/src/fontloader/misc/fontloader-font-osd.lua index 6ff2e38..a3dda67 100644 --- a/src/fontloader/misc/fontloader-font-osd.lua +++ b/src/fontloader/misc/fontloader-font-osd.lua @@ -79,13 +79,10 @@ fonts.analyzers.methods  = fonts.analyzers.methods or { node = { otf = { } } }  local otf                = fonts.handlers.otf -local nodecodes          = nodes.nodecodes -local glyph_code         = nodecodes.glyph -  local handlers           = otf.handlers  local methods            = fonts.analyzers.methods -local otffeatures        = fonts.constructors.newfeatures("otf") +local otffeatures        = fonts.constructors.features.otf  local registerotffeature = otffeatures.register  local nuts               = nodes.nuts diff --git a/src/fontloader/misc/fontloader-font-ota.lua b/src/fontloader/misc/fontloader-font-ota.lua index 6a3804a..4ddb831 100644 --- a/src/fontloader/misc/fontloader-font-ota.lua +++ b/src/fontloader/misc/fontloader-font-ota.lua @@ -44,7 +44,6 @@ local getchar             = nuts.getchar  local ischar              = nuts.is_char  local traverse_id         = nuts.traverse_id -local traverse_node_list  = nuts.traverse  local end_of_math         = nuts.end_of_math  local nodecodes           = nodes.nodecodes @@ -56,7 +55,7 @@ local fontdata            = fonts.hashes.identifiers  local categories          = characters and characters.categories or { } -- sorry, only in context  local chardata            = characters and characters.data -local otffeatures         = fonts.constructors.newfeatures("otf") +local otffeatures         = fonts.constructors.features.otf  local registerotffeature  = otffeatures.register  --[[ldx-- diff --git a/src/fontloader/misc/fontloader-font-otd.lua b/src/fontloader/misc/fontloader-font-otd.lua index 2257caa..64cb1bc 100644 --- a/src/fontloader/misc/fontloader-font-otd.lua +++ b/src/fontloader/misc/fontloader-font-otd.lua @@ -36,9 +36,6 @@ local contextmerged      = specifiers.contextmerged  local setmetatableindex  = table.setmetatableindex -local otffeatures        = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register -  local a_to_script        = { }  local a_to_language      = { } @@ -135,6 +132,10 @@ local wildcard = "*"  -- needs checking: some added features can pass twice +local P, C, Cc, lpegmatch = lpeg.P, lpeg.C, lpeg.Cc, lpeg.match + +local pattern = P("always") * (P(-1) * Cc(true) + P(":") * C((1-P(-1))^1)) +  local function initialize(sequence,script,language,s_enabled,a_enabled,font,attr,dynamic,ra,autoscript,autolanguage)      local features = sequence.features      if features then @@ -151,21 +152,34 @@ local function initialize(sequence,script,language,s_enabled,a_enabled,font,attr                      e_e = s_enabled and s_enabled[kind] -- the value (font)                  end                  if e_e then -                    local scripts = features[kind] -- -                    local languages = scripts[script] or scripts[wildcard] -                    if not languages and autoscript then -                        langages = defaultscript(featuretype,autoscript,scripts) -                    end -                    if languages then -                        -- we need detailed control over default becase we want to trace -                        -- only first attribute match check, so we assume simple fina's -                        local valid = false -                        if languages[language] then -                            valid = e_e -                        elseif languages[wildcard] then -                            valid = e_e -                        elseif autolanguage and defaultlanguage(featuretype,autolanguage,languages) then -                            valid = e_e +                    local valid = type(e_e) == "string" and lpegmatch(pattern,e_e) +                    if valid then +                        -- we have hit always +                        local attribute = autofeatures[kind] or false +                        if trace_applied then +                            report_process( +                                "font %s, dynamic %a (%a), feature %a, script %a, language %a, lookup %a, value %a", +                                    font,attr or 0,dynamic,kind,"*","*",sequence.name,valid) +                        end +                        ra[#ra+1] = { valid, attribute, sequence, kind } +                    else +                        -- we already checked for e_e +                        local scripts   = features[kind] -- +                        local languages = scripts[script] or scripts[wildcard] +                        if not languages and autoscript then +                            langages = defaultscript(featuretype,autoscript,scripts) +                        end +                        if languages then +                            -- we need detailed control over default becase we want to trace +                            -- only first attribute match check, so we assume simple fina's +                         -- local valid = false +                            if languages[language] then +                                valid = e_e +                            elseif languages[wildcard] then +                                valid = e_e +                            elseif autolanguage and defaultlanguage(featuretype,autolanguage,languages) then +                                valid = e_e +                            end                          end                          if valid then                              local attribute = autofeatures[kind] or false @@ -244,6 +258,7 @@ function otf.dataset(tfmdata,font,attr) -- attr only when explicit (as in specia              local autoscript   = (s_enabled and s_enabled.autoscript  ) or (a_enabled and a_enabled.autoscript  )              local autolanguage = (s_enabled and s_enabled.autolanguage) or (a_enabled and a_enabled.autolanguage)              for s=1,#sequences do +                -- just return nil or ra step                  initialize(sequences[s],script,language,s_enabled,a_enabled,font,attr,dynamic,ra,autoscript,autolanguage)              end          end diff --git a/src/fontloader/misc/fontloader-font-oti.lua b/src/fontloader/misc/fontloader-font-oti.lua index bacd001..d74d2d5 100644 --- a/src/fontloader/misc/fontloader-font-oti.lua +++ b/src/fontloader/misc/fontloader-font-oti.lua @@ -11,8 +11,8 @@ local lower = string.lower  local fonts              = fonts  local constructors       = fonts.constructors -local otf                = constructors.newhandler("otf") -local otffeatures        = constructors.newfeatures("otf") +local otf                = constructors.handlers.otf +local otffeatures        = constructors.features.otf  local registerotffeature = otffeatures.register  local otftables          = otf.tables or { } diff --git a/src/fontloader/misc/fontloader-font-otj.lua b/src/fontloader/misc/fontloader-font-otj.lua index 6ff80d8..d1408fd 100644 --- a/src/fontloader/misc/fontloader-font-otj.lua +++ b/src/fontloader/misc/fontloader-font-otj.lua @@ -24,10 +24,11 @@ if not modules then modules = { } end modules ['font-otj'] = {  -- The use_advance code is just a test and is meant for testing and manuals. There is no  -- performance (or whatever) gain and using kerns is somewhat cleaner (at least for now). +-- Maybe: subtype fontkern when pure kerns. +  if not nodes.properties then return end  local next, rawget = next, rawget -local utfchar = utf.char  local fastcopy = table.fastcopy  local registertracker = trackers.register @@ -92,7 +93,6 @@ local traverse_id        = nuts.traverse_id  local traverse_char      = nuts.traverse_char  local insert_node_before = nuts.insert_before  local insert_node_after  = nuts.insert_after -local find_tail          = nuts.tail  local properties         = nodes.properties.data @@ -755,7 +755,7 @@ local function inject_pairs_only(head,where)                          end                          local leftkern = i.leftkern                          if leftkern and leftkern ~= 0 then -                            insert_node_before(head,current,newkern(leftkern)) +                            head = insert_node_before(head,current,newkern(leftkern))                          end                          local rightkern = i.rightkern                          if rightkern and rightkern ~= 0 then @@ -1255,11 +1255,11 @@ local function inject_everything(head,where)                                  insert_node_after(pre,n,newkern(rightkern))                                  done = true                              end -                        end -                        if hasmarks then -                            local pm = i.markbasenode -                            if pm then -                                processmark(pm,current,i) +                            if hasmarks then +                                local pm = i.markbasenode +                                if pm then +                                    processmark(pm,current,i) +                                end                              end                          end                      end @@ -1287,11 +1287,11 @@ local function inject_everything(head,where)                                  insert_node_after(post,n,newkern(rightkern))                                  done = true                              end -                        end -                        if hasmarks then -                            local pm = i.markbasenode -                            if pm then -                                processmark(pm,current,i) +                            if hasmarks then +                                local pm = i.markbasenode +                                if pm then +                                    processmark(pm,current,i) +                                end                              end                          end                      end @@ -1319,11 +1319,11 @@ local function inject_everything(head,where)                                  insert_node_after(replace,n,newkern(rightkern))                                  done = true                              end -                        end -                        if hasmarks then -                            local pm = i.markbasenode -                            if pm then -                                processmark(pm,current,i) +                            if hasmarks then +                                local pm = i.markbasenode +                                if pm then +                                    processmark(pm,current,i) +                                end                              end                          end                      end @@ -1523,10 +1523,19 @@ function injections.handler(head,where)          head = injectspaces(head)      end      if nofregisteredmarks > 0 or nofregisteredcursives > 0 then +        if trace_injections then +            report_injections("injection variant %a","everything") +        end          return inject_everything(head,where)      elseif nofregisteredpairs > 0 then +        if trace_injections then +            report_injections("injection variant %a","pairs") +        end          return inject_pairs_only(head,where)      elseif nofregisteredkerns > 0 then +        if trace_injections then +            report_injections("injection variant %a","kerns") +        end          return inject_kerns_only(head,where)      else          return head, false diff --git a/src/fontloader/misc/fontloader-font-otl.lua b/src/fontloader/misc/fontloader-font-otl.lua index f7b6eb5..c7c278a 100644 --- a/src/fontloader/misc/fontloader-font-otl.lua +++ b/src/fontloader/misc/fontloader-font-otl.lua @@ -53,8 +53,12 @@ local report_otf         = logs.reporter("fonts","otf loading")  local fonts              = fonts  local otf                = fonts.handlers.otf -otf.version              = 3.019 -- beware: also sync font-mis.lua and in mtx-fonts +otf.version              = 3.022 -- beware: also sync font-mis.lua and in mtx-fonts  otf.cache                = containers.define("fonts", "otl", otf.version, true) +otf.svgcache             = containers.define("fonts", "svg", otf.version, true) +otf.pdfcache             = containers.define("fonts", "pdf", otf.version, true) + +otf.svgenabled           = false  local otfreaders         = otf.readers @@ -63,7 +67,7 @@ local definers           = fonts.definers  local readers            = fonts.readers  local constructors       = fonts.constructors -local otffeatures        = constructors.newfeatures("otf") +local otffeatures        = constructors.features.otf  local registerotffeature = otffeatures.register  local enhancers          = allocate() @@ -101,6 +105,12 @@ registerdirective("fonts.otf.loader.forcenotdef",   function(v) forcenotdef   =  --     end  -- end +-- Enhancers are used to apply fixes and extensions to fonts. For instance, we use them +-- to implement tlig and trep features. They are not neccessarily bound to opentype +-- fonts but can also apply to type one fonts, given that they obey the structure of an +-- opentype font. They are not to be confused with format specific features but maybe +-- some are so generic that they might eventually move to this mechanism. +  local ordered_enhancers = {      "check extra features",  } @@ -264,6 +274,25 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone          --          --          if data then +            -- +            local resources = data.resources +            local svgshapes = resources.svgshapes +            if svgshapes then +                resources.svgshapes = nil +                if otf.svgenabled then +                    local timestamp = os.date() +                    -- work in progress ... a bit boring to do +                    containers.write(otf.svgcache,hash, { +                        svgshapes = svgshapes, +                        timestamp = timestamp, +                    }) +                    data.properties.svg = { +                        hash      = hash, +                        timestamp = timestamp, +                    } +                end +            end +            --              otfreaders.compact(data)              otfreaders.rehash(data,"unicodes")              otfreaders.addunicodetable(data) @@ -302,7 +331,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone          --          enhancers.apply(data,filename,data)          -- -        constructors.addcoreunicodes(unicodes) +     -- constructors.addcoreunicodes(data.resources.unicodes) -- still needed ?          --          if applyruntimefixes then              applyruntimefixes(filename,data) @@ -340,7 +369,6 @@ end  local function copytotfm(data,cache_id)      if data then          local metadata       = data.metadata -        local resources      = data.resources          local properties     = derivetable(data.properties)          local descriptions   = derivetable(data.descriptions)          local goodies        = derivetable(data.goodies) diff --git a/src/fontloader/misc/fontloader-font-oto.lua b/src/fontloader/misc/fontloader-font-oto.lua index b7ee717..1199778 100644 --- a/src/fontloader/misc/fontloader-font-oto.lua +++ b/src/fontloader/misc/fontloader-font-oto.lua @@ -14,8 +14,6 @@ local concat, unpack = table.concat, table.unpack  local insert, remove = table.insert, table.remove  local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip  local type, next, tonumber, tostring, rawget = type, next, tonumber, tostring, rawget -local lpegmatch = lpeg.match -local utfchar = utf.char  local trace_baseinit         = false  trackers.register("otf.baseinit",         function(v) trace_baseinit         = v end)  local trace_singles          = false  trackers.register("otf.singles",          function(v) trace_singles          = v end) @@ -450,3 +448,5 @@ registerotffeature {          base     = featuresinitializer,      }  } + +otf.basemodeinitializer = featuresinitializer diff --git a/src/fontloader/misc/fontloader-font-otr.lua b/src/fontloader/misc/fontloader-font-otr.lua index 24f6854..7d0bf04 100644 --- a/src/fontloader/misc/fontloader-font-otr.lua +++ b/src/fontloader/misc/fontloader-font-otr.lua @@ -99,7 +99,7 @@ readers.streamreader    = streamreader  local openfile          = streamreader.open  local closefile         = streamreader.close -local skipbytes         = streamreader.skip +----- skipbytes         = streamreader.skip  local setposition       = streamreader.setposition  local skipshort         = streamreader.skipshort  local readbytes         = streamreader.readbytes @@ -108,7 +108,7 @@ local readbyte          = streamreader.readcardinal1  --  8-bit unsigned integer  local readushort        = streamreader.readcardinal2  -- 16-bit unsigned integer  local readuint          = streamreader.readcardinal3  -- 24-bit unsigned integer  local readulong         = streamreader.readcardinal4  -- 24-bit unsigned integer -local readchar          = streamreader.readinteger1   --  8-bit   signed integer +----- readchar          = streamreader.readinteger1   --  8-bit   signed integer  local readshort         = streamreader.readinteger2   -- 16-bit   signed integer  local readlong          = streamreader.readinteger4   -- 24-bit unsigned integer  local readfixed         = streamreader.readfixed4 @@ -129,12 +129,11 @@ local function readlongdatetime(f)      return 0x100000000 * d + 0x1000000 * e + 0x10000 * f + 0x100 * g + h  end -local tableversion      = 0.004 -local privateoffset     = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF +local tableversion    = 0.004 +readers.tableversion  = tableversion +local privateoffset   = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF +local reportedskipped = { } -readers.tableversion    = tableversion - -local reportedskipped   = { }  local function reportskippedtable(tag)      if not reportedskipped[tag] then @@ -1657,6 +1656,26 @@ function readers.glyf(f,fontdata,specification) -- part goes to cff module      end  end +-- Experimental (we need fonts). + +function readers.colr(f,fontdata,specification) +    if specification.glyphs then +        reportskippedtable("colr") +    end +end + +function readers.cpal(f,fontdata,specification) +    if specification.glyphs then +        reportskippedtable("cpal") +    end +end + +function readers.svg(f,fontdata,specification) +    if specification.glyphs then +        reportskippedtable("svg") +    end +end +  -- Here we have a table that we really need for later processing although a more advanced gpos table  -- can also be available. Todo: we need a 'fake' lookup for this (analogue to ff). @@ -1874,6 +1893,7 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames)           -- format         = fontdata.format,              fontname       = fontname,              fullname       = fullname, +         -- cfffullname    = cff.fullname,              family         = family,              subfamily      = subfamily,              familyname     = familyname, @@ -1996,6 +2016,9 @@ local function readdata(f,offset,specification)      readers["cmap"](f,fontdata,specification)      readers["loca"](f,fontdata,specification)      readers["glyf"](f,fontdata,specification) +    readers["colr"](f,fontdata,specification) +    readers["cpal"](f,fontdata,specification) +    readers["svg" ](f,fontdata,specification)      readers["kern"](f,fontdata,specification)      readers["gdef"](f,fontdata,specification)      readers["gsub"](f,fontdata,specification) @@ -2164,7 +2187,9 @@ function readers.loadfont(filename,n)              goodies       = { },              metadata      = getinfo(fontdata,n), -- no platformnames here !              properties    = { -                hasitalics = fontdata.hasitalics or false, +                hasitalics    = fontdata.hasitalics or false, +                maxcolorclass = fontdata.maxcolorclass, +                hascolor      = fontdata.hascolor or false,              },              resources     = {               -- filename      = fontdata.filename, @@ -2181,6 +2206,8 @@ function readers.loadfont(filename,n)                  version       = getname(fontdata,"version"),                  cidinfo       = fontdata.cidinfo,                  mathconstants = fontdata.mathconstants, +                colorpalettes = fontdata.colorpalettes, +                svgshapes     = fontdata.svgshapes,              },          }      end diff --git a/src/fontloader/misc/fontloader-font-ots.lua b/src/fontloader/misc/fontloader-font-ots.lua index 21225c2..51704bf 100644 --- a/src/fontloader/misc/fontloader-font-ots.lua +++ b/src/fontloader/misc/fontloader-font-ots.lua @@ -124,7 +124,7 @@ local trace_cursive      = false  registertracker("otf.cursive",      function(v  local trace_preparing    = false  registertracker("otf.preparing",    function(v) trace_preparing    = v end)  local trace_bugs         = false  registertracker("otf.bugs",         function(v) trace_bugs         = v end)  local trace_details      = false  registertracker("otf.details",      function(v) trace_details      = v end) -local trace_applied      = false  registertracker("otf.applied",      function(v) trace_applied      = v end) +----- trace_applied      = false  registertracker("otf.applied",      function(v) trace_applied      = v end)  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) @@ -145,10 +145,8 @@ local report_direct   = logs.reporter("fonts","otf direct")  local report_subchain = logs.reporter("fonts","otf subchain")  local report_chain    = logs.reporter("fonts","otf chain")  local report_process  = logs.reporter("fonts","otf process") ------ report_prepare  = logs.reporter("fonts","otf prepare")  local report_warning  = logs.reporter("fonts","otf warning")  local report_run      = logs.reporter("fonts","otf run") -local report_check    = logs.reporter("fonts","otf check")  registertracker("otf.replacements", "otf.singles,otf.multiples,otf.alternatives,otf.ligatures")  registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") @@ -185,10 +183,7 @@ local setlink            = nuts.setlink  local ischar             = nuts.is_char -local insert_node_before = nuts.insert_before  local insert_node_after  = nuts.insert_after -local delete_node        = nuts.delete -local remove_node        = nuts.remove  local copy_node          = nuts.copy  local copy_node_list     = nuts.copy_list  local find_node_tail     = nuts.tail @@ -244,7 +239,7 @@ local cursonce           = true  local fonthashes         = fonts.hashes  local fontdata           = fonthashes.identifiers -local otffeatures        = fonts.constructors.newfeatures("otf") +local otffeatures        = fonts.constructors.features.otf  local registerotffeature = otffeatures.register  local onetimemessage     = fonts.loggers.onetimemessage or function() end @@ -3064,7 +3059,7 @@ local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlm      while start do          local char = ischar(start,font)          if char then -            local a = getattr(start,0) +            local a = attr and getattr(start,0)              if not a or (a == attr) then                  local lookupmatch = lookupcache[char]                  if lookupmatch then @@ -3097,7 +3092,7 @@ local function t_run_single(start,stop,font,attr,lookupcache)      while start ~= stop do          local char = ischar(start,font)          if char then -            local a = getattr(start,0) +            local a = attr and getattr(start,0)              if not a or (a == attr) then                  local lookupmatch = lookupcache[char]                  if lookupmatch then -- hm, hyphens can match (tlig) so we need to really check @@ -3132,7 +3127,7 @@ local function t_run_single(start,stop,font,attr,lookupcache)  end  -- local function d_run_single(prev,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) ---     local a = getattr(prev,0) +--     local a = attr and getattr(prev,0)  --     if not a or (a == attr) then  --         local char = ischar(prev) -- can be disc  --         if char then @@ -3149,7 +3144,7 @@ end  -- end  local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) -    local a = getattr(sub,0) +    local a = attr and getattr(sub,0)      if not a or (a == attr) then          for n in traverse_nodes(sub) do -- only gpos              if n == last then @@ -3181,7 +3176,7 @@ local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlm      while start do          local char = ischar(start,font)          if char then -            local a = getattr(start,0) +            local a = attr and getattr(start,0)              if not a or (a == attr) then                  for i=1,nofsteps do                      local step        = steps[i] @@ -3228,7 +3223,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)      while start ~= stop do          local char = ischar(start,font)          if char then -            local a = getattr(start,0) +            local a = attr and getattr(start,0)              if not a or (a == attr) then                  for i=1,nofsteps do                      local step = steps[i] @@ -3271,7 +3266,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)  end  -- local function d_run_multiple(prev,attr,steps,nofsteps,dataset,sequence,rlmode,handler) ---     local a = getattr(prev,0) +--     local a = attr and getattr(prev,0)  --     if not a or (a == attr) then  --         local char = ischar(prev) -- can be disc  --         if char then @@ -3297,7 +3292,7 @@ end  -- end  local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) -    local a = getattr(sub,0) +    local a = attr and getattr(sub,0)      if not a or (a == attr) then          for n in traverse_nodes(sub) do -- only gpos              if n == last then @@ -3394,6 +3389,14 @@ local function featuresprocessor(head,font,attr)      end +    -- some 10% faster when no dynamics but hardly measureable on real runs .. but: it only +    -- works when we have no other dynamics as otherwise the zero run will be applied to the +    -- whole stream for which we then need to pass another variable which we won't + +    -- if attr == 0 then +    --     attr = false +    -- end +      head = tonut(head)      if trace_steps then @@ -3405,7 +3408,7 @@ local function featuresprocessor(head,font,attr)      local done      = false      local datasets  = otf.dataset(tfmdata,font,attr) -    local dirstack  = { } -- could move outside function btu we can have local runss +    local dirstack  = { } -- could move outside function but we can have local runs      sweephead       = { } @@ -3451,7 +3454,7 @@ local function featuresprocessor(head,font,attr)              while start do                  local char = ischar(start,font)                  if char then -                    local a = getattr(start,0) +                    local a = attr and getattr(start,0)                      if not a or (a == attr) then                          for i=1,nofsteps do                              local step = steps[i] @@ -3485,18 +3488,15 @@ local function featuresprocessor(head,font,attr)              local start = head -- local ?              rlmode = 0 -- to be checked ?              if nofsteps == 1 then -- happens often -                  local step = steps[1]                  local lookupcache = step.coverage                  if not lookupcache then -                 -- can't happen, no check in loop either                      report_missing_coverage(dataset,sequence)                  else -                      while start do                          local char, id = ischar(start,font)                          if char then -                            local a = getattr(start,0) +                            local a = attr and getattr(start,0)                              if a then                                  a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)                              else @@ -3553,7 +3553,7 @@ local function featuresprocessor(head,font,attr)                  while start do                      local char, id = ischar(start,font)                      if char then -                        local a = getattr(start,0) +                        local a = attr and getattr(start,0)                          if a then                              a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)                          else @@ -3652,6 +3652,9 @@ registerotffeature {      }  } +otf.nodemodeinitializer = featuresinitializer +otf.featuresprocessor   = featuresprocessor +  -- This can be used for extra handlers, but should be used with care!  otf.handlers = handlers -- used in devanagari diff --git a/src/fontloader/misc/fontloader-font-oup.lua b/src/fontloader/misc/fontloader-font-oup.lua index 3b6d8ea..e2d209a 100644 --- a/src/fontloader/misc/fontloader-font-oup.lua +++ b/src/fontloader/misc/fontloader-font-oup.lua @@ -586,7 +586,8 @@ local function checklookups(fontdata,missing,nofmissing)          local done = { }          for i, r in next, missing do              if r then -                local name = descriptions[i].name or f_index(i) +                local data = descriptions[i] +                local name = data and data.name or f_index(i)                  if not ignore[name] then                      done[name] = true                  end @@ -706,6 +707,19 @@ local function unifyglyphs(fontdata,usenames)          end      end      -- +    local colorpalettes = resources.colorpalettes +    if colorpalettes then +        for index=1,#glyphs do +            local colors = glyphs[index].colors +            if colors then +                for i=1,#colors do +                    local c = colors[i] +                    c.slot = indices[c.slot] +                end +            end +        end +    end +    --      fontdata.private      = private      fontdata.glyphs       = nil      fontdata.names        = names @@ -1158,6 +1172,7 @@ function readers.pack(data)          local sequences  = resources.sequences          local sublookups = resources.sublookups          local features   = resources.features +        local palettes   = resources.colorpalettes          local chardata     = characters and characters.data          local descriptions = data.descriptions or data.glyphs @@ -1190,6 +1205,14 @@ function readers.pack(data)                          end                      end                  end +             -- if palettes then +             --     local color = description.color +             --     if color then +             --         for i=1,#color do +             --             color[i] = pack_normal(color[i]) +             --         end +             --     end +             -- end              end              local function packthem(sequences) @@ -1314,6 +1337,16 @@ function readers.pack(data)                  end              end +            if palettes then +                for i=1,#palettes do +                    local p = palettes[i] +                    for j=1,#p do +                        p[j] = pack_indexed(p[j]) +                    end +                end + +            end +              if not success(1,pass) then                  return              end @@ -1461,6 +1494,7 @@ function readers.unpack(data)              local sequences    = resources.sequences              local sublookups   = resources.sublookups              local features     = resources.features +            local palettes     = resources.colorpalettes              local unpacked     = { }              setmetatable(unpacked,unpacked_mt)              for unicode, description in next, descriptions do @@ -1487,6 +1521,17 @@ function readers.unpack(data)                          end                      end                  end +             -- if palettes then +             --     local color = description.color +             --     if color then +             --         for i=1,#color do +             --             local tv = tables[color[i]] +             --             if tv then +             --                 color[i] = tv +             --             end +             --         end +             --     end +             -- end              end              local function unpackthem(sequences) @@ -1716,6 +1761,18 @@ function readers.unpack(data)                  end              end +            if palettes then +                for i=1,#palettes do +                    local p = palettes[i] +                    for j=1,#p do +                        local tv = tables[p[j]] +                        if tv then +                            p[j] = tv +                        end +                    end +                end +            end +              data.tables = nil          end      end diff --git a/src/fontloader/misc/fontloader-font-tfm.lua b/src/fontloader/misc/fontloader-font-tfm.lua index 8e92c48..ab6d795 100644 --- a/src/fontloader/misc/fontloader-font-tfm.lua +++ b/src/fontloader/misc/fontloader-font-tfm.lua @@ -23,13 +23,13 @@ local readers                  = fonts.readers  local constructors             = fonts.constructors  local encodings                = fonts.encodings -local tfm                      = constructors.newhandler("tfm") +local tfm                      = constructors.handlers.tfm  tfm.version                    = 1.000  tfm.maxnestingdepth            = 5  tfm.maxnestingsize             = 65536*1024 -local tfmfeatures              = constructors.newfeatures("tfm") -local registertfmfeature       = tfmfeatures.register +local tfmfeatures              = constructors.features.tfm +----- registertfmfeature       = tfmfeatures.register  constructors.resolvevirtualtoo = false -- wil be set in font-ctx.lua diff --git a/src/fontloader/misc/fontloader-fonts-ext.lua b/src/fontloader/misc/fontloader-fonts-ext.lua index b60d045..9d8d307 100644 --- a/src/fontloader/misc/fontloader-fonts-ext.lua +++ b/src/fontloader/misc/fontloader-fonts-ext.lua @@ -12,7 +12,7 @@ if context then  end  local fonts       = fonts -local otffeatures = fonts.constructors.newfeatures("otf") +local otffeatures = fonts.constructors.features.otf  -- A few generic extensions. diff --git a/src/fontloader/misc/fontloader-fonts.lua b/src/fontloader/misc/fontloader-fonts.lua index e1ec376..83d52d9 100644 --- a/src/fontloader/misc/fontloader-fonts.lua +++ b/src/fontloader/misc/fontloader-fonts.lua @@ -186,6 +186,7 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then          loadmodule("l-file.lua")          loadmodule("l-boolean.lua")          loadmodule("l-math.lua") +        loadmodule("l-unicode.lua")          -- A few slightly higher level support modules: @@ -230,8 +231,6 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then          loadmodule('luatex-fonts-syn.lua')          loadmodule('font-tfm.lua') -        loadmodule('font-afm.lua') -        loadmodule('font-afk.lua')          loadmodule('font-oti.lua')          -- These are the old loader and processing modules. These use the built-in font loader and @@ -259,6 +258,13 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then          loadmodule('font-ota.lua')          loadmodule('font-ots.lua')          loadmodule('font-osd.lua') +        loadmodule('font-ocl.lua') -- svg needs 0.97 (for fix in memstreams) + +        -- type one code + +        loadmodule('font-onr.lua') -- was font-afm.lua +        loadmodule('font-one.lua') -- was font-afm.lua +        loadmodule('font-afk.lua')          -- common code diff --git a/src/fontloader/misc/fontloader-math.tex b/src/fontloader/misc/fontloader-math.tex index 604b4a1..b249021 100644 --- a/src/fontloader/misc/fontloader-math.tex +++ b/src/fontloader/misc/fontloader-math.tex @@ -40,9 +40,9 @@     \font\tenbf   = file:lmroman10-bold.otf:+liga;+kern;+tlig;+trep         at 10pt     \font\tenbi   = file:lmroman10-bolditalic.otf:+liga;+kern;+tlig;+trep   at 10pt     % -   \font\mathfonttextupright         = file:latinmodern-math.otf:ssty=0;fixmath=yes at 10pt -   \font\mathfontscriptupright       = file:latinmodern-math.otf:ssty=1;fixmath=yes at  7pt -   \font\mathfontscriptscriptupright = file:latinmodern-math.otf:ssty=2;fixmath=yes at  5pt +   \font\mathfonttextupright         = file:latinmodern-math.otf:script=math;ssty=0;mathsize=yes at 10pt +   \font\mathfontscriptupright       = file:latinmodern-math.otf:script=math;ssty=1;mathsize=yes at  7pt +   \font\mathfontscriptscriptupright = file:latinmodern-math.otf:script=math;ssty=2;mathsize=yes at  5pt     %     \textfont         0 = \mathfonttextupright     \scriptfont       0 = \mathfontscriptupright @@ -61,9 +61,9 @@     \font\tenbf   = file:lucidabrightot-demi.otf:+liga;+kern;+tlig;+trep       at 10pt     \font\tenbi   = file:lucidabrightot-demiitalic.otf:+liga;+kern;+tlig;+trep at 10pt     % -   \font\mathfonttextupright         = file:lucidabrightmathot.otf:ssty=0;fixmath=yes at 10pt -   \font\mathfontscriptupright       = file:lucidabrightmathot.otf:ssty=1;fixmath=yes at  7pt -   \font\mathfontscriptscriptupright = file:lucidabrightmathot.otf:ssty=2;fixmath=yes at  5pt +   \font\mathfonttextupright         = file:lucidabrightmathot.otf:script=math;ssty=0;mathsize=yes at 10pt +   \font\mathfontscriptupright       = file:lucidabrightmathot.otf:script=math;ssty=1;mathsize=yes at  7pt +   \font\mathfontscriptscriptupright = file:lucidabrightmathot.otf:script=math;ssty=2;mathsize=yes at  5pt     %     \textfont         0 = \mathfonttextupright     \scriptfont       0 = \mathfontscriptupright | 
