summaryrefslogtreecommitdiff
path: root/otfl-font-map.lua
blob: 9e85516d6bff5c121fcb1ac42df75904664b1335 (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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
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 = string.match, string.format, string.find, table.concat, string.gsub
local lpegmatch = lpeg.match

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

local ctxcatcodes = tex and tex.ctxcatcodes

--[[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]]--

fonts               = fonts               or { }
fonts.map           = fonts.map           or { }
fonts.map.data      = fonts.map.data      or { }
fonts.map.encodings = fonts.map.encodings or { }
fonts.map.done      = fonts.map.done      or { }
fonts.map.loaded    = fonts.map.loaded    or { }
fonts.map.direct    = fonts.map.direct    or { }
fonts.map.line      = fonts.map.line      or { }

function fonts.map.line.pdfmapline(tag,str)
    return "\\loadmapline[" .. tag .. "][" .. str .. "]"
end

function fonts.map.line.pdftex(e) -- so far no combination of slant and extend
    if e.name and e.fontfile then
        local fullname = e.fullname or ""
        if e.slant and e.slant ~= 0 then
            if e.encoding then
                return fonts.map.line.pdfmapline("=",format('%s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.fontfile))
            else
                return fonts.map.line.pdfmapline("=",format('%s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.fontfile))
            end
        elseif e.extend and e.extend ~= 1 and e.extend ~= 0 then
            if e.encoding then
                return fonts.map.line.pdfmapline("=",format('%s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.extend,e.encoding,e.fontfile))
            else
                return fonts.map.line.pdfmapline("=",format('%s %s "%g ExtendFont" <%s',e.name,fullname,e.extend,e.fontfile))
            end
        else
            if e.encoding then
                return fonts.map.line.pdfmapline("=",format('%s %s <%s <%s',e.name,fullname,e.encoding,e.fontfile))
            else
                return fonts.map.line.pdfmapline("=",format('%s %s <%s',e.name,fullname,e.fontfile))
            end
        end
    else
        return nil
    end
end

function fonts.map.flush(backend) -- will also erase the accumulated data
    local flushline = fonts.map.line[backend or "pdftex"] or fonts.map.line.pdftex
    for _, e in pairs(fonts.map.data) do
        tex.sprint(ctxcatcodes,flushline(e))
    end
    fonts.map.data = { }
end

fonts.map.line.dvips     = fonts.map.line.pdftex
fonts.map.line.dvipdfmx  = function() end

function fonts.map.convert_entries(filename)
    if not fonts.map.loaded[filename] then
        fonts.map.data, fonts.map.encodings = fonts.map.load_file(filename,fonts.map.data, fonts.map.encodings)
        fonts.map.loaded[filename] = true
    end
end

function fonts.map.load_file(filename, entries, encodings)
    entries   = entries   or { }
    encodings = encodings or { }
    local f = io.open(filename)
    if f then
        local data = f:read("*a")
        if data then
            for line in gmatch(data,"(.-)[\n\t]") do
                if find(line,"^[%#%%%s]") then
                    -- print(line)
                else
                    local extend, slant, name, fullname, fontfile, encoding
                    line = gsub(line,'"(.+)"', function(s)
                        extend = find(s,'"([^"]+) ExtendFont"')
                        slant = find(s,'"([^"]+) SlantFont"')
                        return ""
                    end)
                    if not name then
                        -- name fullname encoding fontfile
                        name, fullname, encoding, fontfile = match(line,"^(%S+)%s+(%S*)[%s<]+(%S*)[%s<]+(%S*)%s*$")
                    end
                    if not name then
                        -- name fullname (flag) fontfile encoding
                        name, fullname, fontfile, encoding = match(line,"^(%S+)%s+(%S*)[%d%s<]+(%S*)[%s<]+(%S*)%s*$")
                    end
                    if not name then
                        -- name fontfile
                        name, fontfile = match(line,"^(%S+)%s+[%d%s<]+(%S*)%s*$")
                    end
                    if name then
                        if encoding == "" then encoding = nil end
                        entries[name] = {
                            name     = name, -- handy
                            fullname = fullname,
                            encoding = encoding,
                            fontfile = fontfile,
                            slant    = tonumber(slant),
                            extend   = tonumber(extend)
                        }
                        encodings[name] = encoding
                    elseif line ~= "" then
                    --  print(line)
                    end
                end
            end
        end
        f:close()
    end
    return entries, encodings
end

function fonts.map.load_lum_table(filename)
    local lumname = file.replacesuffix(file.basename(filename),"lum")
    local lumfile = resolvers.find_file(lumname,"map") or ""
    if lumfile ~= "" and lfs.isfile(lumfile) then
        if trace_loading or trace_unimapping then
            logs.report("load otf","enhance: loading %s ",lumfile)
        end
        lumunic = dofile(lumfile)
        return lumunic, lumfile
    end
end

local hex     = lpeg.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     = (lpeg.R("09")^1)  / tonumber
local period  = lpeg.P(".")

local unicode = lpeg.P("uni")   * (hexfour * (period + lpeg.P(-1)) * lpeg.Cc(false) + lpeg.Ct(hexfour^1) * lpeg.Cc(true))
local ucode   = lpeg.P("u")     * (hexsix  * (period + lpeg.P(-1)) * lpeg.Cc(false) + lpeg.Ct(hexsix ^1) * lpeg.Cc(true))
local index   = lpeg.P("index") * dec * lpeg.Cc(false)

local parser  = unicode + ucode + index

local parsers = { }

function fonts.map.make_name_parser(str)
    if not str or str == "" then
        return parser
    else
        local p = parsers[str]
        if not p then
            p = lpeg.P(str) * period * dec * lpeg.Cc(false)
            parsers[str] = p
        end
        return p
    end
end

--~ local parser = fonts.map.make_name_parser("Japan1")
--~ local parser = fonts.map.make_name_parser()
--~ 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")

function fonts.map.tounicode16(unicode)
    if unicode < 0x10000 then
        return format("%04X",unicode)
    else
        return format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00)
    end
end

function fonts.map.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

--~ 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 fonts.map.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