diff options
| -rw-r--r-- | otfl-data-con.lua | 135 | ||||
| -rw-r--r-- | otfl-font-cid.lua | 165 | ||||
| -rw-r--r-- | otfl-font-con.lua | 1332 | ||||
| -rw-r--r-- | otfl-font-ini.lua | 38 | ||||
| -rw-r--r-- | otfl-font-map.lua | 307 | ||||
| -rw-r--r-- | otfl-font-ota.lua | 373 | ||||
| -rw-r--r-- | otfl-font-otb.lua | 636 | ||||
| -rw-r--r-- | otfl-font-otf.lua | 2080 | ||||
| -rw-r--r-- | otfl-font-oti.lua | 92 | ||||
| -rw-r--r-- | otfl-font-otn.lua | 2712 | 
10 files changed, 0 insertions, 7870 deletions
diff --git a/otfl-data-con.lua b/otfl-data-con.lua deleted file mode 100644 index ed4f2de..0000000 --- a/otfl-data-con.lua +++ /dev/null @@ -1,135 +0,0 @@ -if not modules then modules = { } end modules ['data-con'] = { -    version   = 1.100, -    comment   = "companion to luat-lib.mkiv", -    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", -    copyright = "PRAGMA ADE / ConTeXt Development Team", -    license   = "see context related readme files" -} - -local format, lower, gsub = string.format, string.lower, string.gsub - -local trace_cache      = false  trackers.register("resolvers.cache",      function(v) trace_cache      = v end) -local trace_containers = false  trackers.register("resolvers.containers", function(v) trace_containers = v end) -local trace_storage    = false  trackers.register("resolvers.storage",    function(v) trace_storage    = v end) - ---[[ldx-- -<p>Once we found ourselves defining similar cache constructs -several times, containers were introduced. Containers are used -to collect tables in memory and reuse them when possible based -on (unique) hashes (to be provided by the calling function).</p> - -<p>Caching to disk is disabled by default. Version numbers are -stored in the saved table which makes it possible to change the -table structures without bothering about the disk cache.</p> - -<p>Examples of usage can be found in the font related code.</p> ---ldx]]-- - -containers          = containers or { } -local containers    = containers -containers.usecache = true - -local report_containers = logs.reporter("resolvers","containers") - -local function report(container,tag,name) -    if trace_cache or trace_containers then -        report_containers("container: %s, tag: %s, name: %s",container.subcategory,tag,name or 'invalid') -    end -end - -local allocated = { } - -local mt = { -    __index = function(t,k) -        if k == "writable" then -            local writable = caches.getwritablepath(t.category,t.subcategory) or { "." } -            t.writable = writable -            return writable -        elseif k == "readables" then -            local readables = caches.getreadablepaths(t.category,t.subcategory) or { "." } -            t.readables = readables -            return readables -        end -    end, -    __storage__ = true -} - -function containers.define(category, subcategory, version, enabled) -    if category and subcategory then -        local c = allocated[category] -        if not c then -            c  = { } -            allocated[category] = c -        end -        local s = c[subcategory] -        if not s then -            s = { -                category    = category, -                subcategory = subcategory, -                storage     = { }, -                enabled     = enabled, -                version     = version or math.pi, -- after all, this is TeX -                trace       = false, -             -- writable    = caches.getwritablepath  and caches.getwritablepath (category,subcategory) or { "." }, -             -- readables   = caches.getreadablepaths and caches.getreadablepaths(category,subcategory) or { "." }, -            } -            setmetatable(s,mt) -            c[subcategory] = s -        end -        return s -    end -end - -function containers.is_usable(container, name) -    return container.enabled and caches and caches.is_writable(container.writable, name) -end - -function containers.is_valid(container, name) -    if name and name ~= "" then -        local storage = container.storage[name] -        return storage and storage.cache_version == container.version -    else -        return false -    end -end - -function containers.read(container,name) -    local storage = container.storage -    local stored = storage[name] -    if not stored and container.enabled and caches and containers.usecache then -        stored = caches.loaddata(container.readables,name) -        if stored and stored.cache_version == container.version then -            report(container,"loaded",name) -        else -            stored = nil -        end -        storage[name] = stored -    elseif stored then -        report(container,"reusing",name) -    end -    return stored -end - -function containers.write(container, name, data) -    if data then -        data.cache_version = container.version -        if container.enabled and caches then -            local unique, shared = data.unique, data.shared -            data.unique, data.shared = nil, nil -            caches.savedata(container.writable, name, data) -            report(container,"saved",name) -            data.unique, data.shared = unique, shared -        end -        report(container,"stored",name) -        container.storage[name] = data -    end -    return data -end - -function containers.content(container,name) -    return container.storage[name] -end - -function containers.cleanname(name) -    return (gsub(lower(name),"[^%w%d]+","-")) -end diff --git a/otfl-font-cid.lua b/otfl-font-cid.lua deleted file mode 100644 index 4a4c4d2..0000000 --- a/otfl-font-cid.lua +++ /dev/null @@ -1,165 +0,0 @@ -if not modules then modules = { } end modules ['font-cid'] = { -    version   = 1.001, -    comment   = "companion to font-otf.lua (cidmaps)", -    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", -    copyright = "PRAGMA ADE / ConTeXt Development Team", -    license   = "see context related readme files" -} - -local format, match, lower = string.format, string.match, string.lower -local tonumber = tonumber -local P, S, R, C, V, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.match - -local trace_loading = false  trackers.register("otf.loading", function(v) trace_loading = v end) - -local report_otf = logs.reporter("fonts","otf loading") - -local fonts  = fonts - -local cid    = { } -fonts.cid    = cid - -local cidmap = { } -local cidmax = 10 - --- original string parser: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap --- --- 18964 18964 (leader) --- 0 /.notdef --- 1..95 0020 --- 99 3000 - -local number  = C(R("09","af","AF")^1) -local space   = S(" \n\r\t") -local spaces  = space^0 -local period  = P(".") -local periods = period * period -local name    = P("/") * C((1-space)^1) - -local unicodes, names = { }, { } -- we could use Carg now - -local function do_one(a,b) -    unicodes[tonumber(a)] = tonumber(b,16) -end - -local function do_range(a,b,c) -    c = tonumber(c,16) -    for i=tonumber(a),tonumber(b) do -        unicodes[i] = c -        c = c + 1 -    end -end - -local function do_name(a,b) -    names[tonumber(a)] = b -end - -local grammar = P { "start", -    start  = number * spaces * number * V("series"), -    series = (spaces * (V("one") + V("range") + V("named")))^1, -    one    = (number * spaces  * number) / do_one, -    range  = (number * periods * number * spaces * number) / do_range, -    named  = (number * spaces  * name) / do_name -} - -local function loadcidfile(filename) -    local data = io.loaddata(filename) -    if data then -        unicodes, names = { }, { } -        lpegmatch(grammar,data) -        local supplement, registry, ordering = match(filename,"^(.-)%-(.-)%-()%.(.-)$") -        return { -            supplement = supplement, -            registry   = registry, -            ordering   = ordering, -            filename   = filename, -            unicodes   = unicodes, -            names      = names -        } -    end -end - -cid.loadfile = loadcidfile -- we use the frozen variant - -local template = "%s-%s-%s.cidmap" - -local function locate(registry,ordering,supplement) -    local filename = format(template,registry,ordering,supplement) -    local hashname = lower(filename) -    local found    = cidmap[hashname] -    if not found then -        if trace_loading then -            report_otf("checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename) -        end -        local fullname = resolvers.findfile(filename,'cid') or "" -        if fullname ~= "" then -            found = loadcidfile(fullname) -            if found then -                if trace_loading then -                    report_otf("using cidmap file %s",filename) -                end -                cidmap[hashname] = found -                found.usedname = file.basename(filename) -            end -        end -    end -    return found -end - --- cf Arthur R. we can safely scan upwards since cids are downward compatible - -function cid.getmap(specification) -    if not specification then -        report_otf("invalid cidinfo specification (table expected)") -        return -    end -    local registry = specification.registry -    local ordering = specification.ordering -    local supplement = specification.supplement -    -- check for already loaded file -    local filename = format(registry,ordering,supplement) -    local found = cidmap[lower(filename)] -    if found then -        return found -    end -    if trace_loading then -        report_otf("needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement) -    end -    found = locate(registry,ordering,supplement) -    if not found then -        local supnum = tonumber(supplement) -        local cidnum = nil -        -- next highest (alternatively we could start high) -        if supnum < cidmax then -            for s=supnum+1,cidmax do -                local c = locate(registry,ordering,s) -                if c then -                    found, cidnum = c, s -                    break -                end -            end -        end -        -- next lowest (least worse fit) -        if not found and supnum > 0 then -            for s=supnum-1,0,-1 do -                local c = locate(registry,ordering,s) -                if c then -                    found, cidnum = c, s -                    break -                end -            end -        end -        -- prevent further lookups -- somewhat tricky -        registry = lower(registry) -        ordering = lower(ordering) -        if found and cidnum > 0 then -            for s=0,cidnum-1 do -                local filename = format(template,registry,ordering,s) -                if not cidmap[filename] then -                    cidmap[filename] = found -                end -            end -        end -    end -    return found -end diff --git a/otfl-font-con.lua b/otfl-font-con.lua deleted file mode 100644 index 9280996..0000000 --- a/otfl-font-con.lua +++ /dev/null @@ -1,1332 +0,0 @@ -if not modules then modules = { } end modules ['font-con'] = { -    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" -} - - --- some names of table entries will be changed (no _) - -local utf = unicode.utf8 - -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 - -local trace_defining = false  trackers.register("fonts.defining", function(v) trace_defining = v end) -local trace_scaling  = false  trackers.register("fonts.scaling" , function(v) trace_scaling  = v end) - -local report_defining = logs.reporter("fonts","defining") - --- watch out: no negative depths and negative eights permitted in regular fonts - ---[[ldx-- -<p>Here we only implement a few helper functions.</p> ---ldx]]-- - -local fonts                  = fonts -local constructors           = { } -fonts.constructors           = constructors -local handlers               = { } -fonts.handlers               = handlers - -local specifiers             = fonts.specifiers -local contextsetups          = specifiers.contextsetups -local contextnumbers         = specifiers.contextnumbers - -local allocate               = utilities.storage.allocate -local setmetatableindex      = table.setmetatableindex - --- will be directives - -constructors.dontembed       = allocate() -constructors.autocleanup     = true -constructors.namemode        = "fullpath" -- will be a function - -constructors.version         = 1.01 -constructors.cache           = containers.define("fonts", "constructors", constructors.version, false) - -constructors.privateoffset   = 0xF0000 -- 0x10FFFF - --- Some experimental helpers (handy for tracing): --- --- todo: extra: --- --- extra_space       => space.extra --- space             => space.width --- space_stretch     => space.stretch --- space_shrink      => space.shrink - --- We do keep the x-height, extra_space, space_shrink and space_stretch --- around as these are low level official names. - -constructors.keys = { -    properties = { -        encodingbytes          = "number", -        embedding              = "number", -        cidinfo                = { -                                 }, -        format                 = "string", -        fontname               = "string", -        fullname               = "string", -        filename               = "filename", -        psname                 = "string", -        name                   = "string", -        virtualized            = "boolean", -        hasitalics             = "boolean", -        autoitalicamount       = "basepoints", -        nostackmath            = "boolean", -        noglyphnames           = "boolean", -        mode                   = "string", -        hasmath                = "boolean", -        mathitalics            = "boolean", -        textitalics            = "boolean", -        finalized              = "boolean", -    }, -    parameters = { -        mathsize               = "number", -        scriptpercentage       = "float", -        scriptscriptpercentage = "float", -        units                  = "cardinal", -        designsize             = "scaledpoints", -        expansion              = { -                                    stretch = "integerscale", -- might become float -                                    shrink  = "integerscale", -- might become float -                                    step    = "integerscale", -- might become float -                                    auto    = "boolean", -                                 }, -        protrusion             = { -                                    auto    = "boolean", -                                 }, -        slantfactor            = "float", -        extendfactor           = "float", -        factor                 = "float", -        hfactor                = "float", -        vfactor                = "float", -        size                   = "scaledpoints", -        units                  = "scaledpoints", -        scaledpoints           = "scaledpoints", -        slantperpoint          = "scaledpoints", -        spacing                = { -                                    width   = "scaledpoints", -                                    stretch = "scaledpoints", -                                    shrink  = "scaledpoints", -                                    extra   = "scaledpoints", -                                 }, -        xheight                = "scaledpoints", -        quad                   = "scaledpoints", -        ascender               = "scaledpoints", -        descender              = "scaledpoints", -        synonyms               = { -                                    space         = "spacing.width", -                                    spacestretch  = "spacing.stretch", -                                    spaceshrink   = "spacing.shrink", -                                    extraspace    = "spacing.extra", -                                    x_height      = "xheight", -                                    space_stretch = "spacing.stretch", -                                    space_shrink  = "spacing.shrink", -                                    extra_space   = "spacing.extra", -                                    em            = "quad", -                                    ex            = "xheight", -                                    slant         = "slantperpoint", -                                  }, -    }, -    description = { -        width                  = "basepoints", -        height                 = "basepoints", -        depth                  = "basepoints", -        boundingbox            = { }, -    }, -    character = { -        width                  = "scaledpoints", -        height                 = "scaledpoints", -        depth                  = "scaledpoints", -        italic                 = "scaledpoints", -    }, -} - --- This might become an interface: - -local designsizes           = allocate() -constructors.designsizes    = designsizes -local loadedfonts           = allocate() -constructors.loadedfonts    = loadedfonts - ---[[ldx-- -<p>We need to normalize the scale factor (in scaled points). This has to -do with the fact that <l n='tex'/> uses a negative multiple of 1000 as -a signal for a font scaled based on the design size.</p> ---ldx]]-- - -local factors = { -    pt = 65536.0, -    bp = 65781.8, -} - -function constructors.setfactor(f) -    constructors.factor = factors[f or 'pt'] or factors.pt -end - -constructors.setfactor() - -function constructors.scaled(scaledpoints, designsize) -- handles designsize in sp as well -    if scaledpoints < 0 then -        if designsize then -            local factor = constructors.factor -            if designsize > factor then -- or just 1000 / when? mp? -                return (- scaledpoints/1000) * designsize -- sp's -            else -                return (- scaledpoints/1000) * designsize * factor -            end -        else -            return (- scaledpoints/1000) * 10 * factor -        end -    else -        return scaledpoints -    end -end - ---[[ldx-- -<p>Beware, the boundingbox is passed as reference so we may not overwrite it -in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to -excessive memory usage in CJK fonts, we no longer pass the boundingbox.)</p> ---ldx]]-- - --- The scaler is only used for otf and afm and virtual fonts. If --- a virtual font has italic correction make sure to set the --- hasitalics flag. Some more flags will be added in --- the future. - ---[[ldx-- -<p>The reason why the scaler was originally split, is that for a while we experimented -with a helper function. However, in practice the <l n='api'/> calls are too slow to -make this profitable and the <l n='lua'/> based variant was just faster. A days -wasted day but an experience richer.</p> ---ldx]]-- - --- we can get rid of the tfm instance when we have fast access to the --- scaled character dimensions at the tex end, e.g. a fontobject.width --- actually we already have soem of that now as virtual keys in glyphs --- --- flushing the kern and ligature tables from memory saves a lot (only --- base mode) but it complicates vf building where the new characters --- demand this data .. solution: functions that access them - -function constructors.cleanuptable(tfmdata) -    if constructors.autocleanup and tfmdata.properties.virtualized then -        for k, v in next, tfmdata.characters do -            if v.commands then v.commands = nil end -        --  if v.kerns    then v.kerns    = nil end -        end -    end -end - --- experimental, sharing kerns (unscaled and scaled) saves memory --- local sharedkerns, basekerns = constructors.check_base_kerns(tfmdata) --- loop over descriptions (afm and otf have descriptions, tfm not) --- there is no need (yet) to assign a value to chr.tonunicode - --- constructors.prepare_base_kerns(tfmdata) -- optimalization - --- we have target.name=metricfile and target.fullname=RealName and target.filename=diskfilename --- when collapsing fonts, luatex looks as both target.name and target.fullname as ttc files --- can have multiple subfonts - -function constructors.calculatescale(tfmdata,scaledpoints) -    local parameters = tfmdata.parameters -    if scaledpoints < 0 then -        scaledpoints = (- scaledpoints/1000) * (tfmdata.designsize or parameters.designsize) -- already in sp -    end -    return scaledpoints, scaledpoints / (parameters.units or 1000) -- delta -end - -local unscaled = { -    ScriptPercentScaleDown          = true, -    ScriptScriptPercentScaleDown    = true, -    RadicalDegreeBottomRaisePercent = true -} - -function constructors.assignmathparameters(target,original) -- simple variant, not used in context -    -- when a tfm file is loaded, it has already been scaled -    -- and it never enters the scaled so this is otf only and -    -- even then we do some extra in the context math plugins -    local mathparameters = original.mathparameters -    if mathparameters and next(mathparameters) then -        local targetparameters     = target.parameters -        local targetproperties     = target.properties -        local targetmathparameters = { } -        local factor               = targetproperties.math_is_scaled and 1 or targetparameters.factor -        for name, value in next, mathparameters do -            if unscaled[name] then -                targetmathparameters[name] = value -            else -                targetmathparameters[name] = value * factor -            end -        end -        if not targetmathparameters.FractionDelimiterSize then -            targetmathparameters.FractionDelimiterSize = 1.01 * targetparameters.size -        end -        if not mathparameters.FractionDelimiterDisplayStyleSize then -            targetmathparameters.FractionDelimiterDisplayStyleSize = 2.40 * targetparameters.size -        end -        target.mathparameters = targetmathparameters -    end -end - -function constructors.beforecopyingcharacters(target,original) -    -- can be used for additional tweaking -end - -function constructors.aftercopyingcharacters(target,original) -    -- can be used for additional tweaking -end - -function constructors.enhanceparameters(parameters) -    local xheight = parameters.x_height -    local quad    = parameters.quad -    local space   = parameters.space -    local stretch = parameters.space_stretch -    local shrink  = parameters.space_shrink -    local extra   = parameters.extra_space -    local slant   = parameters.slant -    parameters.xheight       = xheight -    parameters.spacestretch  = stretch -    parameters.spaceshrink   = shrink -    parameters.extraspace    = extra -    parameters.em            = quad -    parameters.ex            = xheight -    parameters.slantperpoint = slant -    parameters.spacing = { -        width   = space, -        stretch = stretch, -        shrink  = shrink, -        extra   = extra, -    } -end - -function constructors.scale(tfmdata,specification) -    local target         = { } -- the new table -    -- -    if tonumber(specification) then -        specification    = { size = specification } -    end -    -- -    local scaledpoints   = specification.size -    local relativeid     = specification.relativeid -    -- -    local properties     = tfmdata.properties     or { } -    local goodies        = tfmdata.goodies        or { } -    local resources      = tfmdata.resources      or { } -    local descriptions   = tfmdata.descriptions   or { } -- bad news if empty -    local characters     = tfmdata.characters     or { } -- bad news if empty -    local changed        = tfmdata.changed        or { } -- for base mode -    local shared         = tfmdata.shared         or { } -    local parameters     = tfmdata.parameters     or { } -    local mathparameters = tfmdata.mathparameters or { } -    -- -    local targetcharacters     = { } -    local targetdescriptions   = derivetable(descriptions) -    local targetparameters     = derivetable(parameters) -    local targetproperties     = derivetable(properties) -    local targetgoodies        = goodies                        -- we need to loop so no metatable -    target.characters          = targetcharacters -    target.descriptions        = targetdescriptions -    target.parameters          = targetparameters - -- target.mathparameters      = targetmathparameters           -- happens elsewhere -    target.properties          = targetproperties -    target.goodies             = targetgoodies -    target.shared              = shared -    target.resources           = resources -    target.unscaled            = tfmdata                        -- the original unscaled one -    -- -    -- specification.mathsize : 1=text 2=script 3=scriptscript -    -- specification.textsize : natural (text)size -    -- parameters.mathsize    : 1=text 2=script 3=scriptscript >1000 enforced size (feature value other than yes) -    -- -    local mathsize    = tonumber(specification.mathsize) or 0 -    local textsize    = tonumber(specification.textsize) or scaledpoints -    local forcedsize  = tonumber(parameters.mathsize   ) or 0 -    local extrafactor = tonumber(specification.factor  ) or 1 -    if (mathsize == 2 or forcedsize == 2) and parameters.scriptpercentage then -        scaledpoints = parameters.scriptpercentage * textsize / 100 -    elseif (mathsize == 3 or forcedsize == 3) and parameters.scriptscriptpercentage then -        scaledpoints = parameters.scriptscriptpercentage * textsize / 100 -    elseif forcedsize > 1000 then -- safeguard -        scaledpoints = forcedsize -    end -    -- -    local tounicode     = resources.tounicode -    local defaultwidth  = resources.defaultwidth  or 0 -    local defaultheight = resources.defaultheight or 0 -    local defaultdepth  = resources.defaultdepth or 0 -    local units         = parameters.units or 1000 -    -- -    if target.fonts then -        target.fonts = fastcopy(target.fonts) -- maybe we virtualize more afterwards -    end -    -- -    -- boundary keys are no longer needed as we now have a string 'right_boundary' -    -- that can be used in relevant tables (kerns and ligatures) ... not that I ever -    -- used them -    -- - -- boundarychar_label = 0,     -- not needed - -- boundarychar       = 65536, -- there is now a string 'right_boundary' - -- false_boundarychar = 65536, -- produces invalid tfm in luatex -    -- -    targetproperties.language = properties.language or "dflt" -- inherited -    targetproperties.script   = properties.script   or "dflt" -- inherited -    targetproperties.mode     = properties.mode     or "base" -- inherited -    -- -    local askedscaledpoints   = scaledpoints -    local scaledpoints, delta = constructors.calculatescale(tfmdata,scaledpoints) -- no shortcut, dan be redefined -    -- -    local hdelta         = delta -    local vdelta         = delta -    -- -    target.designsize    = parameters.designsize -- not really needed so it muight become obsolete -    target.units_per_em  = units                 -- just a trigger for the backend (does luatex use this? if not it will go) -    -- -    local direction      = properties.direction or tfmdata.direction or 0 -- pointless, as we don't use omf fonts at all -    target.direction     = direction -    properties.direction = direction -    -- -    target.size          = scaledpoints -    -- -    target.encodingbytes = properties.encodingbytes or 1 -    target.embedding     = properties.embedding or "subset" -    target.tounicode     = 1 -    target.cidinfo       = properties.cidinfo -    target.format        = properties.format -    -- -    local fontname = properties.fontname or tfmdata.fontname -- for the moment we fall back on -    local fullname = properties.fullname or tfmdata.fullname -- names in the tfmdata although -    local filename = properties.filename or tfmdata.filename -- that is not the right place to -    local psname   = properties.psname   or tfmdata.psname   -- pass them -    local name     = properties.name     or tfmdata.name -    -- -    if not psname or psname == "" then -     -- name used in pdf file as well as for selecting subfont in ttc/dfont -        psname = fontname or (fullname and fonts.names.cleanname(fullname)) -    end -    target.fontname = fontname -    target.fullname = fullname -    target.filename = filename -    target.psname   = psname -    target.name     = name -    -- -    properties.fontname = fontname -    properties.fullname = fullname -    properties.filename = filename -    properties.psname   = psname -    properties.name     = name -    -- expansion (hz) -    local expansion = parameters.expansion -    if expansion then -        target.stretch     = expansion.stretch -        target.shrink      = expansion.shrink -        target.step        = expansion.step -        target.auto_expand = expansion.auto -    end -    -- protrusion -    local protrusion = parameters.protrusion -    if protrusion then -        target.auto_protrude = protrusion.auto -    end -    -- widening -    local extendfactor = parameters.extendfactor or 0 -    if extendfactor ~= 0 and extendfactor ~= 1 then -        hdelta = hdelta * extendfactor -        target.extend = extendfactor * 1000 -- extent ? -    else -        target.extend = 1000 -- extent ? -    end -    -- slanting -    local slantfactor = parameters.slantfactor or 0 -    if slantfactor ~= 0 then -        target.slant = slantfactor * 1000 -    else -        target.slant = 0 -    end -    -- -    targetparameters.factor       = delta -    targetparameters.hfactor      = hdelta -    targetparameters.vfactor      = vdelta -    targetparameters.size         = scaledpoints -    targetparameters.units        = units -    targetparameters.scaledpoints = askedscaledpoints -    -- -    local isvirtual        = properties.virtualized or tfmdata.type == "virtual" -    local hasquality       = target.auto_expand or target.auto_protrude -    local hasitalics       = properties.hasitalics -    local autoitalicamount = properties.autoitalicamount -    local stackmath        = not properties.nostackmath -    local nonames          = properties.noglyphnames -    local nodemode         = properties.mode == "node" -    -- -    if changed and not next(changed) then -        changed = false -    end -    -- -    target.type = isvirtual and "virtual" or "real" -    -- -    target.postprocessors = tfmdata.postprocessors -    -- -    local targetslant         = (parameters.slant         or parameters[1] or 0) -    local targetspace         = (parameters.space         or parameters[2] or 0)*hdelta -    local targetspace_stretch = (parameters.space_stretch or parameters[3] or 0)*hdelta -    local targetspace_shrink  = (parameters.space_shrink  or parameters[4] or 0)*hdelta -    local targetx_height      = (parameters.x_height      or parameters[5] or 0)*vdelta -    local targetquad          = (parameters.quad          or parameters[6] or 0)*hdelta -    local targetextra_space   = (parameters.extra_space   or parameters[7] or 0)*hdelta -    -- -    targetparameters.slant         = targetslant -- slantperpoint -    targetparameters.space         = targetspace -    targetparameters.space_stretch = targetspace_stretch -    targetparameters.space_shrink  = targetspace_shrink -    targetparameters.x_height      = targetx_height -    targetparameters.quad          = targetquad -    targetparameters.extra_space   = targetextra_space -    -- -    local ascender = parameters.ascender -    if ascender then -        targetparameters.ascender  = delta * ascender -    end -    local descender = parameters.descender -    if descender then -        targetparameters.descender = delta * descender -    end -    -- -    constructors.enhanceparameters(targetparameters) -- official copies for us -    -- -    local protrusionfactor = (targetquad ~= 0 and 1000/targetquad) or 0 -    local scaledwidth      = defaultwidth  * hdelta -    local scaledheight     = defaultheight * vdelta -    local scaleddepth      = defaultdepth  * vdelta -    -- -    if trace_defining then -        report_defining("scaling by (%s,%s): name '%s', fullname: '%s', filename: '%s'", -            hdelta,vdelta,name or "noname",fullname or "nofullname",filename or "nofilename") -    end -    -- -    local hasmath = (properties.hasmath or next(mathparameters)) and true -    if hasmath then -        if trace_defining then -            report_defining("math enabled for: name '%s', fullname: '%s', filename: '%s'", -                name or "noname",fullname or "nofullname",filename or "nofilename") -        end -        constructors.assignmathparameters(target,tfmdata) -- does scaling and whatever is needed -        properties.hasmath    = true -        target.nomath         = false -        target.MathConstants  = target.mathparameters -    else -        if trace_defining then -            report_defining("math disabled for: name '%s', fullname: '%s', filename: '%s'", -                name or "noname",fullname or "nofullname",filename or "nofilename") -        end -        properties.hasmath    = false -        target.nomath         = true -        target.mathparameters = nil -- nop -    end -    -- -    local italickey = "italic" -    -- -    -- some context specific trickery (this will move to a plugin) -    -- -    if hasmath then -        if properties.mathitalics then -            italickey = "italic_correction" -            if trace_defining then -                report_defining("math italics disabled for: name '%s', fullname: '%s', filename: '%s'", -                    name or "noname",fullname or "nofullname",filename or "nofilename") -            end -        end -        autoitalicamount = false -- new -    else -        if properties.textitalics then -            italickey = "italic_correction" -            if trace_defining then -                report_defining("text italics disabled for: name '%s', fullname: '%s', filename: '%s'", -                    name or "noname",fullname or "nofullname",filename or "nofilename") -            end -            if properties.delaytextitalics then -                autoitalicamount = false -            end -        end -    end -    -- -    -- end of context specific trickery -    -- -    constructors.beforecopyingcharacters(target,tfmdata) -    -- -    local sharedkerns = { } -    -- -    -- we can have a dumb mode (basemode without math etc) that skips most -    -- -    for unicode, character in next, characters do -        local chr, description, index, touni -        if changed then -            -- basemode hack (we try to catch missing tounicodes, e.g. needed for ssty in math cambria) -            local c = changed[unicode] -            if c then -                description = descriptions[c] or descriptions[unicode] or character -                character = characters[c] or character -                index = description.index or c -                if tounicode then -                    touni = tounicode[index] -- nb: index! -                    if not touni then -- goodie -                        local d = descriptions[unicode] or characters[unicode] -                        local i = d.index or unicode -                        touni = tounicode[i] -- nb: index! -                    end -                end -            else -                description = descriptions[unicode] or character -                index = description.index or unicode -                if tounicode then -                    touni = tounicode[index] -- nb: index! -                end -            end -        else -            description = descriptions[unicode] or character -            index = description.index or unicode -            if tounicode then -                touni = tounicode[index] -- nb: index! -            end -        end -        local width  = description.width -        local height = description.height -        local depth  = description.depth -        if width  then width  = hdelta*width  else width  = scaledwidth  end -        if height then height = vdelta*height else height = scaledheight end -    --  if depth  then depth  = vdelta*depth  else depth  = scaleddepth  end -        if depth and depth ~= 0 then -            depth = delta*depth -            if nonames then -                chr = { -                    index  = index, -                    height = height, -                    depth  = depth, -                    width  = width, -                } -            else -                chr = { -                    name   = description.name, -                    index  = index, -                    height = height, -                    depth  = depth, -                    width  = width, -                } -            end -        else -            -- this saves a little bit of memory time and memory, esp for big cjk fonts -            if nonames then -                chr = { -                    index  = index, -                    height = height, -                    width  = width, -                } -            else -                chr = { -                    name   = description.name, -                    index  = index, -                    height = height, -                    width  = width, -                } -            end -        end -        if touni then -            chr.tounicode = touni -        end -    --  if trace_scaling then -    --    report_defining("t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or "",index or 0,description.name or '-',description.class or '-') -    --  end -        if hasquality then -            -- we could move these calculations elsewhere (saves calculations) -            local ve = character.expansion_factor -            if ve then -                chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere -            end -            local vl = character.left_protruding -            if vl then -                chr.left_protruding  = protrusionfactor*width*vl -            end -            local vr = character.right_protruding -            if vr then -                chr.right_protruding  = protrusionfactor*width*vr -            end -        end -        -- -        if autoitalicamount then -            local vi = description.italic -            if not vi then -                local vi = description.boundingbox[3] - description.width + autoitalicamount -                if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic -                    chr[italickey] = vi*hdelta -                end -            elseif vi ~= 0 then -                chr[italickey] = vi*hdelta -            end -        elseif hasitalics then -            local vi = description.italic -            if vi and vi ~= 0 then -                chr[italickey] = vi*hdelta -            end -        end -        -- to be tested -        if hasmath then -            -- todo, just operate on descriptions.math -            local vn = character.next -            if vn then -                chr.next = vn -             -- if character.vert_variants or character.horiz_variants then -             --     report_defining("glyph U+%05X has combination of next, vert_variants and horiz_variants",index) -             -- end -            else -                local vv = character.vert_variants -                if vv then -                    local t = { } -                    for i=1,#vv do -                        local vvi = vv[i] -                        t[i] = { -                            ["start"]    = (vvi["start"]   or 0)*vdelta, -                            ["end"]      = (vvi["end"]     or 0)*vdelta, -                            ["advance"]  = (vvi["advance"] or 0)*vdelta, -                            ["extender"] =  vvi["extender"], -                            ["glyph"]    =  vvi["glyph"], -                        } -                    end -                    chr.vert_variants = t -                else -                    local hv = character.horiz_variants -                    if hv then -                        local t = { } -                        for i=1,#hv do -                            local hvi = hv[i] -                            t[i] = { -                                ["start"]    = (hvi["start"]   or 0)*hdelta, -                                ["end"]      = (hvi["end"]     or 0)*hdelta, -                                ["advance"]  = (hvi["advance"] or 0)*hdelta, -                                ["extender"] =  hvi["extender"], -                                ["glyph"]    =  hvi["glyph"], -                            } -                        end -                        chr.horiz_variants = t -                    end -                end -            end -            local va = character.top_accent -            if va then -                chr.top_accent = vdelta*va -            end -            if stackmath then -                local mk = character.mathkerns -- not in math ? -                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 ! -                end -            end -        end -        if not nodemode then -            local vk = character.kerns -            if vk then -                local s = sharedkerns[vk] -                if not s then -                    s = { } -                    for k,v in next, vk do s[k] = v*hdelta end -                    sharedkerns[vk] = s -                end -                chr.kerns = s -            end -            local vl = character.ligatures -            if vl then -                if true then -                    chr.ligatures = vl -- shared -                else -                    local tt = { } -                    for i,l in next, vl do -                        tt[i] = l -                    end -                    chr.ligatures = tt -                end -            end -        end -        if isvirtual then -            local vc = character.commands -            if vc then -                -- we assume non scaled commands here -                -- tricky .. we need to scale pseudo math glyphs too -                -- which is why we deal with rules too -                local ok = false -                for i=1,#vc do -                    local key = vc[i][1] -                    if key == "right" or key == "down" then -                        ok = true -                        break -                    end -                end -                if ok then -                    local tt = { } -                    for i=1,#vc do -                        local ivc = vc[i] -                        local key = ivc[1] -                        if key == "right" then -                            tt[i] = { key, ivc[2]*hdelta } -                        elseif key == "down" then -                            tt[i] = { key, ivc[2]*vdelta } -                        elseif key == "rule" then -                            tt[i] = { key, ivc[2]*vdelta, ivc[3]*hdelta } -                        else -- not comment -                            tt[i] = ivc -- shared since in cache and untouched -                        end -                    end -                    chr.commands = tt -                else -                    chr.commands = vc -                end -                chr.index = nil -            end -        end -        targetcharacters[unicode] = chr -    end -    -- -    constructors.aftercopyingcharacters(target,tfmdata) -    -- -    return target -end - -function constructors.finalize(tfmdata) -    if tfmdata.properties and tfmdata.properties.finalized then -        return -    end -    -- -    if not tfmdata.characters then -        return nil -    end -    -- -    if not tfmdata.goodies then -        tfmdata.goodies = { } -- context specific -    end -    -- -    local parameters = tfmdata.parameters -    if not parameters then -        return nil -    end -    -- -    if not parameters.expansion then -        parameters.expansion = { -            stretch = tfmdata.stretch     or 0, -            shrink  = tfmdata.shrink      or 0, -            step    = tfmdata.step        or 0, -            auto    = tfmdata.auto_expand or false, -        } -    end -    -- -    if not parameters.protrusion then -        parameters.protrusion = { -            auto = auto_protrude -        } -    end -    -- -    if not parameters.size then -        parameters.size = tfmdata.size -    end -    -- -    if not parameters.extendfactor then -        parameters.extendfactor = tfmdata.extend or 0 -    end -    -- -    if not parameters.slantfactor then -        parameters.slantfactor = tfmdata.slant or 0 -    end -    -- -    if not parameters.designsize then -        parameters.designsize = tfmdata.designsize or 655360 -    end -    -- -    if not parameters.units then -        parameters.units = tfmdata.units_per_em or 1000 -    end -    -- -    if not tfmdata.descriptions then -        local descriptions = { } -- yes or no -        setmetatableindex(descriptions, function(t,k) local v = { } t[k] = v return v end) -        tfmdata.descriptions = descriptions -    end -    -- -    local properties = tfmdata.properties -    if not properties then -        properties = { } -        tfmdata.properties = properties -    end -    -- -    if not properties.virtualized then -        properties.virtualized = tfmdata.type == "virtual" -    end -    -- -    if not tfmdata.properties then -        tfmdata.properties = { -            fontname      = tfmdata.fontname, -            filename      = tfmdata.filename, -            fullname      = tfmdata.fullname, -            name          = tfmdata.name, -            psname        = tfmdata.psname, -            -- -            encodingbytes = tfmdata.encodingbytes or 1, -            embedding     = tfmdata.embedding     or "subset", -            tounicode     = tfmdata.tounicode     or 1, -            cidinfo       = tfmdata.cidinfo       or nil, -            format        = tfmdata.format        or "type1", -            direction     = tfmdata.direction     or 0, -        } -    end -    if not tfmdata.resources then -        tfmdata.resources = { } -    end -    if not tfmdata.shared then -        tfmdata.shared = { } -    end -    -- -    -- tfmdata.fonts -    -- tfmdata.unscaled -    -- -    if not properties.hasmath then -        properties.hasmath  = not tfmdata.nomath -    end -    -- -    tfmdata.MathConstants  = nil -    tfmdata.postprocessors = nil -    -- -    tfmdata.fontname       = nil -    tfmdata.filename       = nil -    tfmdata.fullname       = nil -    tfmdata.name           = nil -- most tricky part -    tfmdata.psname         = nil -    -- -    tfmdata.encodingbytes  = nil -    tfmdata.embedding      = nil -    tfmdata.tounicode      = nil -    tfmdata.cidinfo        = nil -    tfmdata.format         = nil -    tfmdata.direction      = nil -    tfmdata.type           = nil -    tfmdata.nomath         = nil -    tfmdata.designsize     = nil -    -- -    tfmdata.size           = nil -    tfmdata.stretch        = nil -    tfmdata.shrink         = nil -    tfmdata.step           = nil -    tfmdata.auto_expand    = nil -    tfmdata.auto_protrude  = nil -    tfmdata.extend         = nil -    tfmdata.slant          = nil -    tfmdata.units_per_em   = nil -    -- -    properties.finalized   = true -    -- -    return tfmdata -end - ---[[ldx-- -<p>A unique hash value is generated by:</p> ---ldx]]-- - -local hashmethods        = { } -constructors.hashmethods = hashmethods - -function constructors.hashfeatures(specification) -- will be overloaded -    local features = specification.features -    if features then -        local t, tn = { }, 0 -        for category, list in next, features do -            if next(list) then -                local hasher = hashmethods[category] -                if hasher then -                    local hash = hasher(list) -                    if hash then -                        tn = tn + 1 -                        t[tn] = category .. ":" .. hash -                    end -                end -            end -        end -        if tn > 0 then -            return concat(t," & ") -        end -    end -    return "unknown" -end - -hashmethods.normal = function(list) -    local s = { } -    local n = 0 -    for k, v in next, list do -        if k ~= "number" and k ~= "features" then -- I need to figure this out, features -            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 - ---[[ldx-- -<p>In principle we can share tfm tables when we are in node for a font, but then -we need to define a font switch as an id/attr switch which is no fun, so in that -case users can best use dynamic features ... so, we will not use that speedup. Okay, -when we get rid of base mode we can optimize even further by sharing, but then we -loose our testcases for <l n='luatex'/>.</p> ---ldx]]-- - -function constructors.hashinstance(specification,force) -    local hash, size, fallbacks = specification.hash, specification.size, specification.fallbacks -    if force or not hash then -        hash = constructors.hashfeatures(specification) -        specification.hash = hash -    end -    if size < 1000 and designsizes[hash] then -        size = math.round(constructors.scaled(size,designsizes[hash])) -        specification.size = size -    end - -- local mathsize = specification.mathsize or 0 - -- if mathsize > 0 then - --     local textsize = specification.textsize - --     if fallbacks then - --         return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ] @ ' .. fallbacks - --     else - --         return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ]' - --     end - -- else -        if fallbacks then -            return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks -        else -            return hash .. ' @ ' .. tostring(size) -        end - -- end -end - -function constructors.setname(tfmdata,specification) -- todo: get specification from tfmdata -    if constructors.namemode == "specification" then -        -- not to be used in context ! -        local specname = specification.specification -        if specname then -            tfmdata.properties.name = specname -            if trace_defining then -                report_otf("overloaded fontname: '%s'",specname) -            end -        end -    end -end - -function constructors.checkedfilename(data) -    local foundfilename = data.foundfilename -    if not foundfilename then -        local askedfilename = data.filename or "" -        if askedfilename ~= "" then -            askedfilename = resolvers.resolve(askedfilename) -- no shortcut -            foundfilename = resolvers.findbinfile(askedfilename,"") or "" -            if foundfilename == "" then -                report_defining("source file '%s' is not found",askedfilename) -                foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or "" -                if foundfilename ~= "" then -                    report_defining("using source file '%s' (cache mismatch)",foundfilename) -                end -            end -        end -        data.foundfilename = foundfilename -    end -    return foundfilename -end - -local formats = allocate() -fonts.formats = formats - -setmetatableindex(formats, function(t,k) -    local l = lower(k) -    if rawget(t,k) then -        t[k] = l -        return l -    end -    return rawget(t,file.extname(l)) -end) - -local locations = { } - -local function setindeed(mode,target,group,name,action,position) -    local t = target[mode] -    if not t then -        report_defining("fatal error in setting feature '%s', group '%s', mode '%s'",name or "?",group or "?",mode) -        os.exit() -    elseif position then -        -- todo: remove existing -        insert(t, position, { name = name, action = action }) -    else -        for i=1,#t do -            local ti = t[i] -            if ti.name == name then -                ti.action = action -                return -            end -        end -        insert(t, { name = name, action = action }) -    end -end - -local function set(group,name,target,source) -    target = target[group] -    if not target then -        report_defining("fatal target error in setting feature '%s', group '%s'",name or "?",group or "?") -        os.exit() -    end -    local source = source[group] -    if not source then -        report_defining("fatal source error in setting feature '%s', group '%s'",name or "?",group or "?") -        os.exit() -    end -    local node     = source.node -    local base     = source.base -    local position = source.position -    if node then -        setindeed("node",target,group,name,node,position) -    end -    if base then -        setindeed("base",target,group,name,base,position) -    end -end - -local function register(where,specification) -    local name = specification.name -    if name and name ~= "" then -        local default      = specification.default -        local description  = specification.description -        local initializers = specification.initializers -        local processors   = specification.processors -        local manipulators = specification.manipulators -        local modechecker  = specification.modechecker -        if default then -            where.defaults[name] = default -        end -        if description and description ~= "" then -            where.descriptions[name] = description -        end -        if initializers then -            set('initializers',name,where,specification) -        end -        if processors then -            set('processors',  name,where,specification) -        end -        if manipulators then -            set('manipulators',name,where,specification) -        end -        if modechecker then -           where.modechecker = modechecker -        end -    end -end - -constructors.registerfeature = register - -function constructors.getfeatureaction(what,where,mode,name) -    what = handlers[what].features -    if what then -        where = what[where] -        if where then -            mode = where[mode] -            if mode then -                for i=1,#mode do -                    local m = mode[i] -                    if m.name == name then -                        return m.action -                    end -                end -            end -        end -    end -end - -function constructors.newfeatures(what) -    local features = handlers[what].features -    if not features then -        local tables = handlers[what].tables -- can be preloaded -        features = allocate { -            defaults     = { }, -            descriptions = tables and tables.features or { }, -            initializers = { base = { }, node = { } }, -            processors   = { base = { }, node = { } }, -            manipulators = { base = { }, node = { } }, -        } -        features.register = function(specification) return register(features,specification) end -        handlers[what].features = features -- will also become hidden -    end -    return features -end - ---[[ldx-- -<p>We need to check for default features. For this we provide -a helper function.</p> ---ldx]]-- - -function constructors.checkedfeatures(what,features) -    local defaults = handlers[what].features.defaults -    if features and next(features) then -        features = fastcopy(features) -- can be inherited (mt) but then no loops possible -        for key, value in next, defaults do -            if features[key] == nil then -                features[key] = value -            end -        end -        return features -    else -        return fastcopy(defaults) -- we can change features in place -    end -end - --- before scaling - -function constructors.initializefeatures(what,tfmdata,features,trace,report) -    if features and next(features) then -        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" -        properties.mode        = mode -- also status -        features.mode          = mode -- both properties.mode or features.mode can be changed -        -- -        local done             = { } -        while true do -            local redo = false -            local initializers = whatfeatures.initializers[mode] -            if initializers then -                for i=1,#initializers do -                    local step = initializers[i] -                    local feature = step.name --- we could intercept mode here .. needs a rewrite of this whole loop then but it's cleaner that way -                    local value = features[feature] -                    if not value then -                        -- disabled -                    elseif done[feature] then -                        -- already done -                    else -                        local action = step.action -                        if trace then -                            report("initializing feature %s to %s for mode %s for font %s",feature, -                                tostring(value),mode or 'unknown', tfmdata.properties.fullname or 'unknown') -                        end -                        action(tfmdata,value,features) -- can set mode (e.g. goodies) so it can trigger a restart -                        if mode ~= properties.mode or mode ~= features.mode then -                            if whatmodechecker then -                                properties.mode = whatmodechecker(tfmdata,features,properties.mode) -- force checking -                                features.mode   = properties.mode -                            end -                            if mode ~= properties.mode then -                                mode = properties.mode -                                redo = true -                            end -                        end -                        done[feature] = true -                    end -                    if redo then -                        break -                    end -                end -                if not redo then -                    break -                end -            else -                break -            end -        end -        properties.mode = mode -- to be sure -        return true -    else -        return false -    end -end - --- while typesetting - -function constructors.collectprocessors(what,tfmdata,features,trace,report) -    local processes, nofprocesses = { }, 0 -    if features and next(features) then -        local properties     = tfmdata.properties -        local whathandler    = handlers[what] -        local whatfeatures   = whathandler.features -        local whatprocessors = whatfeatures.processors -        local processors     = whatprocessors[properties.mode] -        if processors then -            for i=1,#processors do -                local step = processors[i] -                local feature = step.name -                if features[feature] then -                    local action = step.action -                    if trace then -                        report("installing feature processor %s for mode %s for font %s",feature, -                            mode or 'unknown', tfmdata.properties.fullname or 'unknown') -                    end -                    if action then -                        nofprocesses = nofprocesses + 1 -                        processes[nofprocesses] = action -                    end -                end -            end -        elseif trace then -            report("no feature processors for mode %s for font %s", -                mode or 'unknown', tfmdata.properties.fullname or 'unknown') -        end -    end -    return processes -end - --- after scaling - -function constructors.applymanipulators(what,tfmdata,features,trace,report) -    if features and next(features) then -        local properties       = tfmdata.properties -        local whathandler      = handlers[what] -        local whatfeatures     = whathandler.features -        local whatmanipulators = whatfeatures.manipulators -        local manipulators     = whatmanipulators[properties.mode] -        if manipulators then -            for i=1,#manipulators do -                local step = manipulators[i] -                local feature = step.name -                local value = features[feature] -                if value then -                    local action = step.action -                    if trace then -                        report("applying feature manipulator %s for mode %s for font %s",feature, -                            mode or 'unknown', tfmdata.properties.fullname or 'unknown') -                    end -                    if action then -                        action(tfmdata,feature,value) -                    end -                end -            end -        end -    end -end diff --git a/otfl-font-ini.lua b/otfl-font-ini.lua deleted file mode 100644 index 8eeba0c..0000000 --- a/otfl-font-ini.lua +++ /dev/null @@ -1,38 +0,0 @@ -if not modules then modules = { } end modules ['font-ini'] = { -    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" -} - --- basemethods    -> can also be in list --- presetcontext  -> defaults --- hashfeatures   -> ctx version - ---[[ldx-- -<p>Not much is happening here.</p> ---ldx]]-- - -local lower = string.lower -local allocate, mark = utilities.storage.allocate, utilities.storage.mark - -local report_defining = logs.reporter("fonts","defining") - -fontloader.totable = fontloader.to_table - -fonts               = fonts or { } -- already defined in context -local fonts         = fonts - --- some of these might move to where they are used first: - -fonts.hashes        = { identifiers = allocate() } -fonts.analyzers     = { } -- not needed here -fonts.readers       = { } -fonts.tables        = { } -fonts.definers      = { methods = { } } -fonts.specifiers    = fonts.specifiers or { } -- in format ! -fonts.loggers       = { register = function() end } -fonts.helpers       = { } - -fonts.tracers       = { } -- for the moment till we have move to moduledata diff --git a/otfl-font-map.lua b/otfl-font-map.lua deleted file mode 100644 index 7f5305f..0000000 --- a/otfl-font-map.lua +++ /dev/null @@ -1,307 +0,0 @@ -if not modules then modules = { } end modules ['font-map'] = { -    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" -} - -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 trace_loading = false  trackers.register("fonts.loading",    function(v) trace_loading    = v end) -local trace_mapping = false  trackers.register("fonts.mapping", function(v) trace_unimapping = v end) - -local report_fonts  = logs.reporter("fonts","loading") -- not otf only - -local fonts    = fonts -local mappings = { } -fonts.mappings = mappings - ---[[ldx-- -<p>Eventually this code will disappear because map files are kind -of obsolete. Some code may move to runtime or auxiliary modules.</p> -<p>The name to unciode related code will stay of course.</p> ---ldx]]-- - -local function loadlumtable(filename) -- will move to font goodies -    local lumname = file.replacesuffix(file.basename(filename),"lum") -    local lumfile = resolvers.findfile(lumname,"map") or "" -    if lumfile ~= "" and lfs.isfile(lumfile) then -        if trace_loading or trace_mapping then -            report_fonts("enhance: loading %s ",lumfile) -        end -        lumunic = dofile(lumfile) -        return lumunic, lumfile -    end -end - -local hex     = R("AF","09") -local hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end -local hexsix  = (hex^1)           / function(s) return tonumber(s,16) end -local dec     = (R("09")^1)  / tonumber -local period  = P(".") -local unicode = P("uni")   * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true)) -local ucode   = P("u")     * (hexsix  * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true)) -local index   = P("index") * dec * Cc(false) - -local parser  = unicode + ucode + index - -local parsers = { } - -local function makenameparser(str) -    if not str or str == "" then -        return parser -    else -        local p = parsers[str] -        if not p then -            p = P(str) * period * dec * Cc(false) -            parsers[str] = p -        end -        return p -    end -end - ---~ local parser = mappings.makenameparser("Japan1") ---~ local parser = mappings.makenameparser() ---~ local function test(str) ---~     local b, a = lpegmatch(parser,str) ---~     print((a and table.serialize(b)) or b) ---~ end ---~ test("a.sc") ---~ test("a") ---~ test("uni1234") ---~ test("uni1234.xx") ---~ test("uni12349876") ---~ test("index1234") ---~ test("Japan1.123") - -local function tounicode16(unicode) -    if unicode < 0x10000 then -        return format("%04X",unicode) -    else -        return format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) -    end -end - -local function tounicode16sequence(unicodes) -    local t = { } -    for l=1,#unicodes do -        local unicode = unicodes[l] -        if unicode < 0x10000 then -            t[l] = format("%04X",unicode) -        else -            t[l] = format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) -        end -    end -    return concat(t) -end - -local function fromunicode16(str) -    if #str == 4 then -        return tonumber(str,16) -    else -        local l, r = match(str,"(....)(....)") -        return (tonumber(l,16)- 0xD800)*0x400  + tonumber(r,16) - 0xDC00 -    end -end - ---~ This is quite a bit faster but at the cost of some memory but if we ---~ do this we will also use it elsewhere so let's not follow this route ---~ now. I might use this method in the plain variant (no caching there) ---~ but then I need a flag that distinguishes between code branches. ---~ ---~ local cache = { } ---~ ---~ function mappings.tounicode16(unicode) ---~     local s = cache[unicode] ---~     if not s then ---~         if unicode < 0x10000 then ---~             s = format("%04X",unicode) ---~         else ---~             s = format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00) ---~         end ---~         cache[unicode] = s ---~     end ---~     return s ---~ end - -mappings.loadlumtable        = loadlumtable -mappings.makenameparser      = makenameparser -mappings.tounicode16         = tounicode16 -mappings.tounicode16sequence = tounicode16sequence -mappings.fromunicode16       = fromunicode16 - -local separator   = S("_.") -local other       = C((1 - separator)^1) -local ligsplitter = Ct(other * (separator * other)^0) - ---~ print(table.serialize(lpegmatch(ligsplitter,"this"))) ---~ print(table.serialize(lpegmatch(ligsplitter,"this.that"))) ---~ print(table.serialize(lpegmatch(ligsplitter,"japan1.123"))) ---~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more"))) ---~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more.that"))) - -function mappings.addtounicode(data,filename) -    local resources    = data.resources -    local properties   = data.properties -    local descriptions = data.descriptions -    local unicodes     = resources.unicodes -    if not unicodes then -        return -    end -    -- we need to move this code -    unicodes['space']  = unicodes['space']  or 32 -    unicodes['hyphen'] = unicodes['hyphen'] or 45 -    unicodes['zwj']    = unicodes['zwj']    or 0x200D -    unicodes['zwnj']   = unicodes['zwnj']   or 0x200C -    -- the tounicode mapping is sparse and only needed for alternatives -    local private       = fonts.constructors.privateoffset -    local unknown       = format("%04X",utfbyte("?")) -    local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context -    local tounicode     = { } -    local originals     = { } -    resources.tounicode = tounicode -    resources.originals = originals -    local lumunic, uparser, oparser -    local cidinfo, cidnames, cidcodes, usedmap -    if false then -- will become an option -        lumunic = loadlumtable(filename) -        lumunic = lumunic and lumunic.tounicode -    end -    -- -    cidinfo = properties.cidinfo -    usedmap = cidinfo and fonts.cid.getmap(cidinfo) -    -- -    if usedmap then -        oparser  = usedmap and makenameparser(cidinfo.ordering) -        cidnames = usedmap.names -        cidcodes = usedmap.unicodes -    end -    uparser = makenameparser() -    local ns, nl = 0, 0 -    for unic, glyph in next, descriptions do -        local index = glyph.index -        local name  = glyph.name -        if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then -            local unicode = lumunic and lumunic[name] or unicodevector[name] -            if unicode then -                originals[index] = unicode -                tounicode[index] = tounicode16(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 -                        originals[index] = unicode -                        tounicode[index] = tounicode16(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 -                                    originals[index] = unicode -                                    tounicode[index] = tounicode16(unicode) -                                    ns               = ns + 1 -                                end -                            end -                            if not unicode then -                                local foundcodes, multiple = lpegmatch(uparser,reference) -                                if foundcodes then -                                    originals[index] = foundcodes -                                    if multiple then -                                        tounicode[index] = tounicode16sequence(foundcodes) -                                        nl               = nl + 1 -                                        unicode          = true -                                    else -                                        tounicode[index] = tounicode16(foundcodes) -                                        ns               = ns + 1 -                                        unicode          = foundcodes -                                    end -                                end -                            end -                        end -                    end -                end -            end -            -- a.whatever or a_b_c.whatever or a_b_c (no numbers) -            if not unicode then -                local split = lpegmatch(ligsplitter,name) -                local nplit = split and #split or 0 -                if nplit >= 2 then -                    local t, n = { }, 0 -                    for l=1,nplit do -                        local base = split[l] -                        local u = unicodes[base] or unicodevector[base] -                        if not u then -                            break -                        elseif type(u) == "table" then -                            n = n + 1 -                            t[n] = u[1] -                        else -                            n = n + 1 -                            t[n] = u -                        end -                    end -                    if n == 0 then -- done then -                        -- nothing -                    elseif n == 1 then -                        originals[index] = t[1] -                        tounicode[index] = tounicode16(t[1]) -                    else -                        originals[index] = t -                        tounicode[index] = tounicode16sequence(t) -                    end -                    nl = nl + 1 -                    unicode = true -                else -                    -- skip: already checked and we don't want privates here -                end -            end -            -- last resort (we might need to catch private here as well) -            if not unicode then -                local foundcodes, multiple = lpegmatch(uparser,name) -                if foundcodes then -                    if multiple then -                        originals[index] = foundcodes -                        tounicode[index] = tounicode16sequence(foundcodes) -                        nl               = nl + 1 -                        unicode          = true -                    else -                        originals[index] = foundcodes -                        tounicode[index] = tounicode16(foundcodes) -                        ns               = ns + 1 -                        unicode          = foundcodes -                    end -                end -            end -         -- if not unicode then -         --     originals[index] = 0xFFFD -         --     tounicode[index] = "FFFD" -         -- end -        end -    end -    if trace_mapping then -        for unic, glyph in table.sortedhash(descriptions) do -            local name  = glyph.name -            local index = glyph.index -            local toun  = tounicode[index] -            if toun then -                report_fonts("internal: 0x%05X, name: %s, unicode: U+%05X, tounicode: %s",index,name,unic,toun) -            else -                report_fonts("internal: 0x%05X, name: %s, unicode: U+%05X",index,name,unic) -            end -        end -    end -    if trace_loading and (ns > 0 or nl > 0) then -        report_fonts("enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) -    end -end diff --git a/otfl-font-ota.lua b/otfl-font-ota.lua deleted file mode 100644 index c4663e1..0000000 --- a/otfl-font-ota.lua +++ /dev/null @@ -1,373 +0,0 @@ -if not modules then modules = { } end modules ['font-ota'] = { -    version   = 1.001, -    comment   = "companion to font-otf.lua (analysing)", -    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", -    copyright = "PRAGMA ADE / ConTeXt Development Team", -    license   = "see context related readme files" -} - --- this might become scrp-*.lua - -local type, tostring, match, format, concat = type, tostring, string.match, string.format, table.concat - -if not trackers then trackers = { register = function() end } end - -local trace_analyzing = false  trackers.register("otf.analyzing",  function(v) trace_analyzing = v end) - -local fonts, nodes, node = fonts, nodes, node - -local allocate            = utilities.storage.allocate - -local otf                 = fonts.handlers.otf - -local analyzers           = fonts.analyzers -local initializers        = allocate() -local methods             = allocate() - -analyzers.initializers    = initializers -analyzers.methods         = methods -analyzers.useunicodemarks = false - -local nodecodes           = nodes.nodecodes -local glyph_code          = nodecodes.glyph - -local set_attribute       = node.set_attribute -local has_attribute       = node.has_attribute -local traverse_id         = node.traverse_id -local traverse_node_list  = node.traverse - -local fontdata            = fonts.hashes.identifiers -local state               = attributes.private('state') -local categories          = characters and characters.categories or { } -- sorry, only in context - -local tracers             = nodes.tracers -local colortracers        = tracers and tracers.colors -local setnodecolor        = colortracers and colortracers.set   or function() end -local resetnodecolor      = colortracers and colortracers.reset or function() end - -local otffeatures         = fonts.constructors.newfeatures("otf") -local registerotffeature  = otffeatures.register - ---[[ldx-- -<p>Analyzers run per script and/or language and are needed in order to -process features right.</p> ---ldx]]-- - --- todo: analyzers per script/lang, cross font, so we need an font id hash -> script --- e.g. latin -> hyphenate, arab -> 1/2/3 analyze -- its own namespace - -local state = attributes.private('state') - -function analyzers.setstate(head,font) -    local useunicodemarks  = analyzers.useunicodemarks -    local tfmdata = fontdata[font] -    local characters = tfmdata.characters -    local descriptions = tfmdata.descriptions -    local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean -    while current do -        local id = current.id -        if id == glyph_code and current.font == font then -            local char = current.char -            local d = descriptions[char] -            if d then -                if d.class == "mark" or (useunicodemarks and categories[char] == "mn") then -                    done = true -                    set_attribute(current,state,5) -- mark -                elseif n == 0 then -                    first, last, n = current, current, 1 -                    set_attribute(current,state,1) -- init -                else -                    last, n = current, n+1 -                    set_attribute(current,state,2) -- medi -                end -            else -- finish -                if first and first == last then -                    set_attribute(last,state,4) -- isol -                elseif last then -                    set_attribute(last,state,3) -- fina -                end -                first, last, n = nil, nil, 0 -            end -        elseif id == disc_code then -            -- always in the middle -            set_attribute(current,state,2) -- midi -            last = current -        else -- finish -            if first and first == last then -                set_attribute(last,state,4) -- isol -            elseif last then -                set_attribute(last,state,3) -- fina -            end -            first, last, n = nil, nil, 0 -        end -        current = current.next -    end -    if first and first == last then -        set_attribute(last,state,4) -- isol -    elseif last then -        set_attribute(last,state,3) -- fina -    end -    return head, done -end - --- in the future we will use language/script attributes instead of the --- font related value, but then we also need dynamic features which is --- somewhat slower; and .. we need a chain of them - -local function analyzeinitializer(tfmdata,value) -- attr -    local script, language = otf.scriptandlanguage(tfmdata) -- attr -    local action = initializers[script] -    if action then -        if type(action) == "function" then -            return action(tfmdata,value) -        else -            local action = action[language] -            if action then -                return action(tfmdata,value) -            end -        end -    end -end - -local function analyzeprocessor(head,font,attr) -    local tfmdata = fontdata[font] -    local script, language = otf.scriptandlanguage(tfmdata,attr) -    local action = methods[script] -    if action then -        if type(action) == "function" then -            return action(head,font,attr) -        else -            action = action[language] -            if action then -                return action(head,font,attr) -            end -        end -    end -    return head, false -end - -registerotffeature { -    name         = "analyze", -    description  = "analysis of (for instance) character classes", -    default      = true, -    initializers = { -        node     = analyzeinitializer, -    }, -    processors = { -        position = 1, -        node     = analyzeprocessor, -    } -} - --- latin - -methods.latn = analyzers.setstate - --- this info eventually will go into char-def and we will have a state --- table for generic then - -local zwnj = 0x200C -local zwj  = 0x200D - -local isol = { -    [0x0600] = true, [0x0601] = true, [0x0602] = true, [0x0603] = true, -    [0x0608] = true, [0x060B] = true, [0x0621] = true, [0x0674] = true, -    [0x06DD] = true, [zwnj] = true, -} - -local isol_fina = { -    [0x0622] = true, [0x0623] = true, [0x0624] = true, [0x0625] = true, -    [0x0627] = true, [0x0629] = true, [0x062F] = true, [0x0630] = true, -    [0x0631] = true, [0x0632] = true, [0x0648] = true, [0x0671] = true, -    [0x0672] = true, [0x0673] = true, [0x0675] = true, [0x0676] = true, -    [0x0677] = true, [0x0688] = true, [0x0689] = true, [0x068A] = true, -    [0x068B] = true, [0x068C] = true, [0x068D] = true, [0x068E] = true, -    [0x068F] = true, [0x0690] = true, [0x0691] = true, [0x0692] = true, -    [0x0693] = true, [0x0694] = true, [0x0695] = true, [0x0696] = true, -    [0x0697] = true, [0x0698] = true, [0x0699] = true, [0x06C0] = true, -    [0x06C3] = true, [0x06C4] = true, [0x06C5] = true, [0x06C6] = true, -    [0x06C7] = true, [0x06C8] = true, [0x06C9] = true, [0x06CA] = true, -    [0x06CB] = true, [0x06CD] = true, [0x06CF] = true, [0x06D2] = true, -    [0x06D3] = true, [0x06D5] = true, [0x06EE] = true, [0x06EF] = true, -    [0x0759] = true, [0x075A] = true, [0x075B] = true, [0x076B] = true, -    [0x076C] = true, [0x0771] = true, [0x0773] = true, [0x0774] = true, -	[0x0778] = true, [0x0779] = true, [0xFEF5] = true, [0xFEF7] = true, -	[0xFEF9] = true, [0xFEFB] = true, - -    -- syriac - -	[0x0710] = true, [0x0715] = true, [0x0716] = true, [0x0717] = true, -	[0x0718] = true, [0x0719] = true, [0x0728] = true, [0x072A] = true, -	[0x072C] = true, [0x071E] = true, -} - -local isol_fina_medi_init = { -    [0x0626] = true, [0x0628] = true, [0x062A] = true, [0x062B] = true, -    [0x062C] = true, [0x062D] = true, [0x062E] = true, [0x0633] = true, -    [0x0634] = true, [0x0635] = true, [0x0636] = true, [0x0637] = true, -    [0x0638] = true, [0x0639] = true, [0x063A] = true, [0x063B] = true, -    [0x063C] = true, [0x063D] = true, [0x063E] = true, [0x063F] = true, -    [0x0640] = true, [0x0641] = true, [0x0642] = true, [0x0643] = true, -    [0x0644] = true, [0x0645] = true, [0x0646] = true, [0x0647] = true, -    [0x0649] = true, [0x064A] = true, [0x066E] = true, [0x066F] = true, -    [0x0678] = true, [0x0679] = true, [0x067A] = true, [0x067B] = true, -    [0x067C] = true, [0x067D] = true, [0x067E] = true, [0x067F] = true, -    [0x0680] = true, [0x0681] = true, [0x0682] = true, [0x0683] = true, -    [0x0684] = true, [0x0685] = true, [0x0686] = true, [0x0687] = true, -    [0x069A] = true, [0x069B] = true, [0x069C] = true, [0x069D] = true, -    [0x069E] = true, [0x069F] = true, [0x06A0] = true, [0x06A1] = true, -    [0x06A2] = true, [0x06A3] = true, [0x06A4] = true, [0x06A5] = true, -    [0x06A6] = true, [0x06A7] = true, [0x06A8] = true, [0x06A9] = true, -    [0x06AA] = true, [0x06AB] = true, [0x06AC] = true, [0x06AD] = true, -    [0x06AE] = true, [0x06AF] = true, [0x06B0] = true, [0x06B1] = true, -    [0x06B2] = true, [0x06B3] = true, [0x06B4] = true, [0x06B5] = true, -    [0x06B6] = true, [0x06B7] = true, [0x06B8] = true, [0x06B9] = true, -    [0x06BA] = true, [0x06BB] = true, [0x06BC] = true, [0x06BD] = true, -    [0x06BE] = true, [0x06BF] = true, [0x06C1] = true, [0x06C2] = true, -    [0x06CC] = true, [0x06CE] = true, [0x06D0] = true, [0x06D1] = true, -    [0x06FA] = true, [0x06FB] = true, [0x06FC] = true, [0x06FF] = true, -    [0x0750] = true, [0x0751] = true, [0x0752] = true, [0x0753] = true, -    [0x0754] = true, [0x0755] = true, [0x0756] = true, [0x0757] = true, -    [0x0758] = true, [0x075C] = true, [0x075D] = true, [0x075E] = true, -    [0x075F] = true, [0x0760] = true, [0x0761] = true, [0x0762] = true, -    [0x0763] = true, [0x0764] = true, [0x0765] = true, [0x0766] = true, -    [0x0767] = true, [0x0768] = true, [0x0769] = true, [0x076A] = true, -    [0x076D] = true, [0x076E] = true, [0x076F] = true, [0x0770] = true, -    [0x0772] = true, [0x0775] = true, [0x0776] = true, [0x0777] = true, -    [0x077A] = true, [0x077B] = true, [0x077C] = true, [0x077D] = true, -    [0x077E] = true, [0x077F] = true, [zwj] = true, - -    -- syriac - -	[0x0712] = true, [0x0713] = true, [0x0714] = true, [0x071A] = true, -	[0x071B] = true, [0x071C] = true, [0x071D] = true, [0x071F] = true, -	[0x0720] = true, [0x0721] = true, [0x0722] = true, [0x0723] = true, -	[0x0725] = true, [0x0726] = true, [0x0727] = true, [0x0729] = true, -	[0x072B] = true, [0x0724] = true, [0x0706] = true, [0x0707] = true, -} - -local arab_warned = { } - - --- todo: gref - -local function warning(current,what) -    local char = current.char -    if not arab_warned[char] then -        log.report("analyze","arab: character %s (U+%05X) has no %s class", char, char, what) -        arab_warned[char] = true -    end -end - -function methods.nocolor(head,font,attr) -    for n in traverse_id(glyph_code,head) do -        if not font or n.font == font then -            resetnodecolor(n) -        end -    end -    return head, true -end - -local function finish(first,last) -    if last then -        if first == last then -            local fc = first.char -            if isol_fina_medi_init[fc] or isol_fina[fc] then -                set_attribute(first,state,4) -- isol -                if trace_analyzing then setnodecolor(first,"font:isol") end -            else -                warning(first,"isol") -                set_attribute(first,state,0) -- error -                if trace_analyzing then resetnodecolor(first) end -            end -        else -            local lc = last.char -            if isol_fina_medi_init[lc] or isol_fina[lc] then -- why isol here ? -            -- if laststate == 1 or laststate == 2 or laststate == 4 then -                set_attribute(last,state,3) -- fina -                if trace_analyzing then setnodecolor(last,"font:fina") end -            else -                warning(last,"fina") -                set_attribute(last,state,0) -- error -                if trace_analyzing then resetnodecolor(last) end -            end -        end -        first, last = nil, nil -    elseif first then -        -- first and last are either both set so we never com here -        local fc = first.char -        if isol_fina_medi_init[fc] or isol_fina[fc] then -            set_attribute(first,state,4) -- isol -            if trace_analyzing then setnodecolor(first,"font:isol") end -        else -            warning(first,"isol") -            set_attribute(first,state,0) -- error -            if trace_analyzing then resetnodecolor(first) end -        end -        first = nil -    end -    return first, last -end - -function methods.arab(head,font,attr) -- maybe make a special version with no trace -    local useunicodemarks = analyzers.useunicodemarks -    local tfmdata = fontdata[font] -    local marks = tfmdata.resources.marks -    local first, last, current, done = nil, nil, head, false -    while current do -        if current.id == glyph_code and current.subtype<256 and current.font == font and not has_attribute(current,state) then -            done = true -            local char = current.char -            if marks[char] or (useunicodemarks and categories[char] == "mn") then -                set_attribute(current,state,5) -- mark -                if trace_analyzing then setnodecolor(current,"font:mark") end -            elseif isol[char] then -- can be zwj or zwnj too -                first, last = finish(first,last) -                set_attribute(current,state,4) -- isol -                if trace_analyzing then setnodecolor(current,"font:isol") end -                first, last = nil, nil -            elseif not first then -                if isol_fina_medi_init[char] then -                    set_attribute(current,state,1) -- init -                    if trace_analyzing then setnodecolor(current,"font:init") end -                    first, last = first or current, current -                elseif isol_fina[char] then -                    set_attribute(current,state,4) -- isol -                    if trace_analyzing then setnodecolor(current,"font:isol") end -                    first, last = nil, nil -                else -- no arab -                    first, last = finish(first,last) -                end -            elseif isol_fina_medi_init[char] then -                first, last = first or current, current -                set_attribute(current,state,2) -- medi -                if trace_analyzing then setnodecolor(current,"font:medi") end -            elseif isol_fina[char] then -                if not has_attribute(last,state,1) then -                    -- tricky, we need to check what last may be ! -                    set_attribute(last,state,2) -- medi -                    if trace_analyzing then setnodecolor(last,"font:medi") end -                end -                set_attribute(current,state,3) -- fina -                if trace_analyzing then setnodecolor(current,"font:fina") end -                first, last = nil, nil -            elseif char >= 0x0600 and char <= 0x06FF then -                if trace_analyzing then setnodecolor(current,"font:rest") end -                first, last = finish(first,last) -            else --no -                first, last = finish(first,last) -            end -        else -            first, last = finish(first,last) -        end -        current = current.next -    end -    first, last = finish(first,last) -    return head, done -end - -methods.syrc = methods.arab - -directives.register("otf.analyze.useunicodemarks",function(v) -    analyzers.useunicodemarks = v -end) diff --git a/otfl-font-otb.lua b/otfl-font-otb.lua deleted file mode 100644 index 44639a8..0000000 --- a/otfl-font-otb.lua +++ /dev/null @@ -1,636 +0,0 @@ -if not modules then modules = { } end modules ['font-otb'] = { -    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" -} -local concat = table.concat -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 = type, next, tonumber, tostring -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) -local trace_multiples     = false  trackers.register("otf.multiples",    function(v) trace_multiples    = v end) -local trace_alternatives  = false  trackers.register("otf.alternatives", function(v) trace_alternatives = v end) -local trace_ligatures     = false  trackers.register("otf.ligatures",    function(v) trace_ligatures    = v end) -local trace_kerns         = false  trackers.register("otf.kerns",        function(v) trace_kerns        = v end) -local trace_preparing     = false  trackers.register("otf.preparing",    function(v) trace_preparing    = v end) - -local report_prepare      = logs.reporter("fonts","otf prepare") - -local fonts               = fonts -local otf                 = fonts.handlers.otf - -local otffeatures         = fonts.constructors.newfeatures("otf") -local registerotffeature  = otffeatures.register - -otf.defaultbasealternate  = "none" -- first last - -local wildcard = "*" -local default  = "dflt" - -local function gref(descriptions,n) -    if type(n) == "number" then -        local name = descriptions[n].name -        if name then -            return format("U+%05X (%s)",n,name) -        else -            return format("U+%05X") -        end -    elseif n then -        local num, nam = { }, { } -        for i=2,#n do -- first is likely a key -            local ni = n[i] -            num[i] = format("U+%05X",ni) -            nam[i] = descriptions[ni].name or "?" -        end -        return format("%s (%s)",concat(num," "), concat(nam," ")) -    else -        return "?" -    end -end - -local function cref(feature,lookupname) -    if lookupname then -        return format("feature %s, lookup %s",feature,lookupname) -    else -        return format("feature %s",feature) -    end -end - -local function report_alternate(feature,lookupname,descriptions,unicode,replacement,value,comment) -    report_prepare("%s: base alternate %s => %s (%s => %s)",cref(feature,lookupname), -        gref(descriptions,unicode),replacement and gref(descriptions,replacement) or "-", -        tostring(value),comment) -end - -local function report_substitution(feature,lookupname,descriptions,unicode,substitution) -    report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), -        gref(descriptions,unicode),gref(descriptions,substitution)) -end - -local function report_ligature(feature,lookupname,descriptions,unicode,ligature) -    report_prepare("%s: base ligature %s => %s",cref(feature,lookupname), -        gref(descriptions,ligature),gref(descriptions,unicode)) -end - -local basemethods = { } -local basemethod  = "<unset>" - -local function applybasemethod(what,...) -    local m = basemethods[basemethod][what] -    if m then -        return m(...) -    end -end - --- We need to make sure that luatex sees the difference between --- base fonts that have different glyphs in the same slots in fonts --- that have the same fullname (or filename). LuaTeX will merge fonts --- eventually (and subset later on). If needed we can use a more --- verbose name as long as we don't use <()<>[]{}/%> and the length --- is < 128. - -local basehash, basehashes, applied = { }, 1, { } - -local function registerbasehash(tfmdata) -    local properties = tfmdata.properties -    local hash = concat(applied," ") -    local base = basehash[hash] -    if not base then -        basehashes     = basehashes + 1 -        base           = basehashes -        basehash[hash] = base -    end -    properties.basehash = base -    properties.fullname = properties.fullname .. "-" .. base - -- report_prepare("fullname base hash: '%s', featureset '%s'",tfmdata.properties.fullname,hash) -    applied = { } -end - -local function registerbasefeature(feature,value) -    applied[#applied+1] = feature  .. "=" .. tostring(value) -end - --- The original basemode ligature builder used the names of components --- and did some expression juggling to get the chain right. The current --- variant starts with unicodes but still uses names to make the chain. --- This is needed because we have to create intermediates when needed --- but use predefined snippets when available. To some extend the --- current builder is more stupid but I don't worry that much about it --- as ligatures are rather predicatable. --- --- Personally I think that an ff + i == ffi rule as used in for instance --- latin modern is pretty weird as no sane person will key that in and --- expect a glyph for that ligature plus the following character. Anyhow, --- as we need to deal with this, we do, but no guarantes are given. --- ---         latin modern       dejavu --- --- f+f       102 102             102 102 --- f+i       102 105             102 105 --- f+l       102 108             102 108 --- f+f+i                         102 102 105 --- f+f+l     102 102 108         102 102 108 --- ff+i    64256 105           64256 105 --- ff+l                        64256 108 --- --- As you can see here, latin modern is less complete than dejavu but --- in practice one will not notice it. --- --- The while loop is needed because we need to resolve for instance --- pseudo names like hyphen_hyphen to endash so in practice we end --- up with a bit too many definitions but the overhead is neglectable. --- --- Todo: if changed[first] or changed[second] then ... end - -local trace = false - -local function finalize_ligatures(tfmdata,ligatures) -    local nofligatures = #ligatures -    if nofligatures > 0 then -        local characters   = tfmdata.characters -        local descriptions = tfmdata.descriptions -        local resources    = tfmdata.resources -        local unicodes     = resources.unicodes -        local private      = resources.private -        local alldone      = false -        while not alldone do -            local done = 0 -            for i=1,nofligatures do -                local ligature = ligatures[i] -                if ligature then -                    local unicode, lookupdata = ligature[1], ligature[2] -                    if trace then -                        print("BUILDING",concat(lookupdata," "),unicode) -                    end -                    local size = #lookupdata -                    local firstcode = lookupdata[1] -- [2] -                    local firstdata = characters[firstcode] -                    local okay = false -                    if firstdata then -                        local firstname = "ctx_" .. firstcode -                        for i=1,size-1 do -- for i=2,size-1 do -                            local firstdata = characters[firstcode] -                            if not firstdata then -                                firstcode = private -                                if trace then -                                    print(" DEFINING",firstname,firstcode) -                                end -                                unicodes[firstname] = firstcode -                                firstdata = { intermediate = true, ligatures = { } } -                                characters[firstcode] = firstdata -                                descriptions[firstcode] = { name = firstname } -                                private = private + 1 -                            end -                            local target -                            local secondcode = lookupdata[i+1] -                            local secondname = firstname .. "_" .. secondcode -                            if i == size - 1 then -                                target = unicode -                                if not unicodes[secondname] then -                                    unicodes[secondname] = unicode -- map final ligature onto intermediates -                                end -                                okay = true -                            else -                                target = unicodes[secondname] -                                if not target then -                                    break -                                end -                            end -                            if trace then -                                print("CODES",firstname,firstcode,secondname,secondcode,target) -                            end -                            local firstligs = firstdata.ligatures -                            if firstligs then -                                firstligs[secondcode] = { char = target } -                            else -                                firstdata.ligatures = { [secondcode] = { char = target } } -                            end -                            firstcode = target -                            firstname = secondname -                        end -                    end -                    if okay then -                        ligatures[i] = false -                        done = done + 1 -                    end -                end -            end -            alldone = done == 0 -        end -        if trace then -            for k, v in next, characters do -                if v.ligatures then table.print(v,k) end -            end -        end -        tfmdata.resources.private = private -    end -end - -local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) -    local characters   = tfmdata.characters -    local descriptions = tfmdata.descriptions -    local resources    = tfmdata.resources -    local changed      = tfmdata.changed -    local unicodes     = resources.unicodes -    local lookuphash   = resources.lookuphash -    local lookuptypes  = resources.lookuptypes - -    local ligatures    = { } -    local alternate    = tonumber(value) -    local defaultalt   = otf.defaultbasealternate - -    local trace_singles      = trace_baseinit and trace_singles -    local trace_alternatives = trace_baseinit and trace_alternatives -    local trace_ligatures    = trace_baseinit and trace_ligatures - -    local actions      = { -        substitution = function(lookupdata,lookupname,description,unicode) -            if trace_singles then -                report_substitution(feature,lookupname,descriptions,unicode,lookupdata) -            end -            changed[unicode] = lookupdata -        end, -        alternate = function(lookupdata,lookupname,description,unicode) -            local replacement = lookupdata[alternate] -            if replacement then -                changed[unicode] = replacement -                if trace_alternatives then -                    report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal") -                end -            elseif defaultalt == "first" then -                replacement = lookupdata[1] -                changed[unicode] = replacement -                if trace_alternatives then -                    report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) -                end -            elseif defaultalt == "last" then -                replacement = lookupdata[#data] -                if trace_alternatives then -                    report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) -                end -            else -                if trace_alternatives then -                    report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown") -                end -            end -        end, -        ligature = function(lookupdata,lookupname,description,unicode) -            if trace_ligatures then -                report_ligature(feature,lookupname,descriptions,unicode,lookupdata) -            end -            ligatures[#ligatures+1] = { unicode, lookupdata } -        end, -    } - -    for unicode, character in next, characters do -        local description = descriptions[unicode] -        local lookups = description.slookups -        if lookups then -            for l=1,#lookuplist do -                local lookupname = lookuplist[l] -                local lookupdata = lookups[lookupname] -                if lookupdata then -                    local lookuptype = lookuptypes[lookupname] -                    local action = actions[lookuptype] -                    if action then -                        action(lookupdata,lookupname,description,unicode) -                    end -                end -            end -        end -        local lookups = description.mlookups -        if lookups then -            for l=1,#lookuplist do -                local lookupname = lookuplist[l] -                local lookuplist = lookups[lookupname] -                if lookuplist then -                    local lookuptype = lookuptypes[lookupname] -                    local action = actions[lookuptype] -                    if action then -                        for i=1,#lookuplist do -                            action(lookuplist[i],lookupname,description,unicode) -                        end -                    end -                end -            end -        end -    end - -    finalize_ligatures(tfmdata,ligatures) -end - -local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) -- todo what kind of kerns, currently all -    local characters   = tfmdata.characters -    local descriptions = tfmdata.descriptions -    local resources    = tfmdata.resources -    local unicodes     = resources.unicodes -    local sharedkerns  = { } -    local traceindeed  = trace_baseinit and trace_kerns -    for unicode, character in next, characters do -        local description = descriptions[unicode] -        local rawkerns = description.kerns -- shared -        if rawkerns then -            local s = sharedkerns[rawkerns] -            if s == false then -                -- skip -            elseif s then -                character.kerns = s -            else -                local newkerns = character.kerns -                local done     = false -                for l=1,#lookuplist do -                    local lookup = lookuplist[l] -                    local kerns  = rawkerns[lookup] -                    if kerns then -                        for otherunicode, value in next, kerns do -                            if value == 0 then -                                -- maybe no 0 test here -                            elseif not newkerns then -                                newkerns = { [otherunicode] = value } -                                done = true -                                if traceindeed then -                                    report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), -                                        gref(descriptions,unicode),gref(descriptions,otherunicode),value) -                                end -                            elseif not newkerns[otherunicode] then -- first wins -                                newkerns[otherunicode] = value -                                done = true -                                if traceindeed then -                                    report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), -                                        gref(descriptions,unicode),gref(descriptions,otherunicode),value) -                                end -                            end -                        end -                    end -                end -                if done then -                    sharedkerns[rawkerns] = newkerns -                    character.kerns       = newkerns -- no empty assignments -                else -                    sharedkerns[rawkerns] = false -                end -            end -        end -    end -end - -basemethods.independent = { -    preparesubstitutions = preparesubstitutions, -    preparepositionings  = preparepositionings, -} - -local function makefake(tfmdata,name,present) -    local resources = tfmdata.resources -    local private   = resources.private -    local character = { intermediate = true, ligatures = { } } -    resources.unicodes[name] = private -    tfmdata.characters[private] = character -    tfmdata.descriptions[private] = { name = name } -    resources.private = private + 1 -    present[name] = private -    return character -end - -local function make_1(present,tree,name) -    for k, v in next, tree do -        if k == "ligature" then -            present[name] = v -        else -            make_1(present,v,name .. "_" .. k) -        end -    end -end - -local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done,lookupname) -    for k, v in next, tree do -        if k == "ligature" then -            local character = characters[preceding] -            if not character then -                if trace_baseinit then -                    report_prepare("weird ligature in lookup %s: U+%05X (%s), preceding U+%05X (%s)",lookupname,v,utfchar(v),preceding,utfchar(preceding)) -                end -                character = makefake(tfmdata,name,present) -            end -            local ligatures = character.ligatures -            if ligatures then -                ligatures[unicode] = { char = v } -            else -                character.ligatures = { [unicode] = { char = v } } -            end -            if done then -                local d = done[lookupname] -                if not d then -                    done[lookupname] = { "dummy", v } -                else -                    d[#d+1] = v -                end -            end -        else -            local code = present[name] or unicode -            local name = name .. "_" .. k -            make_2(present,tfmdata,characters,v,name,code,k,done,lookupname) -        end -    end -end - -local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) -    local characters   = tfmdata.characters -    local descriptions = tfmdata.descriptions -    local resources    = tfmdata.resources -    local changed      = tfmdata.changed -    local lookuphash   = resources.lookuphash -    local lookuptypes  = resources.lookuptypes - -    local ligatures    = { } -    local alternate    = tonumber(value) -    local defaultalt   = otf.defaultbasealternate - -    local trace_singles      = trace_baseinit and trace_singles -    local trace_alternatives = trace_baseinit and trace_alternatives -    local trace_ligatures    = trace_baseinit and trace_ligatures - -    for l=1,#lookuplist do -        local lookupname = lookuplist[l] -        local lookupdata = lookuphash[lookupname] -        local lookuptype = lookuptypes[lookupname] -        for unicode, data in next, lookupdata do -            if lookuptype == "substitution" then -                if trace_singles then -                    report_substitution(feature,lookupname,descriptions,unicode,data) -                end -                changed[unicode] = data -            elseif lookuptype == "alternate" then -                local replacement = data[alternate] -                if replacement then -                    changed[unicode] = replacement -                    if trace_alternatives then -                        report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal") -                    end -                elseif defaultalt == "first" then -                    replacement = data[1] -                    changed[unicode] = replacement -                    if trace_alternatives then -                        report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) -                    end -                elseif defaultalt == "last" then -                    replacement = data[#data] -                    if trace_alternatives then -                        report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt) -                    end -                else -                    if trace_alternatives then -                        report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown") -                    end -                end -            elseif lookuptype == "ligature" then -                ligatures[#ligatures+1] = { unicode, data, lookupname } -                if trace_ligatures then -                    report_ligature(feature,lookupname,descriptions,unicode,data) -                end -            end -        end -    end - -    local nofligatures = #ligatures - -    if nofligatures > 0 then - -        local characters = tfmdata.characters -        local present    = { } -        local done       = trace_baseinit and trace_ligatures and { } - -        for i=1,nofligatures do -            local ligature = ligatures[i] -            local unicode, tree = ligature[1], ligature[2] -            make_1(present,tree,"ctx_"..unicode) -        end - -        for i=1,nofligatures do -            local ligature = ligatures[i] -            local unicode, tree, lookupname = ligature[1], ligature[2], ligature[3] -            make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,lookupname) -        end - -    end - -end - -local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) -    local characters   = tfmdata.characters -    local descriptions = tfmdata.descriptions -    local resources    = tfmdata.resources -    local lookuphash   = resources.lookuphash -    local traceindeed  = trace_baseinit and trace_kerns - -    -- check out this sharedkerns trickery - -    for l=1,#lookuplist do -        local lookupname = lookuplist[l] -        local lookupdata = lookuphash[lookupname] -        for unicode, data in next, lookupdata do -            local character = characters[unicode] -            local kerns = character.kerns -            if not kerns then -                kerns = { } -                character.kerns = kerns -            end -            if traceindeed then -                for otherunicode, kern in next, data do -                    if not kerns[otherunicode] and kern ~= 0 then -                        kerns[otherunicode] = kern -                        report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), -                            gref(descriptions,unicode),gref(descriptions,otherunicode),kern) -                    end -                end -            else -                for otherunicode, kern in next, data do -                    if not kerns[otherunicode] and kern ~= 0 then -                        kerns[otherunicode] = kern -                    end -                end -            end -        end -    end - -end - -local function initializehashes(tfmdata) -    nodeinitializers.features(tfmdata) -end - -basemethods.shared = { -    initializehashes     = initializehashes, -    preparesubstitutions = preparesubstitutions, -    preparepositionings  = preparepositionings, -} - -basemethod = "independent" - -local function featuresinitializer(tfmdata,value) -    if true then -- value then -        local t = trace_preparing and os.clock() -        local features = tfmdata.shared.features -        if features then -            applybasemethod("initializehashes",tfmdata) -            local collectlookups    = otf.collectlookups -            local rawdata           = tfmdata.shared.rawdata -            local properties        = tfmdata.properties -            local script            = properties.script -            local language          = properties.language -            local basesubstitutions = rawdata.resources.features.gsub -            local basepositionings  = rawdata.resources.features.gpos -            if basesubstitutions then -                for feature, data in next, basesubstitutions do -                    local value = features[feature] -                    if value then -                        local validlookups, lookuplist = collectlookups(rawdata,feature,script,language) -                        if validlookups then -                            applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist) -                            registerbasefeature(feature,value) -                        end -                    end -                end -            end -            if basepositions then -                for feature, data in next, basepositions do -                    local value = features[feature] -                    if value then -                        local validlookups, lookuplist = collectlookups(rawdata,feature,script,language) -                        if validlookups then -                            applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist) -                            registerbasefeature(feature,value) -                        end -                    end -                end -            end -            registerbasehash(tfmdata) -        end -        if trace_preparing then -            report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.properties.fullname or "?") -        end -    end -end - -registerotffeature { -    name         = "features", -    description  = "features", -    default      = true, -    initializers = { - --     position = 1, -- after setscript (temp hack ... we need to force script / language to 1 -        base     = featuresinitializer, -    } -} - --- independent : collect lookups independently (takes more runtime ... neglectable) --- shared      : shares lookups with node mode (takes more memory unless also a node mode variant is used ... noticeable) - -directives.register("fonts.otf.loader.basemethod", function(v) -    if basemethods[v] then -        basemethod = v -    end -end) diff --git a/otfl-font-otf.lua b/otfl-font-otf.lua deleted file mode 100644 index e1339ae..0000000 --- a/otfl-font-otf.lua +++ /dev/null @@ -1,2080 +0,0 @@ -if not modules then modules = { } end modules ['font-otf'] = { -    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" -} - --- langs -> languages enz --- anchor_classes vs kernclasses --- modification/creationtime in subfont is runtime dus zinloos --- to_table -> totable --- ascent descent - --- more checking against low level calls of functions - -local utf = unicode.utf8 - -local utfbyte = utf.byte -local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip -local type, next, tonumber, tostring = type, next, tonumber, tostring -local abs = math.abs -local getn = table.getn -local lpegmatch = lpeg.match -local reversed, concat, remove = table.reversed, table.concat, table.remove -local ioflush = io.flush -local fastcopy, tohash, derivetable = table.fastcopy, table.tohash, table.derive - -local allocate           = utilities.storage.allocate -local registertracker    = trackers.register -local registerdirective  = directives.register -local starttiming        = statistics.starttiming -local stoptiming         = statistics.stoptiming -local elapsedtime        = statistics.elapsedtime -local findbinfile        = resolvers.findbinfile - -local trace_private      = false  registertracker("otf.private",    function(v) trace_private   = v end) -local trace_loading      = false  registertracker("otf.loading",    function(v) trace_loading   = v end) -local trace_features     = false  registertracker("otf.features",   function(v) trace_features  = v end) -local trace_dynamics     = false  registertracker("otf.dynamics",   function(v) trace_dynamics  = v end) -local trace_sequences    = false  registertracker("otf.sequences",  function(v) trace_sequences = v end) -local trace_markwidth    = false  registertracker("otf.markwidth",  function(v) trace_markwidth = v end) -local trace_defining     = false  registertracker("fonts.defining", function(v) trace_defining  = v end) - -local report_otf         = logs.reporter("fonts","otf loading") - -local fonts              = fonts -local otf                = fonts.handlers.otf - -otf.glists               = { "gsub", "gpos" } - -otf.version              = 2.737 -- beware: also sync font-mis.lua -otf.cache                = containers.define("fonts", "otf", otf.version, true) - -local fontdata           = fonts.hashes.identifiers -local chardata           = characters and characters.data -- not used - -local otffeatures        = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register - -local enhancers          = allocate() -otf.enhancers            = enhancers -local patches            = { } -enhancers.patches        = patches - -local definers           = fonts.definers -local readers            = fonts.readers -local constructors       = fonts.constructors - -local forceload          = false -local cleanup            = 0     -- mk: 0=885M 1=765M 2=735M (regular run 730M) -local usemetatables      = false -- .4 slower on mk but 30 M less mem so we might change the default -- will be directive -local packdata           = true -local syncspace          = true -local forcenotdef        = false - -local wildcard           = "*" -local default            = "dflt" - -local fontloaderfields   = fontloader.fields -local mainfields         = nil -local glyphfields        = nil -- not used yet - -registerdirective("fonts.otf.loader.cleanup",       function(v) cleanup       = tonumber(v) or (v and 1) or 0 end) -registerdirective("fonts.otf.loader.force",         function(v) forceload     = v end) -registerdirective("fonts.otf.loader.usemetatables", function(v) usemetatables = v end) -registerdirective("fonts.otf.loader.pack",          function(v) packdata      = v end) -registerdirective("fonts.otf.loader.syncspace",     function(v) syncspace     = v end) -registerdirective("fonts.otf.loader.forcenotdef",   function(v) forcenotdef   = v end) - -local function load_featurefile(raw,featurefile) -    if featurefile and featurefile ~= "" then -        if trace_loading then -            report_otf("featurefile: %s", featurefile) -        end -        fontloader.apply_featurefile(raw, featurefile) -    end -end - -local function showfeatureorder(rawdata,filename) -    local sequences = rawdata.resources.sequences -    if sequences and #sequences > 0 then -        if trace_loading then -            report_otf("font %s has %s sequences",filename,#sequences) -            report_otf(" ") -        end -        for nos=1,#sequences do -            local sequence  = sequences[nos] -            local typ       = sequence.type      or "no-type" -            local name      = sequence.name      or "no-name" -            local subtables = sequence.subtables or { "no-subtables" } -            local features  = sequence.features -            if trace_loading then -                report_otf("%3i  %-15s  %-20s  [%s]",nos,name,typ,concat(subtables,",")) -            end -            if features then -                for feature, scripts in next, features do -                    local tt = { } -                    if type(scripts) == "table" then -                        for script, languages in next, scripts do -                            local ttt = { } -                            for language, _ in next, languages do -                                ttt[#ttt+1] = language -                            end -                            tt[#tt+1] = format("[%s: %s]",script,concat(ttt," ")) -                        end -                        if trace_loading then -                            report_otf("       %s: %s",feature,concat(tt," ")) -                        end -                    else -                        if trace_loading then -                            report_otf("       %s: %s",feature,tostring(scripts)) -                        end -                    end -                end -            end -        end -        if trace_loading then -            report_otf("\n") -        end -    elseif trace_loading then -        report_otf("font %s has no sequences",filename) -    end -end - ---[[ldx-- -<p>We start with a lot of tables and related functions.</p> ---ldx]]-- - -local valid_fields = table.tohash { - -- "anchor_classes", -    "ascent", - -- "cache_version", -    "cidinfo", -    "copyright", - -- "creationtime", -    "descent", -    "design_range_bottom", -    "design_range_top", -    "design_size", -    "encodingchanged", -    "extrema_bound", -    "familyname", -    "fontname", -    "fontname", -    "fontstyle_id", -    "fontstyle_name", -    "fullname", - -- "glyphs", -    "hasvmetrics", - -- "head_optimized_for_cleartype", -    "horiz_base", -    "issans", -    "isserif", -    "italicangle", - -- "kerns", - -- "lookups", -    "macstyle", - -- "modificationtime", -    "onlybitmaps", -    "origname", -    "os2_version", -    "pfminfo", - -- "private", -    "serifcheck", -    "sfd_version", - -- "size", -    "strokedfont", -    "strokewidth", - -- "subfonts", -    "table_version", - -- "tables", - -- "ttf_tab_saved", -    "ttf_tables", -    "uni_interp", -    "uniqueid", -    "units_per_em", -    "upos", -    "use_typo_metrics", -    "uwidth", - -- "validation_state", -    "version", -    "vert_base", -    "weight", -    "weight_width_slope_only", - -- "xuid", -} - -local ordered_enhancers = { -    "prepare tables", -    "prepare glyphs", -    "prepare lookups", - -    "analyze glyphs", -    "analyze math", - -    "prepare tounicode", -- maybe merge with prepare - -    "reorganize lookups", -    "reorganize mark classes", -    "reorganize anchor classes", - -    "reorganize glyph kerns", -    "reorganize glyph lookups", -    "reorganize glyph anchors", - -    "merge kern classes", - -    "reorganize features", -    "reorganize subtables", - -    "check glyphs", -    "check metadata", -    "check extra features", -- after metadata - -    "add duplicates", -    "check encoding", - -    "cleanup tables", -} - ---[[ldx-- -<p>Here we go.</p> ---ldx]]-- - -local actions  = allocate() -local before   = allocate() -local after    = allocate() - -patches.before = before -patches.after  = after - -local function enhance(name,data,filename,raw) -    local enhancer = actions[name] -    if enhancer then -        if trace_loading then -            report_otf("enhance: %s (%s)",name,filename) -            ioflush() -        end -        enhancer(data,filename,raw) -    elseif trace_loading then -     -- report_otf("enhance: %s is undefined",name) -    end -end - -function enhancers.apply(data,filename,raw) -    local basename = file.basename(lower(filename)) -    if trace_loading then -        report_otf("start enhancing: %s",filename) -    end -    ioflush() -- we want instant messages -    for e=1,#ordered_enhancers do -        local enhancer = ordered_enhancers[e] -        local b = before[enhancer] -        if b then -            for pattern, action in next, b do -                if find(basename,pattern) then -                    action(data,filename,raw) -                end -            end -        end -        enhance(enhancer,data,filename,raw) -        local a = after[enhancer] -        if a then -            for pattern, action in next, a do -                if find(basename,pattern) then -                    action(data,filename,raw) -                end -            end -        end -        ioflush() -- we want instant messages -    end -    if trace_loading then -        report_otf("stop enhancing") -    end -    ioflush() -- we want instant messages -end - --- patches.register("before","migrate metadata","cambria",function() end) - -function patches.register(what,where,pattern,action) -    local pw = patches[what] -    if pw then -        local ww = pw[where] -        if ww then -            ww[pattern] = action -        else -            pw[where] = { [pattern] = action} -        end -    end -end - -function patches.report(fmt,...) -    if trace_loading then -        report_otf("patching: " ..fmt,...) -    end -end - -function enhancers.register(what,action) -- only already registered can be overloaded -    actions[what] = action -end - -function otf.load(filename,format,sub,featurefile) -    local name = file.basename(file.removesuffix(filename)) -    local attr = lfs.attributes(filename) -    local size = attr and attr.size or 0 -    local time = attr and attr.modification or 0 -    if featurefile then -        name = name .. "@" .. file.removesuffix(file.basename(featurefile)) -    end -    if sub == "" then -        sub = false -    end -    local hash = name -    if sub then -        hash = hash .. "-" .. sub -    end -    hash = containers.cleanname(hash) -    local featurefiles -    if featurefile then -        featurefiles = { } -        for s in gmatch(featurefile,"[^,]+") do -            local name = resolvers.findfile(file.addsuffix(s,'fea'),'fea') or "" -            if name == "" then -                report_otf("loading: no featurefile '%s'",s) -            else -                local attr = lfs.attributes(name) -                featurefiles[#featurefiles+1] = { -                    name = name, -                    size = attr and attr.size or 0, -                    time = attr and attr.modification or 0, -                } -            end -        end -        if #featurefiles == 0 then -            featurefiles = nil -        end -    end -    local data = containers.read(otf.cache,hash) -    local reload = not data or data.size ~= size or data.time ~= time -    if forceload then -        report_otf("loading: forced reload due to hard coded flag") -        reload = true -    end -    if not reload then -        local featuredata = data.featuredata -        if featurefiles then -            if not featuredata or #featuredata ~= #featurefiles then -                reload = true -            else -                for i=1,#featurefiles do -                    local fi, fd = featurefiles[i], featuredata[i] -                    if fi.name ~= fd.name or fi.size ~= fd.size or fi.time ~= fd.time then -                        reload = true -                        break -                    end -                end -            end -        elseif featuredata then -            reload = true -        end -        if reload then -           report_otf("loading: forced reload due to changed featurefile specification: %s",featurefile or "--") -        end -     end -     if reload then -        report_otf("loading: %s (hash: %s)",filename,hash) -        local fontdata, messages -        if sub then -            fontdata, messages = fontloader.open(filename,sub) -        else -            fontdata, messages = fontloader.open(filename) -        end -        if fontdata then -            mainfields = mainfields or (fontloaderfields and fontloaderfields(fontdata)) -        end -        if trace_loading and messages and #messages > 0 then -            if type(messages) == "string" then -                report_otf("warning: %s",messages) -            else -                for m=1,#messages do -                    report_otf("warning: %s",tostring(messages[m])) -                end -            end -        else -            report_otf("font loaded okay") -        end -        if fontdata then -            if featurefiles then -                for i=1,#featurefiles do -                    load_featurefile(fontdata,featurefiles[i].name) -                end -            end -            local unicodes = { -                -- names to unicodes -            } -            local splitter = lpeg.splitter(" ",unicodes) -            data = { -                size        = size, -                time        = time, -                format      = format, -                featuredata = featurefiles, -                resources   = { -                    filename = resolvers.unresolve(filename), -- no shortcut -                    version  = otf.version, -                    creator  = "context mkiv", -                    unicodes = unicodes, -                    indices  = { -                        -- index to unicodes -                    }, -                    duplicates = { -                        -- alternative unicodes -                    }, -                    variants = { -                        -- alternative unicodes (variants) -                    }, -                    lookuptypes = { -                    }, -                }, -                metadata    = { -                    -- raw metadata, not to be used -                }, -                properties   = { -                    -- normalized metadata -                }, -                descriptions = { -                }, -                goodies = { -                }, -                helpers = { -                    tounicodelist  = splitter, -                    tounicodetable = lpeg.Ct(splitter), -                }, -            } -            starttiming(data) -            report_otf("file size: %s", size) -            enhancers.apply(data,filename,fontdata) -            if packdata then -                if cleanup > 0 then -                    collectgarbage("collect") ---~ lua.collectgarbage() -                end -                enhance("pack",data,filename,nil) -            end -            report_otf("saving in cache: %s",filename) -            data = containers.write(otf.cache, hash, data) -            if cleanup > 1 then -                collectgarbage("collect") ---~ lua.collectgarbage() -            end -            stoptiming(data) -            if elapsedtime then -- not in generic -                report_otf("preprocessing and caching took %s seconds",elapsedtime(data)) -            end -            fontloader.close(fontdata) -- free memory -            if cleanup > 3 then -                collectgarbage("collect") ---~ lua.collectgarbage() -            end -            data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one -            if cleanup > 2 then -                collectgarbage("collect") ---~ lua.collectgarbage() -            end -        else -            data = nil -            report_otf("loading failed (file read error)") -        end -    end -    if data then -        if trace_defining then -            report_otf("loading from cache: %s",hash) -        end -        enhance("unpack",data,filename,nil,false) -        enhance("add dimensions",data,filename,nil,false) -        if trace_sequences then -            showfeatureorder(data,filename) -        end -    end -    return data -end - -local mt = { -    __index = function(t,k) -- maybe set it -        if k == "height" then -            local ht = t.boundingbox[4] -            return ht < 0 and 0 or ht -        elseif k == "depth" then -            local dp = -t.boundingbox[2] -            return dp < 0 and 0 or dp -        elseif k == "width" then -            return 0 -        elseif k == "name" then -- or maybe uni* -            return forcenotdef and ".notdef" -        end -    end -} - -actions["prepare tables"] = function(data,filename,raw) -    data.properties.hasitalics = false -end - -actions["add dimensions"] = function(data,filename) -    -- todo: forget about the width if it's the defaultwidth (saves mem) -    -- we could also build the marks hash here (instead of storing it) -    if data then -        local descriptions  = data.descriptions -        local resources     = data.resources -        local defaultwidth  = resources.defaultwidth  or 0 -        local defaultheight = resources.defaultheight or 0 -        local defaultdepth  = resources.defaultdepth  or 0 -        if usemetatables then -            for _, d in next, descriptions do -                local wd = d.width -                if not wd then -                    d.width = defaultwidth -                elseif trace_markwidth and wd ~= 0 and d.class == "mark" then -                    report_otf("mark with width %s (%s) in %s",wd,d.name or "<noname>",file.basename(filename)) -                 -- d.width  = -wd -                end -                setmetatable(d,mt) -            end -        else -            for _, d in next, descriptions do -                local bb, wd = d.boundingbox, d.width -                if not wd then -                    d.width = defaultwidth -                elseif trace_markwidth and wd ~= 0 and d.class == "mark" then -                    report_otf("mark with width %s (%s) in %s",wd,d.name or "<noname>",file.basename(filename)) -                 -- d.width  = -wd -                end -             -- if forcenotdef and not d.name then -             --     d.name = ".notdef" -             -- end -                if bb then -                    local ht, dp = bb[4], -bb[2] -                    if ht == 0 or ht < 0 then -                        -- not set -                    else -                        d.height = ht -                    end -                    if dp == 0 or dp < 0 then -                        -- not set -                    else -                        d.depth  = dp -                    end -                end -            end -        end -    end -end - -local function somecopy(old) -- fast one -    if old then -        local new = { } -        if type(old) == "table" then -            for k, v in next, old do -                if k == "glyphs" then -                    -- skip -                elseif type(v) == "table" then -                    new[k] = somecopy(v) -                else -                    new[k] = v -                end -            end -        else -            for i=1,#mainfields do -                local k = mainfields[i] -                local v = old[k] -                if k == "glyphs" then -                    -- skip -                elseif type(v) == "table" then -                    new[k] = somecopy(v) -                else -                    new[k] = v -                end -            end -        end -        return new -    else -        return { } -    end -end - --- not setting hasitalics and class (when nil) during --- table cronstruction can save some mem - -actions["prepare glyphs"] = function(data,filename,raw) -    local rawglyphs    = raw.glyphs -    local rawsubfonts  = raw.subfonts -    local rawcidinfo   = raw.cidinfo -    local criterium    = constructors.privateoffset -    local private      = criterium -    local resources    = data.resources -    local metadata     = data.metadata -    local properties   = data.properties -    local descriptions = data.descriptions -    local unicodes     = resources.unicodes -- name to unicode -    local indices      = resources.indices  -- index to unicode -    local duplicates   = resources.duplicates -    local variants     = resources.variants - -    if rawsubfonts then - -        metadata.subfonts  = { } -        properties.cidinfo = rawcidinfo - -        if rawcidinfo.registry then -            local cidmap = fonts.cid.getmap(rawcidinfo) -            if cidmap then -                rawcidinfo.usedname = cidmap.usedname -                local nofnames, nofunicodes = 0, 0 -                local cidunicodes, cidnames = cidmap.unicodes, cidmap.names -                for cidindex=1,#rawsubfonts do -                    local subfont   = rawsubfonts[cidindex] -                    local cidglyphs = subfont.glyphs -                    metadata.subfonts[cidindex] = somecopy(subfont) -                    for index=0,subfont.glyphcnt-1 do -- we could take the previous glyphcnt instead of 0 -                        local glyph = cidglyphs[index] -                        if glyph then -                            local unicode = glyph.unicode -                            local name    = glyph.name or cidnames[index] -                            if not unicode or unicode == -1 or unicode >= criterium then -                                unicode = cidunicodes[index] -                            end -                            if not unicode or unicode == -1 or unicode >= criterium then -                                if not name then -                                    name = format("u%06X",private) -                                end -                                unicode = private -                                unicodes[name] = private -                                if trace_private then -                                    report_otf("enhance: glyph %s at index 0x%04X is moved to private unicode slot U+%05X",name,index,private) -                                end -                                private = private + 1 -                                nofnames = nofnames + 1 -                            else -                                if not name then -                                    name = format("u%06X",unicode) -                                end -                                unicodes[name] = unicode -                                nofunicodes = nofunicodes + 1 -                            end -                            indices[index] = unicode -- each index is unique (at least now) - -                            local description = { -                             -- width       = glyph.width, -                                boundingbox = glyph.boundingbox, -                                name        = glyph.name or name or "unknown", -- uniXXXX -                                cidindex    = cidindex, -                                index       = index, -                                glyph       = glyph, -                            } - -                            descriptions[unicode] = description -                        else -                         -- report_otf("potential problem: glyph 0x%04X is used but empty",index) -                        end -                    end -                end -                if trace_loading then -                    report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) -                end -            elseif trace_loading then -                report_otf("unable to remap cid font, missing cid file for %s",filename) -            end -        elseif trace_loading then -            report_otf("font %s has no glyphs",filename) -        end - -    else - -        for index=0,raw.glyphcnt-1 do -- not raw.glyphmax-1 (as that will crash) -            local glyph = rawglyphs[index] -            if glyph then -                local unicode = glyph.unicode -                local name    = glyph.name -                if not unicode or unicode == -1 or unicode >= criterium then -                    unicode = private -                    unicodes[name] = private -                    if trace_private then -                        report_otf("enhance: glyph %s at index 0x%04X is moved to private unicode slot U+%05X",name,index,private) -                    end -                    private = private + 1 -                else -                    unicodes[name] = unicode -                end -                indices[index] = unicode -                if not name then -                    name = format("u%06X",unicode) -                end -                descriptions[unicode] = { -                 -- width       = glyph.width, -                    boundingbox = glyph.boundingbox, -                    name        = name, -                    index       = index, -                    glyph       = glyph, -                } -                local altuni = glyph.altuni -                if altuni then -                    local d -                    for i=1,#altuni do -                        local a = altuni[i] -                        local u = a.unicode -                        local v = a.variant -                        if v then -                            local vv = variants[v] -                            if vv then -                                vv[u] = unicode -                            else -- xits-math has some: -                                vv = { [u] = unicode } -                                variants[v] = vv -                            end -                        elseif d then -                            d[#d+1] = u -                        else -                            d = { u } -                        end -                    end -                    if d then -                        duplicates[unicode] = d -                    end -                end -            else -                report_otf("potential problem: glyph 0x%04X is used but empty",index) -            end -        end - -    end - -    resources.private = private - -end - --- the next one is still messy but will get better when we have --- flattened map/enc tables in the font loader - -actions["check encoding"] = function(data,filename,raw) -    local descriptions = data.descriptions -    local resources    = data.resources -    local properties   = data.properties -    local unicodes     = resources.unicodes -- name to unicode -    local indices      = resources.indices  -- index to unicodes - -    -- begin of messy (not needed when cidmap) - -    local mapdata        = raw.map or { } -    local unicodetoindex = mapdata and mapdata.map or { } - -- local encname        = lower(data.enc_name or raw.enc_name or mapdata.enc_name or "") -    local encname        = lower(data.enc_name or mapdata.enc_name or "") -    local criterium      = 0xFFFF -- for instance cambria has a lot of mess up there - -    -- end of messy - -    if find(encname,"unicode") then -- unicodebmp, unicodefull, ... -        if trace_loading then -            report_otf("checking embedded unicode map '%s'",encname) -        end -        for unicode, index in next, unicodetoindex do -- altuni already covers this -            if unicode <= criterium and not descriptions[unicode] then -                local parent = indices[index] -- why nil? -                if parent then -                    report_otf("weird, unicode U+%05X points to U+%05X with index 0x%04X",unicode,parent,index) -                else -                    report_otf("weird, unicode U+%05X points to nowhere with index 0x%04X",unicode,index) -                end -            end -        end -    elseif properties.cidinfo then -        report_otf("warning: no unicode map, used cidmap '%s'",properties.cidinfo.usedname or "?") -    else -        report_otf("warning: non unicode map '%s', only using glyph unicode data",encname or "whatever") -    end - -    if mapdata then -        mapdata.map = { } -- clear some memory -    end -end - --- for the moment we assume that a fotn with lookups will not use --- altuni so we stick to kerns only - -actions["add duplicates"] = function(data,filename,raw) -    local descriptions = data.descriptions -    local resources    = data.resources -    local properties   = data.properties -    local unicodes     = resources.unicodes -- name to unicode -    local indices      = resources.indices  -- index to unicodes -    local duplicates   = resources.duplicates - -    for unicode, d in next, duplicates do -        for i=1,#d do -            local u = d[i] -            if not descriptions[u] then -                local description = descriptions[unicode] -                local duplicate = table.copy(description) -- else packing problem -                duplicate.comment = format("copy of U+%05X", unicode) -                descriptions[u] = duplicate -                local n = 0 -                for _, description in next, descriptions do -                    if kerns then -                        local kerns = description.kerns -                        for _, k in next, kerns do -                            local ku = k[unicode] -                            if ku then -                                k[u] = ku -                                n = n + 1 -                            end -                        end -                    end -                    -- todo: lookups etc -                end -                if trace_loading then -                    report_otf("duplicating U+%05X to U+%05X with index 0x%04X (%s kerns)",unicode,u,description.index,n) -                end -            end -        end -    end -end - --- class      : nil base mark ligature component (maybe we don't need it in description) --- boundingbox: split into ht/dp takes more memory (larger tables and less sharing) - -actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this in the previous -    local descriptions      = data.descriptions -    local resources         = data.resources -    local metadata          = data.metadata -    local properties        = data.properties -    local hasitalics        = false -    local widths            = { } -    local marks             = { } -- always present (saves checking) -    for unicode, description in next, descriptions do -        local glyph = description.glyph -        local italic = glyph.italic_correction -        if not italic then -            -- skip -        elseif italic == 0 then -            -- skip -        else -            description.italic = italic -            hasitalics = true -        end -        local width = glyph.width -        widths[width] = (widths[width] or 0) + 1 -        local class = glyph.class -        if class then -            if class == "mark" then -                marks[unicode] = true -            end -            description.class = class -        end -    end -    -- flag italic -    properties.hasitalics = hasitalics -    -- flag marks -    resources.marks = marks -    -- share most common width for cjk fonts -    local wd, most = 0, 1 -    for k,v in next, widths do -        if v > most then -            wd, most = k, v -        end -    end -    if most > 1000 then -- maybe 500 -        if trace_loading then -            report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most) -        end -        for unicode, description in next, descriptions do -            if description.width == wd then -             -- description.width = nil -            else -                description.width = description.glyph.width -            end -        end -        resources.defaultwidth = wd -    else -        for unicode, description in next, descriptions do -            description.width = description.glyph.width -        end -    end -end - -actions["reorganize mark classes"] = function(data,filename,raw) -    local mark_classes = raw.mark_classes -    if mark_classes then -        local resources       = data.resources -        local unicodes        = resources.unicodes -        local markclasses     = { } -        resources.markclasses = markclasses -- reversed -        for name, class in next, mark_classes do -            local t = { } -            for s in gmatch(class,"[^ ]+") do -                t[unicodes[s]] = true -            end -            markclasses[name] = t -        end -    end -end - -actions["reorganize features"] = function(data,filename,raw) -- combine with other -    local features = { } -    data.resources.features = features -    for k, what in next, otf.glists do -        local dw = raw[what] -        if dw then -            local f = { } -            features[what] = f -            for i=1,#dw do -                local d= dw[i] -                local dfeatures = d.features -                if dfeatures then -                    for i=1,#dfeatures do -                        local df = dfeatures[i] -                        local tag = strip(lower(df.tag)) -                        local ft = f[tag] -                        if not ft then -                            ft = { } -                            f[tag] = ft -                        end -                        local dscripts = df.scripts -                        for i=1,#dscripts do -                            local d = dscripts[i] -                            local languages = d.langs -                            local script = strip(lower(d.script)) -                            local fts = ft[script] if not fts then fts = {} ft[script] = fts end -                            for i=1,#languages do -                                fts[strip(lower(languages[i]))] = true -                            end -                        end -                    end -                end -            end -        end -    end -end - -actions["reorganize anchor classes"] = function(data,filename,raw) -    local resources            = data.resources -    local anchor_to_lookup     = { } -    local lookup_to_anchor     = { } -    resources.anchor_to_lookup = anchor_to_lookup -    resources.lookup_to_anchor = lookup_to_anchor -    local classes              = raw.anchor_classes -- anchor classes not in final table -    if classes then -        for c=1,#classes do -            local class   = classes[c] -            local anchor  = class.name -            local lookups = class.lookup -            if type(lookups) ~= "table" then -                lookups = { lookups } -            end -            local a = anchor_to_lookup[anchor] -            if not a then -                a = { } -                anchor_to_lookup[anchor] = a -            end -            for l=1,#lookups do -                local lookup = lookups[l] -                local l = lookup_to_anchor[lookup] -                if l then -                    l[anchor] = true -                else -                    l = { [anchor] = true } -                    lookup_to_anchor[lookup] = l -                end -                a[lookup] = true -            end -        end -    end -end - -actions["prepare tounicode"] = function(data,filename,raw) -    fonts.mappings.addtounicode(data,filename) -end - -local g_directions = { -    gsub_contextchain        =  1, -    gpos_contextchain        =  1, - -- gsub_context             =  1, - -- gpos_context             =  1, -    gsub_reversecontextchain = -1, -    gpos_reversecontextchain = -1, -} - --- Research by Khaled Hosny has demonstrated that the font loader merges --- regular and AAT features and that these can interfere (especially because --- we dropped checking for valid features elsewhere. So, we just check for --- the special flag and drop the feature if such a tag is found. - -local function supported(features) -    for i=1,#features do -        if features[i].ismac then -            return false -        end -    end -    return true -end - -actions["reorganize subtables"] = function(data,filename,raw) -    local resources       = data.resources -    local sequences       = { } -    local lookups         = { } -    local chainedfeatures = { } -    resources.sequences   = sequences -    resources.lookups     = lookups -    for _, what in next, otf.glists do -        local dw = raw[what] -        if dw then -            for k=1,#dw do -                local gk = dw[k] -                local features = gk.features ---              if features and supported(features) then -                if not features or supported(features) then -- not always features ! -                    local typ = gk.type -                    local chain = g_directions[typ] or 0 -                    local subtables = gk.subtables -                    if subtables then -                        local t = { } -                        for s=1,#subtables do -                            t[s] = subtables[s].name -                        end -                        subtables = t -                    end -                    local flags, markclass = gk.flags, nil -                    if flags then -                        local t = { -- forcing false packs nicer -                            (flags.ignorecombiningmarks and "mark")     or false, -                            (flags.ignoreligatures      and "ligature") or false, -                            (flags.ignorebaseglyphs     and "base")     or false, -                             flags.r2l                                  or false, -                        } -                        markclass = flags.mark_class -                        if markclass then -                            markclass = resources.markclasses[markclass] -                        end -                        flags = t -                    end -                    -- -                    local name = gk.name -                    -- -                    if features then -                        -- scripts, tag, ismac -                        local f = { } -                        for i=1,#features do -                            local df = features[i] -                            local tag = strip(lower(df.tag)) -                            local ft = f[tag] if not ft then ft = {} f[tag] = ft end -                            local dscripts = df.scripts -                            for i=1,#dscripts do -                                local d = dscripts[i] -                                local languages = d.langs -                                local script = strip(lower(d.script)) -                                local fts = ft[script] if not fts then fts = {} ft[script] = fts end -                                for i=1,#languages do -                                    fts[strip(lower(languages[i]))] = true -                                end -                            end -                        end -                        sequences[#sequences+1] = { -                            type      = typ, -                            chain     = chain, -                            flags     = flags, -                            name      = name, -                            subtables = subtables, -                            markclass = markclass, -                            features  = f, -                        } -                    else -                        lookups[name] = { -                            type      = typ, -                            chain     = chain, -                            flags     = flags, -                            subtables = subtables, -                            markclass = markclass, -                        } -                    end -                end -            end -        end -    end -end - --- test this: --- ---    for _, what in next, otf.glists do ---        raw[what] = nil ---    end - -actions["prepare lookups"] = function(data,filename,raw) -    local lookups = raw.lookups -    if lookups then -        data.lookups = lookups -    end -end - --- The reverse handler does a bit redundant splitting but it's seldom --- seen so we don' tbother too much. We could store the replacement --- in the current list (value instead of true) but it makes other code --- uglier. Maybe some day. - -local function t_uncover(splitter,cache,covers) -    local result = { } -    for n=1,#covers do -        local cover = covers[n] -        local uncovered = cache[cover] -        if not uncovered then -            uncovered = lpegmatch(splitter,cover) -            cache[cover] = uncovered -        end -        result[n] = uncovered -    end -    return result -end - -local function t_hashed(t,cache) -    if t then -        local ht = { } -        for i=1,#t do -            local ti = t[i] -            local tih = cache[ti] -            if not tih then -                tih = { } -                for i=1,#ti do -                    tih[ti[i]] = true -                end -                cache[ti] = tih -            end -            ht[i] = tih -        end -        return ht -    else -        return nil -    end -end - -local function s_uncover(splitter,cache,cover) -    if cover == "" then -        return nil -    else -        local uncovered = cache[cover] -        if not uncovered then -            uncovered = lpegmatch(splitter,cover) -            for i=1,#uncovered do -                uncovered[i] = { [uncovered[i]] = true } -            end -            cache[cover] = uncovered -        end -        return uncovered -    end -end - -local s_hashed = t_hashed - -local function r_uncover(splitter,cache,cover,replacements) -    if cover == "" then -        return nil -    else -        -- we always have current as { } even in the case of one -        local uncovered = cover[1] -        local replaced = cache[replacements] -        if not replaced then -            replaced = lpegmatch(splitter,replacements) -            cache[replacements] = replaced -        end -        local nu, nr = #uncovered, #replaced -        local r = { } -        if nu == nr then -            for i=1,nu do -                r[uncovered[i]] = replaced[i] -            end -        end -        return r -    end -end - -actions["reorganize lookups"] = function(data,filename,raw) -    -- we prefer the before lookups in a normal order -    if data.lookups then -        local splitter = data.helpers.tounicodetable -        local cache, h_cache = { }, { } -        for _, lookup in next, data.lookups do -            local rules = lookup.rules -            if rules then -                local format = lookup.format -                if format == "class" then -                    local before_class = lookup.before_class -                    if before_class then -                        before_class = t_uncover(splitter,cache,reversed(before_class)) -                    end -                    local current_class = lookup.current_class -                    if current_class then -                        current_class = t_uncover(splitter,cache,current_class) -                    end -                    local after_class = lookup.after_class -                    if after_class then -                        after_class = t_uncover(splitter,cache,after_class) -                    end -                    for i=1,#rules do -                        local rule = rules[i] -                        local class = rule.class -                        local before = class.before -                        if before then -                            for i=1,#before do -                                before[i] = before_class[before[i]] or { } -                            end -                            rule.before = t_hashed(before,h_cache) -                        end -                        local current = class.current -                        local lookups = rule.lookups -                        if current then -                            for i=1,#current do -                                current[i] = current_class[current[i]] or { } -                                if lookups and not lookups[i] then -                                    lookups[i] = false -- e.g. we can have two lookups and one replacement -                                end -                            end -                            rule.current = t_hashed(current,h_cache) -                        end -                        local after = class.after -                        if after then -                            for i=1,#after do -                                after[i] = after_class[after[i]] or { } -                            end -                            rule.after = t_hashed(after,h_cache) -                        end -                        rule.class = nil -                    end -                    lookup.before_class  = nil -                    lookup.current_class = nil -                    lookup.after_class   = nil -                    lookup.format        = "coverage" -                elseif format == "coverage" then -                    for i=1,#rules do -                        local rule = rules[i] -                        local coverage = rule.coverage -                        if coverage then -                            local before = coverage.before -                            if before then -                                before = t_uncover(splitter,cache,reversed(before)) -                                rule.before = t_hashed(before,h_cache) -                            end -                            local current = coverage.current -                            if current then -                                current = t_uncover(splitter,cache,current) -                                rule.current = t_hashed(current,h_cache) -                            end -                            local after = coverage.after -                            if after then -                                after = t_uncover(splitter,cache,after) -                                rule.after = t_hashed(after,h_cache) -                            end -                            rule.coverage = nil -                        end -                    end -                elseif format == "reversecoverage" then -- special case, single substitution only -                    for i=1,#rules do -                        local rule = rules[i] -                        local reversecoverage = rule.reversecoverage -                        if reversecoverage then -                            local before = reversecoverage.before -                            if before then -                                before = t_uncover(splitter,cache,reversed(before)) -                                rule.before = t_hashed(before,h_cache) -                            end -                            local current = reversecoverage.current -                            if current then -                                current = t_uncover(splitter,cache,current) -                                rule.current = t_hashed(current,h_cache) -                            end -                            local after = reversecoverage.after -                            if after then -                                after = t_uncover(splitter,cache,after) -                                rule.after = t_hashed(after,h_cache) -                            end -                            local replacements = reversecoverage.replacements -                            if replacements then -                                rule.replacements = r_uncover(splitter,cache,current,replacements) -                            end -                            rule.reversecoverage = nil -                        end -                    end -                elseif format == "glyphs" then -                    for i=1,#rules do -                        local rule = rules[i] -                        local glyphs = rule.glyphs -                        if glyphs then -                            local fore = glyphs.fore -                            if fore then -                                fore = s_uncover(splitter,cache,fore) -                                rule.before = s_hashed(fore,h_cache) -                            end -                            local back = glyphs.back -                            if back then -                                back = s_uncover(splitter,cache,back) -                                rule.after = s_hashed(back,h_cache) -                            end -                            local names = glyphs.names -                            if names then -                                names = s_uncover(splitter,cache,names) -                                rule.current = s_hashed(names,h_cache) -                            end -                            rule.glyphs = nil -                        end -                    end -                end -            end -        end -    end -end - -local function check_variants(unicode,the_variants,splitter,unicodes) -    local variants = the_variants.variants -    if variants then -- use splitter -        local glyphs = lpegmatch(splitter,variants) -        local done   = { [unicode] = true } -        local n      = 0 -        for i=1,#glyphs do -            local g = glyphs[i] -            if done[g] then -                report_otf("skipping cyclic reference U+%05X in math variant U+%05X",g,unicode) -            else -                if n == 0 then -                    n = 1 -                    variants = { g } -                else -                    n = n + 1 -                    variants[n] = g -                end -                done[g] = true -            end -        end -        if n == 0 then -            variants = nil -        end -    end -    local parts = the_variants.parts -    if parts then -        local p = #parts -        if p > 0 then -            for i=1,p do -                local pi = parts[i] -                pi.glyph = unicodes[pi.component] or 0 -                pi.component = nil -            end -        else -            parts = nil -        end -    end -    local italic_correction = the_variants.italic_correction -    if italic_correction and italic_correction == 0 then -        italic_correction = nil -    end -    return variants, parts, italic_correction -end - -actions["analyze math"] = function(data,filename,raw) -    if raw.math then -        data.metadata.math = raw.math -        local unicodes = data.resources.unicodes -        local splitter = data.helpers.tounicodetable -        for unicode, description in next, data.descriptions do -            local glyph          = description.glyph -            local mathkerns      = glyph.mathkern -- singular -            local horiz_variants = glyph.horiz_variants -            local vert_variants  = glyph.vert_variants -            local top_accent     = glyph.top_accent -            if mathkerns or horiz_variants or vert_variants or top_accent then -                local math = { } -                if top_accent then -                    math.top_accent = top_accent -                end -                if mathkerns then -                    for k, v in next, mathkerns do -                        if not next(v) then -                            mathkerns[k] = nil -                        else -                            for k, v in next, v do -                                if v == 0 then -                                    k[v] = nil -- height / kern can be zero -                                end -                            end -                        end -                    end -                    math.kerns = mathkerns -                end -                if horiz_variants then -                    math.horiz_variants, math.horiz_parts, math.horiz_italic_correction = check_variants(unicode,horiz_variants,splitter,unicodes) -                end -                if vert_variants then -                    math.vert_variants, math.vert_parts, math.vert_italic_correction = check_variants(unicode,vert_variants,splitter,unicodes) -                end -                local italic_correction = description.italic -                if italic_correction and italic_correction ~= 0 then -                    math.italic_correction = italic_correction -                end -                description.math = math -            end -        end -    end -end - -actions["reorganize glyph kerns"] = function(data,filename,raw) -    local descriptions = data.descriptions -    local resources    = data.resources -    local unicodes     = resources.unicodes -    for unicode, description in next, descriptions do -        local kerns = description.glyph.kerns -        if kerns then -            local newkerns = { } -            for k, kern in next, kerns do -                local name   = kern.char -                local offset = kern.off -                local lookup = kern.lookup -                if name and offset and lookup then -                    local unicode = unicodes[name] -                    if unicode then -                        if type(lookup) == "table" then -                            for l=1,#lookup do -                                local lookup = lookup[l] -                                local lookupkerns = newkerns[lookup] -                                if lookupkerns then -                                    lookupkerns[unicode] = offset -                                else -                                    newkerns[lookup] = { [unicode] = offset } -                                end -                            end -                        else -                            local lookupkerns = newkerns[lookup] -                            if lookupkerns then -                                lookupkerns[unicode] = offset -                            else -                                newkerns[lookup] = { [unicode] = offset } -                            end -                        end -                    elseif trace_loading then -                        report_otf("problems with unicode %s of kern %s of glyph U+%05X",name,k,unicode) -                    end -                end -            end -            description.kerns = newkerns -        end -    end -end - -actions["merge kern classes"] = function(data,filename,raw) -    local gposlist = raw.gpos -    if gposlist then -        local descriptions = data.descriptions -        local resources    = data.resources -        local unicodes     = resources.unicodes -        local splitter     = data.helpers.tounicodetable -        for gp=1,#gposlist do -            local gpos = gposlist[gp] -            local subtables = gpos.subtables -            if subtables then -                for s=1,#subtables do -                    local subtable = subtables[s] -                    local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes -                    if kernclass then -- the next one is quite slow -                        local split = { } -- saves time -                        for k=1,#kernclass do -                            local kcl = kernclass[k] -                            local firsts  = kcl.firsts -                            local seconds = kcl.seconds -                            local offsets = kcl.offsets -                            local lookups = kcl.lookup  -- singular -                            if type(lookups) ~= "table" then -                                lookups = { lookups } -                            end -                            -- we can check the max in the loop -                         -- local maxseconds = getn(seconds) -                            for n, s in next, firsts do -                                split[s] = split[s] or lpegmatch(splitter,s) -                            end -                            local maxseconds = 0 -                            for n, s in next, seconds do -                                if n > maxseconds then -                                    maxseconds = n -                                end -                                split[s] = split[s] or lpegmatch(splitter,s) -                            end -                            for l=1,#lookups do -                                local lookup = lookups[l] -                                for fk=1,#firsts do -- maxfirsts ? -                                    local fv = firsts[fk] -                                    local splt = split[fv] -                                    if splt then -                                        local extrakerns = { } -                                        local baseoffset = (fk-1) * maxseconds -                                     -- for sk=2,maxseconds do -                                     --     local sv = seconds[sk] -                                        for sk, sv in next, seconds do -                                            local splt = split[sv] -                                            if splt then -- redundant test -                                                local offset = offsets[baseoffset + sk] -                                                if offset then -                                                    for i=1,#splt do -                                                        extrakerns[splt[i]] = offset -                                                    end -                                                end -                                            end -                                        end -                                        for i=1,#splt do -                                            local first_unicode = splt[i] -                                            local description = descriptions[first_unicode] -                                            if description then -                                                local kerns = description.kerns -                                                if not kerns then -                                                    kerns = { } -- unicode indexed ! -                                                    description.kerns = kerns -                                                end -                                                local lookupkerns = kerns[lookup] -                                                if not lookupkerns then -                                                    lookupkerns = { } -                                                    kerns[lookup] = lookupkerns -                                                end -                                                for second_unicode, kern in next, extrakerns do -                                                    lookupkerns[second_unicode] = kern -                                                end -                                            elseif trace_loading then -                                                report_otf("no glyph data for U+%05X", first_unicode) -                                            end -                                        end -                                    end -                                end -                            end -                        end -                        subtable.kernclass = { } -                    end -                end -            end -        end -    end -end - -actions["check glyphs"] = function(data,filename,raw) -    for unicode, description in next, data.descriptions do -        description.glyph = nil -    end -end - --- future versions will remove _ - -actions["check metadata"] = function(data,filename,raw) -    local metadata = data.metadata -    for _, k in next, mainfields do -        if valid_fields[k] then -            local v = raw[k] -            if not metadata[k] then -                metadata[k] = v -            end -        end -    end - -- metadata.pfminfo = raw.pfminfo -- not already done? -    local ttftables = metadata.ttf_tables -    if ttftables then -        for i=1,#ttftables do -            ttftables[i].data = "deleted" -        end -    end -end - -actions["cleanup tables"] = function(data,filename,raw) -    data.resources.indices = nil -- not needed -    data.helpers = nil -end - --- kern: ttf has a table with kerns --- --- Weird, as maxfirst and maxseconds can have holes, first seems to be indexed, but --- seconds can start at 2 .. this need to be fixed as getn as well as # are sort of --- unpredictable alternatively we could force an [1] if not set (maybe I will do that --- anyway). - --- we can share { } as it is never set - ---- ligatures have an extra specification.char entry that we don't use - -actions["reorganize glyph lookups"] = function(data,filename,raw) -    local resources    = data.resources -    local unicodes     = resources.unicodes -    local descriptions = data.descriptions -    local splitter     = data.helpers.tounicodelist - -    local lookuptypes  = resources.lookuptypes - -    for unicode, description in next, descriptions do -        local lookups = description.glyph.lookups -        if lookups then -            for tag, lookuplist in next, lookups do -                for l=1,#lookuplist do -                    local lookup        = lookuplist[l] -                    local specification = lookup.specification -                    local lookuptype    = lookup.type -                    local lt = lookuptypes[tag] -                    if not lt then -                        lookuptypes[tag] = lookuptype -                    elseif lt ~= lookuptype then -                        report_otf("conflicting lookuptypes: %s => %s and %s",tag,lt,lookuptype) -                    end -                    if lookuptype == "ligature" then -                        lookuplist[l] = { lpegmatch(splitter,specification.components) } -                    elseif lookuptype == "alternate" then -                        lookuplist[l] = { lpegmatch(splitter,specification.components) } -                    elseif lookuptype == "substitution" then -                        lookuplist[l] = unicodes[specification.variant] -                    elseif lookuptype == "multiple" then -                        lookuplist[l] = { lpegmatch(splitter,specification.components) } -                    elseif lookuptype == "position" then -                        lookuplist[l] = { -                            specification.x or 0, -                            specification.y or 0, -                            specification.h or 0, -                            specification.v or 0 -                        } -                    elseif lookuptype == "pair" then -                        local one    = specification.offsets[1] -                        local two    = specification.offsets[2] -                        local paired = unicodes[specification.paired] -                        if one then -                            if two then -                                lookuplist[l] = { paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } } -                            else -                                lookuplist[l] = { paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } -                            end -                        else -                            if two then -                                lookuplist[l] = { paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } -                            else -                                lookuplist[l] = { paired } -                            end -                        end -                    end -                end -            end -            local slookups, mlookups -            for tag, lookuplist in next, lookups do -                if #lookuplist == 1 then -                    if slookups then -                        slookups[tag] = lookuplist[1] -                    else -                        slookups = { [tag] = lookuplist[1] } -                    end -                else -                    if mlookups then -                        mlookups[tag] = lookuplist -                    else -                        mlookups = { [tag] = lookuplist } -                    end -                end -            end -            if slookups then -                description.slookups = slookups -            end -            if mlookups then -                description.mlookups = mlookups -            end -        end -    end - -end - -actions["reorganize glyph anchors"] = function(data,filename,raw) -- when we replace inplace we safe entries -    local descriptions = data.descriptions -    for unicode, description in next, descriptions do -        local anchors = description.glyph.anchors -        if anchors then -            for class, data in next, anchors do -                if class == "baselig" then -                    for tag, specification in next, data do -                        for i=1,#specification do -                            local si = specification[i] -                            specification[i] = { si.x or 0, si.y or 0 } -                        end -                    end -                else -                    for tag, specification in next, data do -                        data[tag] = { specification.x or 0, specification.y or 0 } -                    end -                end -            end -            description.anchors = anchors -        end -    end -end - --- modes: node, base, none - -function otf.setfeatures(tfmdata,features) -    local okay = constructors.initializefeatures("otf",tfmdata,features,trace_features,report_otf) -    if okay then -        return constructors.collectprocessors("otf",tfmdata,features,trace_features,report_otf) -    else -        return { } -- will become false -    end -end - --- the first version made a top/mid/not extensible table, now we just --- pass on the variants data and deal with it in the tfm scaler (there --- is no longer an extensible table anyway) --- --- we cannot share descriptions as virtual fonts might extend them (ok, --- we could use a cache with a hash --- --- we already assing an empty tabel to characters as we can add for --- instance protruding info and loop over characters; one is not supposed --- to change descriptions and if one does so one should make a copy! - -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) -        local characters     = { } -        local parameters     = { } -        local mathparameters = { } -        -- -        local pfminfo        = metadata.pfminfo  or { } -        local resources      = data.resources -        local unicodes       = resources.unicodes -     -- local mode           = data.mode or "base" -        local spaceunits     = 500 -        local spacer         = "space" -        local designsize     = metadata.designsize or metadata.design_size or 100 -        local mathspecs      = metadata.math -        -- -        if designsize == 0 then -            designsize = 100 -        end -        if mathspecs then -            for name, value in next, mathspecs do -                mathparameters[name] = value -            end -        end -        for unicode, _ in next, data.descriptions do -- use parent table -            characters[unicode] = { } -        end -        if mathspecs then -            -- we could move this to the scaler but not that much is saved -            -- and this is cleaner -            for unicode, character in next, characters do -                local d = descriptions[unicode] -                local m = d.math -                if m then -                    -- watch out: luatex uses horiz_variants for the parts -                    local variants = m.horiz_variants -                    local parts    = m.horiz_parts -                 -- local done     = { [unicode] = true } -                    if variants then -                        local c = character -                        for i=1,#variants do -                            local un = variants[i] -                         -- if done[un] then -                         --  -- report_otf("skipping cyclic reference U+%05X in math variant U+%05X",un,unicode) -                         -- else -                                c.next = un -                                c = characters[un] -                         --     done[un] = true -                         -- end -                        end -- c is now last in chain -                        c.horiz_variants = parts -                    elseif parts then -                        character.horiz_variants = parts -                    end -                    local variants = m.vert_variants -                    local parts    = m.vert_parts -                 -- local done     = { [unicode] = true } -                    if variants then -                        local c = character -                        for i=1,#variants do -                            local un = variants[i] -                         -- if done[un] then -                         --  -- report_otf("skipping cyclic reference U+%05X in math variant U+%05X",un,unicode) -                         -- else -                                c.next = un -                                c = characters[un] -                         --     done[un] = true -                         -- end -                        end -- c is now last in chain -                        c.vert_variants = parts -                    elseif parts then -                        character.vert_variants = parts -                    end -                    local italic_correction = m.vert_italic_correction -                    if italic_correction then -                        character.vert_italic_correction = italic_correction -- was c. -                    end -                    local top_accent = m.top_accent -                    if top_accent then -                        character.top_accent = top_accent -                    end -                    local kerns = m.kerns -                    if kerns then -                        character.mathkerns = kerns -                    end -                end -            end -        end -        -- end math -        local monospaced  = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced") -        local charwidth   = pfminfo.avgwidth -- or unset -        local italicangle = metadata.italicangle -        local charxheight = pfminfo.os2_xheight and pfminfo.os2_xheight > 0 and pfminfo.os2_xheight -        properties.monospaced  = monospaced -        parameters.italicangle = italicangle -        parameters.charwidth   = charwidth -        parameters.charxheight = charxheight -        -- -        local space  = 0x0020 -- unicodes['space'], unicodes['emdash'] -        local emdash = 0x2014 -- unicodes['space'], unicodes['emdash'] -        if monospaced then -            if descriptions[space] then -                spaceunits, spacer = descriptions[space].width, "space" -            end -            if not spaceunits and descriptions[emdash] then -                spaceunits, spacer = descriptions[emdash].width, "emdash" -            end -            if not spaceunits and charwidth then -                spaceunits, spacer = charwidth, "charwidth" -            end -        else -            if descriptions[space] then -                spaceunits, spacer = descriptions[space].width, "space" -            end -            if not spaceunits and descriptions[emdash] then -                spaceunits, spacer = descriptions[emdash].width/2, "emdash/2" -            end -            if not spaceunits and charwidth then -                spaceunits, spacer = charwidth, "charwidth" -            end -        end -        spaceunits = tonumber(spaceunits) or 500 -- brrr -        -- we need a runtime lookup because of running from cdrom or zip, brrr (shouldn't we use the basename then?) -        local filename = constructors.checkedfilename(resources) -        local fontname = metadata.fontname -        local fullname = metadata.fullname or fontname -        local units    = metadata.units_per_em or 1000 -        -- -        if units == 0 then -- catch bugs in fonts -            units = 1000 -            metadata.units_per_em = 1000 -        end -        -- -        parameters.slant         = 0 -        parameters.space         = spaceunits          -- 3.333 (cmr10) -        parameters.space_stretch = units/2   --  500   -- 1.666 (cmr10) -        parameters.space_shrink  = 1*units/3 --  333   -- 1.111 (cmr10) -        parameters.x_height      = 2*units/5 --  400 -        parameters.quad          = units     -- 1000 -        if spaceunits < 2*units/5 then -            -- todo: warning -        end -        if italicangle then -            parameters.italicangle  = italicangle -            parameters.italicfactor = math.cos(math.rad(90+italicangle)) -            parameters.slant        = - math.round(math.tan(italicangle*math.pi/180)) -        end -        if monospaced then -            parameters.space_stretch = 0 -            parameters.space_shrink  = 0 -        elseif syncspace then -- -            parameters.space_stretch = spaceunits/2 -            parameters.space_shrink  = spaceunits/3 -        end -        parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10) -        if charxheight then -            parameters.x_height = charxheight -        else -            local x = 0x78 -- unicodes['x'] -            if x then -                local x = descriptions[x] -                if x then -                    parameters.x_height = x.height -                end -            end -        end -        -- -        parameters.designsize = (designsize/10)*65536 -        parameters.ascender   = abs(metadata.ascent  or 0) -        parameters.descender  = abs(metadata.descent or 0) -        parameters.units      = units -        -- -        properties.space         = spacer -        properties.encodingbytes = 2 -        properties.format        = data.format or fonts.formats[filename] or "opentype" -        properties.noglyphnames  = true -        properties.filename      = filename -        properties.fontname      = fontname -        properties.fullname      = fullname -        properties.psname        = fontname or fullname -        properties.name          = filename or fullname -        -- -     -- properties.name          = specification.name -     -- properties.sub           = specification.sub -        return { -            characters     = characters, -            descriptions   = descriptions, -            parameters     = parameters, -            mathparameters = mathparameters, -            resources      = resources, -            properties     = properties, -            goodies        = goodies, -        } -    end -end - -local function otftotfm(specification) -    local cache_id = specification.hash -    local tfmdata  = containers.read(constructors.cache,cache_id) -    if not tfmdata then -        local name     = specification.name -        local sub      = specification.sub -        local filename = specification.filename -        local format   = specification.format -        local features = specification.features.normal -        local rawdata  = otf.load(filename,format,sub,features and features.featurefile) -        if rawdata and next(rawdata) then -            rawdata.lookuphash = { } -            tfmdata = copytotfm(rawdata,cache_id) -            if tfmdata and next(tfmdata) then -                -- at this moment no characters are assigned yet, only empty slots -                local features     = constructors.checkedfeatures("otf",features) -                local shared       = tfmdata.shared -                if not shared then -                    shared         = { } -                    tfmdata.shared = shared -                end -                shared.rawdata     = rawdata -             -- shared.features    = features -- default -                shared.dynamics    = { } -             -- shared.processes   = { } -                tfmdata.changed    = { } -                shared.features    = features -                shared.processes   = otf.setfeatures(tfmdata,features) -            end -        end -        containers.write(constructors.cache,cache_id,tfmdata) -    end -    return tfmdata -end - -local function read_from_otf(specification) -    local tfmdata = otftotfm(specification) -    if tfmdata then -        -- this late ? .. needs checking -        tfmdata.properties.name = specification.name -        tfmdata.properties.sub  = specification.sub -        -- -        tfmdata = constructors.scale(tfmdata,specification) -        local allfeatures = tfmdata.shared.features or specification.features.normal -        constructors.applymanipulators("otf",tfmdata,allfeatures,trace_features,report_otf) -        constructors.setname(tfmdata,specification) -- only otf? -        fonts.loggers.register(tfmdata,file.extname(specification.filename),specification) -    end -    return tfmdata -end - -local function checkmathsize(tfmdata,mathsize) -    local mathdata = tfmdata.shared.rawdata.metadata.math -    local mathsize = tonumber(mathsize) -    if mathdata then -- we cannot use mathparameters as luatex will complain -        local parameters = tfmdata.parameters -        parameters.scriptpercentage       = mathdata.ScriptPercentScaleDown -        parameters.scriptscriptpercentage = mathdata.ScriptScriptPercentScaleDown -        parameters.mathsize               = mathsize -    end -end - -registerotffeature { -    name         = "mathsize", -    description  = "apply mathsize as specified in the font", -    initializers = { -        base = checkmathsize, -        node = checkmathsize, -    } -} - --- helpers - -function otf.collectlookups(rawdata,kind,script,language) -    local sequences = rawdata.resources.sequences -    if sequences then -        local featuremap, featurelist = { }, { } -        for s=1,#sequences do -            local sequence = sequences[s] -            local features = sequence.features -            features = features and features[kind] -            features = features and (features[script]   or features[default] or features[wildcard]) -            features = features and (features[language] or features[default] or features[wildcard]) -            if features then -                local subtables = sequence.subtables -                if subtables then -                    for s=1,#subtables do -                        local ss = subtables[s] -                        if not featuremap[s] then -                            featuremap[ss] = true -                            featurelist[#featurelist+1] = ss -                        end -                    end -                end -            end -        end -        if #featurelist > 0 then -            return featuremap, featurelist -        end -    end -    return nil, nil -end - --- readers - -local function check_otf(forced,specification,suffix,what) -    local name = specification.name -    if forced then -        name = file.addsuffix(name,suffix,true) -    end -    local fullname = findbinfile(name,suffix) or "" -    if fullname == "" then -        fullname = fonts.names.getfilename(name,suffix) or "" -    end -    if fullname ~= "" then -        specification.filename = fullname -        specification.format   = what -        return read_from_otf(specification) -    end -end - -local function opentypereader(specification,suffix,what) -    local forced = specification.forced or "" -    if forced == "otf" then -        return check_otf(true,specification,forced,"opentype") -    elseif forced == "ttf" or forced == "ttc" or forced == "dfont" then -        return check_otf(true,specification,forced,"truetype") -    else -        return check_otf(false,specification,suffix,what) -    end -end - -readers.opentype = opentypereader - -local formats = fonts.formats - -formats.otf   = "opentype" -formats.ttf   = "truetype" -formats.ttc   = "truetype" -formats.dfont = "truetype" - -function readers.otf  (specification) return opentypereader(specification,"otf",formats.otf  ) end -function readers.ttf  (specification) return opentypereader(specification,"ttf",formats.ttf  ) end -function readers.ttc  (specification) return opentypereader(specification,"ttf",formats.ttc  ) end -function readers.dfont(specification) return opentypereader(specification,"ttf",formats.dfont) end - --- this will be overloaded - -function otf.scriptandlanguage(tfmdata,attr) -    local properties = tfmdata.properties -    return properties.script or "dflt", properties.language or "dflt" -end diff --git a/otfl-font-oti.lua b/otfl-font-oti.lua deleted file mode 100644 index d6853db..0000000 --- a/otfl-font-oti.lua +++ /dev/null @@ -1,92 +0,0 @@ -if not modules then modules = { } end modules ['font-oti'] = { -    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" -} - -local lower = string.lower - -local allocate = utilities.storage.allocate - -local fonts              = fonts -local otf                = { } -fonts.handlers.otf       = otf - -local otffeatures        = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register - -registerotffeature { -    name        = "features", -    description = "initialization of feature handler", -    default     = true, -} - --- these are later hooked into node and base initializaters - -local otftables = otf.tables -- not always defined - -local function setmode(tfmdata,value) -    if value then -        tfmdata.properties.mode = lower(value) -    end -end - -local function setlanguage(tfmdata,value) -    if value then -        local cleanvalue = lower(value) -        local languages  = otftables and otftables.languages -        local properties = tfmdata.properties -        if not languages then -            properties.language = cleanvalue -        elseif languages[value] then -            properties.language = cleanvalue -        else -            properties.language = "dflt" -        end -    end -end - -local function setscript(tfmdata,value) -    if value then -        local cleanvalue = lower(value) -        local scripts    = otftables and otftables.scripts -        local properties = tfmdata.properties -        if not scripts then -            properties.script = cleanvalue -        elseif scripts[value] then -            properties.script = cleanvalue -        else -            properties.script = "dflt" -        end -    end -end - -registerotffeature { -    name        = "mode", -    description = "mode", -    initializers = { -        base = setmode, -        node = setmode, -    } -} - -registerotffeature { -    name         = "language", -    description  = "language", -    initializers = { -        base = setlanguage, -        node = setlanguage, -    } -} - -registerotffeature { -    name        = "script", -    description = "script", -    initializers = { -        base = setscript, -        node = setscript, -    } -} - diff --git a/otfl-font-otn.lua b/otfl-font-otn.lua deleted file mode 100644 index 8e67597..0000000 --- a/otfl-font-otn.lua +++ /dev/null @@ -1,2712 +0,0 @@ -if not modules then modules = { } end modules ['font-otn'] = { -    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" -} - --- this is still somewhat preliminary and it will get better in due time; --- much functionality could only be implemented thanks to the husayni font --- of Idris Samawi Hamid to who we dedicate this module. - --- in retrospect it always looks easy but believe it or not, it took a lot --- of work to get proper open type support done: buggy fonts, fuzzy specs, --- special made testfonts, many skype sessions between taco, idris and me, --- torture tests etc etc ... unfortunately the code does not show how much --- time it took ... - --- todo: --- --- kerning is probably not yet ok for latin around dics nodes --- extension infrastructure (for usage out of context) --- sorting features according to vendors/renderers --- alternative loop quitters --- check cursive and r2l --- find out where ignore-mark-classes went --- default features (per language, script) --- handle positions (we need example fonts) --- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) --- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests) - ---[[ldx-- -<p>This module is a bit more split up that I'd like but since we also want to test -with plain <l n='tex'/> it has to be so. This module is part of <l n='context'/> -and discussion about improvements and functionality mostly happens on the -<l n='context'/> mailing list.</p> - -<p>The specification of OpenType is kind of vague. Apart from a lack of a proper -free specifications there's also the problem that Microsoft and Adobe -may have their own interpretation of how and in what order to apply features. -In general the Microsoft website has more detailed specifications and is a -better reference. There is also some information in the FontForge help files.</p> - -<p>Because there is so much possible, fonts might contain bugs and/or be made to -work with certain rederers. These may evolve over time which may have the side -effect that suddenly fonts behave differently.</p> - -<p>After a lot of experiments (mostly by Taco, me and Idris) we're now at yet another -implementation. Of course all errors are mine and of course the code can be -improved. There are quite some optimizations going on here and processing speed -is currently acceptable. Not all functions are implemented yet, often because I -lack the fonts for testing. Many scripts are not yet supported either, but I will -look into them as soon as <l n='context'/> users ask for it.</p> - -<p>Because there are different interpretations possible, I will extend the code -with more (configureable) variants. I can also add hooks for users so that they can -write their own extensions.</p> - -<p>Glyphs are indexed not by unicode but in their own way. This is because there is no -relationship with unicode at all, apart from the fact that a font might cover certain -ranges of characters. One character can have multiple shapes. However, at the -<l n='tex'/> end we use unicode so and all extra glyphs are mapped into a private -space. This is needed because we need to access them and <l n='tex'/> has to include -then in the output eventually.</p> - -<p>The raw table as it coms from <l n='fontforge'/> gets reorganized in to fit out needs. -In <l n='context'/> that table is packed (similar tables are shared) and cached on disk -so that successive runs can use the optimized table (after loading the table is -unpacked). The flattening code used later is a prelude to an even more compact table -format (and as such it keeps evolving).</p> - -<p>This module is sparsely documented because it is a moving target. The table format -of the reader changes and we experiment a lot with different methods for supporting -features.</p> - -<p>As with the <l n='afm'/> code, we may decide to store more information in the -<l n='otf'/> table.</p> - -<p>Incrementing the version number will force a re-cache. We jump the number by one -when there's a fix in the <l n='fontforge'/> library or <l n='lua'/> code that -results in different tables.</p> ---ldx]]-- - --- action                    handler     chainproc             chainmore              comment --- --- gsub_single               ok          ok                    ok --- gsub_multiple             ok          ok                    not implemented yet --- gsub_alternate            ok          ok                    not implemented yet --- gsub_ligature             ok          ok                    ok --- gsub_context              ok          -- --- gsub_contextchain         ok          -- --- gsub_reversecontextchain  ok          -- --- chainsub                  --          ok --- reversesub                --          ok --- gpos_mark2base            ok          ok --- gpos_mark2ligature        ok          ok --- gpos_mark2mark            ok          ok --- gpos_cursive              ok          untested --- gpos_single               ok          ok --- gpos_pair                 ok          ok --- gpos_context              ok          -- --- gpos_contextchain         ok          -- --- --- todo: contextpos and contextsub and class stuff --- --- actions: --- --- handler   : actions triggered by lookup --- chainproc : actions triggered by contextual lookup --- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij) --- --- remark: the 'not implemented yet' variants will be done when we have fonts that use them --- remark: we need to check what to do with discretionaries - --- We used to have independent hashes for lookups but as the tags are unique --- we now use only one hash. If needed we can have multiple again but in that --- case I will probably prefix (i.e. rename) the lookups in the cached font file. - -local concat, insert, remove = table.concat, 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 = type, next, tonumber, tostring -local lpegmatch = lpeg.match -local random = math.random - -local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes - -local registertracker = trackers.register - -local fonts = fonts -local otf   = fonts.handlers.otf - -local trace_lookups      = false  registertracker("otf.lookups",      function(v) trace_lookups      = v end) -local trace_singles      = false  registertracker("otf.singles",      function(v) trace_singles      = v end) -local trace_multiples    = false  registertracker("otf.multiples",    function(v) trace_multiples    = v end) -local trace_alternatives = false  registertracker("otf.alternatives", function(v) trace_alternatives = v end) -local trace_ligatures    = false  registertracker("otf.ligatures",    function(v) trace_ligatures    = v end) -local trace_contexts     = false  registertracker("otf.contexts",     function(v) trace_contexts     = v end) -local trace_marks        = false  registertracker("otf.marks",        function(v) trace_marks        = v end) -local trace_kerns        = false  registertracker("otf.kerns",        function(v) trace_kerns        = v end) -local trace_cursive      = false  registertracker("otf.cursive",      function(v) trace_cursive      = v end) -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) -local trace_steps        = false  registertracker("otf.steps",        function(v) trace_steps        = v end) -local trace_skips        = false  registertracker("otf.skips",        function(v) trace_skips        = v end) -local trace_directions   = false  registertracker("otf.directions",   function(v) trace_directions   = v end) - -local report_direct   = logs.reporter("fonts","otf direct") -local report_subchain = logs.reporter("fonts","otf subchain") -local report_chain    = logs.reporter("fonts","otf chain") -local report_process  = logs.reporter("fonts","otf process") -local report_prepare  = logs.reporter("fonts","otf prepare") - -registertracker("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) -registertracker("otf.normal_chain",  function(v) otf.setcontextchain(v and "normal")  end) - -registertracker("otf.replacements", "otf.singles,otf.multiples,otf.alternatives,otf.ligatures") -registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") -registertracker("otf.actions","otf.replacements,otf.positions") -registertracker("otf.injections","nodes.injections") - -registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") - -local insert_node_after  = node.insert_after -local delete_node        = nodes.delete -local copy_node          = node.copy -local find_node_tail     = node.tail or node.slide -local set_attribute      = node.set_attribute -local has_attribute      = node.has_attribute -local flush_node_list    = node.flush_list - -local setmetatableindex  = table.setmetatableindex - -local zwnj               = 0x200C -local zwj                = 0x200D -local wildcard           = "*" -local default            = "dflt" - -local nodecodes          = nodes.nodecodes -local whatcodes          = nodes.whatcodes -local glyphcodes         = nodes.glyphcodes - -local glyph_code         = nodecodes.glyph -local glue_code          = nodecodes.glue -local disc_code          = nodecodes.disc -local whatsit_code       = nodecodes.whatsit - -local dir_code           = whatcodes.dir -local localpar_code      = whatcodes.localpar - -local ligature_code      = glyphcodes.ligature - -local privateattribute   = attributes.private - --- Something is messed up: we have two mark / ligature indices, one at the injection --- end and one here ... this is bases in KE's patches but there is something fishy --- there as I'm pretty sure that for husayni we need some connection (as it's much --- more complex than an average font) but I need proper examples of all cases, not --- of only some. - -local state              = privateattribute('state') -local markbase           = privateattribute('markbase') -local markmark           = privateattribute('markmark') -local markdone           = privateattribute('markdone') -- assigned at the injection end -local cursbase           = privateattribute('cursbase') -local curscurs           = privateattribute('curscurs') -local cursdone           = privateattribute('cursdone') -local kernpair           = privateattribute('kernpair') -local ligacomp           = privateattribute('ligacomp') -- assigned here (ideally it should be combined) - -local injections         = nodes.injections -local setmark            = injections.setmark -local setcursive         = injections.setcursive -local setkern            = injections.setkern -local setpair            = injections.setpair - -local markonce           = true -local cursonce           = true -local kernonce           = true - -local fonthashes         = fonts.hashes -local fontdata           = fonthashes.identifiers - -local otffeatures        = fonts.constructors.newfeatures("otf") -local registerotffeature = otffeatures.register - -local onetimemessage     = fonts.loggers.onetimemessage - -otf.defaultnodealternate = "none" -- first last - --- we share some vars here, after all, we have no nested lookups and --- less code - -local tfmdata             = false -local characters          = false -local descriptions        = false -local resources           = false -local marks               = false -local currentfont         = false -local lookuptable         = false -local anchorlookups       = false -local lookuptypes         = false -local handlers            = { } -local rlmode              = 0 -local featurevalue        = false - --- we cannot optimize with "start = first_glyph(head)" because then we don't --- know which rlmode we're in which messes up cursive handling later on --- --- head is always a whatsit so we can safely assume that head is not changed - --- we use this for special testing and documentation - -local checkstep       = (nodes and nodes.tracers and nodes.tracers.steppers.check)    or function() end -local registerstep    = (nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end -local registermessage = (nodes and nodes.tracers and nodes.tracers.steppers.message)  or function() end - -local function logprocess(...) -    if trace_steps then -        registermessage(...) -    end -    report_direct(...) -end - -local function logwarning(...) -    report_direct(...) -end - -local function gref(n) -    if type(n) == "number" then -        local description = descriptions[n] -        local name = description and description.name -        if name then -            return format("U+%05X (%s)",n,name) -        else -            return format("U+%05X",n) -        end -    elseif not n then -        return "<error in tracing>" -    else -        local num, nam = { }, { } -        for i=1,#n do -            local ni = n[i] -            if tonumber(ni) then -- later we will start at 2 -                local di = descriptions[ni] -                num[i] = format("U+%05X",ni) -                nam[i] = di and di.name or "?" -            end -        end -        return format("%s (%s)",concat(num," "), concat(nam," ")) -    end -end - -local function cref(kind,chainname,chainlookupname,lookupname,index) -    if index then -        return format("feature %s, chain %s, sub %s, lookup %s, index %s",kind,chainname,chainlookupname,lookupname,index) -    elseif lookupname then -        return format("feature %s, chain %s, sub %s, lookup %s",kind,chainname or "?",chainlookupname or "?",lookupname) -    elseif chainlookupname then -        return format("feature %s, chain %s, sub %s",kind,chainname or "?",chainlookupname) -    elseif chainname then -        return format("feature %s, chain %s",kind,chainname) -    else -        return format("feature %s",kind) -    end -end - -local function pref(kind,lookupname) -    return format("feature %s, lookup %s",kind,lookupname) -end - --- we can assume that languages that use marks are not hyphenated --- we can also assume that at most one discretionary is present - -local function markstoligature(kind,lookupname,start,stop,char) -    local n = copy_node(start) -    local keep = start -    local current -    current, start = insert_node_after(start,start,n) -    local snext = stop.next -    current.next = snext -    if snext then -        snext.prev = current -    end -    start.prev, stop.next = nil, nil -    current.char, current.subtype, current.components = char, ligature_code, start -    return keep -end - -local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- brr head -    if start == stop then -        start.char = char -        return start -    elseif discfound then -     -- print("start->stop",nodes.tosequence(start,stop)) -        local components = start.components -        if components then -            flush_node_list(components) -            start.components = nil -        end -        local lignode = copy_node(start) -        lignode.font = start.font -        lignode.char = char -        lignode.subtype = ligature_code -        local next = stop.next -        local prev = start.prev -        stop.next = nil -        start.prev = nil -        lignode.components = start -     -- print("lignode",nodes.tosequence(lignode)) -     -- print("components",nodes.tosequence(lignode.components)) -        prev.next = lignode -        if next then -            next.prev = lignode -        end -        lignode.next = next -        lignode.prev = prev -     -- print("start->end",nodes.tosequence(start)) -        return lignode -    else -        -- start is the ligature -        local deletemarks = markflag ~= "mark" -        local n = copy_node(start) -        local current -        current, start = insert_node_after(start,start,n) -        local snext = stop.next -        current.next = snext -        if snext then -            snext.prev = current -        end -        start.prev = nil -        stop.next = nil -        current.char = char -        current.subtype = ligature_code -        current.components = start -        local head = current -        -- this is messy ... we should get rid of the components eventually -        local i = 0 -- is index of base -        while start do -            if not marks[start.char] then -                i = i + 1 -            elseif not deletemarks then -- quite fishy -                set_attribute(start,ligacomp,i) -                if trace_marks then -                    logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) -                end -                head, current = insert_node_after(head,current,copy_node(start)) -            end -            start = start.next -        end -        start = current.next -        while start and start.id == glyph_code do -            if marks[start.char] then -                set_attribute(start,ligacomp,i) -                if trace_marks then -                    logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) -                end -            else -                break -            end -            start = start.next -        end -        -- -        -- we do need components in funny kerning mode but maybe I can better reconstruct then -        -- as we do have the font components info available; removing components makes the -        -- previous code much simpler -        -- -        -- flush_node_list(head.components) -        return head -    end -end - -function handlers.gsub_single(start,kind,lookupname,replacement) -    if trace_singles then -        logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) -    end -    start.char = replacement -    return start, true -end - -local function get_alternative_glyph(start,alternatives,value) -    -- needs checking: (global value, brrr) -    local choice = nil -    local n      = #alternatives -    local char   = start.char -    -- -    if value == "random" then -        local r = random(1,n) -        value, choice = format("random, choice %s",r), alternatives[r] -    elseif value == "first" then -        value, choice = format("first, choice %s",1), alternatives[1] -    elseif value == "last" then -        value, choice = format("last, choice %s",n), alternatives[n] -    else -        value = tonumber(value) -        if type(value) ~= "number" then -            value, choice = "default, choice 1", alternatives[1] -        elseif value > n then -            local defaultalt = otf.defaultnodealternate -            if defaultalt == "first" then -                value, choice = format("no %s variants, taking %s",value,n), alternatives[n] -            elseif defaultalt == "last" then -                value, choice = format("no %s variants, taking %s",value,1), alternatives[1] -            else -                value, choice = format("no %s variants, ignoring",value), false -            end -        elseif value == 0 then -            value, choice = format("choice %s (no change)",value), char -        elseif value < 1 then -            value, choice = format("no %s variants, taking %s",value,1), alternatives[1] -        else -            value, choice = format("choice %s",value), alternatives[value] -        end -    end -    return choice -end - -local function multiple_glyphs(start,multiple) -- marks ? -    local nofmultiples = #multiple -    if nofmultiples > 0 then -        start.char = multiple[1] -        if nofmultiples > 1 then -            local sn = start.next -            for k=2,nofmultiples do -- todo: use insert_node -                local n = copy_node(start) -                n.char = multiple[k] -                n.next = sn -                n.prev = start -                if sn then -                    sn.prev = n -                end -                start.next = n -                start = n -            end -        end -        return start, true -    else -        if trace_multiples then -            logprocess("no multiple for %s",gref(start.char)) -        end -        return start, false -    end -end - -function handlers.gsub_alternate(start,kind,lookupname,alternative,sequence) -    local value  = featurevalue == true and tfmdata.shared.features[kind] or featurevalue -    local choice = get_alternative_glyph(start,alternative,value) -    if choice then -        if trace_alternatives then -            logprocess("%s: replacing %s by alternative %s (%s)",pref(kind,lookupname),gref(char),gref(choice),choice) -        end -        start.char = choice -    else -        if trace_alternatives then -            logwarning("%s: no variant %s for %s",pref(kind,lookupname),tostring(value),gref(char)) -        end -    end -    return start, true -end - -function handlers.gsub_multiple(start,kind,lookupname,multiple) -    if trace_multiples then -        logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) -    end -    return multiple_glyphs(start,multiple) -end - -function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) -    local s, stop, discfound = start.next, nil, false -    local startchar = start.char -    if marks[startchar] then -        while s do -            local id = s.id -            if id == glyph_code and s.subtype<256 and s.font == currentfont then -                local lg = ligature[s.char] -                if lg then -                    stop = s -                    ligature = lg -                    s = s.next -                else -                    break -                end -            else -                break -            end -        end -        if stop then -            local lig = ligature.ligature -            if lig then -                if trace_ligatures then -                    local stopchar = stop.char -                    start = markstoligature(kind,lookupname,start,stop,lig) -                    logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) -                else -                    start = markstoligature(kind,lookupname,start,stop,lig) -                end -                return start, true -            else -                -- ok, goto next lookup -            end -        end -    else -        local skipmark = sequence.flags[1] -        while s do -            local id = s.id -            if id == glyph_code and s.subtype<256 then -                if s.font == currentfont then -                    local char = s.char -                    if skipmark and marks[char] then -                        s = s.next -                    else -                        local lg = ligature[char] -                        if lg then -                            stop = s -                            ligature = lg -                            s = s.next -                        else -                            break -                        end -                    end -                else -                    break -                end -            elseif id == disc_code then -                discfound = true -                s = s.next -            else -                break -            end -        end -        if stop then -            local lig = ligature.ligature -            if lig then -                if trace_ligatures then -                    local stopchar = stop.char -                    start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) -                    logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) -                else -                    start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) -                end -                return start, true -            else -                -- ok, goto next lookup -            end -        end -    end -    return start, false -end - ---[[ldx-- -<p>We get hits on a mark, but we're not sure if the it has to be applied so -we need to explicitly test for basechar, baselig and basemark entries.</p> ---ldx]]-- - -function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) -    local markchar = start.char -    if marks[markchar] then -        local base = start.prev -- [glyph] [start=mark] -        if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -            local basechar = base.char -            if marks[basechar] then -                while true do -                    base = base.prev -                    if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -                        basechar = base.char -                        if not marks[basechar] then -                            break -                        end -                    else -                        if trace_bugs then -                            logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) -                        end -                        return start, false -                    end -                end -            end -            local baseanchors = descriptions[basechar] -            if baseanchors then -                baseanchors = baseanchors.anchors -            end -            if baseanchors then -                local baseanchors = baseanchors['basechar'] -                if baseanchors then -                    local al = anchorlookups[lookupname] -                    for anchor,ba in next, baseanchors do -                        if al[anchor] then -                            local ma = markanchors[anchor] -                            if ma then -                                local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -                                if trace_marks then -                                    logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", -                                        pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) -                                end -                                return start, true -                            end -                        end -                    end -                    if trace_bugs then -                        logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) -                    end -                end -            else -- if trace_bugs then -            --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) -                onetimemessage(currentfont,basechar,"no base anchors",report_fonts) -            end -        elseif trace_bugs then -            logwarning("%s: prev node is no char",pref(kind,lookupname)) -        end -    elseif trace_bugs then -        logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) -    end -    return start, false -end - -function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) -    -- check chainpos variant -    local markchar = start.char -    if marks[markchar] then -        local base = start.prev -- [glyph] [optional marks] [start=mark] -        if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -            local basechar = base.char -            if marks[basechar] then -                while true do -                    base = base.prev -                    if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -                        basechar = base.char -                        if not marks[basechar] then -                            break -                        end -                    else -                        if trace_bugs then -                            logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) -                        end -                        return start, false -                    end -                end -            end -            local index = has_attribute(start,ligacomp) -            local baseanchors = descriptions[basechar] -            if baseanchors then -                baseanchors = baseanchors.anchors -                if baseanchors then -                   local baseanchors = baseanchors['baselig'] -                   if baseanchors then -                        local al = anchorlookups[lookupname] -                        for anchor,ba in next, baseanchors do -                            if al[anchor] then -                                local ma = markanchors[anchor] -                                if ma then -                                    ba = ba[index] -                                    if ba then -                                        local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index -                                        if trace_marks then -                                            logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", -                                                pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) -                                        end -                                        return start, true -                                    end -                                end -                            end -                        end -                        if trace_bugs then -                            logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) -                        end -                    end -                end -            else -- if trace_bugs then -            --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) -                onetimemessage(currentfont,basechar,"no base anchors",report_fonts) -            end -        elseif trace_bugs then -            logwarning("%s: prev node is no char",pref(kind,lookupname)) -        end -    elseif trace_bugs then -        logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) -    end -    return start, false -end - -function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) -    local markchar = start.char -    if marks[markchar] then -        local base = start.prev -- [glyph] [basemark] [start=mark] -     -- while base and has_attribute(base,ligacomp) and has_attribute(base,ligacomp) ~= has_attribute(start,ligacomp) do -     --     base = base.prev -- KE: prevents mkmk for marks on different components of a ligature -     -- end -        local slc = has_attribute(start,ligacomp) -        if slc then -- a rather messy loop ... needs checking with husayni -            while base do -                local blc = has_attribute(base,ligacomp) -                if blc and blc ~= slc then -                    base = base.prev -                else -                    break -                end -            end -        end -        if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -- subtype test can go -            local basechar = base.char -            local baseanchors = descriptions[basechar] -            if baseanchors then -                baseanchors = baseanchors.anchors -                if baseanchors then -                    baseanchors = baseanchors['basemark'] -                    if baseanchors then -                        local al = anchorlookups[lookupname] -                        for anchor,ba in next, baseanchors do -                            if al[anchor] then -                                local ma = markanchors[anchor] -                                if ma then -                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -                                    if trace_marks then -                                        logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", -                                            pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) -                                    end -                                    return start,true -                                end -                            end -                        end -                        if trace_bugs then -                            logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) -                        end -                    end -                end -            else -- if trace_bugs then -            --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) -                onetimemessage(currentfont,basechar,"no base anchors",report_fonts) -            end -        elseif trace_bugs then -            logwarning("%s: prev node is no mark",pref(kind,lookupname)) -        end -    elseif trace_bugs then -        logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) -    end -    return start,false -end - -function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to be checked -    local alreadydone = cursonce and has_attribute(start,cursbase) -    if not alreadydone then -        local done = false -        local startchar = start.char -        if marks[startchar] then -            if trace_cursive then -                logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) -            end -        else -            local nxt = start.next -            while not done and nxt and nxt.id == glyph_code and nxt.subtype<256 and nxt.font == currentfont do -                local nextchar = nxt.char -                if marks[nextchar] then -                    -- should not happen (maybe warning) -                    nxt = nxt.next -                else -                    local entryanchors = descriptions[nextchar] -                    if entryanchors then -                        entryanchors = entryanchors.anchors -                        if entryanchors then -                            entryanchors = entryanchors['centry'] -                            if entryanchors then -                                local al = anchorlookups[lookupname] -                                for anchor, entry in next, entryanchors do -                                    if al[anchor] then -                                        local exit = exitanchors[anchor] -                                        if exit then -                                            local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) -                                            if trace_cursive then -                                                logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) -                                            end -                                            done = true -                                            break -                                        end -                                    end -                                end -                            end -                        end -                    else -- if trace_bugs then -                    --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) -                        onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) -                    end -                    break -                end -            end -        end -        return start, done -    else -        if trace_cursive and trace_details then -            logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) -        end -        return start, false -    end -end - -function handlers.gpos_single(start,kind,lookupname,kerns,sequence) -    local startchar = start.char -    local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) -    if trace_kerns then -        logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) -    end -    return start, false -end - -function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) -    -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too -    -- todo: kerns in components of ligatures -    local snext = start.next -    if not snext then -        return start, false -    else -        local prev, done = start, false -        local factor = tfmdata.parameters.factor -        local lookuptype = lookuptypes[lookupname] -        while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do -            local nextchar = snext.char -            local krn = kerns[nextchar] -            if not krn and marks[nextchar] then -                prev = snext -                snext = snext.next -            else -                local krn = kerns[nextchar] -                if not krn then -                    -- skip -                elseif type(krn) == "table" then -                    if lookuptype == "pair" then -- probably not needed -                        local a, b = krn[2], krn[3] -                        if a and #a > 0 then -                            local startchar = start.char -                            local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) -                            if trace_kerns then -                                logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) -                            end -                        end -                        if b and #b > 0 then -                            local startchar = start.char -                            local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) -                            if trace_kerns then -                                logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) -                            end -                        end -                    else -- wrong ... position has different entries -                        report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) -                     -- local a, b = krn[2], krn[6] -                     -- if a and a ~= 0 then -                     --     local k = setkern(snext,factor,rlmode,a) -                     --     if trace_kerns then -                     --         logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) -                     --     end -                     -- end -                     -- if b and b ~= 0 then -                     --     logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) -                     -- end -                    end -                    done = true -                elseif krn ~= 0 then -                    local k = setkern(snext,factor,rlmode,krn) -                    if trace_kerns then -                        logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) -                    end -                    done = true -                end -                break -            end -        end -        return start, done -    end -end - ---[[ldx-- -<p>I will implement multiple chain replacements once I run into a font that uses -it. It's not that complex to handle.</p> ---ldx]]-- - -local chainmores = { } -local chainprocs = { } - -local function logprocess(...) -    if trace_steps then -        registermessage(...) -    end -    report_subchain(...) -end - -local logwarning = report_subchain - -local function logprocess(...) -    if trace_steps then -        registermessage(...) -    end -    report_chain(...) -end - -local logwarning = report_chain - --- We could share functions but that would lead to extra function calls with many --- arguments, redundant tests and confusing messages. - -function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) -    logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) -    return start, false -end - -function chainmores.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) -    logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) -    return start, false -end - --- The reversesub is a special case, which is why we need to store the replacements --- in a bit weird way. There is no lookup and the replacement comes from the lookup --- itself. It is meant mostly for dealing with Urdu. - -function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,lookuphash,replacements) -    local char = start.char -    local replacement = replacements[char] -    if replacement then -        if trace_singles then -            logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) -        end -        start.char = replacement -        return start, true -    else -        return start, false -    end -end - ---[[ldx-- -<p>This chain stuff is somewhat tricky since we can have a sequence of actions to be -applied: single, alternate, multiple or ligature where ligature can be an invalid -one in the sense that it will replace multiple by one but not neccessary one that -looks like the combination (i.e. it is the counterpart of multiple then). For -example, the following is valid:</p> - -<typing> -<line>xxxabcdexxx [single a->A][multiple b->BCD][ligature cde->E] xxxABCDExxx</line> -</typing> - -<p>Therefore we we don't really do the replacement here already unless we have the -single lookup case. The efficiency of the replacements can be improved by deleting -as less as needed but that would also make the code even more messy.</p> ---ldx]]-- - -local function delete_till_stop(start,stop,ignoremarks) -- keeps start -    local n = 1 -    if start == stop then -        -- done -    elseif ignoremarks then -        repeat -- start x x m x x stop => start m -            local next = start.next -            if not marks[next.char] then -                delete_node(start,next) -            end -            n = n + 1 -        until next == stop -    else -- start x x x stop => start -        repeat -            local next = start.next -            delete_node(start,next) -            n = n + 1 -        until next == stop -    end -    return n -end - ---[[ldx-- -<p>Here we replace start by a single variant, First we delete the rest of the -match.</p> ---ldx]]-- - -function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) -    -- todo: marks ? -    local current = start -    local subtables = currentlookup.subtables -    if #subtables > 1 then -        logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) -    end -    while current do -        if current.id == glyph_code then -            local currentchar = current.char -            local lookupname = subtables[1] -- only 1 -            local replacement = lookuphash[lookupname] -            if not replacement then -                if trace_bugs then -                    logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) -                end -            else -                replacement = replacement[currentchar] -                if not replacement then -                    if trace_bugs then -                        logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) -                    end -                else -                    if trace_singles then -                        logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) -                    end -                    current.char = replacement -                end -            end -            return start, true -        elseif current == stop then -            break -        else -            current = current.next -        end -    end -    return start, false -end - -chainmores.gsub_single = chainprocs.gsub_single - ---[[ldx-- -<p>Here we replace start by a sequence of new glyphs. First we delete the rest of -the match.</p> ---ldx]]-- - -function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    delete_till_stop(start,stop) -- we could pass ignoremarks as #3 .. -    local startchar = start.char -    local subtables = currentlookup.subtables -    local lookupname = subtables[1] -    local replacements = lookuphash[lookupname] -    if not replacements then -        if trace_bugs then -            logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) -        end -    else -        replacements = replacements[startchar] -        if not replacements then -            if trace_bugs then -                logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) -            end -        else -            if trace_multiples then -                logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) -            end -            return multiple_glyphs(start,replacements) -        end -    end -    return start, false -end - --- function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) ---     logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) ---     return start, false --- end - -chainmores.gsub_multiple = chainprocs.gsub_multiple - ---[[ldx-- -<p>Here we replace start by new glyph. First we delete the rest of the match.</p> ---ldx]]-- - --- char_1 mark_1 -> char_x mark_1 (ignore marks) --- char_1 mark_1 -> char_x - --- to be checked: do we always have just one glyph? --- we can also have alternates for marks --- marks come last anyway --- are there cases where we need to delete the mark - -function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    local current = start -    local subtables = currentlookup.subtables -    local value  = featurevalue == true and tfmdata.shared.features[kind] or featurevalue -    while current do -        if current.id == glyph_code then -- is this check needed? -            local currentchar = current.char -            local lookupname = subtables[1] -            local alternatives = lookuphash[lookupname] -            if not alternatives then -                if trace_bugs then -                    logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname)) -                end -            else -                alternatives = alternatives[currentchar] -                if alternatives then -                    local choice = get_alternative_glyph(current,alternatives,value) -                    if choice then -                        if trace_alternatives then -                            logprocess("%s: replacing %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),gref(char),gref(choice),choice) -                        end -                        start.char = choice -                    else -                        if trace_alternatives then -                            logwarning("%s: no variant %s for %s",cref(kind,chainname,chainlookupname,lookupname),tostring(value),gref(char)) -                        end -                    end -                elseif trace_bugs then -                    logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar)) -                end -            end -            return start, true -        elseif current == stop then -            break -        else -            current = current.next -        end -    end -    return start, false -end - --- function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) ---     logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) ---     return start, false --- end - -chainmores.gsub_alternate = chainprocs.gsub_alternate - ---[[ldx-- -<p>When we replace ligatures we use a helper that handles the marks. I might change -this function (move code inline and handle the marks by a separate function). We -assume rather stupid ligatures (no complex disc nodes).</p> ---ldx]]-- - -function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) -    local startchar = start.char -    local subtables = currentlookup.subtables -    local lookupname = subtables[1] -    local ligatures = lookuphash[lookupname] -    if not ligatures then -        if trace_bugs then -            logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) -        end -    else -        ligatures = ligatures[startchar] -        if not ligatures then -            if trace_bugs then -                logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) -            end -        else -            local s, discfound, last, nofreplacements = start.next, false, stop, 0 -            while s do -                local id = s.id -                if id == disc_code then -                    s = s.next -                    discfound = true -                else -                    local schar = s.char -                    if marks[schar] then -- marks -                        s = s.next -                    else -                        local lg = ligatures[schar] -                        if lg then -                            ligatures, last, nofreplacements = lg, s, nofreplacements + 1 -                            if s == stop then -                                break -                            else -                                s = s.next -                            end -                        else -                            break -                        end -                    end -                end -            end -            local l2 = ligatures.ligature -            if l2 then -                if chainindex then -                    stop = last -                end -                if trace_ligatures then -                    if start == stop then -                        logprocess("%s: replacing character %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) -                    else -                        logprocess("%s: replacing character %s upto %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) -                    end -                end -                start = toligature(kind,lookupname,start,stop,l2,currentlookup.flags[1],discfound) -                return start, true, nofreplacements -            elseif trace_bugs then -                if start == stop then -                    logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) -                else -                    logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) -                end -            end -        end -    end -    return start, false, 0 -end - -chainmores.gsub_ligature = chainprocs.gsub_ligature - -function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    local markchar = start.char -    if marks[markchar] then -        local subtables = currentlookup.subtables -        local lookupname = subtables[1] -        local markanchors = lookuphash[lookupname] -        if markanchors then -            markanchors = markanchors[markchar] -        end -        if markanchors then -            local base = start.prev -- [glyph] [start=mark] -            if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -                local basechar = base.char -                if marks[basechar] then -                    while true do -                        base = base.prev -                        if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -                            basechar = base.char -                            if not marks[basechar] then -                                break -                            end -                        else -                            if trace_bugs then -                                logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) -                            end -                            return start, false -                        end -                    end -                end -                local baseanchors = descriptions[basechar].anchors -                if baseanchors then -                    local baseanchors = baseanchors['basechar'] -                    if baseanchors then -                        local al = anchorlookups[lookupname] -                        for anchor,ba in next, baseanchors do -                            if al[anchor] then -                                local ma = markanchors[anchor] -                                if ma then -                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -                                    if trace_marks then -                                        logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", -                                            cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) -                                    end -                                    return start, true -                                end -                            end -                        end -                        if trace_bugs then -                            logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) -                        end -                    end -                end -            elseif trace_bugs then -                logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) -            end -        elseif trace_bugs then -            logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) -        end -    elseif trace_bugs then -        logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) -    end -    return start, false -end - -function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    local markchar = start.char -    if marks[markchar] then -        local subtables = currentlookup.subtables -        local lookupname = subtables[1] -        local markanchors = lookuphash[lookupname] -        if markanchors then -            markanchors = markanchors[markchar] -        end -        if markanchors then -            local base = start.prev -- [glyph] [optional marks] [start=mark] -            if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -                local basechar = base.char -                if marks[basechar] then -                    while true do -                        base = base.prev -                        if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -                            basechar = base.char -                            if not marks[basechar] then -                                break -                            end -                        else -                            if trace_bugs then -                                logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) -                            end -                            return start, false -                        end -                    end -                end -                -- todo: like marks a ligatures hash -                local index = has_attribute(start,ligacomp) -                local baseanchors = descriptions[basechar].anchors -                if baseanchors then -                   local baseanchors = baseanchors['baselig'] -                   if baseanchors then -                        local al = anchorlookups[lookupname] -                        for anchor,ba in next, baseanchors do -                            if al[anchor] then -                                local ma = markanchors[anchor] -                                if ma then -                                    ba = ba[index] -                                    if ba then -                                        local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index -                                        if trace_marks then -                                            logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", -                                                cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) -                                        end -                                        return start, true -                                    end -                                end -                            end -                        end -                        if trace_bugs then -                            logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) -                        end -                    end -                end -            elseif trace_bugs then -                logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) -            end -        elseif trace_bugs then -            logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) -        end -    elseif trace_bugs then -        logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) -    end -    return start, false -end - -function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    local markchar = start.char -    if marks[markchar] then ---~         local alreadydone = markonce and has_attribute(start,markmark) ---~         if not alreadydone then -        --  local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark -            local subtables = currentlookup.subtables -            local lookupname = subtables[1] -            local markanchors = lookuphash[lookupname] -            if markanchors then -                markanchors = markanchors[markchar] -            end -            if markanchors then -                local base = start.prev -- [glyph] [basemark] [start=mark] -             -- while (base and has_attribute(base,ligacomp) and has_attribute(base,ligacomp) ~= has_attribute(start,ligacomp)) do -             --     base = base.prev -- KE: prevents mkmk for marks on different components of a ligature -             -- end -                local slc = has_attribute(start,ligacomp) -                if slc then -- a rather messy loop ... needs checking with husayni -                    while base do -                        local blc = has_attribute(base,ligacomp) -                        if blc and blc ~= slc then -                            base = base.prev -                        else -                            break -                        end -                    end -                end -                if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -- subtype test can go -                    local basechar = base.char -                    local baseanchors = descriptions[basechar].anchors -                    if baseanchors then -                        baseanchors = baseanchors['basemark'] -                        if baseanchors then -                            local al = anchorlookups[lookupname] -                            for anchor,ba in next, baseanchors do -                                if al[anchor] then -                                    local ma = markanchors[anchor] -                                    if ma then -                                        local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -                                        if trace_marks then -                                            logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", -                                                cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) -                                        end -                                        return start, true -                                    end -                                end -                            end -                            if trace_bugs then -                                logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) -                            end -                        end -                    end -                elseif trace_bugs then -                    logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) -                end -            elseif trace_bugs then -                logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) -            end ---~         elseif trace_marks and trace_details then ---~             logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) ---~         end -    elseif trace_bugs then -        logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) -    end -    return start, false -end - --- ! ! ! untested ! ! ! - -function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    local alreadydone = cursonce and has_attribute(start,cursbase) -    if not alreadydone then -        local startchar = start.char -        local subtables = currentlookup.subtables -        local lookupname = subtables[1] -        local exitanchors = lookuphash[lookupname] -        if exitanchors then -            exitanchors = exitanchors[startchar] -        end -        if exitanchors then -            local done = false -            if marks[startchar] then -                if trace_cursive then -                    logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) -                end -            else -                local nxt = start.next -                while not done and nxt and nxt.id == glyph_code and nxt.subtype<256 and nxt.font == currentfont do -                    local nextchar = nxt.char -                    if marks[nextchar] then -                        -- should not happen (maybe warning) -                        nxt = nxt.next -                    else -                        local entryanchors = descriptions[nextchar] -                        if entryanchors then -                            entryanchors = entryanchors.anchors -                            if entryanchors then -                                entryanchors = entryanchors['centry'] -                                if entryanchors then -                                    local al = anchorlookups[lookupname] -                                    for anchor, entry in next, entryanchors do -                                        if al[anchor] then -                                            local exit = exitanchors[anchor] -                                            if exit then -                                                local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) -                                                if trace_cursive then -                                                    logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) -                                                end -                                                done = true -                                                break -                                            end -                                        end -                                    end -                                end -                            end -                        else -- if trace_bugs then -                        --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) -                            onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) -                        end -                        break -                    end -                end -            end -            return start, done -        else -            if trace_cursive and trace_details then -                logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) -            end -            return start, false -        end -    end -    return start, false -end - -function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -    -- untested .. needs checking for the new model -    local startchar = start.char -    local subtables = currentlookup.subtables -    local lookupname = subtables[1] -    local kerns = lookuphash[lookupname] -    if kerns then -        kerns = kerns[startchar] -- needed ? -        if kerns then -            local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) -            if trace_kerns then -                logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) -            end -        end -    end -    return start, false -end - --- when machines become faster i will make a shared function - -function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) ---    logwarning("%s: gpos_pair not yet supported",cref(kind,chainname,chainlookupname)) -    local snext = start.next -    if snext then -        local startchar = start.char -        local subtables = currentlookup.subtables -        local lookupname = subtables[1] -        local kerns = lookuphash[lookupname] -        if kerns then -            kerns = kerns[startchar] -            if kerns then -                local lookuptype = lookuptypes[lookupname] -                local prev, done = start, false -                local factor = tfmdata.parameters.factor -                while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do -                    local nextchar = snext.char -                    local krn = kerns[nextchar] -                    if not krn and marks[nextchar] then -                        prev = snext -                        snext = snext.next -                    else -                        if not krn then -                            -- skip -                        elseif type(krn) == "table" then -                            if lookuptype == "pair" then -                                local a, b = krn[2], krn[3] -                                if a and #a > 0 then -                                    local startchar = start.char -                                    local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) -                                    if trace_kerns then -                                        logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) -                                    end -                                end -                                if b and #b > 0 then -                                    local startchar = start.char -                                    local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) -                                    if trace_kerns then -                                        logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) -                                    end -                                end -                            else -                                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) -                                local a, b = krn[2], krn[6] -                                if a and a ~= 0 then -                                    local k = setkern(snext,factor,rlmode,a) -                                    if trace_kerns then -                                        logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) -                                    end -                                end -                                if b and b ~= 0 then -                                    logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) -                                end -                            end -                            done = true -                        elseif krn ~= 0 then -                            local k = setkern(snext,factor,rlmode,krn) -                            if trace_kerns then -                                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) -                            end -                            done = true -                        end -                        break -                    end -                end -                return start, done -            end -        end -    end -    return start, false -end - --- what pointer to return, spec says stop --- to be discussed ... is bidi changer a space? --- elseif char == zwnj and sequence[n][32] then -- brrr - --- somehow l or f is global --- we don't need to pass the currentcontext, saves a bit --- make a slow variant then can be activated but with more tracing - -local function show_skip(kind,chainname,char,ck,class) -    if ck[9] then -        logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) -    else -        logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2]) -    end -end - -local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,lookuphash) -    --  local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] -    local flags        = sequence.flags -    local done         = false -    local skipmark     = flags[1] -    local skipligature = flags[2] -    local skipbase     = flags[3] -    local someskip     = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) -    local markclass    = sequence.markclass                   -- todo, first we need a proper test -    local skipped      = false -    for k=1,#contexts do -        local match   = true -        local current = start -        local last    = start -        local ck      = contexts[k] -        local seq     = ck[3] -        local s       = #seq -        -- f..l = mid string -        if s == 1 then -            -- never happens -            match = current.id == glyph_code and current.subtype<256 and current.font == currentfont and seq[1][current.char] -        else -            -- maybe we need a better space check (maybe check for glue or category or combination) -            -- we cannot optimize for n=2 because there can be disc nodes -            local f, l = ck[4], ck[5] -            -- current match -            if f == 1 and f == l then -- current only -                -- already a hit -             -- match = true -            else -- before/current/after | before/current | current/after -                -- no need to test first hit (to be optimized) -                if f == l then -- new, else last out of sync (f is > 1) -                 -- match = true -                else -                    local n = f + 1 -                    last = last.next -                    while n <= l do -                        if last then -                            local id = last.id -                            if id == glyph_code then -                                if last.subtype<256 and last.font == currentfont then -                                    local char = last.char -                                    local ccd = descriptions[char] -                                    if ccd then -                                        local class = ccd.class -                                        if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then -                                            skipped = true -                                            if trace_skips then -                                                show_skip(kind,chainname,char,ck,class) -                                            end -                                            last = last.next -                                        elseif seq[n][char] then -                                            if n < l then -                                                last = last.next -                                            end -                                            n = n + 1 -                                        else -                                            match = false -                                            break -                                        end -                                    else -                                        match = false -                                        break -                                    end -                                else -                                    match = false -                                    break -                                end -                            elseif id == disc_code then -                                last = last.next -                            else -                                match = false -                                break -                            end -                        else -                            match = false -                            break -                        end -                    end -                end -            end -            -- before -            if match and f > 1 then -                local prev = start.prev -                if prev then -                    local n = f-1 -                    while n >= 1 do -                        if prev then -                            local id = prev.id -                            if id == glyph_code then -                                if prev.subtype<256 and prev.font == currentfont then -- normal char -                                    local char = prev.char -                                    local ccd = descriptions[char] -                                    if ccd then -                                        local class = ccd.class -                                        if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then -                                            skipped = true -                                            if trace_skips then -                                                show_skip(kind,chainname,char,ck,class) -                                            end -                                        elseif seq[n][char] then -                                            n = n -1 -                                        else -                                            match = false -                                            break -                                        end -                                    else -                                        match = false -                                        break -                                    end -                                else -                                    match = false -                                    break -                                end -                            elseif id == disc_code then -                                -- skip 'm -                            elseif seq[n][32] then -                                n = n -1 -                            else -                                match = false -                                break -                            end -                            prev = prev.prev -                        elseif seq[n][32] then -- somehat special, as zapfino can have many preceding spaces -                            n = n -1 -                        else -                            match = false -                            break -                        end -                    end -                elseif f == 2 then -                    match = seq[1][32] -                else -                    for n=f-1,1 do -                        if not seq[n][32] then -                            match = false -                            break -                        end -                    end -                end -            end -            -- after -            if match and s > l then -                local current = last and last.next -                if current then -                    -- removed optimization for s-l == 1, we have to deal with marks anyway -                    local n = l + 1 -                    while n <= s do -                        if current then -                            local id = current.id -                            if id == glyph_code then -                                if current.subtype<256 and current.font == currentfont then -- normal char -                                    local char = current.char -                                    local ccd = descriptions[char] -                                    if ccd then -                                        local class = ccd.class -                                        if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then -                                            skipped = true -                                            if trace_skips then -                                                show_skip(kind,chainname,char,ck,class) -                                            end -                                        elseif seq[n][char] then -                                            n = n + 1 -                                        else -                                            match = false -                                            break -                                        end -                                    else -                                        match = false -                                        break -                                    end -                                else -                                    match = false -                                    break -                                end -                            elseif id == disc_code then -                                -- skip 'm -                            elseif seq[n][32] then -- brrr -                                n = n + 1 -                            else -                                match = false -                                break -                            end -                            current = current.next -                        elseif seq[n][32] then -                            n = n + 1 -                        else -                            match = false -                            break -                        end -                    end -                elseif s-l == 1 then -                    match = seq[s][32] -                else -                    for n=l+1,s do -                        if not seq[n][32] then -                            match = false -                            break -                        end -                    end -                end -            end -        end -        if match then -            -- ck == currentcontext -            if trace_contexts then -                local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5] -                local char = start.char -                if ck[9] then -                    logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s (%s=>%s)", -                        cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) -                else -                    logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s", -                        cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) -                end -            end -            local chainlookups = ck[6] -            if chainlookups then -                local nofchainlookups = #chainlookups -                -- we can speed this up if needed -                if nofchainlookups == 1 then -                    local chainlookupname = chainlookups[1] -                    local chainlookup = lookuptable[chainlookupname] -                    if chainlookup then -                        local cp = chainprocs[chainlookup.type] -                        if cp then -                            start, done = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) -                        else -                            logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) -                        end -                    else -- shouldn't happen -                        logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname)) -                    end -                 else -                    local i = 1 -                    repeat -                        if skipped then -                            while true do -                                local char = start.char -                                local ccd = descriptions[char] -                                if ccd then -                                    local class = ccd.class -                                    if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then -                                        start = start.next -                                    else -                                        break -                                    end -                                else -                                    break -                                end -                            end -                        end -                        local chainlookupname = chainlookups[i] -                        local chainlookup = lookuptable[chainlookupname] -- can be false (n matches, <n replacement) -                        local cp = chainlookup and chainmores[chainlookup.type] -                        if cp then -                            local ok, n -                            start, ok, n = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) -                            -- messy since last can be changed ! -                            if ok then -                                done = true -                                -- skip next one(s) if ligature -                                i = i + (n or 1) -                            else -                                i = i + 1 -                            end -                        else -                         -- is valid -                         -- logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup and chainlookup.type or "?") -                            i = i + 1 -                        end -                        start = start.next -                    until i > nofchainlookups -                end -            else -                local replacements = ck[7] -                if replacements then -                    start, done = chainprocs.reversesub(start,last,kind,chainname,ck,lookuphash,replacements) -- sequence -                else -                    done = true -- can be meant to be skipped -                    if trace_contexts then -                        logprocess("%s: skipping match",cref(kind,chainname)) -                    end -                end -            end -        end -    end -    return start, done -end - --- Because we want to keep this elsewhere (an because speed is less an issue) we --- pass the font id so that the verbose variant can access the relevant helper tables. - -local verbose_handle_contextchain = function(font,...) -    logwarning("no verbose handler installed, reverting to 'normal'") -    otf.setcontextchain() -    return normal_handle_contextchain(...) -end - -otf.chainhandlers = { -    normal = normal_handle_contextchain, -    verbose = verbose_handle_contextchain, -} - -function otf.setcontextchain(method) -    if not method or method == "normal" or not otf.chainhandlers[method] then -        if handlers.contextchain then -- no need for a message while making the format -            logwarning("installing normal contextchain handler") -        end -        handlers.contextchain = normal_handle_contextchain -    else -        logwarning("installing contextchain handler '%s'",method) -        local handler = otf.chainhandlers[method] -        handlers.contextchain = function(...) -            return handler(currentfont,...) -- hm, get rid of ... -        end -    end -    handlers.gsub_context             = handlers.contextchain -    handlers.gsub_contextchain        = handlers.contextchain -    handlers.gsub_reversecontextchain = handlers.contextchain -    handlers.gpos_contextchain        = handlers.contextchain -    handlers.gpos_context             = handlers.contextchain -end - -otf.setcontextchain() - -local missing = { } -- we only report once - -local function logprocess(...) -    if trace_steps then -        registermessage(...) -    end -    report_process(...) -end - -local logwarning = report_process - -local function report_missing_cache(typ,lookup) -    local f = missing[currentfont] if not f then f = { } missing[currentfont] = f end -    local t = f[typ]               if not t then t = { } f[typ]               = t end -    if not t[lookup] then -        t[lookup] = true -        logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.properties.fullname) -    end -end - -local resolved = { } -- we only resolve a font,script,language pair once - --- todo: pass all these 'locals' in a table - -local lookuphashes = { } - -setmetatableindex(lookuphashes, function(t,font) -    local lookuphash = fontdata[font].resources.lookuphash -    if not lookuphash or not next(lookuphash) then -        lookuphash = false -    end -    t[font] = lookuphash -    return lookuphash -end) - --- fonts.hashes.lookups = lookuphashes - -local special_attributes = { -    init = 1, -    medi = 2, -    fina = 3, -    isol = 4 -} - -local function initialize(sequence,script,language,enabled) -    local features = sequence.features -    if features then -        for kind, scripts in next, features do -            local valid = enabled[kind] -            if valid then -                local languages = scripts[script] or scripts[wildcard] -                if languages and (languages[language] or languages[wildcard]) then -                    return { valid, special_attributes[kind] or false, sequence.chain or 0, kind, sequence } -                end -            end -        end -    end -    return false -end - -function otf.dataset(tfmdata,sequences,font) -- generic variant, overloaded in context -    local shared     = tfmdata.shared -    local properties = tfmdata.properties -    local language   = properties.language or "dflt" -    local script     = properties.script   or "dflt" -    local enabled    = shared.features -    local res = resolved[font] -    if not res then -        res = { } -        resolved[font] = res -    end -    local rs = res[script] -    if not rs then -        rs = { } -        res[script] = rs -    end -    local rl = rs[language] -    if not rl then -        rl = { } -        rs[language] = rl -        setmetatableindex(rl, function(t,k) -            local v = enabled and initialize(sequences[k],script,language,enabled) -            t[k] = v -            return v -        end) -    end -    return rl -end - --- elseif id == glue_code then ---     if p[5] then -- chain ---         local pc = pp[32] ---         if pc then ---             start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) ---             if ok then ---                 done = true ---             end ---             if start then start = start.next end ---         else ---             start = start.next ---         end ---     else ---         start = start.next ---     end - -local function featuresprocessor(head,font,attr) - -    local lookuphash = lookuphashes[font] -- we can also check sequences here - -    if not lookuphash then -        return head, false -    end - -    if trace_steps then -        checkstep(head) -    end - -    tfmdata                = fontdata[font] -    descriptions           = tfmdata.descriptions -    characters             = tfmdata.characters -    resources              = tfmdata.resources - -    marks                  = resources.marks -    anchorlookups          = resources.lookup_to_anchor -    lookuptable            = resources.lookups -    lookuptypes            = resources.lookuptypes - -    currentfont            = font -    rlmode                 = 0 - -    local sequences        = resources.sequences -    local done             = false -    local datasets         = otf.dataset(tfmdata,sequences,font,attr) - -    local dirstack = { } -- could move outside function - -    -- We could work on sub start-stop ranges instead but I wonder if there is that -    -- much speed gain (experiments showed that it made not much sense) and we need -    -- to keep track of directions anyway. Also at some point I want to play with -    -- font interactions and then we do need the full sweeps. - -    for s=1,#sequences do -        local dataset = datasets[s] -        if dataset then -            featurevalue = dataset[1] -- todo: pass to function instead of using a global -            if featurevalue then -                local sequence  = sequences[s] -- also dataset[5] -                local rlparmode = 0 -                local topstack  = 0 -                local success   = false -                local attribute = dataset[2] -                local chain     = dataset[3] -- sequence.chain or 0 -                local typ       = sequence.type -                local subtables = sequence.subtables -                if chain < 0 then -                    -- this is a limited case, no special treatments like 'init' etc -                    local handler = handlers[typ] -                    -- we need to get rid of this slide! probably no longer needed in latest luatex -                    local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo -                    while start do -                        local id = start.id -                        if id == glyph_code then -                            if start.subtype<256 and start.font == font then -                                local a = has_attribute(start,0) -                                if a then -                                    a = a == attr -                                else -                                    a = true -                                end -                                if a then -                                    for i=1,#subtables do -                                        local lookupname = subtables[i] -                                        local lookupcache = lookuphash[lookupname] -                                        if lookupcache then -                                            local lookupmatch = lookupcache[start.char] -                                            if lookupmatch then -                                                start, success = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) -                                                if success then -                                                    break -                                                end -                                            end -                                        else -                                            report_missing_cache(typ,lookupname) -                                        end -                                    end -                                    if start then start = start.prev end -                                else -                                    start = start.prev -                                end -                            else -                                start = start.prev -                            end -                        else -                            start = start.prev -                        end -                    end -                else -                    local handler = handlers[typ] -                    local ns = #subtables -                    local start = head -- local ? -                    rlmode = 0 -- to be checked ? -                    if ns == 1 then -- happens often -                        local lookupname = subtables[1] -                        local lookupcache = lookuphash[lookupname] -                        if not lookupcache then -- also check for empty cache -                            report_missing_cache(typ,lookupname) -                        else -                            while start do -                                local id = start.id -                                if id == glyph_code then -                                    if start.subtype<256 and start.font == font then -                                        local a = has_attribute(start,0) -                                        if a then -                                            a = (a == attr) and (not attribute or has_attribute(start,state,attribute)) -                                        else -                                            a = not attribute or has_attribute(start,state,attribute) -                                        end -                                        if a then -                                            local lookupmatch = lookupcache[start.char] -                                            if lookupmatch then -                                                -- sequence kan weg -                                                local ok -                                                start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) -                                                if ok then -                                                    success = true -                                                end -                                            end -                                            if start then start = start.next end -                                        else -                                            start = start.next -                                        end -                                    else -                                        start = start.next -                                    end -                                elseif id == whatsit_code then -- will be function -                                    local subtype = start.subtype -                                    if subtype == dir_code then -                                        local dir = start.dir -                                        if     dir == "+TRT" or dir == "+TLT" then -                                            topstack = topstack + 1 -                                            dirstack[topstack] = dir -                                        elseif dir == "-TRT" or dir == "-TLT" then -                                            topstack = topstack - 1 -                                        end -                                        local newdir = dirstack[topstack] -                                        if newdir == "+TRT" then -                                            rlmode = -1 -                                        elseif newdir == "+TLT" then -                                            rlmode = 1 -                                        else -                                            rlmode = rlparmode -                                        end -                                        if trace_directions then -                                            report_process("directions after txtdir %s: txtdir=%s:%s, parmode=%s, txtmode=%s",dir,topstack,newdir or "unset",rlparmode,rlmode) -                                        end -                                    elseif subtype == localpar_code then -                                        local dir = start.dir -                                        if dir == "TRT" then -                                            rlparmode = -1 -                                        elseif dir == "TLT" then -                                            rlparmode = 1 -                                        else -                                            rlparmode = 0 -                                        end -                                        rlmode = rlparmode -                                        if trace_directions then -                                            report_process("directions after pardir %s: parmode=%s, txtmode=%s",dir,rlparmode,rlmode) -                                        end -                                    end -                                    start = start.next -                                else -                                    start = start.next -                                end -                            end -                        end -                    else -                        while start do -                            local id = start.id -                            if id == glyph_code then -                                if start.subtype<256 and start.font == font then -                                    local a = has_attribute(start,0) -                                    if a then -                                        a = (a == attr) and (not attribute or has_attribute(start,state,attribute)) -                                    else -                                        a = not attribute or has_attribute(start,state,attribute) -                                    end -                                    if a then -                                        for i=1,ns do -                                            local lookupname = subtables[i] -                                            local lookupcache = lookuphash[lookupname] -                                            if lookupcache then -                                                local lookupmatch = lookupcache[start.char] -                                                if lookupmatch then -                                                    -- we could move all code inline but that makes things even more unreadable -                                                    local ok -                                                    start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) -                                                    if ok then -                                                        success = true -                                                        break -                                                    end -                                                end -                                            else -                                                report_missing_cache(typ,lookupname) -                                            end -                                        end -                                        if start then start = start.next end -                                    else -                                        start = start.next -                                    end -                                else -                                    start = start.next -                                end -                            elseif id == whatsit_code then -                                local subtype = start.subtype -                                if subtype == dir_code then -                                    local dir = start.dir -                                    if     dir == "+TRT" or dir == "+TLT" then -                                        topstack = topstack + 1 -                                        dirstack[topstack] = dir -                                    elseif dir == "-TRT" or dir == "-TLT" then -                                        topstack = topstack - 1 -                                    end -                                    local newdir = dirstack[topstack] -                                    if newdir == "+TRT" then -                                        rlmode = -1 -                                    elseif newdir == "+TLT" then -                                        rlmode = 1 -                                    else -                                        rlmode = rlparmode -                                    end -                                    if trace_directions then -                                        report_process("directions after txtdir %s: txtdir=%s:%s, parmode=%s, txtmode=%s",dir,topstack,newdir or "unset",rlparmode,rlmode) -                                    end -                                elseif subtype == localpar_code then -                                    local dir = start.dir -                                    if dir == "TRT" then -                                        rlparmode = -1 -                                    elseif dir == "TLT" then -                                        rlparmode = 1 -                                    else -                                        rlparmode = 0 -                                    end -                                    rlmode = rlparmode -                                    if trace_directions then -                                        report_process("directions after pardir %s: parmode=%s, txtmode=%s",dir,rlparmode,rlmode) -                                    end -                                end -                                start = start.next -                            else -                                start = start.next -                            end -                        end -                    end -                end -                if success then -                    done = true -                end -                if trace_steps then -- ? -                    registerstep(head) -                end -            end -        end -    end -    return head, done -end - -local function generic(lookupdata,lookupname,unicode,lookuphash) -    local target = lookuphash[lookupname] -    if target then -        target[unicode] = lookupdata -    else -        lookuphash[lookupname] = { [unicode] = lookupdata } -    end -end - -local action = { - -    substitution = generic, -    multiple     = generic, -    alternate    = generic, -    position     = generic, - -    ligature = function(lookupdata,lookupname,unicode,lookuphash) -        local target = lookuphash[lookupname] -        if not target then -            target = { } -            lookuphash[lookupname] = target -        end -        for i=1,#lookupdata do -            local li = lookupdata[i] -            local tu = target[li] -            if not tu then -                tu = { } -                target[li] = tu -            end -            target = tu -        end -        target.ligature = unicode -    end, - -    pair = function(lookupdata,lookupname,unicode,lookuphash) -        local target = lookuphash[lookupname] -        if not target then -            target = { } -            lookuphash[lookupname] = target -        end -        local others = target[unicode] -        local paired = lookupdata[1] -        if others then -            others[paired] = lookupdata -        else -            others = { [paired] = lookupdata } -            target[unicode] = others -        end -    end, - -} - -local function prepare_lookups(tfmdata) - -    local rawdata          = tfmdata.shared.rawdata -    local resources        = rawdata.resources -    local lookuphash       = resources.lookuphash -    local anchor_to_lookup = resources.anchor_to_lookup -    local lookup_to_anchor = resources.lookup_to_anchor -    local lookuptypes      = resources.lookuptypes -    local characters       = tfmdata.characters -    local descriptions     = tfmdata.descriptions - -    -- we cannot free the entries in the descriptions as sometimes we access -    -- then directly (for instance anchors) ... selectively freeing does save -    -- much memory as it's only a reference to a table and the slot in the -    -- description hash is not freed anyway - -    for unicode, character in next, characters do -- we cannot loop over descriptions ! - -        local description = descriptions[unicode] - -        if description then - -            local lookups = description.slookups -            if lookups then -                for lookupname, lookupdata in next, lookups do -                    action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) -                end -            end - -            local lookups = description.mlookups -            if lookups then -                for lookupname, lookuplist in next, lookups do -                    local lookuptype = lookuptypes[lookupname] -                    for l=1,#lookuplist do -                        local lookupdata = lookuplist[l] -                        action[lookuptype](lookupdata,lookupname,unicode,lookuphash) -                    end -                end -            end - -            local list = description.kerns -            if list then -                for lookup, krn in next, list do  -- ref to glyph, saves lookup -                    local target = lookuphash[lookup] -                    if target then -                        target[unicode] = krn -                    else -                        lookuphash[lookup] = { [unicode] = krn } -                    end -                end -            end - -            local list = description.anchors -            if list then -                for typ, anchors in next, list do -- types -                    if typ == "mark" or typ == "cexit" then -- or entry? -                        for name, anchor in next, anchors do -                            local lookups = anchor_to_lookup[name] -                            if lookups then -                                for lookup, _ in next, lookups do -                                    local target = lookuphash[lookup] -                                    if target then -                                        target[unicode] = anchors -                                    else -                                        lookuphash[lookup] = { [unicode] = anchors } -                                    end -                                end -                            end -                        end -                    end -                end -            end - -        end - -    end - -end - -local function split(replacement,original) -    local result = { } -    for i=1,#replacement do -        result[original[i]] = replacement[i] -    end -    return result -end - --- not shared as we hook into lookups now - ---~ local function uncover_1(covers,result) -- multiple covers ---~     local nofresults = #result ---~     for n=1,#covers do ---~         nofresults = nofresults + 1 ---~         local u = { } ---~         local c = covers[n] ---~         for i=1,#c do ---~             u[c[i]] = true ---~         end ---~         result[nofresults] = u ---~     end ---~ end - ---~ local function uncover_2(covers,result) -- single covers (turned into multiple with n=1) ---~     local nofresults = #result ---~     for n=1,#covers do ---~         nofresults = nofresults + 1 ---~         result[nofresults] = { [covers[n]] = true } ---~     end ---~ end - ---~ local function uncover_1(covers,result) -- multiple covers ---~     local nofresults = #result ---~     for n=1,#covers do ---~         nofresults = nofresults + 1 ---~         result[nofresults] = covers[n] ---~     end ---~ end - ---~ local function prepare_contextchains(tfmdata) ---~     local rawdata    = tfmdata.shared.rawdata ---~     local resources  = rawdata.resources ---~     local lookuphash = resources.lookuphash ---~     local lookups    = rawdata.lookups ---~     if lookups then ---~         for lookupname, lookupdata in next, rawdata.lookups do ---~             local lookuptype = lookupdata.type ---~             if not lookuptype then ---~                 report_prepare("missing lookuptype for %s",lookupname) ---~             else -- => lookuphash[lookupname][unicode] ---~                 local rules = lookupdata.rules ---~                 if rules then ---~                     local fmt = lookupdata.format ---~                  -- if fmt == "coverage" then ---~                     if fmt == "coverage" or fmt == "glyphs" then ---~                         if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then ---~                             -- todo: dejavu-serif has one (but i need to see what use it has) ---~                             report_prepare("unsupported coverage %s for %s",lookuptype,lookupname) ---~                         else ---~                             local contexts = lookuphash[lookupname] ---~                             if not contexts then ---~                                 contexts = { } ---~                                 lookuphash[lookupname] = contexts ---~                             end ---~                             local t, nt = { }, 0 ---~                             for nofrules=1,#rules do -- does #rules>1 happen often? ---~                                 local rule     = rules[nofrules] ---~                                 local current  = rule.current ---~                                 local before   = rule.before ---~                                 local after    = rule.after ---~                                 local sequence = { } ---~                                 if before then ---~                                     uncover_1(before,sequence) ---~                                 end ---~                                 local start = #sequence + 1 ---~                                 uncover_1(current,sequence) ---~                                 local stop = #sequence ---~                                 if after then ---~                                     uncover_1(after,sequence) ---~                                 end ---~                                 if sequence[1] then ---~                                     nt = nt + 1 ---~                                     t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } ---~                                     for unic, _ in next, sequence[start] do ---~                                         local cu = contexts[unic] ---~                                         if not cu then ---~                                             contexts[unic] = t ---~                                         end ---~                                     end ---~                                 end ---~                             end ---~                         end ---~                     elseif fmt == "reversecoverage" then -- we could combine both branches (only dufference is replacements) ---~                         if lookuptype ~= "reversesub" then ---~                             report_prepare("unsupported reverse coverage %s for %s",lookuptype,lookupname) ---~                         else ---~                             local contexts = lookuphash[lookupname] ---~                             if not contexts then ---~                                 contexts = { } ---~                                 lookuphash[lookupname] = contexts ---~                             end ---~                             local t, nt = { }, 0 ---~                             for nofrules=1,#rules do ---~                                 local rule         = rules[nofrules] ---~                                 local current      = rule.current ---~                                 local before       = rule.before ---~                                 local after        = rule.after ---~                                 local replacements = rule.replacements ---~                                 local sequence     = { } ---~                                 if before then ---~                                     uncover_1(before,sequence) ---~                                 end ---~                                 local start = #sequence + 1 ---~                                 uncover_1(current,sequence) ---~                                 local stop = #sequence ---~                                 if after then ---~                                     uncover_1(after,sequence) ---~                                 end ---~                                 if sequence[1] then ---~                                     nt = nt + 1 ---~                                     t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } ---~                                     for unic, _  in next, sequence[start] do ---~                                         local cu = contexts[unic] ---~                                         if not cu then ---~                                             contexts[unic] = t ---~                                         end ---~                                     end ---~                                 end ---~                             end ---~                         end ---~                  -- elseif fmt == "glyphs" then --maybe just make then before = { fore } and share with coverage ---~                  --     if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then ---~                  --         report_prepare("unsupported coverage %s for %s",lookuptype,lookupname) ---~                  --     else ---~                  --         local contexts = lookuphash[lookupname] ---~                  --         if not contexts then ---~                  --             contexts = { } ---~                  --             lookuphash[lookupname] = contexts ---~                  --         end ---~                  --         local t, nt = { }, 0 ---~                  --         for nofrules=1,#rules do -- we can make glyphs a special case (less tables) ---~                  --             local rule     = rules[nofrules] ---~                  --             local current  = rule.names ---~                  --             local before   = rule.fore ---~                  --             local after    = rule.back ---~                  --             local sequence = { } ---~                  --             if before then ---~                  --                 uncover_1(before,sequence) ---~                  --             end ---~                  --             local start = #sequence + 1 ---~                  --             uncover_1(current,sequence) ---~                  --             local stop = #sequence ---~                  --             if after then ---~                  --                 uncover_1(after,sequence) ---~                  --             end ---~                  --             if sequence then ---~                  --                 nt = nt + 1 ---~                  --                 t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } ---~                  --                 for unic, _ in next, sequence[start] do ---~                  --                     local cu = contexts[unic] ---~                  --                     if not cu then ---~                  --                         contexts[unic] = t ---~                  --                     end ---~                  --                 end ---~                  --             end ---~                  --         end ---~                  --     end ---~                     end ---~                 end ---~             end ---~         end ---~     end ---~ end - -local valid = { -    coverage        = { chainsub = true, chainpos = true, contextsub = true }, -    reversecoverage = { reversesub = true }, -    glyphs          = { chainsub = true, chainpos = true }, -} - -local function prepare_contextchains(tfmdata) -    local rawdata    = tfmdata.shared.rawdata -    local resources  = rawdata.resources -    local lookuphash = resources.lookuphash -    local lookups    = rawdata.lookups -    if lookups then -        for lookupname, lookupdata in next, rawdata.lookups do -            local lookuptype = lookupdata.type -            if lookuptype then -                local rules = lookupdata.rules -                if rules then -                    local format = lookupdata.format -                    local validformat = valid[format] -                    if not validformat then -                        report_prepare("unsupported format %s",format) -                    elseif not validformat[lookuptype] then -                        -- todo: dejavu-serif has one (but i need to see what use it has) -                        report_prepare("unsupported %s %s for %s",format,lookuptype,lookupname) -                    else -                        local contexts = lookuphash[lookupname] -                        if not contexts then -                            contexts = { } -                            lookuphash[lookupname] = contexts -                        end -                        local t, nt = { }, 0 -                        for nofrules=1,#rules do -                            local rule         = rules[nofrules] -                            local current      = rule.current -                            local before       = rule.before -                            local after        = rule.after -                            local replacements = rule.replacements -                            local sequence     = { } -                            local nofsequences = 0 -                            -- Wventually we can store start, stop and sequence in the cached file -                            -- but then less sharing takes place so best not do that without a lot -                            -- of profiling so let's forget about it. -                            if before then -                                for n=1,#before do -                                    nofsequences = nofsequences + 1 -                                    sequence[nofsequences] = before[n] -                                end -                            end -                            local start = nofsequences + 1 -                            for n=1,#current do -                                nofsequences = nofsequences + 1 -                                sequence[nofsequences] = current[n] -                            end -                            local stop = nofsequences -                            if after then -                                for n=1,#after do -                                    nofsequences = nofsequences + 1 -                                    sequence[nofsequences] = after[n] -                                end -                            end -                            if sequence[1] then -                                -- Replacements only happen with reverse lookups as they are single only. We -                                -- could pack them into current (replacement value instead of true) and then -                                -- use sequence[start] instead but it's somewhat ugly. -                                nt = nt + 1 -                                t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } -                                for unic, _  in next, sequence[start] do -                                    local cu = contexts[unic] -                                    if not cu then -                                        contexts[unic] = t -                                    end -                                end -                            end -                        end -                    end -                else -                    -- no rules -                end -            else -                report_prepare("missing lookuptype for %s",lookupname) -            end -        end -    end -end - --- we can consider lookuphash == false (initialized but empty) vs lookuphash == table - -local function featuresinitializer(tfmdata,value) -    if true then -- value then -        -- beware we need to use the topmost properties table -        local rawdata    = tfmdata.shared.rawdata -        local properties = rawdata.properties -        if not properties.initialized then -            local starttime = trace_preparing and os.clock() -            local resources = rawdata.resources -            resources.lookuphash = resources.lookuphash or { } -            prepare_contextchains(tfmdata) -            prepare_lookups(tfmdata) -            properties.initialized = true -            if trace_preparing then -                report_prepare("preparation time is %0.3f seconds for %s",os.clock()-starttime,tfmdata.properties.fullname or "?") -            end -        end -    end -end - -registerotffeature { -    name         = "features", -    description  = "features", -    default      = true, -    initializers = { -        position = 1, -        node     = featuresinitializer, -    }, -    processors   = { -        node     = featuresprocessor, -    } -}  | 
