summaryrefslogtreecommitdiff
path: root/otfl-font-cid.lua
blob: 4a4c4d20916322e1af339b54e1d9b07c935955e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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