font-cid.lua / last modification: 2020-01-30 14:16
if not modules then modules = { } end modules ['font-cid'] = {
    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 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 fonts, logs, trackers = fonts, logs, trackers

local trace_loading = false  trackers.register("otf.loading", function(v) trace_loading = v end)

local report_otf    = logs.reporter("fonts","otf loading")

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 %a, ordering %a, supplement %a, filename %a",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 %a",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
    local filename   = format(registry,ordering,supplement)
    local lowername  = lower(filename)
    local found      = cidmap[lowername]
    if found then
        return found
    end
    if ordering == "Identity" then
        local found = {
            supplement = supplement,
            registry   = registry,
            ordering   = ordering,
            filename   = filename,
            unicodes   = { },
            names      = { },
        }
        cidmap[lowername] = found
        return found
    end
    -- check for already loaded file
    if trace_loading then
        report_otf("cidmap needed, registry %a, ordering %a, supplement %a",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