diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luamplib/luamplib.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/luamplib/luamplib.lua | 1060 |
1 files changed, 660 insertions, 400 deletions
diff --git a/Master/texmf-dist/tex/luatex/luamplib/luamplib.lua b/Master/texmf-dist/tex/luatex/luamplib/luamplib.lua index acd3e4aec35..972e36172fd 100644 --- a/Master/texmf-dist/tex/luatex/luamplib/luamplib.lua +++ b/Master/texmf-dist/tex/luatex/luamplib/luamplib.lua @@ -11,8 +11,8 @@ luatexbase.provides_module { name = "luamplib", - version = "2.32.2", - date = "2024/06/14", + version = "2.34.5", + date = "2024/08/03", description = "Lua package to typeset Metapost with LuaTeX's MPLib.", } @@ -45,7 +45,6 @@ local function termorlog (target, text, kind) if kind == "Error" then error() end end end - local function warn (...) -- beware '%' symbol termorlog("term and log", select("#",...) > 1 and format(...) or ...) end @@ -60,22 +59,19 @@ luamplib.showlog = luamplib.showlog or false local tableconcat = table.concat local tableinsert = table.insert +local tableunpack = table.unpack 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 @@ -86,7 +82,6 @@ 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_" @@ -105,11 +100,8 @@ local mk_full_path = lfs.mkdirp or lfs.mkdirs or function(path) end end -local luamplibtime = kpse.find_file("luamplib.lua") -luamplibtime = luamplibtime and lfsattributes(luamplibtime,"modification") - +local luamplibtime = lfsattributes(kpse.find_file"luamplib.lua", "modification") local currenttime = os.time() - local outputdir, cachedir if lfstouch then for i,v in ipairs{'TEXMFVAR','TEXMF_OUTPUT_DIRECTORY','.','TEXMFOUTPUT'} do @@ -146,7 +138,6 @@ function luamplib.getcachedir(dir) end end end - local noneedtoreplace = { ["boxes.mp"] = true, -- ["format.mp"] = true, ["graph.mp"] = true, ["marith.mp"] = true, ["mfplain.mp"] = true, @@ -164,7 +155,6 @@ local noneedtoreplace = { ["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 @@ -182,12 +172,10 @@ local function replaceformatmp(file,newfile,ofmodify) 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 @@ -200,19 +188,15 @@ local function replaceinputmpfile (name,file) 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"); @@ -222,7 +206,6 @@ local function replaceinputmpfile (name,file) end return file end - fh = ioopen(newfile,"w") if not fh then return file end fh:write(data); fh:close() @@ -238,12 +221,10 @@ do end mpkpse = kpse.new(arg[exe], "mpost") end - local special_ftype = { pfb = "type1 fonts", enc = "enc files", } - function luamplib.finder (name, mode, ftype) if mode == "w" then if name and name ~= "mpout.log" then @@ -273,16 +254,14 @@ local preamble = [[ let normalfontsize = fontsize; input %s ; ]] - local currentformat = "plain" function luamplib.setformat (name) currentformat = name end - luamplib.codeinherit = false local mplibinstances = {} +luamplib.instances = mplibinstances local has_instancename = false - local function reporterror (result, prevlog) if not result then err("no result object returned") @@ -314,7 +293,7 @@ local function reporterror (result, prevlog) return log end end - +if not math.initialseed then math.randomseed(currenttime) end local function luamplibload (name) local mpx = mplib.new { ini_version = true, @@ -329,7 +308,7 @@ local function luamplibload (name) local preamble = tableconcat{ format(preamble, replacesuffix(name,"mp")), luamplib.preambles.mplibcode, - luamplib.legacy_verbatimtex and luamplib.preambles.legacyverbatimtex or "", + luamplib.legacyverbatimtex and luamplib.preambles.legacyverbatimtex or "", luamplib.textextlabel and luamplib.preambles.textextlabel or "", } local result, log @@ -341,7 +320,6 @@ local function luamplibload (name) log = reporterror(result) return mpx, result, log end - local function process (data, instancename) local currfmt if instancename and instancename ~= "" then @@ -352,7 +330,7 @@ local function process (data, instancename) currentformat, luamplib.numbersystem or "scaled", tostring(luamplib.textextlabel), - tostring(luamplib.legacy_verbatimtex), + tostring(luamplib.legacyverbatimtex), } has_instancename = false end @@ -382,20 +360,17 @@ local function process (data, instancename) 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")' - + 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) @@ -407,8 +382,7 @@ local function process_tex_text (str) else local boxid = texboxes.globalid + 1 texboxes.globalid = boxid - run_tex_code(format( - [[\expandafter\newbox\csname luamplib.box.%s\endcsname]], 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)) @@ -434,7 +408,6 @@ local mplibcolorfmt = { [[\color_select:n%s\endgroup]], }, } - local colfmt = is_defined'color_select:n' and "l3color" or "xcolor" if colfmt == "l3color" then run_tex_code{ @@ -448,7 +421,6 @@ if colfmt == "l3color" then } end local ccexplat = luatexbase.registernumber"luamplibcctabexplat" - local function process_color (str) if str then if not str:find("%b{}") then @@ -499,14 +471,12 @@ 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) @@ -528,7 +498,6 @@ 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 {} @@ -557,7 +526,6 @@ local function mpprint(buffer,...) end end end - function luamplib.runscript (code) local id, str = code:match("(.-){(.*)}") if id and str then @@ -579,7 +547,7 @@ function luamplib.runscript (code) return buffer end buffer = {} - mpprint(buffer, table.unpack(res)) + mpprint(buffer, tableunpack(res)) return tableconcat(buffer) end return "" @@ -592,9 +560,7 @@ local function protecttexcontents (str) :gsub("%zPerCent%z", "\\%%") :gsub("%s+", " ") end - -luamplib.legacy_verbatimtex = true - +luamplib.legacyverbatimtex = true function luamplib.maketext (str, what) if str and str ~= "" then str = protecttexcontents(str) @@ -603,7 +569,7 @@ function luamplib.maketext (str, what) 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.legacyverbatimtex then if luamplib.in_the_fig then return process_verbatimtex_infig(str) else @@ -621,10 +587,10 @@ function luamplib.maketext (str, what) end local function colorsplit (res) - local t, tt = { }, res:gsub("[%[%]]",""):explode() + local t, tt = { }, res:gsub("[%[%]]","",2):explode() local be = tt[1]:find"^%d" and 1 or 2 for i=be, #tt do - if tt[i]:find"^%a" then break end + if not tonumber(tt[i]) then break end t[#t+1] = tt[i] end return t @@ -659,28 +625,21 @@ luamplib.shadecolor = function (str) run_tex_code({ [[\color_export:nnN{]], str, [[}{backend}\mplib_@tempa]], },ccexplat) - local name = get_macro'mplib_@tempa':match'{(.-)}{.+}' + local name, value = 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 + obj = format("%s 0 R", ltx.pdf.object_id( t[1]:sub(2,-1) )) else obj = t[2] end - local value = t[3]:match"%[(.-)%]" or t[3] - return format('(%s) withprescript"mplib_spotcolor=%s:%s"', value,obj,name) + return format('(1) withprescript"mplib_spotcolor=%s:%s:%s"', value,obj,name) end return colorsplit(res) end -local running = -1073741824 +local decimals = "%.%d+" +local function rmzeros(str) return str:gsub("%.?0+$","") end + local emboldenfonts = { } local function getemboldenwidth (curr, fakebold) local width = emboldenfonts.width @@ -713,13 +672,14 @@ local function getrulewhatsit (line, wd, ht, dp) fmt = "pdf:content "..fmt pl = node.new("whatsit","special") end - pl.data = fmt:format(line, 0, -dp, wd, ht+dp, "B") + pl.data = fmt:format(line, 0, -dp, wd, ht+dp, "B") :gsub(decimals,rmzeros) local ss = node.new"glue" node.setglue(ss, 0, 65536, 65536, 2, 2) pl.next = ss return pl end local function getrulemetric (box, curr, bp) + local running = -1073741824 local wd,ht,dp = curr.width, curr.height, curr.depth wd = wd == running and box.width or wd ht = ht == running and box.height or ht @@ -771,7 +731,8 @@ local function embolden (box, curr, fakebold) head, curr = node.remove(head, curr) elseif curr.id == node.id"glyph" and curr.font > 0 then local f = curr.font - local i = emboldenfonts[f] + local key = format("%s:%s",f,fakebold) + local i = emboldenfonts[key] if not i then local ft = font.getfont(f) or font.getcopy(f) if pdfmode then @@ -787,7 +748,7 @@ local function embolden (box, curr, fakebold) name = format('%s;embolden=%s;',name,fakebold) _, i = fonts.constructors.readanddefine(name,ft.size) end - emboldenfonts[f] = i + emboldenfonts[key] = i end curr.font = i end @@ -799,6 +760,9 @@ end local function graphictextcolor (col, filldraw) if col:find"^[%d%.:]+$" then col = col:explode":" + for i=1,#col do + col[i] = format("%.3f", col[i]) + end if pdfmode then local op = #col == 4 and "k" or #col == 3 and "rg" or "g" col[#col+1] = filldraw == "fill" and op or op:upper() @@ -973,7 +937,7 @@ function luamplib.glyph (f, c) if not gid then return mperr"cannot get GID (glyph id)" end local fac = 1000 / (shapedata.units or 1000) local t = shapedata.glyphs[gid].segments - if not t then return "image(fill fullcircle scaled 0;)" end + if not t then return "image()" end for i,v in ipairs(t) do if type(v) == "table" then for ii,vv in ipairs(v) do @@ -988,7 +952,7 @@ function luamplib.glyph (f, c) end local rulefmt = "mpliboutlinepic[%i]:=image(addto currentpicture contour \z -unitsquare shifted - center unitsquare;) xscaled %f yscaled %f shifted (%f,%f);" + unitsquare shifted - center unitsquare;) xscaled %f yscaled %f shifted (%f,%f);" local outline_horz, outline_vert function outline_vert (res, box, curr, xshift, yshift) local b2u = box.dir == "LTL" @@ -1223,7 +1187,7 @@ function luamplib.outlinetext (text) local id = tonumber(fmt:match"mplibtexboxid=(%d+):") local box = texgetbox(id) local res = outline_horz({ }, box, box.head, 0, 0) - if #res == 0 then res = { "mpliboutlinepic[1]:=image(fill fullcircle scaled 0;);" } end + if #res == 0 then res = { "mpliboutlinepic[1]:=image();" } end return tableconcat(res) .. format("mpliboutlinenum:=%i;", #res) end @@ -1410,7 +1374,13 @@ vardef mpliboutlinetext@# (expr t) text rest = ) mplib_do_outline_options_r; ) enddef ; primarydef t withpattern p = - image( fill t withprescript "mplibpattern=" & if numeric p: decimal fi p; ) + image( + if cycle t: + fill + else: + draw + fi + t withprescript "mplibpattern=" & if numeric p: decimal fi p; ) enddef; vardef mplibtransformmatrix (text e) = save t; transform t; @@ -1424,6 +1394,75 @@ vardef mplibtransformmatrix (text e) = & decimal ypart t & "," & "}"); enddef; +primarydef p withfademethod s = + if picture p: + image( + draw p; + draw center p withprescript "mplibfadestate=stop"; + ) + else: + p withprescript "mplibfadestate=stop" + fi + withprescript "mplibfadetype=" & s + withprescript "mplibfadebbox=" & + decimal (xpart llcorner p -1/4) & ":" & + decimal (ypart llcorner p -1/4) & ":" & + decimal (xpart urcorner p +1/4) & ":" & + decimal (ypart urcorner p +1/4) +enddef; +def withfadeopacity (expr a,b) = + withprescript "mplibfadeopacity=" & + decimal a & ":" & + decimal b +enddef; +def withfadevector (expr a,b) = + withprescript "mplibfadevector=" & + decimal xpart a & ":" & + decimal ypart a & ":" & + decimal xpart b & ":" & + decimal ypart b +enddef; +let withfadecenter = withfadevector; +def withfaderadius (expr a,b) = + withprescript "mplibfaderadius=" & + decimal a & ":" & + decimal b +enddef; +def withfadebbox (expr a,b) = + withprescript "mplibfadebbox=" & + decimal xpart a & ":" & + decimal ypart a & ":" & + decimal xpart b & ":" & + decimal ypart b +enddef; +primarydef p asgroup s = + image( + draw center p + withprescript "mplibgroupbbox=" & + decimal (xpart llcorner p -1/4) & ":" & + decimal (ypart llcorner p -1/4) & ":" & + decimal (xpart urcorner p +1/4) & ":" & + decimal (ypart urcorner p +1/4) + withprescript "gr_state=start" + withprescript "gr_type=" & s; + draw p; + draw center p withprescript "gr_state=stop"; + ) +enddef; +def withgroupbbox (expr a,b) = + withprescript "mplibgroupbbox=" & + decimal xpart a & ":" & + decimal ypart a & ":" & + decimal xpart b & ":" & + decimal ypart b +enddef; +def withgroupname expr s = + withprescript "mplibgroupname=" & s +enddef; +def usemplibgroup primary s = + draw maketext("\usemplibgroup{" & s & "}") + shifted runscript("return luamplib.trgroupshifts['" & s & "']") +enddef; ]], legacyverbatimtex = [[ def specialVerbatimTeX (text t) = runscript("luamplibprefig{"&t&"}") enddef; @@ -1449,7 +1488,6 @@ enddef; } luamplib.verbatiminput = false - local function protect_expansion (str) if str then str = str:gsub("\\","!!!Control!!!") @@ -1460,7 +1498,6 @@ local function protect_expansion (str) return format("\\unexpanded{%s}",str) end end - local function unprotect_expansion (str) if str then return str:gsub("!!!Control!!!", "\\") @@ -1470,22 +1507,17 @@ local function unprotect_expansion (str) :gsub("!!!RBrace!!!", "}") end end - luamplib.everymplib = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end }) luamplib.everyendmplib = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end }) - function luamplib.process_mplibcode (data, instancename) texboxes.localid = 4096 - - if luamplib.legacy_verbatimtex then + if luamplib.legacyverbatimtex 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\")") @@ -1514,163 +1546,42 @@ function luamplib.process_mplibcode (data, instancename) return format("verbatimtex %s etex", unprotect_expansion(str)) end) end - process(data, instancename) end -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 + t[k] = v end end return t end -local function getobjects(result,figure,f) - return figure:objects() -end - -function luamplib.convert (result, flusher) - luamplib.flush(result, flusher) - return true -- done -end - 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 +local function pdf_literalcode (...) + put2output{ -2, format(...) :gsub(decimals,rmzeros) } end +local start_pdf_code = pdfmode + and function() pdf_literalcode"q" end + or function() put2output"\\special{pdf:bcontent}" end +local stop_pdf_code = pdfmode + and function() pdf_literalcode"Q" end + or function() put2output"\\special{pdf:econtent}" end local function put_tex_boxes (object,prescript) - local box = prescript.mplibtexboxid + local box = prescript.mplibtexboxid:explode":" local n,tw,th = box[1],tonumber(box[2]),tonumber(box[3]) if n and tw and th then local op = object.path @@ -1726,32 +1637,42 @@ local pdfobjs, pdfetcs = {}, {} pdfetcs.pgfextgs = "pgf@sys@addpdfresource@extgs@plain" pdfetcs.pgfpattern = "pgf@sys@addpdfresource@patterns@plain" pdfetcs.pgfcolorspace = "pgf@sys@addpdfresource@colorspaces@plain" - -local function update_pdfobjs (os) - local on = pdfobjs[os] +local function update_pdfobjs (os, stream) + local key = os + if stream then key = key..stream end + local on = pdfobjs[key] if on then return on,false end if pdfmode then - on = pdf.immediateobj(os) + if stream then + on = pdf.immediateobj("stream",stream,os) + else + on = pdf.immediateobj(os) + end else on = pdfetcs.cnt or 1 - texsprint(format("\\special{pdf:obj @mplibpdfobj%s %s}",on,os)) + if stream then + texsprint(format("\\special{pdf:stream @mplibpdfobj%s (%s) <<%s>>}",on,stream,os)) + else + texsprint(format("\\special{pdf:obj @mplibpdfobj%s %s}",on,os)) + end pdfetcs.cnt = on + 1 end - pdfobjs[os] = on + pdfobjs[key] = on return on,true end - +pdfetcs.resfmt = pdfmode and "%s 0 R" or "@mplibpdfobj%s" if pdfmode then pdfetcs.getpageres = pdf.getpageresources or function() return pdf.pageresources end - pdfetcs.setpageres = pdf.setpageresources or function(s) pdf.pageresources = s end - pdfetcs.initialize_resources = function (name) + local getpageres = pdfetcs.getpageres + local setpageres = pdf.setpageresources or function(s) pdf.pageresources = s end + local initialize_resources = function (name) local tabname = format("%s_res",name) pdfetcs[tabname] = { } if luatexbase.callbacktypes.finish_pdffile then -- ltluatex local obj = pdf.reserveobj() - pdfetcs.setpageres(format("%s/%s %i 0 R", pdfetcs.getpageres() or "", name, obj)) + setpageres(format("%s/%s %i 0 R", getpageres() or "", name, obj)) luatexbase.add_to_callback("finish_pdffile", function() pdf.immediateobj(obj, format("<<%s>>", tableconcat(pdfetcs[tabname]))) end, @@ -1759,21 +1680,38 @@ if pdfmode then end end pdfetcs.fallback_update_resources = function (name, res) + local tabname = format("%s_res",name) + if not pdfetcs[tabname] then + initialize_resources(name) + end if luatexbase.callbacktypes.finish_pdffile then - local t = pdfetcs[format("%s_res",name)] + local t = pdfetcs[tabname] t[#t+1] = res else - local tpr, n = pdfetcs.getpageres() or "", 0 + local tpr, n = getpageres() or "", 0 tpr, n = tpr:gsub(format("/%s<<",name), "%1"..res) if n == 0 then tpr = format("%s/%s<<%s>>", tpr, name, res) end - pdfetcs.setpageres(tpr) + setpageres(tpr) end end else - texsprint("\\special{pdf:obj @MPlibTr<<>>}","\\special{pdf:obj @MPlibSh<<>>}", - "\\special{pdf:obj @MPlibCS<<>>}","\\special{pdf:obj @MPlibPt<<>>}") + texsprint { + "\\luamplibatfirstshipout{", + "\\special{pdf:obj @MPlibTr<<>>}", + "\\special{pdf:obj @MPlibSh<<>>}", + "\\special{pdf:obj @MPlibCS<<>>}", + "\\special{pdf:obj @MPlibPt<<>>}}", + } + pdfetcs.resadded = { } + pdfetcs.fallback_update_resources = function (name,res,obj) + texsprint{"\\special{pdf:put ", obj, " <<", res, ">>}"} + if not pdfetcs.resadded[name] then + texsprint{"\\luamplibateveryshipout{\\special{pdf:put @resources <</", name, " ", obj, ">>}}"} + pdfetcs.resadded[name] = obj + end + end end local transparancy_modes = { [0] = "Normal", @@ -1783,22 +1721,19 @@ local transparancy_modes = { [0] = "Normal", "Hue", "Saturation", "Color", "Luminosity", "Compatible", } - -local function update_tr_res(mode,opaq) - local os = format("<</BM /%s/ca %.3f/CA %.3f/AIS false>>",mode,opaq,opaq) - local on, new = update_pdfobjs(os) - if not new then return on end +local function add_extgs_resources (on, new) local key = format("MPlibTr%s", on) - local val = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s", on) - if pdfmanagement then - texsprint(ccexplat, - format("\\pdfmanagement_add:nnn{Page/Resources/ExtGState}{%s}{%s}", key, val)) - else - local tr = format("/%s %s", key, val) - if is_defined(pdfetcs.pgfextgs) then - texsprint(format("\\csname %s\\endcsname{%s}", pdfetcs.pgfextgs,tr)) - elseif pdfmode then - if is_defined"TRP@list" then + if new then + local val = format(pdfetcs.resfmt, on) + if pdfmanagement then + texsprint { + "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/ExtGState}{", key, "}{", val, "}" + } + else + local tr = format("/%s %s", key, val) + if is_defined(pdfetcs.pgfextgs) then + texsprint { "\\csname ", pdfetcs.pgfextgs, "\\endcsname{", tr, "}" } + elseif is_defined"TRP@list" then texsprint(catat11,{ [[\if@filesw\immediate\write\@auxout{]], [[\string\g@addto@macro\string\TRP@list{]], @@ -1809,86 +1744,87 @@ local function update_tr_res(mode,opaq) texsprint(catat11,[[\global\TRP@reruntrue]]) end else - if not pdfetcs.ExtGState_res then - pdfetcs.initialize_resources"ExtGState" - end - pdfetcs.fallback_update_resources("ExtGState", tr) + pdfetcs.fallback_update_resources("ExtGState",tr,"@MPlibTr") end - else - texsprint(format("\\special{pdf:put @MPlibTr<<%s>>}",tr)) - texsprint"\\special{pdf:put @resources<</ExtGState @MPlibTr>>}" end end - return on + return key end - local function do_preobj_TR(object,prescript) if object.postscript == "collect" then return end local opaq = prescript and prescript.tr_transparency - local tron_no if opaq then + local key, on, os, new 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) + mode = transparancy_modes[tonumber(mode)] or mode + opaq = format("%.3f", opaq) :gsub(decimals,rmzeros) + for i,v in ipairs{ {mode,opaq},{"Normal",1} } do + os = format("<</BM/%s/ca %s/CA %s/AIS false>>",v[1],v[2],v[2]) + on, new = update_pdfobjs(os) + key = add_extgs_resources(on,new) + if i == 1 then + pdf_literalcode("/%s gs",key) + else + return format("/%s gs",key) + end + end end - return tron_no end local function sh_pdfpageresources(shtype,domain,colorspace,ca,cb,coordinates,steps,fractions) - local fun2fmt,os = "<</FunctionType 2/Domain [%s]/C0 [%s]/C1 [%s]/N 1>>" + for _,v in ipairs{ca,cb} do + for i,vv in ipairs(v) do + for ii,vvv in ipairs(vv) do + v[i][ii] = tonumber(vvv) and format("%.3f",vvv) or vvv + end + end + end + local fun2fmt,os = "<</FunctionType 2/Domain[%s]/C0[%s]/C1[%s]/N 1>>" if steps > 1 then local list,bounds,encode = { },{ },{ } for i=1,steps do if i < steps then - bounds[i] = fractions[i] or 1 + bounds[i] = format("%.3f", 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)) + :gsub(decimals,rmzeros) + list[i] = format(pdfetcs.resfmt, update_pdfobjs(os)) end os = tableconcat { "<</FunctionType 3", - format("/Bounds [%s]", tableconcat(bounds,' ')), - format("/Encode [%s]", tableconcat(encode,' ')), - format("/Functions [%s]", tableconcat(list, ' ')), - format("/Domain [%s]>>", domain), - } + format("/Bounds[%s]", tableconcat(bounds,' ')), + format("/Encode[%s]", tableconcat(encode,' ')), + format("/Functions[%s]", tableconcat(list, ' ')), + format("/Domain[%s]>>", domain), + } :gsub(decimals,rmzeros) else os = fun2fmt:format(domain,tableconcat(ca[1],' '),tableconcat(cb[1],' ')) + :gsub(decimals,rmzeros) end - local objref = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s",update_pdfobjs(os)) + local objref = format(pdfetcs.resfmt, update_pdfobjs(os)) os = tableconcat { format("<</ShadingType %i", shtype), format("/ColorSpace %s", colorspace), format("/Function %s", objref), - format("/Coords [%s]", coordinates), - "/Extend [true true]/AntiAlias true>>", - } + format("/Coords[%s]", coordinates), + "/Extend[true true]/AntiAlias true>>", + } :gsub(decimals,rmzeros) local on, new = update_pdfobjs(os) - if not new then return on end - local key = format("MPlibSh%s", on) - local val = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s", on) - if pdfmanagement then - texsprint(ccexplat, - format("\\pdfmanagement_add:nnn{Page/Resources/Shading}{%s}{%s}", key, val)) - else - local res = format("/%s %s", key, val) - if pdfmode then - if not pdfetcs.Shading_res then - pdfetcs.initialize_resources"Shading" - end - pdfetcs.fallback_update_resources("Shading", res) + if new then + local key, val = format("MPlibSh%s", on), format(pdfetcs.resfmt, on) + if pdfmanagement then + texsprint { + "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/Shading}{", key, "}{", val, "}" + } else - texsprint(format("\\special{pdf:put @MPlibSh<<%s>>}", res)) - texsprint"\\special{pdf:put @resources<</Shading @MPlibSh>>}" + local res = format("/%s %s", key, val) + pdfetcs.fallback_update_resources("Shading",res,"@MPlibSh") end end return on end - local function color_normalize(ca,cb) if #cb == 1 then if #ca == 4 then @@ -1900,7 +1836,6 @@ local function color_normalize(ca,cb) cb[1], cb[2], cb[3], cb[4] = 1-cb[1], 1-cb[2], 1-cb[3], 0 end end - pdfetcs.clrspcs = setmetatable({ }, { __index = function(t,names) run_tex_code({ [[\color_model_new:nnn]], @@ -1912,7 +1847,6 @@ pdfetcs.clrspcs = setmetatable({ }, { __index = function(t,names) t[names] = colorspace return colorspace end }) - local function do_preobj_SH(object,prescript) local shade_no local sh_type = prescript and prescript.sh_type @@ -1920,14 +1854,14 @@ local function do_preobj_SH(object,prescript) return else 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 centera = (prescript.sh_center_a or "0 0"):explode() + local centerb = (prescript.sh_center_b or "0 0"):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 first = (prescript.sh_first or "0 0"):explode() + local setx = (prescript.sh_set_x or "0 0"):explode() + local sety = (prescript.sh_set_y or "0 0"):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 @@ -1949,15 +1883,15 @@ local function do_preobj_SH(object,prescript) 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} } + ca = { (prescript.sh_color_a_1 or prescript.sh_color_a or "0"):explode":" } + cb = { (prescript.sh_color_b_1 or prescript.sh_color_b or "1"):explode":" } 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} + ca[i] = (prescript[format("sh_color_a_%i",i)] or "0"):explode":" + cb[i] = (prescript[format("sh_color_b_%i",i)] or "1"):explode":" end end if prescript.mplib_spotcolor then @@ -1966,15 +1900,14 @@ local function do_preobj_SH(object,prescript) 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"=(.+)" + local t, name, value = script[i]:explode"="[2]:explode":" + value, objref, name = t[1], t[2], t[3] if not names[name] then pos = pos+1 names[name] = pos names[#names+1] = name end - local t = { } + t = { } for j=1,names[name] do t[#t+1] = 0 end t[#t+1] = value tableinsert(#ca == #cb and ca or cb, t) @@ -2025,29 +1958,93 @@ local function do_preobj_SH(object,prescript) else err"unknown shading type" end - pdf_literalcode("q /Pattern cs") end return shade_no end -patterns = { } +pdfetcs.patterns = { } +local function gather_resources (optres) + local t, do_pattern = { }, not optres + local names = {"ExtGState","ColorSpace","Shading"} + if do_pattern then + names[#names+1] = "Pattern" + end + if pdfmode then + if pdfmanagement then + for _,v in ipairs(names) do + local pp = get_macro(format("g__pdfdict_/g__pdf_Core/Page/Resources/%s_prop",v)) + if pp and pp:find"__prop_pair" then + t[#t+1] = format("/%s %s 0 R", v, ltx.pdf.object_id("__pdf/Page/Resources/"..v)) + end + end + else + local res = pdfetcs.getpageres() or "" + run_tex_code[[\mplibtmptoks\expandafter{\the\pdfvariable pageresources}]] + res = res .. texgettoks'mplibtmptoks' + if do_pattern then return res end + res = res:explode"/+" + for _,v in ipairs(res) do + v = v:match"^%s*(.-)%s*$" + if not v:find"Pattern" and not optres:find(v) then + t[#t+1] = "/" .. v + end + end + end + else + if pdfmanagement then + for _,v in ipairs(names) do + local pp = get_macro(format("g__pdfdict_/g__pdf_Core/Page/Resources/%s_prop",v)) + if pp and pp:find"__prop_pair" then + run_tex_code { + "\\mplibtmptoks\\expanded{{", + format("/%s \\csname pdf_object_ref:n\\endcsname{__pdf/Page/Resources/%s}",v,v), + "}}", + } + t[#t+1] = texgettoks'mplibtmptoks' + end + end + elseif is_defined(pdfetcs.pgfextgs) then + run_tex_code ({ + "\\mplibtmptoks\\expanded{{", + "\\ifpgf@sys@pdf@extgs@exists /ExtGState @pgfextgs\\fi", + "\\ifpgf@sys@pdf@colorspaces@exists /ColorSpace @pgfcolorspaces\\fi", + do_pattern and "\\ifpgf@sys@pdf@patterns@exists /Pattern @pgfpatterns \\fi" or "", + "}}", + }, catat11) + t[#t+1] = texgettoks'mplibtmptoks' + else + for _,v in ipairs(names) do + local vv = pdfetcs.resadded[v] + if vv then + t[#t+1] = format("/%s %s", v, vv) + end + end + end + end + return tableconcat(t) +end function luamplib.registerpattern ( boxid, name, opts ) local box = texgetbox(boxid) local wd = format("%.3f",box.width/factor) local hd = format("%.3f",(box.height+box.depth)/factor) - info("w/h/d of '%s': %s %s 0.0", name, wd, hd) + info("w/h/d of pattern '%s': %s 0", name, format("%s %s",wd, hd):gsub(decimals,rmzeros)) if opts.xstep == 0 then opts.xstep = nil end if opts.ystep == 0 then opts.ystep = nil end - if opts.colored == nil then opts.colored = true end + if opts.colored == nil then + opts.colored = opts.coloured + if opts.colored == nil then + opts.colored = true + end + end if type(opts.matrix) == "table" then opts.matrix = tableconcat(opts.matrix," ") end if type(opts.bbox) == "table" then opts.bbox = tableconcat(opts.bbox," ") end if opts.matrix and opts.matrix:find"%a" then local data = format("mplibtransformmatrix(%s);",opts.matrix) process(data,"@mplibtransformmatrix") local t = luamplib.transformmatrix - opts.matrix = format("%s %s %s %s", t[1], t[2], t[3], t[4]) - opts.xshift = opts.xshift or t[5] - opts.yshift = opts.yshift or t[6] + opts.matrix = format("%f %f %f %f", t[1], t[2], t[3], t[4]) + opts.xshift = opts.xshift or format("%f",t[5]) + opts.yshift = opts.yshift or format("%f",t[6]) end local attr = { "/Type/Pattern", @@ -2056,87 +2053,52 @@ function luamplib.registerpattern ( boxid, name, opts ) "/TilingType 2", format("/XStep %s", opts.xstep or wd), format("/YStep %s", opts.ystep or hd), - format("/Matrix [%s %s %s]", opts.matrix or "1 0 0 1", opts.xshift or 0, opts.yshift or 0), + format("/Matrix[%s %s %s]", opts.matrix or "1 0 0 1", opts.xshift or 0, opts.yshift or 0), } + local optres = opts.resources or "" + optres = optres .. gather_resources(optres) + local patterns = pdfetcs.patterns if pdfmode then - local optres, t = opts.resources or "", { } - if pdfmanagement then - for _,v in ipairs{"ExtGState","ColorSpace","Shading"} do - local pp = get_macro(format("g__pdfdict_/g__pdf_Core/Page/Resources/%s_prop",v)) - if pp and pp:find"__prop_pair" then - t[#t+1] = format("/%s %s", v, ltx.__pdf.object["__pdf/Page/Resources/"..v]) - end - end - else - local res = pdfetcs.getpageres() or "" - run_tex_code[[\mplibtmptoks\expandafter{\the\pdfvariable pageresources}]] - res = (res .. texgettoks'mplibtmptoks'):explode() - res = tableconcat(res," "):explode"/+" - for _,v in ipairs(res) do - if not v:find"Pattern" and not optres:find(v) then - t[#t+1] = "/" .. v - end - end - end - optres = optres .. tableconcat(t) if opts.bbox then - attr[#attr+1] = format("/BBox [%s]", opts.bbox) + attr[#attr+1] = format("/BBox[%s]", opts.bbox) end - local index = tex.saveboxresource(boxid, tableconcat(attr), optres, true, opts.bbox and 4 or 1) + attr = tableconcat(attr) :gsub(decimals,rmzeros) + local index = tex.saveboxresource(boxid, attr, optres, true, opts.bbox and 4 or 1) patterns[name] = { id = index, colored = opts.colored } else - local objname = "@mplibpattern"..name + local cnt = #patterns + 1 + local objname = "@mplibpattern" .. cnt local metric = format("bbox %s", opts.bbox or format("0 0 %s %s",wd,hd)) - local optres, t = opts.resources or "", { } - if pdfmanagement then - for _,v in ipairs{"ExtGState","ColorSpace","Shading"} do - run_tex_code(format( - [[\mplibtmptoks\expanded{{/%s \pdf_object_ref:n{__pdf/Page/Resources/%s}}}]],v,v),ccexplat) - t[#t+1] = texgettoks'mplibtmptoks' - end - elseif is_defined(pdfetcs.pgfextgs) then - run_tex_code("\\mplibtmptoks\\expanded{{\z - \\ifpgf@sys@pdf@extgs@exists /ExtGState @pgfextgs\\fi\z - \\ifpgf@sys@pdf@colorspaces@exists /ColorSpace @pgfcolorspaces\\fi}}",catat11) - t[#t+1] = texgettoks'mplibtmptoks' - else - t[#t+1] = "/ExtGState @MPlibTr/Shading @MPlibSh/ColorSpace @MPlibCS" - end - optres = optres .. tableconcat(t) texsprint { - [[\ifvmode\nointerlineskip\fi]], - format([[\hbox to0pt{\vbox to0pt{\hsize=\wd %i\vss\noindent]], boxid), -- force horiz mode? - [[\special{pdf:bcontent}]], - [[\special{pdf:bxobj ]], objname, format(" %s}", metric), - format([[\raise\dp %i\box %i]], boxid, boxid), - format([[\special{pdf:put @resources <<%s>>}]], optres), - [[\special{pdf:exobj <<]], tableconcat(attr), ">>}", - [[\special{pdf:econtent}]], - [[\par}\hss}]], + "\\expandafter\\newbox\\csname luamplib.patternbox.", cnt, "\\endcsname", + "\\global\\setbox\\csname luamplib.patternbox.", cnt, "\\endcsname", + "\\hbox{\\unhbox ", boxid, "}\\luamplibatnextshipout{", + "\\special{pdf:bcontent}", + "\\special{pdf:bxobj ", objname, " ", metric, "}", + "\\raise\\dp\\csname luamplib.patternbox.", cnt, "\\endcsname", + "\\box\\csname luamplib.patternbox.", cnt, "\\endcsname", + "\\special{pdf:put @resources <<", optres, ">>}", + "\\special{pdf:exobj <<", tableconcat(attr), ">>}", + "\\special{pdf:econtent}}", } - patterns[#patterns+1] = objname - patterns[name] = { id = #patterns, colored = opts.colored } + patterns[cnt] = objname + patterns[name] = { id = cnt, colored = opts.colored } end end local function pattern_colorspace (cs) local on, new = update_pdfobjs(format("[/Pattern %s]", cs)) if new then - local key = format("MPlibCS%i",on) - local val = pdfmode and format("%i 0 R",on) or format("@mplibpdfobj%i",on) + local key, val = format("MPlibCS%i",on), format(pdfetcs.resfmt,on) if pdfmanagement then - texsprint(ccexplat,format("\\pdfmanagement_add:nnn{Page/Resources/ColorSpace}{%s}{%s}",key,val)) + texsprint { + "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/ColorSpace}{", key, "}{", val, "}" + } else local res = format("/%s %s", key, val) if is_defined(pdfetcs.pgfcolorspace) then - texsprint(format("\\csname %s\\endcsname{%s}", pdfetcs.pgfcolorspace, res)) - elseif pdfmode then - if not pdfetcs.ColorSpace_res then - pdfetcs.initialize_resources"ColorSpace" - end - pdfetcs.fallback_update_resources("ColorSpace", res) + texsprint { "\\csname ", pdfetcs.pgfcolorspace, "\\endcsname{", res, "}" } else - texsprint(format("\\special{pdf:put @MPlibCS<<%s>>}", res)) - texsprint"\\special{pdf:put @resources<</ColorSpace @MPlibCS>>}" + pdfetcs.fallback_update_resources("ColorSpace",res,"@MPlibCS") end end end @@ -2145,6 +2107,7 @@ end local function do_preobj_PAT(object, prescript) local name = prescript and prescript.mplibpattern if not name then return end + local patterns = pdfetcs.patterns local patt = patterns[name] local index = patt and patt.id or err("cannot get pattern object '%s'", name) local key = format("MPlibPt%s",index) @@ -2174,27 +2137,310 @@ local function do_preobj_PAT(object, prescript) end pdf_literalcode("/MPlibCS%i cs %s /%s scn", pattern_colorspace(cs), color, key) end - if patt.done then return end - local val = pdfmode and format("%s 0 R",index) or patterns[index] - if pdfmanagement then - texsprint(ccexplat, format("\\pdfmanagement_add:nnn{Page/Resources/Pattern}{%s}{%s}",key,val)) - else - local res = format("/%s %s", key, val) - if is_defined(pdfetcs.pgfpattern) then - texsprint(format("\\csname %s\\endcsname{%s}", pdfetcs.pgfpattern, res)) - elseif pdfmode then - if not pdfetcs.Pattern_res then - pdfetcs.initialize_resources"Pattern" - end - pdfetcs.fallback_update_resources("Pattern", res) + if not patt.done then + local val = pdfmode and format("%s 0 R",index) or patterns[index] + if pdfmanagement then + texsprint { + "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/Pattern}{", key, "}{", val, "}" + } else - texsprint(format("\\special{pdf:put @MPlibPt<<%s>>}", res)) - texsprint"\\special{pdf:put @resources<</Pattern @MPlibPt>>}" + local res = format("/%s %s", key, val) + if is_defined(pdfetcs.pgfpattern) then + texsprint { "\\csname ", pdfetcs.pgfpattern, "\\endcsname{", res, "}" } + else + pdfetcs.fallback_update_resources("Pattern",res,"@MPlibPt") + end end end patt.done = true end +pdfetcs.fading = { } +local function do_preobj_FADE (object, prescript) + local fd_type = prescript and prescript.mplibfadetype + local fd_stop = prescript and prescript.mplibfadestate + if not fd_type then + return fd_stop -- returns "stop" (if picture) or nil + end + local bbox = prescript.mplibfadebbox:explode":" + local dx, dy = -bbox[1], -bbox[2] + local vec = prescript.mplibfadevector; vec = vec and vec:explode":" + if not vec then + if fd_type == "linear" then + vec = {bbox[1], bbox[2], bbox[3], bbox[2]} -- left to right + else + local centerx, centery = (bbox[1]+bbox[3])/2, (bbox[2]+bbox[4])/2 + vec = {centerx, centery, centerx, centery} -- center for both circles + end + end + local coords = { vec[1]+dx, vec[2]+dy, vec[3]+dx, vec[4]+dy } + if fd_type == "linear" then + coords = format("%f %f %f %f", tableunpack(coords)) + elseif fd_type == "circular" then + local width, height = bbox[3]-bbox[1], bbox[4]-bbox[2] + local radius = (prescript.mplibfaderadius or "0:"..math.sqrt(width^2+height^2)/2):explode":" + tableinsert(coords, 3, radius[1]) + tableinsert(coords, radius[2]) + coords = format("%f %f %f %f %f %f", tableunpack(coords)) + else + err("unknown fading method '%s'", fd_type) + end + fd_type = fd_type == "linear" and 2 or 3 + local opaq = (prescript.mplibfadeopacity or "1:0"):explode":" + local on, os, new + on = sh_pdfpageresources(fd_type, "0 1", "/DeviceGray", {{opaq[1]}}, {{opaq[2]}}, coords, 1) + os = format("<</PatternType 2/Shading %s>>", format(pdfetcs.resfmt, on)) + on = update_pdfobjs(os) + bbox = format("0 0 %f %f", bbox[3]+dx, bbox[4]+dy) + local streamtext = format("q /Pattern cs/MPlibFd%s scn %s re f Q", on, bbox) + :gsub(decimals,rmzeros) + os = format("<</Pattern<</MPlibFd%s %s>>>>", on, format(pdfetcs.resfmt, on)) + on = update_pdfobjs(os) + local resources = format(pdfetcs.resfmt, on) + on = update_pdfobjs"<</S/Transparency/CS/DeviceGray>>" + local attr = tableconcat{ + "/Subtype/Form", + "/BBox[", bbox, "]", + "/Matrix[1 0 0 1 ", format("%f %f", -dx,-dy), "]", + "/Resources ", resources, + "/Group ", format(pdfetcs.resfmt, on), + } :gsub(decimals,rmzeros) + on = update_pdfobjs(attr, streamtext) + os = "<</SMask<</S/Luminosity/G " .. format(pdfetcs.resfmt, on) .. ">>>>" + on, new = update_pdfobjs(os) + local key = add_extgs_resources(on,new) + start_pdf_code() + pdf_literalcode("/%s gs", key) + if fd_stop then return "standalone" end + return "start" +end + +pdfetcs.tr_group = { shifts = { } } +luamplib.trgroupshifts = pdfetcs.tr_group.shifts +local function do_preobj_GRP (object, prescript) + local grstate = prescript and prescript.gr_state + if not grstate then return end + local trgroup = pdfetcs.tr_group + if grstate == "start" then + trgroup.name = prescript.mplibgroupname or "lastmplibgroup" + trgroup.isolated, trgroup.knockout = false, false + for _,v in ipairs(prescript.gr_type:explode",+") do + trgroup[v] = true + end + trgroup.bbox = prescript.mplibgroupbbox:explode":" + put2output[[\begingroup\setbox\mplibscratchbox\hbox\bgroup]] + elseif grstate == "stop" then + local llx,lly,urx,ury = tableunpack(trgroup.bbox) + put2output(tableconcat{ + "\\egroup", + format("\\wd\\mplibscratchbox %fbp", urx-llx), + format("\\ht\\mplibscratchbox %fbp", ury-lly), + "\\dp\\mplibscratchbox 0pt", + }) + local grattr = format("/Group<</S/Transparency/I %s/K %s>>",trgroup.isolated,trgroup.knockout) + local res = gather_resources() + local bbox = format("%f %f %f %f", llx,lly,urx,ury) :gsub(decimals,rmzeros) + if pdfmode then + put2output(tableconcat{ + "\\saveboxresource type 2 attr{/Type/XObject/Subtype/Form/FormType 1", + "/BBox[", bbox, "]", grattr, "} resources{", res, "}\\mplibscratchbox", + [[\setbox\mplibscratchbox\hbox{\useboxresource\lastsavedboxresourceindex}]], + [[\wd\mplibscratchbox 0pt\ht\mplibscratchbox 0pt\dp\mplibscratchbox 0pt]], + [[\box\mplibscratchbox\endgroup]], + "\\expandafter\\xdef\\csname luamplib.group.", trgroup.name, "\\endcsname{", + "\\noexpand\\mplibstarttoPDF{",llx,"}{",lly,"}{",urx,"}{",ury,"}", + "\\useboxresource \\the\\lastsavedboxresourceindex\\noexpand\\mplibstoptoPDF}", + }) + else + trgroup.cnt = (trgroup.cnt or 0) + 1 + local objname = format("@mplibtrgr%s", trgroup.cnt) + put2output(tableconcat{ + "\\special{pdf:bxobj ", objname, " bbox ", bbox, "}", + "\\unhbox\\mplibscratchbox", + "\\special{pdf:put @resources <<", res, ">>}", + "\\special{pdf:exobj <<", grattr, ">>}", + "\\special{pdf:uxobj ", objname, "}\\endgroup", + }) + token.set_macro("luamplib.group."..trgroup.name, tableconcat{ + "\\mplibstarttoPDF{",llx,"}{",lly,"}{",urx,"}{",ury,"}", + "\\special{pdf:uxobj ", objname, "}\\mplibstoptoPDF", + }, "global") + end + trgroup.shifts[trgroup.name] = { llx, lly } + end + return grstate +end +function luamplib.registergroup (boxid, name, opts) + local box = texgetbox(boxid) + local wd, ht, dp = node.getwhd(box) + local res = (opts.resources or "") .. gather_resources() + local attr = { "/Type/XObject/Subtype/Form/FormType 1" } + if type(opts.matrix) == "table" then opts.matrix = tableconcat(opts.matrix," ") end + if type(opts.bbox) == "table" then opts.bbox = tableconcat(opts.bbox," ") end + if opts.matrix and opts.matrix:find"%a" then + local data = format("mplibtransformmatrix(%s);",opts.matrix) + process(data,"@mplibtransformmatrix") + opts.matrix = format("%f %f %f %f %f %f",tableunpack(luamplib.transformmatrix)) + end + local grtype = 3 + if opts.bbox then + attr[#attr+1] = format("/BBox[%s]", opts.bbox) + grtype = 2 + end + if opts.matrix then + attr[#attr+1] = format("/Matrix[%s]", opts.matrix) + grtype = opts.bbox and 4 or 1 + end + if opts.asgroup then + local t = { isolated = false, knockout = false } + for _,v in ipairs(opts.asgroup:explode",+") do t[v] = true end + attr[#attr+1] = format("/Group<</S/Transparency/I %s/K %s>>", t.isolated, t.knockout) + end + local trgroup = pdfetcs.tr_group + trgroup.shifts[name] = { get_macro'MPllx', get_macro'MPlly' } + local whd + if pdfmode then + attr = tableconcat(attr) :gsub(decimals,rmzeros) + local index = tex.saveboxresource(boxid, attr, res, true, grtype) + token.set_macro("luamplib.group."..name, "\\useboxresource "..index, "global") + whd = format("%.3f %.3f 0", wd/factor, (ht+dp)/factor) :gsub(decimals,rmzeros) + else + trgroup.cnt = (trgroup.cnt or 0) + 1 + local objname = format("@mplibtrgr%s", trgroup.cnt) + texsprint { + "\\expandafter\\newbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname", + "\\global\\setbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname", + "\\hbox{\\unhbox ", boxid, "}\\luamplibatnextshipout{", + "\\special{pdf:bcontent}", + "\\special{pdf:bxobj ", objname, " width ", wd, "sp height ", ht, "sp depth ", dp, "sp}", + "\\unhbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname", + "\\special{pdf:put @resources <<", res, ">>}", + "\\special{pdf:exobj <<", tableconcat(attr), ">>}", + "\\special{pdf:econtent}}", + } + token.set_macro("luamplib.group."..name, tableconcat{ + "\\begingroup\\setbox\\mplibscratchbox\\hbox{\\special{pdf:uxobj ", objname, "}}", + "\\wd\\mplibscratchbox ", wd, "sp", + "\\ht\\mplibscratchbox ", ht, "sp", + "\\dp\\mplibscratchbox ", dp, "sp", + "\\box\\mplibscratchbox\\endgroup", + }, "global") + whd = format("%.3f %.3f %.3f", wd/factor, ht/factor, dp/factor) :gsub(decimals,rmzeros) + end + info("w/h/d of group '%s': %s", name, whd) +end + +local function stop_special_effects(fade,opaq,over) + if fade then -- fading + stop_pdf_code() + end + if opaq then -- opacity + pdf_literalcode(opaq) + end + if over then -- color + put2output"\\special{pdf:ec}" + end +end + +local function getobjects(result,figure,f) + return figure:objects() +end + +function luamplib.convert (result, flusher) + luamplib.flush(result, flusher) + return true -- done +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 + function luamplib.flush (result,flusher) if result then local figures = result.fig @@ -2224,6 +2470,9 @@ function luamplib.flush (result,flusher) 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(object,prescript) -- opacity + local fading_ = do_preobj_FADE(object,prescript) -- fading + local trgroup = do_preobj_GRP(object,prescript) -- transparency group + local pattern_ = do_preobj_PAT(object,prescript) -- pattern if prescript and prescript.mplibtexboxid then put_tex_boxes(object,prescript) elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then --skip @@ -2245,7 +2494,7 @@ function luamplib.flush (result,flusher) 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 + elseif not trgroup and fading_ ~= "stop" then local evenodd, collect, both = false, false, false local postscript = object.postscript if not object.istext then @@ -2269,8 +2518,6 @@ function luamplib.flush (result,flusher) savedhtap[#savedhtap+1] = object.htap or false end else - local shade_no = do_preobj_SH(object,prescript) -- shading - local pattern_ = do_preobj_PAT(object,prescript) -- pattern local ml = object.miterlimit if ml and ml ~= miterlimit then miterlimit = ml @@ -2312,6 +2559,11 @@ function luamplib.flush (result,flusher) objecttype = 'fill' end end + local shade_no = do_preobj_SH(object,prescript) -- shading + if shade_no then + pdf_literalcode"q /Pattern cs" + objecttype = false + end if transformed then start_pdf_code() end @@ -2332,18 +2584,16 @@ function luamplib.flush (result,flusher) 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 + 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 if transformed then @@ -2383,15 +2633,25 @@ function luamplib.flush (result,flusher) end end if shade_no then -- shading - pdf_literalcode("W n /MPlibSh%s sh Q",shade_no) + pdf_literalcode("W%s n /MPlibSh%s sh Q",evenodd and "*" or "",shade_no) end end end - if tr_opaq then -- opacity - stop_pdf_code() + if fading_ == "start" then + pdfetcs.fading.specialeffects = {fading_, tr_opaq, cr_over} + elseif trgroup == "start" then + pdfetcs.tr_group.specialeffects = {fading_, tr_opaq, cr_over} + elseif fading_ == "stop" then + local se = pdfetcs.fading.specialeffects + if se then stop_special_effects(se[1], se[2], se[3]) end + elseif trgroup == "stop" then + local se = pdfetcs.tr_group.specialeffects + if se then stop_special_effects(se[1], se[2], se[3]) end + else + stop_special_effects(fading_, tr_opaq, cr_over) end - if cr_over then -- color - put2output"\\special{pdf:ec}" + if fading_ or trgroup then -- extgs resetted + miterlimit, linecap, linejoin, dashed = -1, -1, -1, false end end end |