font-cid.lua /size: 5502 b    last modification: 2020-07-01 14:35
1if not modules then modules = { } end modules ['font-cid'] = {
2    version   = 1.001,
3    comment   = "companion to font-ini.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9local format, match, lower = string.format, string.match, string.lower
10local tonumber = tonumber
11local P, S, R, C, V, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.match
12
13local fonts, logs, trackers = fonts, logs, trackers
14
15local trace_loading = false  trackers.register("otf.loading", function(v) trace_loading = v end)
16
17local report_otf    = logs.reporter("fonts","otf loading")
18
19local cid           = { }
20fonts.cid           = cid
21
22local cidmap        = { }
23local cidmax        = 10
24
25-- original string parser: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap
26--
27-- 18964 18964 (leader)
28-- 0 /.notdef
29-- 1..95 0020
30-- 99 3000
31
32local number  = C(R("09","af","AF")^1)
33local space   = S(" \n\r\t")
34local spaces  = space^0
35local period  = P(".")
36local periods = period * period
37local name    = P("/") * C((1-space)^1)
38
39local unicodes, names = { }, { } -- we could use Carg now
40
41local function do_one(a,b)
42    unicodes[tonumber(a)] = tonumber(b,16)
43end
44
45local function do_range(a,b,c)
46    c = tonumber(c,16)
47    for i=tonumber(a),tonumber(b) do
48        unicodes[i] = c
49        c = c + 1
50    end
51end
52
53local function do_name(a,b)
54    names[tonumber(a)] = b
55end
56
57local grammar = P { "start",
58    start  = number * spaces * number * V("series"),
59    series = (spaces * (V("one") + V("range") + V("named")))^1,
60    one    = (number * spaces  * number) / do_one,
61    range  = (number * periods * number * spaces * number) / do_range,
62    named  = (number * spaces  * name) / do_name
63}
64
65local function loadcidfile(filename)
66    local data = io.loaddata(filename)
67    if data then
68        unicodes, names = { }, { }
69        lpegmatch(grammar,data)
70        local supplement, registry, ordering = match(filename,"^(.-)%-(.-)%-()%.(.-)$")
71        return {
72            supplement = supplement,
73            registry   = registry,
74            ordering   = ordering,
75            filename   = filename,
76            unicodes   = unicodes,
77            names      = names,
78        }
79    end
80end
81
82cid.loadfile   = loadcidfile -- we use the frozen variant
83local template = "%s-%s-%s.cidmap"
84
85local function locate(registry,ordering,supplement)
86    local filename = format(template,registry,ordering,supplement)
87    local hashname = lower(filename)
88    local found    = cidmap[hashname]
89    if not found then
90        if trace_loading then
91            report_otf("checking cidmap, registry %a, ordering %a, supplement %a, filename %a",registry,ordering,supplement,filename)
92        end
93        local fullname = resolvers.findfile(filename,'cid') or ""
94        if fullname ~= "" then
95            found = loadcidfile(fullname)
96            if found then
97                if trace_loading then
98                    report_otf("using cidmap file %a",filename)
99                end
100                cidmap[hashname] = found
101                found.usedname = file.basename(filename)
102            end
103        end
104    end
105    return found
106end
107
108-- cf Arthur R. we can safely scan upwards since cids are downward compatible
109
110function cid.getmap(specification)
111    if not specification then
112        report_otf("invalid cidinfo specification, table expected")
113        return
114    end
115    local registry   = specification.registry
116    local ordering   = specification.ordering
117    local supplement = specification.supplement
118    local filename   = format(registry,ordering,supplement)
119    local lowername  = lower(filename)
120    local found      = cidmap[lowername]
121    if found then
122        return found
123    end
124    if ordering == "Identity" then
125        local found = {
126            supplement = supplement,
127            registry   = registry,
128            ordering   = ordering,
129            filename   = filename,
130            unicodes   = { },
131            names      = { },
132        }
133        cidmap[lowername] = found
134        return found
135    end
136    -- check for already loaded file
137    if trace_loading then
138        report_otf("cidmap needed, registry %a, ordering %a, supplement %a",registry,ordering,supplement)
139    end
140    found = locate(registry,ordering,supplement)
141    if not found then
142        local supnum = tonumber(supplement)
143        local cidnum = nil
144        -- next highest (alternatively we could start high)
145        if supnum < cidmax then
146            for s=supnum+1,cidmax do
147                local c = locate(registry,ordering,s)
148                if c then
149                    found, cidnum = c, s
150                    break
151                end
152            end
153        end
154        -- next lowest (least worse fit)
155        if not found and supnum > 0 then
156            for s=supnum-1,0,-1 do
157                local c = locate(registry,ordering,s)
158                if c then
159                    found, cidnum = c, s
160                    break
161                end
162            end
163        end
164        -- prevent further lookups -- somewhat tricky
165        registry = lower(registry)
166        ordering = lower(ordering)
167        if found and cidnum > 0 then
168            for s=0,cidnum-1 do
169                local filename = format(template,registry,ordering,s)
170                if not cidmap[filename] then
171                    cidmap[filename] = found
172                end
173            end
174        end
175    end
176    return found
177end
178