summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt')
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt722
1 files changed, 722 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt
new file mode 100644
index 00000000000..eacb148cc6c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt
@@ -0,0 +1,722 @@
+if not modules then modules = { } end modules ['mlib-run'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- cmyk -> done, native
+-- spot -> done, but needs reworking (simpler)
+-- multitone ->
+-- shade -> partly done, todo: cm
+-- figure -> done
+-- hyperlink -> low priority, easy
+
+-- new * run
+-- or
+-- new * execute^1 * finish
+
+-- a*[b,c] == b + a * (c-b)
+
+--[[ldx--
+<p>The directional helpers and pen analysis are more or less translated from the
+<l n='c'/> code. It really helps that Taco know that source so well. Taco and I spent
+quite some time on speeding up the <l n='lua'/> and <l n='c'/> code. There is not
+much to gain, especially if one keeps in mind that when integrated in <l n='tex'/>
+only a part of the time is spent in <l n='metapost'/>. Of course an integrated
+approach is way faster than an external <l n='metapost'/> and processing time
+nears zero.</p>
+--ldx]]--
+
+local type, tostring, tonumber, next = type, tostring, tonumber, next
+local find, striplines = string.find, utilities.strings.striplines
+local concat, insert, remove, sortedkeys = table.concat, table.insert, table.remove, table.sortedkeys
+
+local emptystring = string.is_empty
+
+local trace_graphics = false trackers.register("metapost.graphics", function(v) trace_graphics = v end)
+local trace_tracingall = false trackers.register("metapost.tracingall", function(v) trace_tracingall = v end)
+
+local report_metapost = logs.reporter("metapost")
+local texerrormessage = logs.texerrormessage
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local formatters = string.formatters
+
+local mplib = mplib
+metapost = metapost or { }
+local metapost = metapost
+
+metapost.showlog = false
+metapost.lastlog = ""
+metapost.texerrors = false
+metapost.exectime = metapost.exectime or { } -- hack
+metapost.nofruns = 0
+
+local mpxformats = { }
+local nofformats = 0
+local mpxpreambles = { }
+local mpxterminals = { }
+local mpxextradata = { }
+
+-- The flatten hack is needed because the library currently barks on \n\n and the
+-- collapse because mp cannot handle snippets due to grouping issues.
+
+-- todo: pass tables to executempx instead of preparing beforehand,
+-- as it's more efficient for the terminal
+
+local function flatten(source,target)
+ for i=1,#source do
+ local d = source[i]
+ if type(d) == "table" then
+ flatten(d,target)
+ elseif d and d ~= "" then
+ target[#target+1] = d
+ end
+ end
+ return target
+end
+
+local function prepareddata(data)
+ if data and data ~= "" then
+ if type(data) == "table" then
+ data = flatten(data,{ })
+ data = #data > 1 and concat(data,"\n") or data[1]
+ end
+ return data
+ end
+end
+
+-- local function executempx(mpx,data)
+-- local terminal = mpxterminals[mpx]
+-- if terminal then
+-- terminal.writer(data)
+-- data = ""
+-- elseif type(data) == "table" then
+-- data = prepareddata(data,collapse)
+-- end
+-- metapost.nofruns = metapost.nofruns + 1
+-- return mpx:execute(data)
+-- end
+
+local function executempx(mpx,data)
+ local terminal = mpxterminals[mpx]
+ if terminal then
+ terminal.writer(data)
+ data = nil
+ elseif type(data) == "table" then
+ data = prepareddata(data,collapse)
+ end
+ metapost.nofruns = metapost.nofruns + 1
+ return mpx:execute(data)
+end
+
+directives.register("mplib.texerrors", function(v) metapost.texerrors = v end)
+trackers.register ("metapost.showlog", function(v) metapost.showlog = v end)
+
+function metapost.resetlastlog()
+ metapost.lastlog = ""
+end
+
+local new_instance = mplib.new
+local find_file = mplib.finder
+
+function metapost.reporterror(result)
+ if not result then
+ report_metapost("error: no result object returned")
+ return true
+ elseif result.status == 0 then
+ return false
+ elseif mplib.realtimelogging then
+ return false -- we already reported
+ else
+ local t = result.term
+ local e = result.error
+ local l = result.log
+ local report = metapost.texerrors and texerrormessage or report_metapost
+ if t and t ~= "" then
+ report("mp error: %s",striplines(t))
+ end
+ if e == "" or e == "no-error" then
+ e = nil
+ end
+ if e then
+ report("mp error: %s",striplines(e))
+ end
+ if not t and not e and l then
+ metapost.lastlog = metapost.lastlog .. "\n" .. l
+ report_metapost("log: %s",l)
+ else
+ report_metapost("error: unknown, no error, terminal or log messages")
+ end
+ return true
+ end
+end
+
+local f_preamble = formatters [ [[
+ boolean mplib ; mplib := true ;
+ let dump = endinput ;
+ input "%s" ;
+ randomseed:=%s;
+]] ]
+
+local methods = {
+ double = "double",
+ scaled = "scaled",
+ -- binary = "binary",
+ binary = "double",
+ decimal = "decimal",
+ default = "scaled",
+}
+
+function metapost.runscript(code)
+ return ""
+end
+
+function metapost.scripterror(str)
+ report_metapost("script error: %s",str)
+end
+
+-- todo: random_seed
+
+local seed = nil
+
+function metapost.load(name,method)
+ starttiming(mplib)
+ if not seed then
+ seed = job.getrandomseed()
+ if seed <= 1 then
+ seed = seed % 1000
+ elseif seed > 4095 then
+ seed = seed % 4096
+ end
+ end
+ method = method and methods[method] or "scaled"
+ local mpx, terminal = new_instance {
+-- noninteractive = true;
+-- ini_version = true,
+ math_mode = method,
+ run_script = metapost.runscript,
+ script_error = metapost.scripterror,
+ make_text = metapost.maketext,
+ extensions = 1,
+ -- random_seed = seed,
+ utf8_mode = true,
+ text_mode = true,
+ }
+ report_metapost("initializing number mode %a",method)
+ local result
+ if not mpx then
+ result = { status = 99, error = "out of memory"}
+ else
+ mpxterminals[mpx] = terminal
+ -- pushing permits advanced features
+ metapost.pushscriptrunner(mpx)
+ result = executempx(mpx,f_preamble(file.addsuffix(name,"mp"),seed))
+ metapost.popscriptrunner()
+ end
+ stoptiming(mplib)
+ metapost.reporterror(result)
+ return mpx, result
+end
+
+function metapost.checkformat(mpsinput,method)
+ local mpsinput = mpsinput or "metafun"
+ local foundfile = ""
+ if file.suffix(mpsinput) ~= "" then
+ foundfile = find_file(mpsinput) or ""
+ end
+ -- if foundfile == "" then
+ -- foundfile = find_file(file.replacesuffix(mpsinput,"mpvi")) or ""
+ -- end
+ if foundfile == "" then
+ foundfile = find_file(file.replacesuffix(mpsinput,"mpxl")) or ""
+ end
+ if foundfile == "" then
+ foundfile = find_file(file.replacesuffix(mpsinput,"mpiv")) or ""
+ end
+ if foundfile == "" then
+ foundfile = find_file(file.replacesuffix(mpsinput,"mp")) or ""
+ end
+ if foundfile == "" then
+ report_metapost("loading %a fails, format not found",mpsinput)
+ else
+ report_metapost("loading %a as %a using method %a",mpsinput,foundfile,method or "default")
+ local mpx, result = metapost.load(foundfile,method)
+ if mpx then
+ return mpx
+ else
+ report_metapost("error in loading %a",mpsinput)
+ metapost.reporterror(result)
+ end
+ end
+end
+
+function metapost.unload(mpx)
+ starttiming(mplib)
+ if mpx then
+ mpx:finish()
+ end
+ stoptiming(mplib)
+end
+
+metapost.defaultformat = "metafun"
+metapost.defaultinstance = "metafun"
+metapost.defaultmethod = "default"
+
+function metapost.getextradata(mpx)
+ return mpxextradata[mpx]
+end
+
+function metapost.pushformat(specification,f,m) -- was: instance, name, method
+ if type(specification) ~= "table" then
+ specification = {
+ instance = specification,
+ format = f,
+ method = m,
+ }
+ end
+ local instance = specification.instance
+ local format = specification.format
+ local method = specification.method
+ local definitions = specification.definitions
+ local extensions = specification.extensions
+ local preamble = nil
+ if not instance or instance == "" then
+ instance = metapost.defaultinstance
+ specification.instance = instance
+ end
+ if not format or format == "" then
+ format = metapost.defaultformat
+ specification.format = format
+ end
+ if not method or method == "" then
+ method = metapost.defaultmethod
+ specification.method = method
+ end
+ if definitions and definitions ~= "" then
+ preamble = definitions
+ end
+ if extensions and extensions ~= "" then
+ if preamble then
+ preamble = preamble .. "\n" .. extensions
+ else
+ preamble = extensions
+ end
+ end
+ nofformats = nofformats + 1
+ local usedinstance = instance .. ":" .. nofformats
+ local mpx = mpxformats [usedinstance]
+ local mpp = mpxpreambles[instance] or ""
+ -- report_metapost("push instance %a (%S)",usedinstance,mpx)
+ if preamble then
+ preamble = prepareddata(preamble)
+ mpp = mpp .. "\n" .. preamble
+ mpxpreambles[instance] = mpp
+ end
+ if not mpx then
+ report_metapost("initializing instance %a using format %a and method %a",usedinstance,format,method)
+ mpx = metapost.checkformat(format,method)
+ mpxformats [usedinstance] = mpx
+ mpxextradata[mpx] = { }
+ if mpp ~= "" then
+ preamble = mpp
+ end
+ end
+ if preamble then
+ metapost.pushscriptrunner(mpx)
+ executempx(mpx,preamble)
+ metapost.popscriptrunner()
+ end
+ specification.mpx = mpx
+ return mpx
+end
+
+-- luatex.wrapup(function()
+-- for k, mpx in next, mpxformats do
+-- mpx:finish()
+-- end
+-- end)
+
+function metapost.popformat()
+ nofformats = nofformats - 1
+end
+
+function metapost.reset(mpx)
+ if not mpx then
+ -- nothing
+ elseif type(mpx) == "string" then
+ if mpxformats[mpx] then
+ local instance = mpxformats[mpx]
+ instance:finish()
+ mpxterminals[mpx] = nil
+ mpxextradata[mpx] = nil
+ mpxformats [mpx] = nil
+ end
+ else
+ for name, instance in next, mpxformats do
+ if instance == mpx then
+ mpx:finish()
+ mpxterminals[mpx] = nil
+ mpxextradata[mpx] = nil
+ mpxformats [mpx] = nil
+ break
+ end
+ end
+ end
+end
+
+local mp_tra = { }
+local mp_tag = 0
+
+-- key/values
+
+do
+
+ local stack, top = { }, nil
+
+ function metapost.setvariable(k,v)
+ if top then
+ top[k] = v
+ else
+ metapost.variables[k] = v
+ end
+ end
+
+ function metapost.pushvariable(k)
+ local t = { }
+ if top then
+ insert(stack,top)
+ top[k] = t
+ else
+ metapost.variables[k] = t
+ end
+ top = t
+ end
+
+ function metapost.popvariable()
+ top = remove(stack)
+ end
+
+ local stack = { }
+
+ function metapost.pushvariables()
+ insert(stack,metapost.variables)
+ metapost.variables = { }
+ end
+
+ function metapost.popvariables()
+ metapost.variables = remove(stack) or metapost.variables
+ end
+
+end
+
+
+if not metapost.process then
+
+ function metapost.process(specification)
+ metapost.run(specification)
+ end
+
+end
+
+-- run, process, convert and flush all work with a specification with the
+-- following (often optional) fields
+--
+-- mpx string or mp object
+-- data string or table of strings
+-- flusher table with flush methods
+-- askedfig string ("all" etc) or number
+-- incontext boolean
+-- plugmode boolean
+
+local function makebeginbanner(specification)
+ return formatters["%% begin graphic: n=%s\n\n"](metapost.n)
+end
+
+local function makeendbanner(specification)
+ return "\n% end graphic\n\n"
+end
+
+function metapost.run(specification)
+ local mpx = specification.mpx
+ local data = specification.data
+ local converted = false
+ local result = { }
+ local mpxdone = type(mpx) == "string"
+ if mpxdone then
+ mpx = metapost.pushformat { instance = mpx, format = mpx }
+ end
+ if mpx and data then
+ local tra = nil
+ starttiming(metapost) -- why not at the outer level ...
+ metapost.variables = { } -- todo also push / pop
+ metapost.pushscriptrunner(mpx)
+ if trace_graphics then
+ tra = mp_tra[mpx]
+ if not tra then
+ mp_tag = mp_tag + 1
+ local jobname = tex.jobname
+ tra = {
+ inp = io.open(formatters["%s-mplib-run-%03i.mp"] (jobname,mp_tag),"w"),
+ log = io.open(formatters["%s-mplib-run-%03i.log"](jobname,mp_tag),"w"),
+ }
+ mp_tra[mpx] = tra
+ end
+ local banner = makebeginbanner(specification)
+ tra.inp:write(banner)
+ tra.log:write(banner)
+ end
+ local function process(d,i)
+ if d then
+ if trace_graphics then
+ if i then
+ tra.inp:write(formatters["\n%% begin snippet %s\n"](i))
+ end
+ if type(d) == "table" then
+ for i=1,#d do
+ tra.inp:write(d[i])
+ end
+ else
+ tra.inp:write(d)
+ end
+ if i then
+ tra.inp:write(formatters["\n%% end snippet %s\n"](i))
+ end
+ end
+ starttiming(metapost.exectime)
+ result = executempx(mpx,d)
+ stoptiming(metapost.exectime)
+ if trace_graphics and result then
+ local str = result.log or result.error
+ if str and str ~= "" then
+ tra.log:write(str)
+ end
+ end
+ if not metapost.reporterror(result) then
+ if metapost.showlog then
+ -- make function and overload in lmtx
+ local str = result.term ~= "" and result.term or "no terminal output"
+ if not emptystring(str) then
+ metapost.lastlog = metapost.lastlog .. "\n" .. str
+ report_metapost("log: %s",str)
+ end
+ end
+ if result.fig then
+ converted = metapost.convert(specification,result)
+ end
+ end
+ elseif i then
+ report_metapost("error: invalid graphic component %s",i)
+ else
+ report_metapost("error: invalid graphic")
+ end
+ end
+
+-- local data = prepareddata(data)
+ if type(data) == "table" then
+ if trace_tracingall then
+ executempx(mpx,"tracingall;")
+ end
+ process(data)
+-- for i=1,#data do
+-- process(data[i],i)
+-- end
+ else
+ if trace_tracingall then
+ data = "tracingall;" .. data
+ end
+ process(data)
+ end
+ if trace_graphics then
+ local banner = makeendbanner(specification)
+ tra.inp:write(banner)
+ tra.log:write(banner)
+ end
+ stoptiming(metapost)
+ metapost.popscriptrunner()
+ end
+ if mpxdone then
+ metapost.popformat()
+ end
+ return converted, result
+end
+
+if not metapost.convert then
+
+ function metapost.convert()
+ report_metapost('warning: no converter set')
+ end
+
+end
+
+-- This will be redone as we no longer output svg of ps!
+
+-- function metapost.directrun(formatname,filename,outputformat,astable,mpdata)
+-- local fullname = file.addsuffix(filename,"mp")
+-- local data = mpdata or io.loaddata(fullname)
+-- if outputformat ~= "svg" then
+-- outputformat = "mps"
+-- end
+-- if not data then
+-- report_metapost("unknown file %a",filename)
+-- else
+-- local mpx = metapost.checkformat(formatname)
+-- if not mpx then
+-- report_metapost("unknown format %a",formatname)
+-- else
+-- report_metapost("processing %a",(mpdata and (filename or "data")) or fullname)
+-- local result = executempx(mpx,data)
+-- if not result then
+-- report_metapost("error: no result object returned")
+-- elseif result.status > 0 then
+-- report_metapost("error: %s",(result.term or "no-term") .. "\n" .. (result.error or "no-error"))
+-- else
+-- if metapost.showlog then
+-- metapost.lastlog = metapost.lastlog .. "\n" .. result.term
+-- report_metapost("info: %s",result.term or "no-term")
+-- end
+-- local figures = result.fig
+-- if figures then
+-- local sorted = table.sortedkeys(figures)
+-- if astable then
+-- local result = { }
+-- report_metapost("storing %s figures in table",#sorted)
+-- for k=1,#sorted do
+-- local v = sorted[k]
+-- if outputformat == "mps" then
+-- result[v] = figures[v]:postscript()
+-- else
+-- result[v] = figures[v]:svg() -- (3) for prologues
+-- end
+-- end
+-- return result
+-- else
+-- local basename = file.removesuffix(file.basename(filename))
+-- for k=1,#sorted do
+-- local v = sorted[k]
+-- local output
+-- if outputformat == "mps" then
+-- output = figures[v]:postscript()
+-- else
+-- output = figures[v]:svg() -- (3) for prologues
+-- end
+-- local outname = formatters["%s-%s.%s"](basename,v,outputformat)
+-- report_metapost("saving %s bytes in %a",#output,outname)
+-- io.savedata(outname,output)
+-- end
+-- return #sorted
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+
+function metapost.directrun(formatname,filename,outputformat,astable,mpdata)
+ report_metapost("producing postscript and svg is no longer supported")
+end
+
+do
+
+ local result = { }
+ local width = 0
+ local height = 0
+ local depth = 0
+ local bbox = { 0, 0, 0, 0 }
+
+ local flusher = {
+ startfigure = function(n,llx,lly,urx,ury)
+ result = { }
+ width = urx - llx
+ height = ury
+ depth = -lly
+ bbox = { llx, lly, urx, ury }
+ end,
+ flushfigure = function(t)
+ local r = #result
+ for i=1,#t do
+ r = r + 1
+ result[r] = t[i]
+ end
+ end,
+ stopfigure = function()
+ end,
+ }
+
+ -- make table variant:
+
+ function metapost.simple(instance,code,useextensions,dontwrap)
+ -- can we pickup the instance ?
+ local mpx = metapost.pushformat {
+ instance = instance or "simplefun",
+ format = "metafun", -- or: minifun
+ method = "double",
+ }
+-- if not code then
+-- code = ""
+-- end
+ metapost.process {
+ mpx = mpx,
+ flusher = flusher,
+ askedfig = 1,
+ useplugins = useextensions,
+ data = dontwrap and { code } or { "beginfig(1);", code, "endfig;" },
+ incontext = false,
+ }
+ metapost.popformat()
+ if result then
+ local stream = concat(result," ")
+ result = { } -- nil -- cleanup .. weird, we can have a dangling q
+ return stream, width, height, depth, bbox
+ else
+ return "", 0, 0, 0, { 0, 0, 0, 0 }
+ end
+ end
+
+end
+
+local getstatistics = mplib.getstatistics
+
+function metapost.getstatistics(memonly)
+ if memonly then
+ local n, m = 0, 0
+ for name, mpx in next, mpxformats do
+ n = n + 1
+ m = m + getstatistics(mpx).memory
+ end
+ return n, m
+ else
+ local t = { }
+ for name, mpx in next, mpxformats do
+ t[name] = getstatistics(mpx)
+ end
+ return t
+ end
+end
+
+local gethashentries = mplib.gethashentries
+
+function metapost.gethashentries(name,full)
+ if name then
+ local mpx = mpxformats[name] or mpxformats[name .. ":1"]
+ if mpx then
+ return gethashentries(mpx,full)
+ end
+ else
+ local t = { }
+ for name, mpx in next, mpxformats do
+ t[name] = gethashtokens(mpx,full)
+ end
+ return t
+ end
+end
+
+local gethashtokens = mplib.gethashtokens
+
+function metapost.getinstancenames()
+ return sortedkeys(mpxformats)
+end