-- -- This is file `luamplib.lua', -- generated with the docstrip utility. -- -- The original source files were: -- -- luamplib.dtx (with options: `lua') -- -- See source file 'luamplib.dtx' for licencing and contact information. -- luatexbase.provides_module { name = "luamplib", version = "2.29.0", date = "2024/05/01", description = "Lua package to typeset Metapost with LuaTeX's MPLib.", } luamplib = luamplib or { } local luamplib = luamplib local format, abs = string.format, math.abs local function termorlog (target, text, kind) if text then local mod, write, append = "luamplib", texio.write_nl, texio.write kind = kind or target == "term" and "Warning (more info in the log)" or target == "log" and "Info" or target == "term and log" and "Warning" or "Error" target = kind == "Error" and "term and log" or target local t = text:explode"\n+" write(target, format("Module %s %s:", mod, kind)) if #t == 1 then append(target, format(" %s", t[1])) else for _,line in ipairs(t) do write(target, line) end write(target, format("(%s) ", mod)) end append(target, format(" on input line %s", tex.inputlineno)) write(target, "") if kind == "Error" then error() end end end local function warn (...) -- beware '%' symbol termorlog("term and log", select("#",...) > 1 and format(...) or ...) end local function info (...) termorlog("log", select("#",...) > 1 and format(...) or ...) end local function err (...) termorlog("error", select("#",...) > 1 and format(...) or ...) end luamplib.showlog = luamplib.showlog or false local tableconcat = table.concat local texsprint = tex.sprint local texgettoks = tex.gettoks local texgetbox = tex.getbox local texruntoks = tex.runtoks if not texruntoks then err("Your LuaTeX version is too old. Please upgrade it to the latest") end local is_defined = token.is_defined local get_macro = token.get_macro local mplib = require ('mplib') local kpse = require ('kpse') local lfs = require ('lfs') local lfsattributes = lfs.attributes local lfsisdir = lfs.isdir local lfsmkdir = lfs.mkdir local lfstouch = lfs.touch local ioopen = io.open local file = file or { } local replacesuffix = file.replacesuffix or function(filename, suffix) return (filename:gsub("%.[%a%d]+$","")) .. "." .. suffix end local is_writable = file.is_writable or function(name) if lfsisdir(name) then name = name .. "/_luam_plib_temp_file_" local fh = ioopen(name,"w") if fh then fh:close(); os.remove(name) return true end end end local mk_full_path = lfs.mkdirp or lfs.mkdirs or function(path) local full = "" for sub in path:gmatch("(/*[^\\/]+)") do full = full .. sub lfsmkdir(full) end end local luamplibtime = kpse.find_file("luamplib.lua") luamplibtime = luamplibtime and lfsattributes(luamplibtime,"modification") local currenttime = os.time() local outputdir if lfstouch then for i,v in ipairs{'TEXMFVAR','TEXMF_OUTPUT_DIRECTORY','.','TEXMFOUTPUT'} do local var = i == 3 and v or kpse.var_value(v) if var and var ~= "" then for _,vv in next, var:explode(os.type == "unix" and ":" or ";") do local dir = format("%s/%s",vv,"luamplib_cache") if not lfsisdir(dir) then mk_full_path(dir) end if is_writable(dir) then outputdir = dir break end end if outputdir then break end end end end outputdir = outputdir or '.' function luamplib.getcachedir(dir) dir = dir:gsub("##","#") dir = dir:gsub("^~", os.type == "windows" and os.getenv("UserProfile") or os.getenv("HOME")) if lfstouch and dir then if lfsisdir(dir) then if is_writable(dir) then luamplib.cachedir = dir else warn("Directory '%s' is not writable!", dir) end else warn("Directory '%s' does not exist!", dir) end end end local noneedtoreplace = { ["boxes.mp"] = true, -- ["format.mp"] = true, ["graph.mp"] = true, ["marith.mp"] = true, ["mfplain.mp"] = true, ["mpost.mp"] = true, ["plain.mp"] = true, ["rboxes.mp"] = true, ["sarith.mp"] = true, ["string.mp"] = true, -- ["TEX.mp"] = true, ["metafun.mp"] = true, ["metafun.mpiv"] = true, ["mp-abck.mpiv"] = true, ["mp-apos.mpiv"] = true, ["mp-asnc.mpiv"] = true, ["mp-bare.mpiv"] = true, ["mp-base.mpiv"] = true, ["mp-blob.mpiv"] = true, ["mp-butt.mpiv"] = true, ["mp-char.mpiv"] = true, ["mp-chem.mpiv"] = true, ["mp-core.mpiv"] = true, ["mp-crop.mpiv"] = true, ["mp-figs.mpiv"] = true, ["mp-form.mpiv"] = true, ["mp-func.mpiv"] = true, ["mp-grap.mpiv"] = true, ["mp-grid.mpiv"] = true, ["mp-grph.mpiv"] = true, ["mp-idea.mpiv"] = true, ["mp-luas.mpiv"] = true, ["mp-mlib.mpiv"] = true, ["mp-node.mpiv"] = true, ["mp-page.mpiv"] = true, ["mp-shap.mpiv"] = true, ["mp-step.mpiv"] = true, ["mp-text.mpiv"] = true, ["mp-tool.mpiv"] = true, ["mp-cont.mpiv"] = true, } luamplib.noneedtoreplace = noneedtoreplace local function replaceformatmp(file,newfile,ofmodify) local fh = ioopen(file,"r") if not fh then return file end local data = fh:read("*all"); fh:close() fh = ioopen(newfile,"w") if not fh then return file end fh:write( "let normalinfont = infont;\n", "primarydef str infont name = rawtextext(str) enddef;\n", data, "vardef Fmant_(expr x) = rawtextext(decimal abs x) enddef;\n", "vardef Fexp_(expr x) = rawtextext(\"$^{\"&decimal x&\"}$\") enddef;\n", "let infont = normalinfont;\n" ); fh:close() lfstouch(newfile,currenttime,ofmodify) return newfile end local name_b = "%f[%a_]" local name_e = "%f[^%a_]" local btex_etex = name_b.."btex"..name_e.."%s*(.-)%s*"..name_b.."etex"..name_e local verbatimtex_etex = name_b.."verbatimtex"..name_e.."%s*(.-)%s*"..name_b.."etex"..name_e local function replaceinputmpfile (name,file) local ofmodify = lfsattributes(file,"modification") if not ofmodify then return file end local cachedir = luamplib.cachedir or outputdir local newfile = name:gsub("%W","_") newfile = cachedir .."/luamplib_input_"..newfile if newfile and luamplibtime then local nf = lfsattributes(newfile) if nf and nf.mode == "file" and ofmodify == nf.modification and luamplibtime < nf.access then return nf.size == 0 and file or newfile end end if name == "format.mp" then return replaceformatmp(file,newfile,ofmodify) end local fh = ioopen(file,"r") if not fh then return file end local data = fh:read("*all"); fh:close() local count,cnt = 0,0 data, cnt = data:gsub(btex_etex, "btex %1 etex ") -- space count = count + cnt data, cnt = data:gsub(verbatimtex_etex, "verbatimtex %1 etex;") -- semicolon count = count + cnt if count == 0 then noneedtoreplace[name] = true fh = ioopen(newfile,"w"); if fh then fh:close() lfstouch(newfile,currenttime,ofmodify) end return file end fh = ioopen(newfile,"w") if not fh then return file end fh:write(data); fh:close() lfstouch(newfile,currenttime,ofmodify) return newfile end local mpkpse do local exe = 0 while arg[exe-1] do exe = exe-1 end mpkpse = kpse.new(arg[exe], "mpost") end local special_ftype = { pfb = "type1 fonts", enc = "enc files", } local function finder(name, mode, ftype) if mode == "w" then if name and name ~= "mpout.log" then kpse.record_output_file(name) -- recorder end return name else ftype = special_ftype[ftype] or ftype local file = mpkpse:find_file(name,ftype) if file then if lfstouch and ftype == "mp" and not noneedtoreplace[name] then file = replaceinputmpfile(name,file) end else file = mpkpse:find_file(name, name:match("%a+$")) end if file then kpse.record_input_file(file) -- recorder end return file end end luamplib.finder = finder local preamble = [[ boolean mplib ; mplib := true ; let dump = endinput ; let normalfontsize = fontsize; input %s ; ]] local currentformat = "plain" local function setformat (name) currentformat = name end luamplib.setformat = setformat luamplib.codeinherit = false local mplibinstances = {} local has_instancename = false local function reporterror (result, prevlog) if not result then err("no result object returned") else local t, e, l = result.term, result.error, result.log local log = l or t or "no-term" log = log:gsub("%(Please type a command or say `end'%)",""):gsub("\n+","\n") if result.status > 0 then local first = log:match"(.-\n! .-)\n! " if first then termorlog("term", first) termorlog("log", log, "Warning") else warn(log) end if result.status > 1 then err(e or "see above messages") end elseif prevlog then log = prevlog..log local show = log:match"\n>>? .+" if show then termorlog("term", show, "Info (more info in the log)") info(log) elseif luamplib.showlog and log:find"%g" then info(log) end end return log end end local function luamplibload (name) local mpx = mplib.new { ini_version = true, find_file = luamplib.finder, make_text = luamplib.maketext, run_script = luamplib.runscript, math_mode = luamplib.numbersystem, job_name = tex.jobname, random_seed = math.random(4095), extensions = 1, } local preamble = tableconcat{ format(preamble, replacesuffix(name,"mp")), luamplib.mplibcodepreamble, luamplib.legacy_verbatimtex and luamplib.legacyverbatimtexpreamble or "", luamplib.textextlabel and luamplib.textextlabelpreamble or "", } local result, log if not mpx then result = { status = 99, error = "out of memory"} else result = mpx:execute(preamble) end log = reporterror(result) return mpx, result, log end local function process (data, instancename) local currfmt if instancename and instancename ~= "" then currfmt = instancename has_instancename = true else currfmt = tableconcat{ currentformat, luamplib.numbersystem or "scaled", tostring(luamplib.textextlabel), tostring(luamplib.legacy_verbatimtex), } has_instancename = false end local mpx = mplibinstances[currfmt] local standalone = not (has_instancename or luamplib.codeinherit) if mpx and standalone then mpx:finish() end local log = "" if standalone or not mpx then mpx, _, log = luamplibload(currentformat) mplibinstances[currfmt] = mpx end local converted, result = false, {} if mpx and data then result = mpx:execute(data) local log = reporterror(result, log) if log then if result.fig then converted = luamplib.convert(result) else info"No figure output. Maybe no beginfig/endfig" end end else err"Mem file unloadable. Maybe generated with a different version of mplib?" end return converted, result end local pdfmode = tex.outputmode > 0 local catlatex = luatexbase.registernumber("catcodetable@latex") local catat11 = luatexbase.registernumber("catcodetable@atletter") local function run_tex_code (str, cat) texruntoks(function() texsprint(cat or catlatex, str) end) end local texboxes = { globalid = 0, localid = 4096 } local factor = 65536*(7227/7200) local textext_fmt = 'image(addto currentpicture doublepath unitsquare \z xscaled %f yscaled %f shifted (0,-%f) \z withprescript "mplibtexboxid=%i:%f:%f")' local function process_tex_text (str) if str then local global = (has_instancename or luamplib.globaltextext or luamplib.codeinherit) and "\\global" or "" local tex_box_id if global == "" then tex_box_id = texboxes.localid + 1 texboxes.localid = tex_box_id else local boxid = texboxes.globalid + 1 texboxes.globalid = boxid run_tex_code(format( [[\expandafter\newbox\csname luamplib.box.%s\endcsname]], boxid)) tex_box_id = tex.getcount'allocationnumber' end run_tex_code(format("%s\\setbox%i\\hbox{%s}", global, tex_box_id, str)) local box = texgetbox(tex_box_id) local wd = box.width / factor local ht = box.height / factor local dp = box.depth / factor return textext_fmt:format(wd, ht+dp, dp, tex_box_id, wd, ht+dp) end return "" end local mplibcolorfmt = { xcolor = tableconcat{ [[\begingroup\let\XC@mcolor\relax]], [[\def\set@color{\global\mplibtmptoks\expandafter{\current@color}}]], [[\color%s\endgroup]], }, l3color = tableconcat{ [[\begingroup\def\__color_select:N#1{\expandafter\__color_select:nn#1}]], [[\def\__color_backend_select:nn#1#2{\global\mplibtmptoks{#1 #2}}]], [[\def\__kernel_backend_literal:e#1{\global\mplibtmptoks\expandafter{\expanded{#1}}}]], [[\color_select:n%s\endgroup]], }, } local colfmt = is_defined'color_select:n' and "l3color" or "xcolor" if colfmt == "l3color" then run_tex_code{ "\\newcatcodetable\\luamplibcctabexplat", "\\begingroup", "\\catcode`@=11 ", "\\catcode`_=11 ", "\\catcode`:=11 ", "\\savecatcodetable\\luamplibcctabexplat", "\\endgroup", } end local ccexplat = luatexbase.registernumber"luamplibcctabexplat" local function process_color (str, kind) if str then if not str:find("%b{}") then str = format("{%s}",str) end local myfmt = mplibcolorfmt[colfmt] if colfmt == "l3color" and is_defined"color" then if str:find("%b[]") then myfmt = mplibcolorfmt.xcolor else for _,v in ipairs(str:match"{(.+)}":explode"!") do if not v:find("^%s*%d+%s*$") then local pp = get_macro(format("l__color_named_%s_prop",v)) if not pp or pp == "" then myfmt = mplibcolorfmt.xcolor break end end end end end if myfmt == mplibcolorfmt.l3color and (kind == "fill" or kind == "draw") then return str end run_tex_code(myfmt:format(str), ccexplat or catat11) local t = texgettoks"mplibtmptoks" if not pdfmode and not t:find"^pdf" then t = t:gsub("%a+ (.+)","pdf:bc [%1]") end if kind then return t end return format('1 withprescript "MPlibOverrideColor=%s"', t) end return "" end local function colorsplit (res) local t, tt = { }, res:gsub("[%[%]]",""):explode() local be = tt[1]:find"^%d" and 1 or 2 for i=be, #tt do if tt[i]:find"^%a" then break end t[#t+1] = tt[i] end return t end luamplib.outlinecolor = function (str, filldraw) local nn = filldraw == "fill" and 'fn:=' or 'dn:=' local cc = filldraw == "fill" and 'fc:=' or 'dc:=' local res = process_color(str, filldraw) if res:match"{(.+)}" == str then return format('%s"n"; %s"%s";', nn,cc,str) end local t = colorsplit(res) local md = #t == 1 and 'gray' or #t == 3 and 'rgb' or #t == 4 and 'cmyk' return format('%s"nn"; %s"%s}{%s";', nn, cc, md, tableconcat(t,',')) end luamplib.gettexcolor = function (str, rgb) local res = process_color(str, "metapost") if res:find" cs " or res:find"@pdf.obj" then if not rgb then warn("%s is a spot color. Forced to CMYK", str) end run_tex_code({ "\\color_export:nnN{", str, "}{", rgb and "space-sep-rgb" or "space-sep-cmyk", "}\\mplib_@tempa", },ccexplat) return get_macro"mplib_@tempa":explode() end local t = colorsplit(res) if #t == 3 or not rgb then return t end run_tex_code({ -- force to rgb "\\color_export:nnnN{", #t == 4 and "cmyk" or "gray", "}{", tableconcat(t,","), "}{space-sep-rgb}\\mplib_@tempa", },ccexplat) return get_macro"mplib_@tempa":explode() end luamplib.shadecolor = function (str) local res = process_color(str, "shade") if res:find" cs " or res:find"@pdf.obj" then -- spot color shade: l3 only run_tex_code({ [[\color_export:nnN{]], str, [[}{backend}\mplib_@tempa]], },ccexplat) local name = get_macro'mplib_@tempa':match'{(.-)}{.+}' local t, obj = res:explode() if pdfmode then obj = t[1]:match"^/(.+)" if ltx.pdf and ltx.pdf.object_id then obj = format("%s 0 R", ltx.pdf.object_id(obj)) else run_tex_code({ [[\edef\mplib_@tempa{\pdf_object_ref:n{]], obj, "}}", },ccexplat) obj = get_macro'mplib_@tempa' end else obj = t[2] end local value = t[3]:match"%[(.-)%]" or t[3] return format('(%s) withprescript"mplib_spotcolor=%s:%s"', value,obj,name) end return colorsplit(res) end local function process_dimen (str) if str then str = str:gsub("{(.+)}","%1") run_tex_code(format([[\mplibtmptoks\expandafter{\the\dimexpr %s\relax}]], str)) return format("begingroup %s endgroup", texgettoks"mplibtmptoks") end return "" end local function process_verbatimtex_text (str) if str then run_tex_code(str) end return "" end local tex_code_pre_mplib = {} luamplib.figid = 1 luamplib.in_the_fig = false local function process_verbatimtex_prefig (str) if str then tex_code_pre_mplib[luamplib.figid] = str end return "" end local function process_verbatimtex_infig (str) if str then return format('special "postmplibverbtex=%s";', str) end return "" end local runscript_funcs = { luamplibtext = process_tex_text, luamplibcolor = process_color, luamplibdimen = process_dimen, luamplibprefig = process_verbatimtex_prefig, luamplibinfig = process_verbatimtex_infig, luamplibverbtex = process_verbatimtex_text, } mp = mp or {} local mp = mp mp.mf_path_reset = mp.mf_path_reset or function() end mp.mf_finish_saving_data = mp.mf_finish_saving_data or function() end mp.report = mp.report or info catcodes = catcodes or {} local catcodes = catcodes catcodes.numbers = catcodes.numbers or {} catcodes.numbers.ctxcatcodes = catcodes.numbers.ctxcatcodes or catlatex catcodes.numbers.texcatcodes = catcodes.numbers.texcatcodes or catlatex catcodes.numbers.luacatcodes = catcodes.numbers.luacatcodes or catlatex catcodes.numbers.notcatcodes = catcodes.numbers.notcatcodes or catlatex catcodes.numbers.vrbcatcodes = catcodes.numbers.vrbcatcodes or catlatex catcodes.numbers.prtcatcodes = catcodes.numbers.prtcatcodes or catlatex catcodes.numbers.txtcatcodes = catcodes.numbers.txtcatcodes or catlatex local function mpprint(buffer,...) for i=1,select("#",...) do local value = select(i,...) if value ~= nil then local t = type(value) if t == "number" then buffer[#buffer+1] = format("%.16f",value) elseif t == "string" then buffer[#buffer+1] = value elseif t == "table" then buffer[#buffer+1] = "(" .. tableconcat(value,",") .. ")" else -- boolean or whatever buffer[#buffer+1] = tostring(value) end end end end function luamplib.runscript (code) local id, str = code:match("(.-){(.*)}") if id and str then local f = runscript_funcs[id] if f then local t = f(str) if t then return t end end end local f = loadstring(code) if type(f) == "function" then local buffer = {} function mp.print(...) mpprint(buffer,...) end local res = {f()} buffer = tableconcat(buffer) if buffer and buffer ~= "" then return buffer end buffer = {} mpprint(buffer, table.unpack(res)) return tableconcat(buffer) end return "" end local function protecttexcontents (str) return str:gsub("\\%%", "\0PerCent\0") :gsub("%%.-\n", "") :gsub("%%.-$", "") :gsub("%zPerCent%z", "\\%%") :gsub("%s+", " ") end luamplib.legacy_verbatimtex = true function luamplib.maketext (str, what) if str and str ~= "" then str = protecttexcontents(str) if what == 1 then if not str:find("\\documentclass"..name_e) and not str:find("\\begin%s*{document}") and not str:find("\\documentstyle"..name_e) and not str:find("\\usepackage"..name_e) then if luamplib.legacy_verbatimtex then if luamplib.in_the_fig then return process_verbatimtex_infig(str) else return process_verbatimtex_prefig(str) end else return process_verbatimtex_text(str) end end else return process_tex_text(str) end end return "" end local mplibcodepreamble = [[ texscriptmode := 2; def rawtextext (expr t) = runscript("luamplibtext{"&t&"}") enddef; def mplibcolor (expr t) = runscript("luamplibcolor{"&t&"}") enddef; def mplibdimen (expr t) = runscript("luamplibdimen{"&t&"}") enddef; def VerbatimTeX (expr t) = runscript("luamplibverbtex{"&t&"}") enddef; def message expr t = if string t: runscript("mp.report[=["&t&"]=]") else: errmessage "Not a string" fi enddef; if known context_mlib: defaultfont := "cmtt10"; let infont = normalinfont; let fontsize = normalfontsize; vardef thelabel@#(expr p,z) = if string p : thelabel@#(p infont defaultfont scaled defaultscale,z) else : p shifted (z + labeloffset*mfun_laboff@# - (mfun_labxf@#*lrcorner p + mfun_labyf@#*ulcorner p + (1-mfun_labxf@#-mfun_labyf@#)*llcorner p)) fi enddef; def colordecimals primary c = if cmykcolor c: decimal cyanpart c & ":" & decimal magentapart c & ":" & decimal yellowpart c & ":" & decimal blackpart c elseif rgbcolor c: decimal redpart c & ":" & decimal greenpart c & ":" & decimal bluepart c elseif string c: colordecimals resolvedcolor(c) else: decimal c fi enddef; def resolvedcolor(expr s) = runscript("return luamplib.shadecolor('"& s &"')") enddef; else: vardef textext@# (text t) = rawtextext (t) enddef; fi def externalfigure primary filename = draw rawtextext("\includegraphics{"& filename &"}") enddef; def TEX = textext enddef; def mplibtexcolor primary c = runscript("return luamplib.gettexcolor('"& c &"')") enddef; def mplibrgbtexcolor primary c = runscript("return luamplib.gettexcolor('"& c &"','rgb')") enddef; def mplibgraphictext primary t = begingroup; mplibgraphictext_ (t) enddef; def mplibgraphictext_ (expr t) text rest = save fakebold, scale, fillcolor, drawcolor, withfillcolor, withdrawcolor, fb, sc, fc, dc, fn, dn, tpic; picture tpic; tpic := nullpicture; numeric fb, sc; string fc, dc, fn, dn; fb:=2; sc:=1; fc:="white"; dc:="black"; fn:=dn:="n"; def fakebold primary c = hide(fb:=c;) enddef; def scale primary c = hide(sc:=c;) enddef; def fillcolor primary c = hide( if string c: runscript("return luamplib.outlinecolor('"& c &"','fill')") else: fn:="nn"; fc:=mpliboutlinecolor_(c); fi ) enddef; def drawcolor primary c = hide( if string c: runscript("return luamplib.outlinecolor('"& c &"','draw')") else: dn:="nn"; dc:=mpliboutlinecolor_(c); fi ) enddef; let withfillcolor = fillcolor; let withdrawcolor = drawcolor; addto tpic doublepath origin rest; tpic:=nullpicture; def fakebold primary c = enddef; def scale primary c = enddef; def fillcolor primary c = enddef; def drawcolor primary c = enddef; let withfillcolor = fillcolor; let withdrawcolor = drawcolor; image(draw rawtextext( "{\addfontfeature{FakeBold="& decimal fb &",Scale="& decimal sc & "}\csname color_fill:"& fn &"\endcsname{"& fc & "}\csname color_stroke:"& dn &"\endcsname{"& dc & "}"& t &"}") rest;) endgroup; enddef; def mpliboutlinecolor_ (expr c) = if color c: "rgb}{" & decimal redpart c & "," & decimal greenpart c & "," & decimal bluepart c elseif cmykcolor c: "cmyk}{" & decimal cyanpart c & "," & decimal magentapart c & "," & decimal yellowpart c & "," & decimal blackpart c else: "gray}{" & decimal c fi enddef; ]] luamplib.mplibcodepreamble = mplibcodepreamble local legacyverbatimtexpreamble = [[ def specialVerbatimTeX (text t) = runscript("luamplibprefig{"&t&"}") enddef; def normalVerbatimTeX (text t) = runscript("luamplibinfig{"&t&"}") enddef; let VerbatimTeX = specialVerbatimTeX; extra_beginfig := extra_beginfig & " let VerbatimTeX = normalVerbatimTeX;"& "runscript(" &ditto& "luamplib.in_the_fig=true" &ditto& ");"; extra_endfig := extra_endfig & " let VerbatimTeX = specialVerbatimTeX;"& "runscript(" &ditto& "if luamplib.in_the_fig then luamplib.figid=luamplib.figid+1 end "& "luamplib.in_the_fig=false" &ditto& ");"; ]] luamplib.legacyverbatimtexpreamble = legacyverbatimtexpreamble local textextlabelpreamble = [[ primarydef s infont f = rawtextext(s) enddef; def fontsize expr f = begingroup save size; numeric size; size := mplibdimen("1em"); if size = 0: 10pt else: size fi endgroup enddef; ]] luamplib.textextlabelpreamble = textextlabelpreamble luamplib.verbatiminput = false local function protect_expansion (str) if str then str = str:gsub("\\","!!!Control!!!") :gsub("%%","!!!Comment!!!") :gsub("#", "!!!HashSign!!!") :gsub("{", "!!!LBrace!!!") :gsub("}", "!!!RBrace!!!") return format("\\unexpanded{%s}",str) end end local function unprotect_expansion (str) if str then return str:gsub("!!!Control!!!", "\\") :gsub("!!!Comment!!!", "%%") :gsub("!!!HashSign!!!","#") :gsub("!!!LBrace!!!", "{") :gsub("!!!RBrace!!!", "}") end end luamplib.everymplib = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end }) luamplib.everyendmplib = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end }) local function process_mplibcode (data, instancename) texboxes.localid = 4096 if luamplib.legacy_verbatimtex then luamplib.figid, tex_code_pre_mplib = 1, {} end local everymplib = luamplib.everymplib[instancename] local everyendmplib = luamplib.everyendmplib[instancename] data = format("\n%s\n%s\n%s\n",everymplib, data, everyendmplib) :gsub("\r","\n") if luamplib.verbatiminput then data = data:gsub("\\mpcolor%s+(.-%b{})","mplibcolor(\"%1\")") :gsub("\\mpdim%s+(%b{})", "mplibdimen(\"%1\")") :gsub("\\mpdim%s+(\\%a+)","mplibdimen(\"%1\")") :gsub(btex_etex, "btex %1 etex ") :gsub(verbatimtex_etex, "verbatimtex %1 etex;") else data = data:gsub(btex_etex, function(str) return format("btex %s etex ", protect_expansion(str)) -- space end) :gsub(verbatimtex_etex, function(str) return format("verbatimtex %s etex;", protect_expansion(str)) -- semicolon end) :gsub("\".-\"", protect_expansion) :gsub("\\%%", "\0PerCent\0") :gsub("%%.-\n","\n") :gsub("%zPerCent%z", "\\%%") run_tex_code(format("\\mplibtmptoks\\expandafter{\\expanded{%s}}",data)) data = texgettoks"mplibtmptoks" :gsub("##", "#") :gsub("\".-\"", unprotect_expansion) :gsub(btex_etex, function(str) return format("btex %s etex", unprotect_expansion(str)) end) :gsub(verbatimtex_etex, function(str) return format("verbatimtex %s etex", unprotect_expansion(str)) end) end process(data, instancename) end luamplib.process_mplibcode = process_mplibcode local further_split_keys = { mplibtexboxid = true, sh_color_a = true, sh_color_b = true, } local function script2table(s) local t = {} for _,i in ipairs(s:explode("\13+")) do local k,v = i:match("(.-)=(.*)") -- v may contain = or empty. if k and v and k ~= "" and not t[k] then if further_split_keys[k] or further_split_keys[k:sub(1,10)] then t[k] = v:explode(":") else t[k] = v end end end return t end local function getobjects(result,figure,f) return figure:objects() end local function convert(result, flusher) luamplib.flush(result, flusher) return true -- done end luamplib.convert = convert local figcontents = { post = { } } local function put2output(a,...) figcontents[#figcontents+1] = type(a) == "string" and format(a,...) or a end local function pdf_startfigure(n,llx,lly,urx,ury) put2output("\\mplibstarttoPDF{%f}{%f}{%f}{%f}",llx,lly,urx,ury) end local function pdf_stopfigure() put2output("\\mplibstoptoPDF") end local function pdf_literalcode (fmt,...) put2output{-2, format(fmt,...)} end local function pdf_textfigure(font,size,text,width,height,depth) text = text:gsub(".",function(c) return format("\\hbox{\\char%i}",string.byte(c)) -- kerning happens in metapost : false end) put2output("\\mplibtextext{%s}{%f}{%s}{%s}{%s}",font,size,text,0,0) end local bend_tolerance = 131/65536 local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1 local function pen_characteristics(object) local t = mplib.pen_info(object) rx, ry, sx, sy, tx, ty = t.rx, t.ry, t.sx, t.sy, t.tx, t.ty divider = sx*sy - rx*ry return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), t.width end local function concat(px, py) -- no tx, ty here return (sy*px-ry*py)/divider,(sx*py-rx*px)/divider end local function curved(ith,pth) local d = pth.left_x - ith.right_x if abs(ith.right_x - ith.x_coord - d) <= bend_tolerance and abs(pth.x_coord - pth.left_x - d) <= bend_tolerance then d = pth.left_y - ith.right_y if abs(ith.right_y - ith.y_coord - d) <= bend_tolerance and abs(pth.y_coord - pth.left_y - d) <= bend_tolerance then return false end end return true end local function flushnormalpath(path,open) local pth, ith for i=1,#path do pth = path[i] if not ith then pdf_literalcode("%f %f m",pth.x_coord,pth.y_coord) elseif curved(ith,pth) then pdf_literalcode("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord) else pdf_literalcode("%f %f l",pth.x_coord,pth.y_coord) end ith = pth end if not open then local one = path[1] if curved(pth,one) then pdf_literalcode("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord ) else pdf_literalcode("%f %f l",one.x_coord,one.y_coord) end elseif #path == 1 then -- special case .. draw point local one = path[1] pdf_literalcode("%f %f l",one.x_coord,one.y_coord) end end local function flushconcatpath(path,open) pdf_literalcode("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty) local pth, ith for i=1,#path do pth = path[i] if not ith then pdf_literalcode("%f %f m",concat(pth.x_coord,pth.y_coord)) elseif curved(ith,pth) then local a, b = concat(ith.right_x,ith.right_y) local c, d = concat(pth.left_x,pth.left_y) pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(pth.x_coord, pth.y_coord)) else pdf_literalcode("%f %f l",concat(pth.x_coord, pth.y_coord)) end ith = pth end if not open then local one = path[1] if curved(pth,one) then local a, b = concat(pth.right_x,pth.right_y) local c, d = concat(one.left_x,one.left_y) pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(one.x_coord, one.y_coord)) else pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord)) end elseif #path == 1 then -- special case .. draw point local one = path[1] pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord)) end end local function start_pdf_code() if pdfmode then pdf_literalcode("q") else put2output"\\special{pdf:bcontent}" end end local function stop_pdf_code() if pdfmode then pdf_literalcode("Q") else put2output"\\special{pdf:econtent}" end end local function put_tex_boxes (object,prescript) local box = prescript.mplibtexboxid local n,tw,th = box[1],tonumber(box[2]),tonumber(box[3]) if n and tw and th then local op = object.path local first, second, fourth = op[1], op[2], op[4] local tx, ty = first.x_coord, first.y_coord local sx, rx, ry, sy = 1, 0, 0, 1 if tw ~= 0 then sx = (second.x_coord - tx)/tw rx = (second.y_coord - ty)/tw if sx == 0 then sx = 0.00001 end end if th ~= 0 then sy = (fourth.y_coord - ty)/th ry = (fourth.x_coord - tx)/th if sy == 0 then sy = 0.00001 end end start_pdf_code() pdf_literalcode("%f %f %f %f %f %f cm",sx,rx,ry,sy,tx,ty) put2output("\\mplibputtextbox{%i}",n) stop_pdf_code() end end local prev_override_color local function do_preobj_CR(object,prescript) local override = prescript and prescript.MPlibOverrideColor if override then if pdfmode then pdf_literalcode(override) override = nil else put2output("\\special{%s}",override) prev_override_color = override end else local cs = object.color if cs and #cs > 0 then pdf_literalcode(luamplib.colorconverter(cs)) prev_override_color = nil elseif not pdfmode then override = prev_override_color if override then put2output("\\special{%s}",override) end end end return override end local pdfmanagement = is_defined'pdfmanagement_add:nnn' local pdfobjs, pdfetcs = {}, {} pdfetcs.pgfextgs = "pgf@sys@addpdfresource@extgs@plain" if pdfmode then pdfetcs.getpageres = pdf.getpageresources or function() return pdf.pageresources end pdfetcs.setpageres = pdf.setpageresources or function(s) pdf.pageresources = s end else texsprint("\\special{pdf:obj @MPlibTr<<>>}","\\special{pdf:obj @MPlibSh<<>>}") end local function update_pdfobjs (os) local on = pdfobjs[os] if on then return on,false end if pdfmode then on = pdf.immediateobj(os) else on = pdfetcs.cnt or 1 texsprint(format("\\special{pdf:obj @mplibpdfobj%s %s}",on,os)) pdfetcs.cnt = on + 1 end pdfobjs[os] = on return on,true end local transparancy_modes = { [0] = "Normal", "Normal", "Multiply", "Screen", "Overlay", "SoftLight", "HardLight", "ColorDodge", "ColorBurn", "Darken", "Lighten", "Difference", "Exclusion", "Hue", "Saturation", "Color", "Luminosity", "Compatible", } local function opacity_initialize () pdfetcs.opacity_res = {} if pdfmode and luatexbase.callbacktypes.finish_pdffile then -- ltluatex local extgstate_obj = pdf.reserveobj() pdfetcs.setpageres(format("%s/ExtGState %i 0 R",pdfetcs.getpageres() or "",extgstate_obj)) luatexbase.add_to_callback("finish_pdffile", function() pdf.immediateobj(extgstate_obj, format("<<%s>>",tableconcat(pdfetcs.opacity_res))) end, "luamplib.opacity.finish_pdffile") end end local function update_tr_res(mode,opaq) if pdfetcs.pgfloaded == nil then pdfetcs.pgfloaded = is_defined(pdfetcs.pgfextgs) if not pdfmanagement and not pdfetcs.pgfloaded and not is_defined"TRP@list" then opacity_initialize() end end local os = format("<>",mode,opaq,opaq) local on, new = update_pdfobjs(os) if new then if pdfmode then if pdfmanagement then texsprint(ccexplat,{ [[\pdfmanagement_add:nnn{Page/Resources/ExtGState}]], format("{MPlibTr%s}{%s 0 R}", on, on), }) else local tr = format("/MPlibTr%s %s 0 R",on,on) if pdfetcs.pgfloaded then texsprint(format("\\csname %s\\endcsname{%s}", pdfetcs.pgfextgs,tr)) elseif is_defined"TRP@list" then texsprint(catat11,{ [[\if@filesw\immediate\write\@auxout{]], [[\string\g@addto@macro\string\TRP@list{]], tr, [[}}\fi]], }) if not get_macro"TRP@list":find(tr) then texsprint(catat11,[[\global\TRP@reruntrue]]) end else if luatexbase.callbacktypes.finish_pdffile then pdfetcs.opacity_res[#pdfetcs.opacity_res+1] = tr else local tpr, n = pdfetcs.getpageres() or "", 0 tpr, n = tpr:gsub("/ExtGState<<", "%1"..tr) if n == 0 then tpr = format("%s/ExtGState<<%s>>", tpr, tr) end pdfetcs.setpageres(tpr) end end end else if pdfmanagement then texsprint(ccexplat,{ [[\pdfmanagement_add:nnn{Page/Resources/ExtGState}]], format("{MPlibTr%s}{@mplibpdfobj%s}", on, on), }) else local tr = format("/MPlibTr%s @mplibpdfobj%s",on,on) if pdfetcs.pgfloaded then texsprint(format("\\csname %s\\endcsname{%s}", pdfetcs.pgfextgs,tr)) else texsprint(format("\\special{pdf:put @MPlibTr<<%s>>}",tr)) texsprint"\\special{pdf:put @resources<>}" end end end end return on end local function do_preobj_TR(prescript) local opaq = prescript and prescript.tr_transparency local tron_no if opaq then local mode = prescript.tr_alternative or 1 mode = transparancy_modes[tonumber(mode)] tron_no = update_tr_res(mode, opaq) start_pdf_code() pdf_literalcode("/MPlibTr%i gs",tron_no) end return tron_no end local function shading_initialize () pdfetcs.shading_res = {} if pdfmode and luatexbase.callbacktypes.finish_pdffile then -- ltluatex local shading_obj = pdf.reserveobj() pdfetcs.setpageres(format("%s/Shading %i 0 R",pdfetcs.getpageres() or "",shading_obj)) luatexbase.add_to_callback("finish_pdffile", function() pdf.immediateobj(shading_obj,format("<<%s>>",tableconcat(pdfetcs.shading_res))) end, "luamplib.shading.finish_pdffile") end end local function sh_pdfpageresources(shtype,domain,colorspace,ca,cb,coordinates,steps,fractions) if not pdfmanagement and not pdfetcs.shading_res then shading_initialize() end local fun2fmt,os = "<>" if steps > 1 then local list,bounds,encode = { },{ },{ } for i=1,steps do if i < steps then bounds[i] = fractions[i] or 1 end encode[2*i-1] = 0 encode[2*i] = 1 os = fun2fmt:format(domain,tableconcat(ca[i],' '),tableconcat(cb[i],' ')) list[i] = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s",update_pdfobjs(os)) end os = tableconcat { "<>", domain), } else os = fun2fmt:format(domain,tableconcat(ca[1],' '),tableconcat(cb[1],' ')) end local objref = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s",update_pdfobjs(os)) os = tableconcat { format("<>", } local on, new = update_pdfobjs(os) if pdfmode then if new then if pdfmanagement then texsprint(ccexplat,{ [[\pdfmanagement_add:nnn{Page/Resources/Shading}]], format("{MPlibSh%s}{%s 0 R}", on, on), }) else local res = format("/MPlibSh%s %s 0 R", on, on) if luatexbase.callbacktypes.finish_pdffile then pdfetcs.shading_res[#pdfetcs.shading_res+1] = res else local pageres = pdfetcs.getpageres() or "" if not pageres:find("/Shading<<.*>>") then pageres = pageres.."/Shading<<>>" end pageres = pageres:gsub("/Shading<<","%1"..res) pdfetcs.setpageres(pageres) end end end else if pdfmanagement then if new then texsprint(ccexplat,{ [[\pdfmanagement_add:nnn{Page/Resources/Shading}]], format("{MPlibSh%s}{@mplibpdfobj%s}", on, on), }) end else if new then texsprint{ "\\special{pdf:put @MPlibSh", format("<>}",on, on), } end texsprint"\\special{pdf:put @resources<>}" end end return on end local function color_normalize(ca,cb) if #cb == 1 then if #ca == 4 then cb[1], cb[2], cb[3], cb[4] = 0, 0, 0, 1-cb[1] else -- #ca = 3 cb[1], cb[2], cb[3] = cb[1], cb[1], cb[1] end elseif #cb == 3 then -- #ca == 4 cb[1], cb[2], cb[3], cb[4] = 1-cb[1], 1-cb[2], 1-cb[3], 0 end end pdfetcs.clrspcs = { } local function do_preobj_SH(object,prescript) local shade_no local sh_type = prescript and prescript.sh_type if sh_type then local domain = prescript.sh_domain or "0 1" local centera = prescript.sh_center_a or "0 0"; centera = centera:explode() local centerb = prescript.sh_center_b or "0 0"; centerb = centerb:explode() local transform = prescript.sh_transform == "yes" local sx,sy,sr,dx,dy = 1,1,1,0,0 if transform then local first = prescript.sh_first or "0 0"; first = first:explode() local setx = prescript.sh_set_x or "0 0"; setx = setx:explode() local sety = prescript.sh_set_y or "0 0"; sety = sety:explode() local x,y = tonumber(setx[1]) or 0, tonumber(sety[1]) or 0 if x ~= 0 and y ~= 0 then local path = object.path local path1x = path[1].x_coord local path1y = path[1].y_coord local path2x = path[x].x_coord local path2y = path[y].y_coord local dxa = path2x - path1x local dya = path2y - path1y local dxb = setx[2] - first[1] local dyb = sety[2] - first[2] if dxa ~= 0 and dya ~= 0 and dxb ~= 0 and dyb ~= 0 then sx = dxa / dxb ; if sx < 0 then sx = - sx end sy = dya / dyb ; if sy < 0 then sy = - sy end sr = math.sqrt(sx^2 + sy^2) dx = path1x - sx*first[1] dy = path1y - sy*first[2] end end end local ca, cb, colorspace, steps, fractions ca = { prescript.sh_color_a_1 or prescript.sh_color_a or {0} } cb = { prescript.sh_color_b_1 or prescript.sh_color_b or {1} } steps = tonumber(prescript.sh_step) or 1 if steps > 1 then fractions = { prescript.sh_fraction_1 or 0 } for i=2,steps do fractions[i] = prescript[format("sh_fraction_%i",i)] or (i/steps) ca[i] = prescript[format("sh_color_a_%i",i)] or {0} cb[i] = prescript[format("sh_color_b_%i",i)] or {1} end end if prescript.mplib_spotcolor then ca, cb = { }, { } local names, pos, objref = { }, -1, "" local script = object.prescript:explode"\13+" for i=#script,1,-1 do if script[i]:find"mplib_spotcolor" then local name, value objref, name = script[i]:match"=(.-):(.+)" value = script[i+1]:match"=(.+)" if not names[name] then pos = pos+1 names[name] = pos names[#names+1] = name end local t = { } for j=1,names[name] do t[#t+1] = 0 end t[#t+1] = value table.insert(#ca == #cb and ca or cb, t) end end for _,t in ipairs{ca,cb} do for _,tt in ipairs(t) do for i=1,#names-#tt do tt[#tt+1] = 0 end end end if #names == 1 then colorspace = objref else local name = tableconcat(names,"-") local obj = pdfetcs.clrspcs[name] if obj then colorspace = obj else run_tex_code({ [[\color_model_new:nnn]], format("{mplibcolorspace_%s}", name), format("{DeviceN}{names={%s}}", tableconcat(names,",")), [[\edef\mplib_@tempa{\pdf_object_ref_last:}]], }, ccexplat) colorspace = get_macro'mplib_@tempa' pdfetcs.clrspcs[name] = colorspace end end else local model = 0 for _,t in ipairs{ca,cb} do for _,tt in ipairs(t) do model = model > #tt and model or #tt end end for _,t in ipairs{ca,cb} do for _,tt in ipairs(t) do if #tt < model then color_normalize(model == 4 and {1,1,1,1} or {1,1,1},tt) end end end colorspace = model == 4 and "/DeviceCMYK" or model == 3 and "/DeviceRGB" or model == 1 and "/DeviceGray" or err"unknown color model" end if sh_type == "linear" then local coordinates = format("%f %f %f %f", dx + sx*centera[1], dy + sy*centera[2], dx + sx*centerb[1], dy + sy*centerb[2]) shade_no = sh_pdfpageresources(2,domain,colorspace,ca,cb,coordinates,steps,fractions) elseif sh_type == "circular" then local factor = prescript.sh_factor or 1 local radiusa = factor * prescript.sh_radius_a local radiusb = factor * prescript.sh_radius_b local coordinates = format("%f %f %f %f %f %f", dx + sx*centera[1], dy + sy*centera[2], sr*radiusa, dx + sx*centerb[1], dy + sy*centerb[2], sr*radiusb) shade_no = sh_pdfpageresources(3,domain,colorspace,ca,cb,coordinates,steps,fractions) else err"unknown shading type" end pdf_literalcode("q /Pattern cs") end return shade_no end local function flush(result,flusher) if result then local figures = result.fig if figures then for f=1, #figures do info("flushing figure %s",f) local figure = figures[f] local objects = getobjects(result,figure,f) local fignum = tonumber(figure:filename():match("([%d]+)$") or figure:charcode() or 0) local miterlimit, linecap, linejoin, dashed = -1, -1, -1, false local bbox = figure:boundingbox() local llx, lly, urx, ury = bbox[1], bbox[2], bbox[3], bbox[4] -- faster than unpack if urx < llx then else if tex_code_pre_mplib[f] then put2output(tex_code_pre_mplib[f]) end pdf_startfigure(fignum,llx,lly,urx,ury) start_pdf_code() if objects then local savedpath = nil local savedhtap = nil for o=1,#objects do local object = objects[o] local objecttype = object.type local prescript = object.prescript prescript = prescript and script2table(prescript) -- prescript is now a table local cr_over = do_preobj_CR(object,prescript) -- color local tr_opaq = do_preobj_TR(prescript) -- opacity if prescript and prescript.mplibtexboxid then put_tex_boxes(object,prescript) elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then --skip elseif objecttype == "start_clip" then local evenodd = not object.istext and object.postscript == "evenodd" start_pdf_code() flushnormalpath(object.path,false) pdf_literalcode(evenodd and "W* n" or "W n") elseif objecttype == "stop_clip" then stop_pdf_code() miterlimit, linecap, linejoin, dashed = -1, -1, -1, false elseif objecttype == "special" then if prescript and prescript.postmplibverbtex then figcontents.post[#figcontents.post+1] = prescript.postmplibverbtex end elseif objecttype == "text" then local ot = object.transform -- 3,4,5,6,1,2 start_pdf_code() pdf_literalcode("%f %f %f %f %f %f cm",ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) pdf_textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth) stop_pdf_code() else local evenodd, collect, both = false, false, false local postscript = object.postscript if not object.istext then if postscript == "evenodd" then evenodd = true elseif postscript == "collect" then collect = true elseif postscript == "both" then both = true elseif postscript == "eoboth" then evenodd = true both = true end end if collect then if not savedpath then savedpath = { object.path or false } savedhtap = { object.htap or false } else savedpath[#savedpath+1] = object.path or false savedhtap[#savedhtap+1] = object.htap or false end else local shade_no = do_preobj_SH(object,prescript) -- shading local ml = object.miterlimit if ml and ml ~= miterlimit then miterlimit = ml pdf_literalcode("%f M",ml) end local lj = object.linejoin if lj and lj ~= linejoin then linejoin = lj pdf_literalcode("%i j",lj) end local lc = object.linecap if lc and lc ~= linecap then linecap = lc pdf_literalcode("%i J",lc) end local dl = object.dash if dl then local d = format("[%s] %f d",tableconcat(dl.dashes or {}," "),dl.offset) if d ~= dashed then dashed = d pdf_literalcode(dashed) end elseif dashed then pdf_literalcode("[] 0 d") dashed = false end local path = object.path local transformed, penwidth = false, 1 local open = path and path[1].left_type and path[#path].right_type local pen = object.pen if pen then if pen.type == 'elliptical' then transformed, penwidth = pen_characteristics(object) -- boolean, value pdf_literalcode("%f w",penwidth) if objecttype == 'fill' then objecttype = 'both' end else -- calculated by mplib itself objecttype = 'fill' end end if transformed then start_pdf_code() end if path then if savedpath then for i=1,#savedpath do local path = savedpath[i] if transformed then flushconcatpath(path,open) else flushnormalpath(path,open) end end savedpath = nil end if transformed then flushconcatpath(path,open) else flushnormalpath(path,open) end if not shade_no then -- conflict with shading if objecttype == "fill" then pdf_literalcode(evenodd and "h f*" or "h f") elseif objecttype == "outline" then if both then pdf_literalcode(evenodd and "h B*" or "h B") else pdf_literalcode(open and "S" or "h S") end elseif objecttype == "both" then pdf_literalcode(evenodd and "h B*" or "h B") end end end if transformed then stop_pdf_code() end local path = object.htap if path then if transformed then start_pdf_code() end if savedhtap then for i=1,#savedhtap do local path = savedhtap[i] if transformed then flushconcatpath(path,open) else flushnormalpath(path,open) end end savedhtap = nil evenodd = true end if transformed then flushconcatpath(path,open) else flushnormalpath(path,open) end if objecttype == "fill" then pdf_literalcode(evenodd and "h f*" or "h f") elseif objecttype == "outline" then pdf_literalcode(open and "S" or "h S") elseif objecttype == "both" then pdf_literalcode(evenodd and "h B*" or "h B") end if transformed then stop_pdf_code() end end if shade_no then -- shading pdf_literalcode("W n /MPlibSh%s sh Q",shade_no) end end end if tr_opaq then -- opacity stop_pdf_code() end if cr_over then -- color put2output"\\special{pdf:ec}" end end end stop_pdf_code() pdf_stopfigure() for _,v in ipairs(figcontents) do if type(v) == "table" then texsprint"\\mplibtoPDF{"; texsprint(v[1], v[2]); texsprint"}" else texsprint(v) end end if #figcontents.post > 0 then texsprint(figcontents.post) end figcontents = { post = { } } end end end end end luamplib.flush = flush local function colorconverter(cr) local n = #cr if n == 4 then local c, m, y, k = cr[1], cr[2], cr[3], cr[4] return format("%.3f %.3f %.3f %.3f k %.3f %.3f %.3f %.3f K",c,m,y,k,c,m,y,k), "0 g 0 G" elseif n == 3 then local r, g, b = cr[1], cr[2], cr[3] return format("%.3f %.3f %.3f rg %.3f %.3f %.3f RG",r,g,b,r,g,b), "0 g 0 G" else local s = cr[1] return format("%.3f g %.3f G",s,s), "0 g 0 G" end end luamplib.colorconverter = colorconverter -- -- End of File `luamplib.lua'.