mlib-ctx.lua /size: 11 Kb    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['mlib-ctx'] = {
2    version   = 1.001,
3    comment   = "companion to mlib-ctx.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 type, tostring = type, tostring
10local format, concat = string.format, table.concat
11local settings_to_hash = utilities.parsers.settings_to_hash
12local formatters = string.formatters
13
14local report_metapost = logs.reporter ("metapost")
15local status_metapost = logs.messenger("metapost")
16
17local starttiming     = statistics.starttiming
18local stoptiming      = statistics.stoptiming
19
20local trace_graphic   = false
21
22trackers.register("metapost.graphics",
23    function(v) trace_graphic = v end
24);
25
26local mplib            = mplib
27
28metapost               = metapost or { }
29local metapost         = metapost
30local context          = context
31
32local setters          = tokens.setters
33local setmacro         = setters.macro
34local implement        = interfaces.implement
35
36local v_no             = interfaces.variables.no
37
38local extensiondata    = metapost.extensiondata or storage.allocate { }
39metapost.extensiondata = extensiondata
40
41storage.register("metapost/extensiondata",extensiondata,"metapost.extensiondata")
42
43function metapost.setextensions(instances,data)
44    if data and data ~= "" then
45        extensiondata[#extensiondata+1] = {
46            usedinall  = not instances or instances == "",
47            instances  = settings_to_hash(instances or ""),
48            extensions = data,
49        }
50    end
51end
52
53function metapost.getextensions(instance,state)
54    if state and state == v_no then
55        return ""
56    else
57        local t = { }
58        for i=1,#extensiondata do
59            local e = extensiondata[i]
60            local status = e.instances[instance]
61            if (status ~= true) and (e.usedinall or status) then
62                t[#t+1] = e.extensions
63                e.instances[instance] = true
64            end
65        end
66        return concat(t," ")
67    end
68end
69
70implement {
71    name      = "setmpextensions",
72    actions   = metapost.setextensions,
73    arguments = "2 strings",
74}
75
76implement {
77    name      = "getmpextensions",
78    actions   = { metapost.getextensions, context } ,
79    arguments = "string"
80}
81
82local patterns = {
83    CONTEXTLMTXMODE > 0 and "meta-imp-%s.mkxl" or "",
84    "meta-imp-%s.mkiv",
85    "meta-imp-%s.tex",
86    -- obsolete:
87    "meta-%s.mkiv",
88    "meta-%s.tex"
89}
90
91local function action(name,foundname)
92    commands.loadlibrary(name,foundname,false)
93    status_metapost("library %a is loaded",name)
94end
95
96local function failure(name)
97    report_metapost("library %a is unknown or invalid",name)
98end
99
100implement {
101    name      = "useMPlibrary",
102    arguments = "string",
103    actions   = function(name)
104        resolvers.uselibrary {
105            name     = name,
106            patterns = patterns,
107            action   = action,
108            failure  = failure,
109            onlyonce = true,
110        }
111    end
112}
113
114-- metapost.variables = { } -- to be stacked
115
116implement {
117    name      = "mprunvar",
118    arguments = "string",
119    actions   = function(name)
120        local value = metapost.variables[name]
121        if value ~= nil then
122            local tvalue = type(value)
123            if tvalue == "table" then
124                context(concat(value," "))
125            elseif tvalue == "number" or tvalue == "boolean" then
126                context(tostring(value))
127            elseif tvalue == "string" then
128                context(value)
129            end
130        end
131    end
132}
133
134implement {
135    name      = "mpruntab",
136    arguments = { "string", "integer" },
137    actions   = function(name,n)
138        local value = metapost.variables[name]
139        if value ~= nil then
140            local tvalue = type(value)
141            if tvalue == "table" then
142                context(value[n])
143            elseif tvalue == "number" or tvalue == "boolean" then
144                context(tostring(value))
145            elseif tvalue == "string" then
146                context(value)
147            end
148        end
149    end
150}
151
152implement {
153    name      = "mprunset",
154    arguments = "2 strings",
155    actions   = function(name,connector)
156        local value = metapost.variables[name]
157        if value ~= nil then
158            local tvalue = type(value)
159            if tvalue == "table" then
160                context(concat(value,connector))
161            elseif tvalue == "number" or tvalue == "boolean" then
162                context(tostring(value))
163            elseif tvalue == "string" then
164                context(value)
165            end
166        end
167    end
168}
169
170-- we need to move more from pps to here as pps is the plugin .. the order is a mess
171-- or just move the scanners to pps
172
173function metapost.graphic(specification)
174    metapost.pushformat(specification)
175    metapost.graphic_base_pass(specification)
176    metapost.popformat()
177end
178
179function metapost.startgraphic(t)
180    if not t then
181        t = { }
182    end
183    if not t.instance then
184        t.instance = metapost.defaultinstance
185    end
186    if not t.format then
187        t.format = metapost.defaultformat
188    end
189    if not t.method then
190        t.method = metapost.defaultmethod
191    end
192    t.data = { }
193    return t
194end
195
196function metapost.stopgraphic(t)
197    if t then
198        t.data = concat(t.data or { },"\n")
199        if trace_graphic then
200            report_metapost("\n"..t.data.."\n")
201        end
202        metapost.graphic(t)
203    end
204end
205
206function metapost.tographic(t,f,s,...)
207    local d = t.data
208    d[#d+1] = s and formatters[f](s,...) or f
209end
210
211implement {
212    name      = "mpgraphic",
213    actions   = metapost.graphic,
214    arguments = {
215        {
216            { "instance" },
217            { "format" },
218            { "data" },
219            { "initializations" },
220            { "extensions" },
221            { "inclusions" },
222            { "definitions" },
223            { "figure" },
224            { "method" },
225            { "namespace" },
226        }
227    }
228}
229
230implement {
231    name      = "mpsetoutercolor",
232    actions   = function(...) metapost.setoutercolor(...) end, -- not yet implemented
233    arguments = { "integer", "integer", "integer", "integer" }
234}
235
236implement {
237    name      = "mpflushreset",
238    actions   = function() metapost.flushreset() end -- not yet implemented
239}
240
241implement {
242    name      = "mpflushliteral",
243    actions   = function(str) metapost.flushliteral(str) end, -- not yet implemented
244    arguments = "string",
245}
246
247-- this has to become a codeinjection
248
249function metapost.getclippath(specification) -- why not a special instance for this
250    local mpx  = metapost.pushformat(specification)
251    local data = specification.data or ""
252    if mpx and data ~= "" then
253        starttiming(metapost)
254        starttiming(metapost.exectime)
255        local result = mpx:execute ( format ( "%s;%s;beginfig(1);%s;%s;endfig;",
256            specification.extensions or "",
257            specification.inclusions or "",
258            specification.initializations or "",
259            data
260        ) )
261        stoptiming(metapost.exectime)
262        if result.status > 0 then
263            report_metapost("%s: %s", result.status, result.error or result.term or result.log)
264            result = nil
265        else
266            result = metapost.filterclippath(result)
267        end
268        stoptiming(metapost)
269        metapost.popformat()
270        return result
271    else
272        metapost.popformat()
273    end
274end
275
276function metapost.filterclippath(result)
277    if result then
278        local figures = result.fig
279        if figures and #figures > 0 then
280            local figure = figures[1]
281            local objects = figure:objects()
282            if objects then
283                local lastclippath
284                for o=1,#objects do
285                    local object = objects[o]
286                    if object.type == "start_clip" then
287                        lastclippath = object.path
288                    end
289                end
290                return lastclippath
291            end
292        end
293    end
294end
295
296function metapost.theclippath(...)
297    local result = metapost.getclippath(...)
298    if result then -- we could just print the table
299        return concat(metapost.flushnormalpath(result)," ")
300    else
301        return ""
302    end
303end
304
305implement {
306    name      = "mpsetclippath",
307    actions   = function(specification)
308        local p = specification.data and metapost.theclippath(specification)
309        if not p or p == "" then
310            local b = number.dimenfactors.bp
311            local w = b * (specification.width or 0)
312            local h = b * (specification.height or 0)
313            p = formatters["0 0 m %.6N 0 l %.6N %.6N l 0 %.6N l"](w,w,h,h)
314        end
315        setmacro("MPclippath",p,"global")
316    end,
317    arguments = {
318        {
319            { "instance" },
320            { "format" },
321            { "data" },
322            { "initializations" },
323            { "useextensions" },
324            { "inclusions" },
325            { "method" },
326            { "namespace" },
327            { "width", "dimension" },
328            { "height", "dimension" },
329        },
330    }
331}
332
333statistics.register("metapost", function()
334    local n = metapost.nofruns
335    if n and n > 0 then
336        local elapsedtime = statistics.elapsedtime
337        local elapsed     = statistics.elapsed
338        local runs, stats = metapost.nofscriptruns()
339        local instances,
340              memory      = metapost.getstatistics(true)
341        return format("%s seconds, loading: %s, execution: %s, n: %s, average: %s, instances: %i, luacalls: %s, memory: %0.3f M",
342            elapsedtime(metapost), elapsedtime(mplib), elapsedtime(metapost.exectime), n,
343            elapsedtime((elapsed(metapost) + elapsed(mplib) + elapsed(metapost.exectime)) / n),
344            instances, stats and stats or runs, memory/(1024*1024))
345    else
346        return nil
347    end
348end)
349
350-- only used in graphictexts
351
352metapost.tex = metapost.tex or { }
353local mptex  = metapost.tex
354
355local environments = { }
356
357function mptex.set(str)
358    environments[#environments+1] = str
359end
360
361function mptex.setfrombuffer(name)
362    environments[#environments+1] = buffers.getcontent(name)
363end
364
365function mptex.get()
366    return concat(environments,"\n")
367end
368
369function mptex.reset()
370    environments = { }
371end
372
373implement {
374    name      = "mppushvariables",
375    actions   = metapost.pushvariables,
376}
377
378implement {
379    name      = "mppopvariables",
380    actions   = metapost.popvariables,
381}
382
383implement {
384    name      = "mptexset",
385    arguments = "string",
386    actions   = mptex.set
387}
388
389implement {
390    name      = "mptexsetfrombuffer",
391    arguments = "string",
392    actions   = mptex.setfrombuffer
393}
394
395implement {
396    name      = "mptexget",
397    actions   = { mptex.get, context }
398}
399
400implement {
401    name      = "mptexreset",
402    actions   = mptex.reset
403}
404
405-- moved from mlib-lua:
406
407mp = mp or {  -- system namespace
408    set    = { },
409    get    = { },
410    aux    = { },
411    scan   = { },
412    skip   = { },
413    inject = { },
414}
415
416MP = MP or { } -- user namespace
417
418-- We had this:
419--
420--   table.setmetatablecall(mp,function(t,k) mpprint(k) end)
421--
422-- but the next one is more interesting because we cannot use calls like:
423--
424--   lua.mp.somedefdname("foo")
425--
426-- which is due to expansion of somedefdname during suffix creation. So:
427--
428--   lua.mp("somedefdname","foo")
429
430table.setmetatablecall(mp,function(t,k,...) return t[k](...) end)
431table.setmetatablecall(MP,function(t,k,...) return t[k](...) end)
432