diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/mlib-pdf.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkiv/mlib-pdf.lua | 598 |
1 files changed, 288 insertions, 310 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/mlib-pdf.lua b/Master/texmf-dist/tex/context/base/mkiv/mlib-pdf.lua index 75f810fb33b..92fde5e1369 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/mlib-pdf.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/mlib-pdf.lua @@ -6,9 +6,8 @@ if not modules then modules = { } end modules ['mlib-pdf'] = { license = "see context related readme files", } --- maybe %s is better than %f - -local format, concat, gsub = string.format, table.concat, string.gsub +local gsub = string.gsub +local concat, insert, remove = table.concat, table.insert, table.remove local abs, sqrt, round = math.abs, math.sqrt, math.round local setmetatable, rawset, tostring, tonumber, type = setmetatable, rawset, tostring, tonumber, type local P, S, C, Ct, Cc, Cg, Cf, Carg = lpeg.P, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.Carg @@ -41,23 +40,34 @@ local pdfflusher = { } metapost.flushers.pdf = pdfflusher metapost.n = 0 -metapost.optimize = true -- false local experiment = true -- uses context(node) that already does delayed nodes local savedliterals = nil -- needs checking -local mpsliteral = nodes.pool.register(node.new("whatsit",nodes.whatsitcodes.pdfliteral)) -- pdfliteral.mode = 1 - -local f_f = formatters["%F"] - -local f_m = formatters["%F %F m"] -local f_c = formatters["%F %F %F %F %F %F c"] -local f_l = formatters["%F %F l"] -local f_cm = formatters["%F %F %F %F %F %F cm"] -local f_M = formatters["%F M"] +local mpsliteral = nodes.pool.originliteral + +local f_f = formatters["%.6F"] +local f_m = formatters["%.6F %.6F m"] +local f_c = formatters["%.6F %.6F %.6F %.6F %.6F %.6F c"] +local f_l = formatters["%.6F %.6F l"] +local f_cm = formatters["%.6F %.6F %.6F %.6F %.6F %.6F cm"] +local f_M = formatters["%.6F M"] local f_j = formatters["%i j"] local f_J = formatters["%i J"] -local f_d = formatters["[%s] %F d"] -local f_w = formatters["%F w"] +local f_d = formatters["[%s] %.6F d"] +local f_w = formatters["%.3F w"] + +directives.register("metapost.stripzeros",function() + f_f = formatters["%.6N"] + f_m = formatters["%.6N %.6N m"] + f_c = formatters["%.6N %.6N %.6N %.6N %.6N %.6N c"] + f_l = formatters["%.6N %.6N l"] + f_cm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N cm"] + f_M = formatters["%.6N M"] + f_j = formatters["%i j"] + f_J = formatters["%i J"] + f_d = formatters["[%s] %.6N d"] + f_w = formatters["%.3N w"] +end) directives.register("metapost.savetable",function(v) if type(v) == "string" then @@ -73,60 +83,28 @@ trackers.register("metapost.forcestroke",function(v) force_stroke = v end) -local pdfliteral = function(pdfcode) - local literal = copy_node(mpsliteral) - literal.data = pdfcode - return literal -end - -- Because in MKiV we always have two passes, we save the objects. When an extra -- mp run is done (due to for instance texts identifier in the parse pass), we -- get a new result table and the stored objects are forgotten. Otherwise they -- are reused. local function getobjects(result,figure,index) - if metapost.optimize then - local robjects = result.objects - if not robjects then - robjects = { } - result.objects = robjects - end - local fobjects = robjects[index or 1] - if not fobjects then - fobjects = figure:objects() - robjects[index] = fobjects - end - return fobjects - else - return figure:objects() - end + return figure:objects() end -function metapost.convert(result, trialrun, flusher, multipass, askedfig) - if trialrun then - local multipassindeed = metapost.parse(result,askedfig) - if multipass and not multipassindeed and metapost.optimize then - if save_table then - table.save(save_table,metapost.totable(result,1)) -- direct - end - metapost.flush(result,flusher,askedfig) -- saves a run - else - return false - end - else - if save_table then - table.save(save_table,metapost.totable(result,1)) -- direct - end - metapost.flush(result,flusher,askedfig) +function metapost.convert(specification,result) + local flusher = specification.flusher + local askedfig = specification.askedfig + if save_table then + table.save(save_table,metapost.totable(result,1)) -- direct end + metapost.flush(specification,result) return true -- done end function metapost.flushliteral(d) if savedliterals then - local literal = copy_node(mpsliteral) - literal.data = savedliterals[d] - write_node(literal) + write_node(mpsliteral(savedliterals[d])) else report_metapost("problem flushing literal %a",d) end @@ -140,7 +118,7 @@ function pdfflusher.comment(message) if message then message = formatters["%% mps graphic %s: %s"](metapost.n,message) if experiment then - context(pdfliteral(message)) + context(mpsliteral(message)) elseif savedliterals then local last = #savedliterals + 1 savedliterals[last] = message @@ -169,7 +147,7 @@ function pdfflusher.flushfigure(pdfliterals) -- table if #pdfliterals > 0 then pdfliterals = concat(pdfliterals,"\n") if experiment then - context(pdfliteral(pdfliterals)) + context(mpsliteral(pdfliterals)) else if savedliterals then local last = #savedliterals + 1 @@ -320,15 +298,6 @@ local p_boolean = P("false") * Cc(false) + P("true") * Cc(true) local p_set = Ct(number^1) local p_path = Ct(Ct(number * number^-5)^1) --- local variable = --- P("1:") * key * p_number --- + P("2:") * key * p_string --- + P("3:") * key * p_boolean --- + S("4568") * P(":") * key * p_set --- + P("7:") * key * p_path --- --- local pattern_key = Cf ( Carg(1) * (Cg(variable * newline^0)^0), rawset) - local variable = P("1:") * p_number + P("2:") * p_string @@ -374,7 +343,9 @@ function metapost.processspecial(str) end end -local function setproperties(figure) +local stack = { } + +local function pushproperties(figure) local boundingbox = figure:boundingbox() local properties = { llx = boundingbox[1], @@ -388,313 +359,321 @@ local function setproperties(figure) italic = figure:italcorr(), number = figure:charcode() or 0, } + insert(stack,properties) metapost.properties = properties return properties end +local function popproperties() + metapost.properties = remove(stack) +end + local function nocomment() end metapost.comment = nocomment -function metapost.flush(result,flusher,askedfig) +function metapost.flush(specification,result) if result then - local figures = result.fig + local flusher = specification.flusher + local askedfig = specification.askedfig + local incontext = specification.incontext + local figures = result.fig if figures then flusher = flusher or pdfflusher - local resetplugins = metapost.resetplugins or ignore -- before figure - local processplugins = metapost.processplugins or ignore -- each object + local resetplugins = metapost.resetplugins or ignore -- before figure + local processplugins = metapost.processplugins or ignore -- each object local synchronizeplugins = metapost.synchronizeplugins or ignore - local pluginactions = metapost.pluginactions or ignore -- before / after - local startfigure = flusher.startfigure - local stopfigure = flusher.stopfigure - local flushfigure = flusher.flushfigure - local textfigure = flusher.textfigure - local processspecial = flusher.processspecial or metapost.processspecial - metapost.comment = flusher.comment or nocomment + local pluginactions = metapost.pluginactions or ignore -- before / after + local startfigure = flusher.startfigure + local stopfigure = flusher.stopfigure + local flushfigure = flusher.flushfigure + local textfigure = flusher.textfigure + local processspecial = flusher.processspecial or metapost.processspecial + metapost.comment = flusher.comment or nocomment for index=1,#figures do - local figure = figures[index] - local properties = setproperties(figure) + local figure = figures[index] + local properties = pushproperties(figure) if askedfig == "direct" or askedfig == "all" or askedfig == properties.number then - local objects = getobjects(result,figure,index) - local result = { } - local miterlimit, linecap, linejoin, dashed = -1, -1, -1, false - local llx = properties.llx - local lly = properties.lly - local urx = properties.urx - local ury = properties.ury + local objects = getobjects(result,figure,index) + local result = { } + local miterlimit = -1 + local linecap = -1 + local linejoin = -1 + local dashed = false + local llx = properties.llx + local lly = properties.lly + local urx = properties.urx + local ury = properties.ury if urx < llx then -- invalid startfigure(properties.number,0,0,0,0,"invalid",figure) stopfigure() else - startfigure(properties.number,llx,lly,urx,ury,"begin",figure) - result[#result+1] = "q" - if objects then - resetplugins(result) -- we should move the colorinitializer here - local savedpath = nil - local savedhtap = nil - for o=1,#objects do - local object = objects[o] - local objecttype = object.type - if objecttype == "text" then - result[#result+1] = "q" - local ot = object.transform -- 3,4,5,6,1,2 - result[#result+1] = f_cm(ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) -- TH: formatters["%F %F m %F %F %F %F 0 0 cm"](unpack(ot)) - flushfigure(result) -- flush accumulated literals - result = { } - textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth) - result[#result+1] = "Q" - elseif objecttype == "special" then - if processspecial then - processspecial(object.prescript) - end - elseif objecttype == "start_clip" then - local evenodd = not object.istext and object.postscript == "evenodd" - result[#result+1] = "q" - flushnormalpath(object.path,result,false) - result[#result+1] = evenodd and "W* n" or "W n" - elseif objecttype == "stop_clip" then - result[#result+1] = "Q" - miterlimit, linecap, linejoin, dashed = -1, -1, -1, "" -- was false - elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then - -- skip - else - -- we use an indirect table as we want to overload - -- entries but this is not possible in userdata - -- - -- can be optimized if no path - -- - local original = object - local object = { } - setmetatable(object, { - __index = original - }) - -- first we analyze - local before, after = processplugins(object) - 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 + + -- we need to be indirect if we want the one-pass solution + + local function processfigure() + result[#result+1] = "q" + if objects then + -- resetplugins(result) -- we should move the colorinitializer here + local savedpath = nil + local savedhtap = nil + for o=1,#objects do + local object = objects[o] + local objecttype = object.type + if objecttype == "text" then + result[#result+1] = "q" + local ot = object.transform -- 3,4,5,6,1,2 + result[#result+1] = f_cm(ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) + flushfigure(result) -- flush accumulated literals + result = { } + textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth) + result[#result+1] = "Q" + elseif objecttype == "special" then + if processspecial then + processspecial(object.prescript) end + elseif objecttype == "start_clip" then + local evenodd = not object.istext and object.postscript == "evenodd" + result[#result+1] = "q" + flushnormalpath(object.path,result,false) + result[#result+1] = evenodd and "W* n" or "W n" + elseif objecttype == "stop_clip" then + result[#result+1] = "Q" + miterlimit, linecap, linejoin, dashed = -1, -1, -1, "" -- was false + elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then + -- skip else - local objecttype = object.type -- can have changed - if before then - result = pluginactions(before,result,flushfigure) - end - local ml = object.miterlimit - if ml and ml ~= miterlimit then - miterlimit = ml - result[#result+1] = f_M(ml) - end - local lj = object.linejoin - if lj and lj ~= linejoin then - linejoin = lj - result[#result+1] = f_j(lj) - end - local lc = object.linecap - if lc and lc ~= linecap then - linecap = lc - result[#result+1] = f_J(lc) + -- we use an indirect table as we want to overload + -- entries but this is not possible in userdata + -- + -- can be optimized if no path + -- + local original = object + local object = { } + setmetatable(object, { + __index = original + }) + local before, + after = processplugins(object) + local evenodd = false + local collect = false + local both = 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 both then - if dashed ~= false then -- was just dashed test - result[#result+1] = "[] 0 d" - dashed = false + -- + 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 dl = object.dash - if dl then - local d = f_d(concat(dl.dashes or {}," "),dl.offset) - if d ~= dashed then - dashed = d - result[#result+1] = d - end - elseif dashed ~= false then -- was just dashed test - result[#result+1] = "[] 0 d" - dashed = false + local objecttype = object.type -- can have changed + if before then + result = pluginactions(before,result,flushfigure) end - end - local path = object.path -- newpath - local transformed, penwidth = false, 1 - local open = path and path[1].left_type and path[#path].right_type -- at this moment only "end_point" - local pen = object.pen - if pen then - if pen.type == 'elliptical' then - transformed, penwidth = pen_characteristics(original) -- boolean, value - result[#result+1] = f_w(penwidth) -- todo: only if changed - if objecttype == 'fill' then - objecttype = 'both' + local ml = object.miterlimit + if ml and ml ~= miterlimit then + miterlimit = ml + result[#result+1] = f_M(ml) + end + local lj = object.linejoin + if lj and lj ~= linejoin then + linejoin = lj + result[#result+1] = f_j(lj) + end + local lc = object.linecap + if lc and lc ~= linecap then + linecap = lc + result[#result+1] = f_J(lc) + end + if both then + if dashed ~= false then -- was just dashed test + result[#result+1] = "[] 0 d" + dashed = false end - else -- calculated by mplib itself - objecttype = 'fill' - end - end - if transformed then - result[#result+1] = "q" - end - if path then - if savedpath then - for i=1,#savedpath do - local path = savedpath[i] - if transformed then - flushconcatpath(path,result,open) - else - flushnormalpath(path,result,open) + else + local dl = object.dash + if dl then + local d = f_d(concat(dl.dashes or {}," "),dl.offset) + if d ~= dashed then + dashed = d + result[#result+1] = d end + elseif dashed ~= false then -- was just dashed test + result[#result+1] = "[] 0 d" + dashed = false end - savedpath = nil end - if transformed then - flushconcatpath(path,result,open) - else - flushnormalpath(path,result,open) - end - if force_stroke then - result[#result+1] = open and "S" or "h S" - elseif objecttype == "fill" then - result[#result+1] = evenodd and "h f*" or "h f" -- f* = eo - elseif objecttype == "outline" then - if both then - result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo - else - result[#result+1] = open and "S" or "h S" - end - elseif objecttype == "both" then - result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo -- b includes closepath + local path = object.path -- newpath + local transformed = false + local penwidth = 1 + local open = path and path[1].left_type and path[#path].right_type -- at this moment only "end_point" + local pen = object.pen + if pen then + if pen.type == "elliptical" then + transformed, penwidth = pen_characteristics(original) -- boolean, value + result[#result+1] = f_w(penwidth) -- todo: only if changed + if objecttype == "fill" then + objecttype = "both" + end + else -- calculated by mplib itself + objecttype = "fill" + end end - end - if transformed then - result[#result+1] = "Q" - end - local path = object.htap - if path then if transformed then result[#result+1] = "q" end - if savedhtap then - for i=1,#savedhtap do - local path = savedhtap[i] - if transformed then - flushconcatpath(path,result,open) + if path then + if savedpath then + for i=1,#savedpath do + local path = savedpath[i] + if transformed then + flushconcatpath(path,result,open) + else + flushnormalpath(path,result,open) + end + end + savedpath = nil + end + if transformed then + flushconcatpath(path,result,open) + else + flushnormalpath(path,result,open) + end + if force_stroke then + result[#result+1] = open and "S" or "h S" + elseif objecttype == "fill" then + result[#result+1] = evenodd and "h f*" or "h f" -- f* = eo + elseif objecttype == "outline" then + if both then + result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo else - flushnormalpath(path,result,open) + result[#result+1] = open and "S" or "h S" end + elseif objecttype == "both" then + result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo -- b includes closepath end - savedhtap = nil - evenodd = true end if transformed then - flushconcatpath(path,result,open) - else - flushnormalpath(path,result,open) + result[#result+1] = "Q" end - if force_stroke then - result[#result+1] = open and "S" or "h S" - elseif objecttype == "fill" then - result[#result+1] = evenodd and "h f*" or "h f" -- f* = eo - elseif objecttype == "outline" then - result[#result+1] = open and "S" or "h S" - elseif objecttype == "both" then - result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo -- b includes closepath + local path = object.htap + if path then + if transformed then + result[#result+1] = "q" + end + if savedhtap then + for i=1,#savedhtap do + local path = savedhtap[i] + if transformed then + flushconcatpath(path,result,open) + else + flushnormalpath(path,result,open) + end + end + savedhtap = nil + evenodd = true + end + if transformed then + flushconcatpath(path,result,open) + else + flushnormalpath(path,result,open) + end + if force_stroke then + result[#result+1] = open and "S" or "h S" + elseif objecttype == "fill" then + result[#result+1] = evenodd and "h f*" or "h f" -- f* = eo + elseif objecttype == "outline" then + result[#result+1] = open and "S" or "h S" + elseif objecttype == "both" then + result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo -- b includes closepath + end + if transformed then + result[#result+1] = "Q" + end end - if transformed then - result[#result+1] = "Q" + if after then + result = pluginactions(after,result,flushfigure) end end - if after then - result = pluginactions(after,result,flushfigure) + if object.grouped then + -- can be qQ'd so changes can end up in groups + miterlimit, linecap, linejoin, dashed = -1, -1, -1, "" -- was false end end - if object.grouped then - -- can be qQ'd so changes can end up in groups - miterlimit, linecap, linejoin, dashed = -1, -1, -1, "" -- was false - end end end + result[#result+1] = "Q" + flushfigure(result) + end + startfigure(properties.number,llx,lly,urx,ury,"begin",figure) + if incontext then + context(function() processfigure() end) + else + processfigure() end - result[#result+1] = "Q" - flushfigure(result) stopfigure("end") + end if askedfig ~= "all" then break end end + popproperties() end metapost.comment = nocomment + resetplugins(result) -- we should move the colorinitializer here end end end -function metapost.parse(result,askedfig) - if result then - local figures = result.fig - if figures then - local multipass = false - local analyzeplugins = metapost.analyzeplugins -- each object - for index=1,#figures do - local figure = figures[index] - local properties = setproperties(figure) - if askedfig == "direct" or askedfig == "all" or askedfig == properties.number then - local objects = getobjects(result,figure,index) - if objects then - for o=1,#objects do - if analyzeplugins(objects[o]) then - multipass = true - end - end - end - if askedfig ~= "all" then - break - end - end +-- tracing: + +do + + local result = { } + + local flusher = { + startfigure = function() + result = { } + context.startnointerference() + end, + flushfigure = function(literals) + local n = #result + for i=1,#literals do + result[n+i] = literals[i] end - return multipass + end, + stopfigure = function() + context.stopnointerference() end - end -end + } --- tracing: + local specification = { + flusher = flusher, + -- incontext = true, + } -local result = { } - -local flusher = { - startfigure = function() - result = { } - context.startnointerference() - end, - flushfigure = function(literals) - local n = #result - for i=1,#literals do - result[n+i] = literals[i] - end - end, - stopfigure = function() - context.stopnointerference() + function metapost.pdfliterals(result) + metapost.flush(specification,result) + return result end -} -function metapost.pdfliterals(result) - metapost.flush(result,flusher) - return result end function metapost.totable(result,askedfig) @@ -702,7 +681,6 @@ function metapost.totable(result,askedfig) local figure = result and result.fig and result.fig[1] if figure then local results = { } - -- local objects = figure:objects() local objects = getobjects(result,figure,askedfig) for o=1,#objects do local object = objects[o] |