diff options
author | Marius <mariausol@gmail.com> | 2013-05-19 20:40:34 +0300 |
---|---|---|
committer | Marius <mariausol@gmail.com> | 2013-05-19 20:40:34 +0300 |
commit | 13ec4b540e0d46c97fd7b089e0b7413da81e0a9f (patch) | |
tree | bebfa563a17c06b3bd3bf8f6f4ba6d025e00d107 /tex/context/base/mlib-pdf.lua | |
parent | 69ad13650cda027526271179e95b5294694143a1 (diff) | |
download | context-13ec4b540e0d46c97fd7b089e0b7413da81e0a9f.tar.gz |
beta 2013.05.19 19:27
Diffstat (limited to 'tex/context/base/mlib-pdf.lua')
-rw-r--r-- | tex/context/base/mlib-pdf.lua | 1060 |
1 files changed, 530 insertions, 530 deletions
diff --git a/tex/context/base/mlib-pdf.lua b/tex/context/base/mlib-pdf.lua index 963309951..6ca50a12f 100644 --- a/tex/context/base/mlib-pdf.lua +++ b/tex/context/base/mlib-pdf.lua @@ -1,530 +1,530 @@ -if not modules then modules = { } end modules ['mlib-pdf'] = { - version = 1.001, - comment = "companion to mlib-ctx.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files", -} - --- maybe %s is better than %f - -local format, concat, gsub = string.format, table.concat, string.gsub -local abs, sqrt, round = math.abs, math.sqrt, math.round -local setmetatable = setmetatable -local Cf, C, Cg, Ct, P, S, lpegmatch = lpeg.Cf, lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.S, lpeg.match -local formatters = string.formatters - -local report_metapost = logs.reporter("metapost") - -local mplib, context = mplib, context - -local allocate = utilities.storage.allocate - -local copy_node = node.copy -local write_node = node.write - -metapost = metapost or { } -local metapost = metapost - -metapost.flushers = metapost.flushers or { } -local pdfflusher = { } -metapost.flushers.pdf = pdfflusher - -metapost.multipass = false -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 pdfliteral = function(s) - local literal = copy_node(mpsliteral) - literal.data = s - 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,f) - if metapost.optimize then - local objects = result.objects - if not objects then - result.objects = { } - end - objects = result.objects[f] - if not objects then - objects = figure:objects() - result.objects[f] = objects - end - return objects - else - return figure:objects() - end -end - -function metapost.convert(result, trialrun, flusher, multipass, askedfig) - if trialrun then - metapost.multipass = false - metapost.parse(result, askedfig) - if multipass and not metapost.multipass and metapost.optimize then - metapost.flush(result, flusher, askedfig) -- saves a run - else - return false - end - else - metapost.flush(result, flusher, askedfig) - end - return true -- done -end - -function metapost.flushliteral(d) - if savedliterals then - local literal = copy_node(mpsliteral) - literal.data = savedliterals[d] - write_node(literal) - else - report_metapost("problem flushing literal %a",d) - end -end - -function metapost.flushreset() -- will become obsolete and internal - savedliterals = nil -end - -function pdfflusher.comment(message) - if message then - message = formatters["%% mps graphic %s: %s"](metapost.n,message) - if experiment then - context(pdfliteral(message)) - else - if savedliterals then - local last = #savedliterals + 1 - savedliterals[last] = message - context.MPLIBtoPDF(last) - else - savedliterals = { message } - context.MPLIBtoPDF(1) - end - end - end -end - -function pdfflusher.startfigure(n,llx,lly,urx,ury,message) - savedliterals = nil - metapost.n = metapost.n + 1 - context.startMPLIBtoPDF(llx,lly,urx,ury) - if message then pdfflusher.comment(message) end -end - -function pdfflusher.stopfigure(message) - if message then pdfflusher.comment(message) end - context.stopMPLIBtoPDF() - context.MPLIBflushreset() -- maybe just at the beginning -end - -function pdfflusher.flushfigure(pdfliterals) -- table - if #pdfliterals > 0 then - pdfliterals = concat(pdfliterals,"\n") - if experiment then - context(pdfliteral(pdfliterals)) - else - if savedliterals then - local last = #savedliterals + 1 - savedliterals[last] = pdfliterals - context.MPLIBtoPDF(last) - else - savedliterals = { pdfliterals } - context.MPLIBtoPDF(1) - end - end - end -end - -function pdfflusher.textfigure(font,size,text,width,height,depth) -- we could save the factor - text = gsub(text,".","\\hbox{%1}") -- kerning happens in metapost (i have to check if this is true for mplib) - context.MPtextext(font,size,text,0,-number.dimenfactors.bp*depth) -end - -local bend_tolerance = 131/65536 - -local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1 - -local pen_info = mplib.pen_info - -local function pen_characteristics(object) - local t = 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 mpconcat(px, py) -- no tx, ty here / we can move this one inline if needed - 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, t, open) - local pth, ith, nt - if t then - nt = #t - else - t = { } - nt = 0 - end - for i=1,#path do - nt = nt + 1 - pth = path[i] - if not ith then - t[nt] = formatters["%f %f m"](pth.x_coord,pth.y_coord) - elseif curved(ith,pth) then - t[nt] = formatters["%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 - t[nt] = formatters["%f %f l"](pth.x_coord,pth.y_coord) - end - ith = pth - end - if not open then - nt = nt + 1 - local one = path[1] - if curved(pth,one) then - t[nt] = formatters["%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 - t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord) - end - elseif #path == 1 then - -- special case .. draw point - local one = path[1] - nt = nt + 1 - t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord) - end - return t -end - -local function flushconcatpath(path, t, open) - local pth, ith, nt - if t then - nt = #t - else - t = { } - nt = 0 - end - nt = nt + 1 - t[nt] = formatters["%f %f %f %f %f %f cm"](sx,rx,ry,sy,tx,ty) - for i=1,#path do - nt = nt + 1 - pth = path[i] - if not ith then - t[nt] = formatters["%f %f m"](mpconcat(pth.x_coord,pth.y_coord)) - elseif curved(ith,pth) then - local a, b = mpconcat(ith.right_x,ith.right_y) - local c, d = mpconcat(pth.left_x,pth.left_y) - t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(pth.x_coord,pth.y_coord)) - else - t[nt] = formatters["%f %f l"](mpconcat(pth.x_coord, pth.y_coord)) - end - ith = pth - end - if not open then - nt = nt + 1 - local one = path[1] - if curved(pth,one) then - local a, b = mpconcat(pth.right_x,pth.right_y) - local c, d = mpconcat(one.left_x,one.left_y) - t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(one.x_coord, one.y_coord)) - else - t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord)) - end - elseif #path == 1 then - -- special case .. draw point - nt = nt + 1 - local one = path[1] - t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord)) - end - return t -end - -metapost.flushnormalpath = flushnormalpath - --- The flusher is pdf based, if another backend is used, we need to overload the --- flusher; this is beta code, the organization will change (already upgraded in --- sync with mplib) --- --- We can avoid the before table but I like symmetry. There is of course a small --- performance penalty, but so is passing extra arguments (result, flusher, after) --- and returning stuff. - -local function ignore() end - -function metapost.flush(result,flusher,askedfig) - if result then - 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 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 - for f=1, #figures do - local figure = figures[f] - local objects = getobjects(result,figure,f) - local fignum = figure:charcode() or 0 - if askedfig == "direct" or askedfig == "all" or askedfig == fignum then - local t = { } - 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] - metapost.llx = llx - metapost.lly = lly - metapost.urx = urx - metapost.ury = ury - if urx < llx then - -- invalid - startfigure(fignum,0,0,0,0,"invalid",figure) - stopfigure() - else - startfigure(fignum,llx,lly,urx,ury,"begin",figure) - t[#t+1] = "q" - if objects then - resetplugins(t) -- we should move the colorinitializer here - for o=1,#objects do - local object = objects[o] - local objecttype = object.type - if objecttype == "start_bounds" or objecttype == "stop_bounds" or objecttype == "special" then - -- skip - elseif objecttype == "start_clip" then - t[#t+1] = "q" - flushnormalpath(object.path,t,false) - t[#t+1] = "W n" - elseif objecttype == "stop_clip" then - t[#t+1] = "Q" - miterlimit, linecap, linejoin, dashed = -1, -1, -1, false - elseif objecttype == "text" then - t[#t+1] = "q" - local ot = object.transform -- 3,4,5,6,1,2 - t[#t+1] = formatters["%f %f %f %f %f %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(t) -- flush accumulated literals - t = { } - textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth) - t[#t+1] = "Q" - 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 objecttype = object.type -- can have changed - if before then - t = pluginactions(before,t,flushfigure) - end - local ml = object.miterlimit - if ml and ml ~= miterlimit then - miterlimit = ml - t[#t+1] = formatters["%f M"](ml) - end - local lj = object.linejoin - if lj and lj ~= linejoin then - linejoin = lj - t[#t+1] = formatters["%i j"](lj) - end - local lc = object.linecap - if lc and lc ~= linecap then - linecap = lc - t[#t+1] = formatters["%i J"](lc) - end - local dl = object.dash - if dl then - local d = formatters["[%s] %f d"](concat(dl.dashes or {}," "),dl.offset) - if d ~= dashed then - dashed = d - t[#t+1] = dashed - end - elseif dashed then - t[#t+1] = "[] 0 d" - dashed = false - 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 - t[#t+1] = formatters["%f w"](penwidth) -- todo: only if changed - if objecttype == 'fill' then - objecttype = 'both' - end - else -- calculated by mplib itself - objecttype = 'fill' - end - end - if transformed then - t[#t+1] = "q" - end - if path then - if transformed then - flushconcatpath(path,t,open) - else - flushnormalpath(path,t,open) - end - if objecttype == "fill" then - t[#t+1] = "h f" - elseif objecttype == "outline" then - t[#t+1] = (open and "S") or "h S" - elseif objecttype == "both" then - t[#t+1] = "h B" - end - end - if transformed then - t[#t+1] = "Q" - end - local path = object.htap - if path then - if transformed then - t[#t+1] = "q" - end - if transformed then - flushconcatpath(path,t,open) - else - flushnormalpath(path,t,open) - end - if objecttype == "fill" then - t[#t+1] = "h f" - elseif objecttype == "outline" then - t[#t+1] = (open and "S") or "h S" - elseif objecttype == "both" then - t[#t+1] = "h B" - end - if transformed then - t[#t+1] = "Q" - end - end - if after then - t = pluginactions(after,t,flushfigure) - end - if object.grouped then - -- can be qQ'd so changes can end up in groups - miterlimit, linecap, linejoin, dashed = -1, -1, -1, false - end - end - end - end - t[#t+1] = "Q" - flushfigure(t) - stopfigure("end") - end - if askedfig ~= "all" then - break - end - end - end - end - end -end - -function metapost.parse(result,askedfig) - if result then - local figures = result.fig - if figures then - local analyzeplugins = metapost.analyzeplugins -- each object - for f=1,#figures do - local figure = figures[f] - local fignum = figure:charcode() or 0 - if askedfig == "direct" or askedfig == "all" or askedfig == fignum then - local bbox = figure:boundingbox() - metapost.llx = bbox[1] - metapost.lly = bbox[2] - metapost.urx = bbox[3] - metapost.ury = bbox[4] - local objects = getobjects(result,figure,f) - if objects then - for o=1,#objects do - analyzeplugins(objects[o]) - end - end - if askedfig ~= "all" then - break - end - end - end - end - end -end - --- tracing: - -local t = { } - -local flusher = { - startfigure = function() - t = { } - context.startnointerference() - end, - flushfigure = function(literals) - local n = #t - for i=1, #literals do - n = n + 1 - t[n] = literals[i] - end - end, - stopfigure = function() - context.stopnointerference() - end -} - -function metapost.pdfliterals(result) - metapost.flush(result,flusher) - return t -end - --- so far - -function metapost.totable(result) - local figure = result and result.fig and result.fig[1] - if figure then - local t = { } - local objects = figure:objects() - for o=1,#objects do - local object = objects[o] - local tt = { } - local fields = mplib.fields(object) - for f=1,#fields do - local field = fields[f] - tt[field] = object[field] - end - t[o] = tt - end - local b = figure:boundingbox() - return { - boundingbox = { llx = b[1], lly = b[2], urx = b[3], ury = b[4] }, - objects = t - } - else - return nil - end -end +if not modules then modules = { } end modules ['mlib-pdf'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- maybe %s is better than %f
+
+local format, concat, gsub = string.format, table.concat, string.gsub
+local abs, sqrt, round = math.abs, math.sqrt, math.round
+local setmetatable = setmetatable
+local Cf, C, Cg, Ct, P, S, lpegmatch = lpeg.Cf, lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.S, lpeg.match
+local formatters = string.formatters
+
+local report_metapost = logs.reporter("metapost")
+
+local mplib, context = mplib, context
+
+local allocate = utilities.storage.allocate
+
+local copy_node = node.copy
+local write_node = node.write
+
+metapost = metapost or { }
+local metapost = metapost
+
+metapost.flushers = metapost.flushers or { }
+local pdfflusher = { }
+metapost.flushers.pdf = pdfflusher
+
+metapost.multipass = false
+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 pdfliteral = function(s)
+ local literal = copy_node(mpsliteral)
+ literal.data = s
+ 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,f)
+ if metapost.optimize then
+ local objects = result.objects
+ if not objects then
+ result.objects = { }
+ end
+ objects = result.objects[f]
+ if not objects then
+ objects = figure:objects()
+ result.objects[f] = objects
+ end
+ return objects
+ else
+ return figure:objects()
+ end
+end
+
+function metapost.convert(result, trialrun, flusher, multipass, askedfig)
+ if trialrun then
+ metapost.multipass = false
+ metapost.parse(result, askedfig)
+ if multipass and not metapost.multipass and metapost.optimize then
+ metapost.flush(result, flusher, askedfig) -- saves a run
+ else
+ return false
+ end
+ else
+ metapost.flush(result, flusher, askedfig)
+ end
+ return true -- done
+end
+
+function metapost.flushliteral(d)
+ if savedliterals then
+ local literal = copy_node(mpsliteral)
+ literal.data = savedliterals[d]
+ write_node(literal)
+ else
+ report_metapost("problem flushing literal %a",d)
+ end
+end
+
+function metapost.flushreset() -- will become obsolete and internal
+ savedliterals = nil
+end
+
+function pdfflusher.comment(message)
+ if message then
+ message = formatters["%% mps graphic %s: %s"](metapost.n,message)
+ if experiment then
+ context(pdfliteral(message))
+ else
+ if savedliterals then
+ local last = #savedliterals + 1
+ savedliterals[last] = message
+ context.MPLIBtoPDF(last)
+ else
+ savedliterals = { message }
+ context.MPLIBtoPDF(1)
+ end
+ end
+ end
+end
+
+function pdfflusher.startfigure(n,llx,lly,urx,ury,message)
+ savedliterals = nil
+ metapost.n = metapost.n + 1
+ context.startMPLIBtoPDF(llx,lly,urx,ury)
+ if message then pdfflusher.comment(message) end
+end
+
+function pdfflusher.stopfigure(message)
+ if message then pdfflusher.comment(message) end
+ context.stopMPLIBtoPDF()
+ context.MPLIBflushreset() -- maybe just at the beginning
+end
+
+function pdfflusher.flushfigure(pdfliterals) -- table
+ if #pdfliterals > 0 then
+ pdfliterals = concat(pdfliterals,"\n")
+ if experiment then
+ context(pdfliteral(pdfliterals))
+ else
+ if savedliterals then
+ local last = #savedliterals + 1
+ savedliterals[last] = pdfliterals
+ context.MPLIBtoPDF(last)
+ else
+ savedliterals = { pdfliterals }
+ context.MPLIBtoPDF(1)
+ end
+ end
+ end
+end
+
+function pdfflusher.textfigure(font,size,text,width,height,depth) -- we could save the factor
+ text = gsub(text,".","\\hbox{%1}") -- kerning happens in metapost (i have to check if this is true for mplib)
+ context.MPtextext(font,size,text,0,-number.dimenfactors.bp*depth)
+end
+
+local bend_tolerance = 131/65536
+
+local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1
+
+local pen_info = mplib.pen_info
+
+local function pen_characteristics(object)
+ local t = 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 mpconcat(px, py) -- no tx, ty here / we can move this one inline if needed
+ 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, t, open)
+ local pth, ith, nt
+ if t then
+ nt = #t
+ else
+ t = { }
+ nt = 0
+ end
+ for i=1,#path do
+ nt = nt + 1
+ pth = path[i]
+ if not ith then
+ t[nt] = formatters["%f %f m"](pth.x_coord,pth.y_coord)
+ elseif curved(ith,pth) then
+ t[nt] = formatters["%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
+ t[nt] = formatters["%f %f l"](pth.x_coord,pth.y_coord)
+ end
+ ith = pth
+ end
+ if not open then
+ nt = nt + 1
+ local one = path[1]
+ if curved(pth,one) then
+ t[nt] = formatters["%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
+ t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord)
+ end
+ elseif #path == 1 then
+ -- special case .. draw point
+ local one = path[1]
+ nt = nt + 1
+ t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord)
+ end
+ return t
+end
+
+local function flushconcatpath(path, t, open)
+ local pth, ith, nt
+ if t then
+ nt = #t
+ else
+ t = { }
+ nt = 0
+ end
+ nt = nt + 1
+ t[nt] = formatters["%f %f %f %f %f %f cm"](sx,rx,ry,sy,tx,ty)
+ for i=1,#path do
+ nt = nt + 1
+ pth = path[i]
+ if not ith then
+ t[nt] = formatters["%f %f m"](mpconcat(pth.x_coord,pth.y_coord))
+ elseif curved(ith,pth) then
+ local a, b = mpconcat(ith.right_x,ith.right_y)
+ local c, d = mpconcat(pth.left_x,pth.left_y)
+ t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(pth.x_coord,pth.y_coord))
+ else
+ t[nt] = formatters["%f %f l"](mpconcat(pth.x_coord, pth.y_coord))
+ end
+ ith = pth
+ end
+ if not open then
+ nt = nt + 1
+ local one = path[1]
+ if curved(pth,one) then
+ local a, b = mpconcat(pth.right_x,pth.right_y)
+ local c, d = mpconcat(one.left_x,one.left_y)
+ t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(one.x_coord, one.y_coord))
+ else
+ t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord))
+ end
+ elseif #path == 1 then
+ -- special case .. draw point
+ nt = nt + 1
+ local one = path[1]
+ t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord))
+ end
+ return t
+end
+
+metapost.flushnormalpath = flushnormalpath
+
+-- The flusher is pdf based, if another backend is used, we need to overload the
+-- flusher; this is beta code, the organization will change (already upgraded in
+-- sync with mplib)
+--
+-- We can avoid the before table but I like symmetry. There is of course a small
+-- performance penalty, but so is passing extra arguments (result, flusher, after)
+-- and returning stuff.
+
+local function ignore() end
+
+function metapost.flush(result,flusher,askedfig)
+ if result then
+ 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 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
+ for f=1, #figures do
+ local figure = figures[f]
+ local objects = getobjects(result,figure,f)
+ local fignum = figure:charcode() or 0
+ if askedfig == "direct" or askedfig == "all" or askedfig == fignum then
+ local t = { }
+ 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]
+ metapost.llx = llx
+ metapost.lly = lly
+ metapost.urx = urx
+ metapost.ury = ury
+ if urx < llx then
+ -- invalid
+ startfigure(fignum,0,0,0,0,"invalid",figure)
+ stopfigure()
+ else
+ startfigure(fignum,llx,lly,urx,ury,"begin",figure)
+ t[#t+1] = "q"
+ if objects then
+ resetplugins(t) -- we should move the colorinitializer here
+ for o=1,#objects do
+ local object = objects[o]
+ local objecttype = object.type
+ if objecttype == "start_bounds" or objecttype == "stop_bounds" or objecttype == "special" then
+ -- skip
+ elseif objecttype == "start_clip" then
+ t[#t+1] = "q"
+ flushnormalpath(object.path,t,false)
+ t[#t+1] = "W n"
+ elseif objecttype == "stop_clip" then
+ t[#t+1] = "Q"
+ miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
+ elseif objecttype == "text" then
+ t[#t+1] = "q"
+ local ot = object.transform -- 3,4,5,6,1,2
+ t[#t+1] = formatters["%f %f %f %f %f %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(t) -- flush accumulated literals
+ t = { }
+ textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth)
+ t[#t+1] = "Q"
+ 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 objecttype = object.type -- can have changed
+ if before then
+ t = pluginactions(before,t,flushfigure)
+ end
+ local ml = object.miterlimit
+ if ml and ml ~= miterlimit then
+ miterlimit = ml
+ t[#t+1] = formatters["%f M"](ml)
+ end
+ local lj = object.linejoin
+ if lj and lj ~= linejoin then
+ linejoin = lj
+ t[#t+1] = formatters["%i j"](lj)
+ end
+ local lc = object.linecap
+ if lc and lc ~= linecap then
+ linecap = lc
+ t[#t+1] = formatters["%i J"](lc)
+ end
+ local dl = object.dash
+ if dl then
+ local d = formatters["[%s] %f d"](concat(dl.dashes or {}," "),dl.offset)
+ if d ~= dashed then
+ dashed = d
+ t[#t+1] = dashed
+ end
+ elseif dashed then
+ t[#t+1] = "[] 0 d"
+ dashed = false
+ 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
+ t[#t+1] = formatters["%f w"](penwidth) -- todo: only if changed
+ if objecttype == 'fill' then
+ objecttype = 'both'
+ end
+ else -- calculated by mplib itself
+ objecttype = 'fill'
+ end
+ end
+ if transformed then
+ t[#t+1] = "q"
+ end
+ if path then
+ if transformed then
+ flushconcatpath(path,t,open)
+ else
+ flushnormalpath(path,t,open)
+ end
+ if objecttype == "fill" then
+ t[#t+1] = "h f"
+ elseif objecttype == "outline" then
+ t[#t+1] = (open and "S") or "h S"
+ elseif objecttype == "both" then
+ t[#t+1] = "h B"
+ end
+ end
+ if transformed then
+ t[#t+1] = "Q"
+ end
+ local path = object.htap
+ if path then
+ if transformed then
+ t[#t+1] = "q"
+ end
+ if transformed then
+ flushconcatpath(path,t,open)
+ else
+ flushnormalpath(path,t,open)
+ end
+ if objecttype == "fill" then
+ t[#t+1] = "h f"
+ elseif objecttype == "outline" then
+ t[#t+1] = (open and "S") or "h S"
+ elseif objecttype == "both" then
+ t[#t+1] = "h B"
+ end
+ if transformed then
+ t[#t+1] = "Q"
+ end
+ end
+ if after then
+ t = pluginactions(after,t,flushfigure)
+ end
+ if object.grouped then
+ -- can be qQ'd so changes can end up in groups
+ miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
+ end
+ end
+ end
+ end
+ t[#t+1] = "Q"
+ flushfigure(t)
+ stopfigure("end")
+ end
+ if askedfig ~= "all" then
+ break
+ end
+ end
+ end
+ end
+ end
+end
+
+function metapost.parse(result,askedfig)
+ if result then
+ local figures = result.fig
+ if figures then
+ local analyzeplugins = metapost.analyzeplugins -- each object
+ for f=1,#figures do
+ local figure = figures[f]
+ local fignum = figure:charcode() or 0
+ if askedfig == "direct" or askedfig == "all" or askedfig == fignum then
+ local bbox = figure:boundingbox()
+ metapost.llx = bbox[1]
+ metapost.lly = bbox[2]
+ metapost.urx = bbox[3]
+ metapost.ury = bbox[4]
+ local objects = getobjects(result,figure,f)
+ if objects then
+ for o=1,#objects do
+ analyzeplugins(objects[o])
+ end
+ end
+ if askedfig ~= "all" then
+ break
+ end
+ end
+ end
+ end
+ end
+end
+
+-- tracing:
+
+local t = { }
+
+local flusher = {
+ startfigure = function()
+ t = { }
+ context.startnointerference()
+ end,
+ flushfigure = function(literals)
+ local n = #t
+ for i=1, #literals do
+ n = n + 1
+ t[n] = literals[i]
+ end
+ end,
+ stopfigure = function()
+ context.stopnointerference()
+ end
+}
+
+function metapost.pdfliterals(result)
+ metapost.flush(result,flusher)
+ return t
+end
+
+-- so far
+
+function metapost.totable(result)
+ local figure = result and result.fig and result.fig[1]
+ if figure then
+ local t = { }
+ local objects = figure:objects()
+ for o=1,#objects do
+ local object = objects[o]
+ local tt = { }
+ local fields = mplib.fields(object)
+ for f=1,#fields do
+ local field = fields[f]
+ tt[field] = object[field]
+ end
+ t[o] = tt
+ end
+ local b = figure:boundingbox()
+ return {
+ boundingbox = { llx = b[1], lly = b[2], urx = b[3], ury = b[4] },
+ objects = t
+ }
+ else
+ return nil
+ end
+end
|