meta-fnt.lua /size: 8841 b    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['meta-fnt'] = {
2    version   = 1.001,
3    comment   = "companion to meta-fnt.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 next = next
10local concat     = table.concat
11local format     = string.format
12local formatters = string.formatters
13local chardata   = characters.data
14local fontdata   = fonts.hashes.identifiers
15local round      = math.round
16
17local vffonts    = fonts.handlers.vf
18
19local mpfonts    = fonts.mp or { }
20fonts.mp         = mpfonts
21
22mpfonts.version  = mpfonts.version or 1.20
23mpfonts.inline   = true
24mpfonts.cache    = containers.define("fonts", "mp", mpfonts.version, true)
25
26metapost.fonts   = metapost.fonts or { }
27
28local function unicodetoactualtext(...)
29    unicodetoactualtext = backends.codeinjections.unicodetoactualtext
30    return unicodetoactualtext(...)
31end
32
33-- a few glocals
34
35local characters, descriptions = { }, { }
36local factor, code, slot, width, height, depth, total, variants, bbox, llx, lly, urx, ury = 100, { }, 0, 0, 0, 0, 0, 0, true, 0, 0, 0, 0
37
38local flusher = {
39    startfigure = function(_chr_,_llx_,_lly_,_urx_,_ury_)
40        code   = { }
41        slot   = _chr_
42        llx    = _llx_
43        lly    = _lly_
44        urx    = _urx_
45        ury    = _ury_
46        width  = urx - llx
47        height = ury
48        depth  = -lly
49        total  = total + 1
50        inline = mpfonts.inline
51    end,
52    flushfigure = function(t)
53        for i=1,#t do
54            code[#code+1] = t[i]
55        end
56    end,
57    stopfigure = function()
58        local cd = chardata[n]
59        local code = unicodetoactualtext(slot,concat(code," ")) or ""
60        descriptions[slot] = {
61        --  unicode     = slot,
62            name        = cd and cd.adobename,
63            width       = width * 100,
64            height      = height * 100,
65            depth       = depth * 100,
66            boundingbox = { llx, lly, urx, ury },
67        }
68        if inline then
69            characters[slot] = {
70                commands = {
71                    { "pdf", "origin", code },
72                }
73            }
74        else
75            characters[slot] = {
76                commands = {
77                    {
78                        "image",
79                        {
80                            stream = code,
81                            bbox   = { 0, -depth * 65536, width * 65536, height * 65536 }
82                        },
83                    },
84                }
85            }
86        end
87        code = nil -- no need to keep that
88    end
89}
90
91local function process(mpxformat,name,instances,scalefactor)
92    local filename = resolvers.findfile(name)
93    local attributes = filename and lfs.isfile(filename) and lfs.attributes(filename)
94    if attributes then
95        statistics.starttiming(metapost.fonts)
96        scalefactor = scalefactor or 1
97        instances = instances or metapost.fonts.instances or 1 -- maybe store in liost too
98        local fontname = file.removesuffix(file.basename(name))
99        local modification = attributes.modification
100        local filesize = attributes.size
101        local hash = file.robustname(formatters["%s %05i %03i"](fontname,round(scalefactor*1000),instances))
102        local lists = containers.read(mpfonts.cache,hash)
103        if not lists or lists.modification ~= modification or lists.filesize ~= filesize or lists.instances ~= instances or lists.scalefactor ~= scalefactor then
104            statistics.starttiming(flusher)
105            local data = io.loaddata(filename)
106            metapost.reset(mpxformat)
107            metapost.setoutercolor(2) -- no outer color and no reset either
108            lists = { }
109            for i=1,instances do
110                characters   = { }
111                descriptions = { }
112                metapost.process {
113                    mpx         = mpxformat,
114                    flusher     = flusher,
115                    askedfig    = "all",
116                 -- incontext   = false,
117                    data        = {
118                        formatters["randomseed := %s ;"](i*10),
119                        formatters["charscale  := %s ;"](scalefactor),
120                        data,
121                    },
122                }
123                lists[i] = {
124                    characters   = characters,
125                    descriptions = descriptions,
126                    parameters   = {
127                        designsize    = 655360,
128                        slant         =      0,
129                        space         =    333   * scalefactor,
130                        space_stretch =    166.5 * scalefactor,
131                        space_shrink  =    111   * scalefactor,
132                        x_height      =    431   * scalefactor,
133                        quad          =   1000   * scalefactor,
134                        extra_space   =      0,
135                    },
136                    properties  = {
137                        name          = formatters["%s-%03i"](hash,i),
138                        virtualized   = true,
139                        spacer        = "space",
140                    }
141                }
142            end
143            lists.version = metapost.variables.fontversion or "1.000"
144            lists.modification = modification
145            lists.filesize = filesize
146            lists.instances = instances
147            lists.scalefactor = scalefactor
148            metapost.reset(mpxformat) -- saves memory
149            lists = containers.write(mpfonts.cache, hash, lists)
150            statistics.stoptiming(flusher)
151        end
152        variants = variants + #lists
153        statistics.stoptiming(metapost.fonts)
154        return lists
155    else
156        return { }
157    end
158end
159
160metapost.fonts.flusher   = flusher
161metapost.fonts.instances = 1
162metapost.fonts.process   = process
163
164local function build(g,v)
165    local size = g.specification.size
166    local data = process(v[2],v[3],v[4],size/655360,v[6])
167    local list = { }
168    local t = { }
169    for d=1,#data do
170        t = fonts.constructors.scale(data[d],-1000)
171        local id = font.nextid()
172        t.fonts = { { id = id } }
173        fontdata[id] = t
174        if v[5] then
175            vffonts.helpers.composecharacters(t)
176        end
177        list[d] = font.define(t)
178    end
179    for k, v in next, t do -- last t
180        g[k] = v -- kind of replace, when not present, make nil
181    end
182    g.properties.virtualized = true
183    g.variants = list
184end
185
186vffonts.combiner.commands.metapost = build
187vffonts.combiner.commands.metafont = build
188
189statistics.register("metapost font generation", function()
190    if total > 0 then
191        local time = statistics.elapsedtime(flusher)
192        if total > 0 then
193            return format("%i glyphs, %s seconds runtime, %.1f glyphs/second", total, time, total/tonumber(time))
194        else
195            return format("%i glyphs, %s seconds runtime", total, time)
196        end
197    end
198end)
199
200statistics.register("metapost font loading",function()
201    if variants > 0 then
202        local time = statistics.elapsedtime(metapost.fonts)
203        if variants > 0 then
204            return format("%s seconds, %i instances, %.3f instances/second", time, variants, variants/tonumber(time))
205        else
206            return format("%s seconds, %i instances", time, variants)
207        end
208    end
209end)
210
211-- fonts.definers.methods.install( "bidi", {
212--     {
213--         "metapost",    -- method
214--         "metafun",     -- format
215--         "fontoeps.mp", -- filename
216--         1,             -- instances
217--         false,         -- compose
218--     },
219-- } )
220
221local report = logs.reporter("metapost","fonts")
222
223function metapost.fonts.define(specification)
224    local fontname = specification.fontname or ""
225    local filename = specification.filename or ""
226    local format   = specification.format   or "metafun"
227    if fontname == "" then
228        report("no fontname given")
229        return
230    end
231    if filename == "" then
232        report("no filename given for %a",fontname)
233        return
234    end
235    local fullname = resolvers.findfile(filename)
236    if fullname == "" then
237        report("unable to locate file %a",filename)
238        return
239    end
240    report("generating font %a using format %a and file %a",fontname,format,filename)
241    fonts.definers.methods.install(fontname, {
242        {
243            specification.engine    or "metapost",
244            format,
245            filename,
246            specification.instances or 1,
247            specification.compose   or false,
248        },
249    } )
250end
251
252interfaces.implement {
253    name      = "definemetafont",
254    actions   = metapost.fonts.define,
255    arguments = {
256        {
257            { "fontname" },
258            { "filename" },
259        }
260    }
261}
262
263-- metapost.fonts.define {
264--     fontname = "bidi",
265--     filename = "bidi-symbols.mp",
266-- }
267