summaryrefslogtreecommitdiff
path: root/tex/context/base/meta-pdf.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/meta-pdf.lua')
-rw-r--r--tex/context/base/meta-pdf.lua1134
1 files changed, 567 insertions, 567 deletions
diff --git a/tex/context/base/meta-pdf.lua b/tex/context/base/meta-pdf.lua
index 32e48902a..15211b560 100644
--- a/tex/context/base/meta-pdf.lua
+++ b/tex/context/base/meta-pdf.lua
@@ -1,567 +1,567 @@
-if not modules then modules = { } end modules ['meta-pdf'] = {
- version = 1.001,
- comment = "companion to meta-pdf.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- Finally we used an optimized version. The test code can be found in
--- meta-pdh.lua but since we no longer want to overload functione we use
--- more locals now. This module keeps changing as it is also a testbed.
---
--- We can make it even more efficient if needed, but as we don't use this
--- code often in \MKIV\ it makes no sense.
-
-local concat, unpack = table.concat, table.unpack
-local gsub, find, byte, gmatch, match = string.gsub, string.find, string.byte, string.gmatch, string.match
-local lpegmatch = lpeg.match
-local round = math.round
-local formatters, format = string.formatters, string.format
-
-local report_mptopdf = logs.reporter("graphics","mptopdf")
-
-local mplib, metapost, lpdf, context = mplib, metapost, lpdf, context
-
-local pdfrgbcode = lpdf.rgbcode
-local pdfcmykcode = lpdf.cmykcode
-local pdfgraycode = lpdf.graycode
-local pdfspotcode = lpdf.spotcode
-local pdftransparencycode = lpdf.transparencycode
-local pdffinishtransparencycode = lpdf.finishtransparencycode
-local pdfliteral = nodes.pool.pdfliteral
-
-metapost.mptopdf = metapost.mptopdf or { }
-local mptopdf = metapost.mptopdf
-
-mptopdf.nofconverted = 0
-
-local f_translate = formatters["1 0 0 0 1 %f %f cm"] -- no %s due to 1e-035 issues
-local f_concat = formatters["%f %f %f %f %f %f cm"] -- no %s due to 1e-035 issues
-
-local m_path, m_stack, m_texts, m_version, m_date, m_shortcuts = { }, { }, { }, 0, 0, false
-
-local m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
-local extra_path_code, ignore_path = nil, false
-local specials = { }
-
-local function resetpath()
- m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
-end
-
-local function resetall()
- m_path, m_stack, m_texts, m_version, m_shortcuts = { }, { }, { }, 0, false
- extra_path_code, ignore_path = nil, false
- specials = { }
- resetpath()
-end
-
-resetall()
-
--- -- this does not work as expected (displacement of text) beware, needs another
--- -- comment hack
---
--- local function pdfcode(str)
--- context(pdfliteral(str))
--- end
-
-local pdfcode = context.pdfliteral
-
-local function mpscode(str)
- if ignore_path then
- pdfcode("h W n")
- if extra_path_code then
- pdfcode(extra_path_code)
- extra_path_code = nil
- end
- ignore_path = false
- else
- pdfcode(str)
- end
-end
-
--- auxiliary functions
-
-local function flushconcat()
- if m_stack_concat then
- mpscode(f_concatm(unpack(m_stack_concat)))
- m_stack_concat = nil
- end
-end
-
-local function flushpath(cmd)
- if #m_stack_path > 0 then
- local path = { }
- if m_stack_concat then
- local sx, sy = m_stack_concat[1], m_stack_concat[4]
- local rx, ry = m_stack_concat[2], m_stack_concat[3]
- local tx, ty = m_stack_concat[5], m_stack_concat[6]
- local d = (sx*sy) - (rx*ry)
- for k=1,#m_stack_path do
- local v = m_stack_path[k]
- local px, py = v[1], v[2] ; v[1], v[2] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[1],v[2])
- if #v == 7 then
- local px, py = v[3], v[4] ; v[3], v[4] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[3],v[4])
- local px, py = v[5], v[6] ; v[5], v[6] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[5],v[6])
- end
- path[k] = concat(v," ")
- end
- else
- for k=1,#m_stack_path do
- path[k] = concat(m_stack_path[k]," ")
- end
- end
- flushconcat()
- pdfcode(concat(path," "))
- if m_stack_close then
- mpscode("h " .. cmd)
- else
- mpscode(cmd)
- end
- end
- resetpath()
-end
-
--- mp interface
-
-local mps = { }
-
-function mps.creator(a, b, c)
- m_version = tonumber(b)
-end
-
-function mps.creationdate(a)
- m_date = a
-end
-
-function mps.newpath()
- m_stack_path = { }
-end
-
-function mps.boundingbox(llx, lly, urx, ury)
- context.setMPboundingbox(llx,lly,urx,ury)
-end
-
-function mps.moveto(x,y)
- m_stack_path[#m_stack_path+1] = { x, y, "m" }
-end
-
-function mps.curveto(ax, ay, bx, by, cx, cy)
- m_stack_path[#m_stack_path+1] = { ax, ay, bx, by, cx, cy, "c" }
-end
-
-function mps.lineto(x,y)
- m_stack_path[#m_stack_path+1] = { x, y, "l" }
-end
-
-function mps.rlineto(x,y)
- local dx, dy = 0, 0
- local topofstack = #m_stack_path
- if topofstack > 0 then
- local msp = m_stack_path[topofstack]
- dx = msp[1]
- dy = msp[2]
- end
- m_stack_path[topofstack+1] = {dx,dy,"l"}
-end
-
-function mps.translate(tx,ty)
- mpscode(f_translate(tx,ty)
-end
-
-function mps.scale(sx,sy)
- m_stack_concat = {sx,0,0,sy,0,0}
-end
-
-function mps.concat(sx, rx, ry, sy, tx, ty)
- m_stack_concat = {sx,rx,ry,sy,tx,ty}
-end
-
-function mps.setlinejoin(d)
- mpscode(d .. " j")
-end
-
-function mps.setlinecap(d)
- mpscode(d .. " J")
-end
-
-function mps.setmiterlimit(d)
- mpscode(d .. " M")
-end
-
-function mps.gsave()
- mpscode("q")
-end
-
-function mps.grestore()
- mpscode("Q")
-end
-
-function mps.setdash(...) -- can be made faster, operate on t = { ... }
- local n = select("#",...)
- mpscode("[" .. concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d")
- -- mpscode("[" .. concat({select(1,n-1)}," ") .. "] " .. select(n,...) .. " d")
-end
-
-function mps.resetdash()
- mpscode("[ ] 0 d")
-end
-
-function mps.setlinewidth(d)
- mpscode(d .. " w")
-end
-
-function mps.closepath()
- m_stack_close = true
-end
-
-function mps.fill()
- flushpath('f')
-end
-
-function mps.stroke()
- flushpath('S')
-end
-
-function mps.both()
- flushpath('B')
-end
-
-function mps.clip()
- flushpath('W n')
-end
-
-function mps.textext(font, scale, str) -- old parser
- local dx, dy = 0, 0
- if #m_stack_path > 0 then
- dx, dy = m_stack_path[1][1], m_stack_path[1][2]
- end
- flushconcat()
- context.MPtextext(font,scale,str,dx,dy)
- resetpath()
-end
-
-local handlers = { }
-
-handlers[1] = function(s)
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfcmykcode(mps.colormodel,s[3],s[4],s[5],s[6]))
-end
-handlers[2] = function(s)
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfspotcode(mps.colormodel,s[3],s[4],s[5],s[6]))
-end
-handlers[3] = function(s)
- pdfcode(pdfrgbcode(mps.colormodel,s[4],s[5],s[6]))
- pdfcode(pdftransparencycode(s[2],s[3]))
-end
-handlers[4] = function(s)
- pdfcode(pdfcmykcode(mps.colormodel,s[4],s[5],s[6],s[7]))
- pdfcode(pdftransparencycode(s[2],s[3]))
-end
-handlers[5] = function(s)
- pdfcode(pdfspotcode(mps.colormodel,s[4],s[5],s[6],s[7]))
- pdfcode(pdftransparencycode(s[2],s[3]))
-end
-
--- todo: color conversion
-
-local nofshades, tn = 0, tonumber
-
-local function linearshade(colorspace,domain,ca,cb,coordinates)
- pdfcode(pdffinishtransparencycode())
- nofshades = nofshades + 1
- local name = formatters["MpsSh%s"](nofshades)
- lpdf.linearshade(name,domain,ca,cb,1,colorspace,coordinates)
- extra_path_code, ignore_path = formatters["/%s sh Q"](name), true
- pdfcode("q /Pattern cs")
-end
-
-local function circularshade(colorspace,domain,ca,cb,coordinates)
- pdfcode(pdffinishtransparencycode())
- nofshades = nofshades + 1
- local name = formatters["MpsSh%s"](nofshades)
- lpdf.circularshade(name,domain,ca,cb,1,colorspace,coordinates)
- extra_path_code, ignore_path = formatters["/%s sh Q"](name), true
- pdfcode("q /Pattern cs")
-end
-
-handlers[30] = function(s)
- linearshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
- { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[10]), tn(s[11]), tn(s[12]) },
- { tn(s[ 8]), tn(s[ 9]), tn(s[13]), tn(s[14]) } )
-end
-
-handlers[31] = function(s)
- circularshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
- { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[11]), tn(s[12]), tn(s[13]) },
- { tn(s[ 8]), tn(s[ 9]), tn(s[10]), tn(s[14]), tn(s[15]), tn(s[16]) } )
-end
-
-handlers[32] = function(s)
- linearshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
- { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[11]), tn(s[12]), tn(s[13]), tn(s[14]) },
- { tn(s[ 9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
-end
-
-handlers[33] = function(s)
- circularshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
- { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[12]), tn(s[13]), tn(s[14]), tn(s[15]) },
- { tn(s[ 9]), tn(s[10]), tn(s[11]), tn(s[16]), tn(s[17]), tn(s[18]) } )
-end
-
-handlers[34] = function(s) -- todo (after further cleanup)
- linearshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
-end
-
-handlers[35] = function(s) -- todo (after further cleanup)
- circularshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
-end
-
--- not supported in mkiv , use mplib instead
-
-handlers[10] = function() report_mptopdf("skipping special %s",10) end
-handlers[20] = function() report_mptopdf("skipping special %s",20) end
-handlers[50] = function() report_mptopdf("skipping special %s",50) end
-
---end of not supported
-
-function mps.setrgbcolor(r,g,b) -- extra check
- r, g = tonumber(r), tonumber(g) -- needed when we use lpeg
- if r == 0.0123 and g < 0.1 then
- g, b = round(g*10000), round(b*10000)
- local s = specials[b]
- local h = round(s[#s])
- local handler = handlers[h]
- if handler then
- handler(s)
- else
- report_mptopdf("unknown special handler %s (1)",h)
- end
- elseif r == 0.123 and g < 0.1 then
- g, b = round(g*1000), round(b*1000)
- local s = specials[b]
- local h = round(s[#s])
- local handler = handlers[h]
- if handler then
- handler(s)
- else
- report_mptopdf("unknown special handler %s (2)",h)
- end
- else
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfrgbcode(mps.colormodel,r,g,b))
- end
-end
-
-function mps.setcmykcolor(c,m,y,k)
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfcmykcode(mps.colormodel,c,m,y,k))
-end
-
-function mps.setgray(s)
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfgraycode(mps.colormodel,s))
-end
-
-function mps.specials(version,signal,factor) -- 2.0 123 1000
-end
-
-function mps.special(...) -- 7 1 0.5 1 0 0 1 3
- local t = { ... }
- local n = tonumber(t[#t-1])
- specials[n] = t
-end
-
-function mps.begindata()
-end
-
-function mps.enddata()
-end
-
-function mps.showpage()
-end
-
--- lpeg parser
-
--- The lpeg based parser is rather optimized for the kind of output
--- that MetaPost produces. It's my first real lpeg code, which may
--- show. Because the parser binds to functions, we define it last.
-
-local lpegP, lpegR, lpegS, lpegC, lpegCc, lpegCs = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs
-
-local digit = lpegR("09")
-local eol = lpegS('\r\n')^1
-local sp = lpegP(' ')^1
-local space = lpegS(' \r\n')^1
-local number = lpegS('0123456789.-+')^1
-local nonspace = lpegP(1-lpegS(' \r\n'))^1
-
-local spec = digit^2 * lpegP("::::") * digit^2
-local text = lpegCc("{") * (
- lpegP("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) +
- lpegP(" ") / function(n) return "\\c32" end + -- never in new mp
- lpegP(1) / function(n) return "\\c" .. byte(n) end
- ) * lpegCc("}")
-local package = lpegCs(spec + text^0)
-
-function mps.fshow(str,font,scale) -- lpeg parser
- mps.textext(font,scale,lpegmatch(package,str))
-end
-
-local cnumber = lpegC(number)
-local cstring = lpegC(nonspace)
-
-local specials = (lpegP("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials
-local special = (lpegP("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special
-local boundingbox = (lpegP("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
-local highresboundingbox = (lpegP("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
-
-local setup = lpegP("%%BeginSetup") * (1 - lpegP("%%EndSetup") )^1
-local prolog = lpegP("%%BeginProlog") * (1 - lpegP("%%EndProlog"))^1
-local comment = lpegP('%')^1 * (1 - eol)^1
-
-local curveto = ((cnumber * sp)^6 * lpegP("curveto") ) / mps.curveto
-local lineto = ((cnumber * sp)^2 * lpegP("lineto") ) / mps.lineto
-local rlineto = ((cnumber * sp)^2 * lpegP("rlineto") ) / mps.rlineto
-local moveto = ((cnumber * sp)^2 * lpegP("moveto") ) / mps.moveto
-local setrgbcolor = ((cnumber * sp)^3 * lpegP("setrgbcolor") ) / mps.setrgbcolor
-local setcmykcolor = ((cnumber * sp)^4 * lpegP("setcmykcolor") ) / mps.setcmykcolor
-local setgray = ((cnumber * sp)^1 * lpegP("setgray") ) / mps.setgray
-local newpath = ( lpegP("newpath") ) / mps.newpath
-local closepath = ( lpegP("closepath") ) / mps.closepath
-local fill = ( lpegP("fill") ) / mps.fill
-local stroke = ( lpegP("stroke") ) / mps.stroke
-local clip = ( lpegP("clip") ) / mps.clip
-local both = ( lpegP("gsave fill grestore")) / mps.both
-local showpage = ( lpegP("showpage") )
-local setlinejoin = ((cnumber * sp)^1 * lpegP("setlinejoin") ) / mps.setlinejoin
-local setlinecap = ((cnumber * sp)^1 * lpegP("setlinecap") ) / mps.setlinecap
-local setmiterlimit = ((cnumber * sp)^1 * lpegP("setmiterlimit") ) / mps.setmiterlimit
-local gsave = ( lpegP("gsave") ) / mps.gsave
-local grestore = ( lpegP("grestore") ) / mps.grestore
-
-local setdash = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("setdash")) / mps.setdash
-local concat = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("concat") ) / mps.concat
-local scale = ( (cnumber * sp^0)^6 * sp * lpegP("concat") ) / mps.concat
-
-local fshow = (lpegP("(") * lpegC((1-lpegP(")"))^1) * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
-local fshow = (lpegP("(") * lpegCs( ( lpegP("\\(")/"\\050" + lpegP("\\)")/"\\051" + (1-lpegP(")")) )^1 )
- * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
-
-local setlinewidth_x = (lpegP("0") * sp * cnumber * sp * lpegP("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth
-local setlinewidth_y = (cnumber * sp * lpegP("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth
-
-local c = ((cnumber * sp)^6 * lpegP("c") ) / mps.curveto -- ^6 very inefficient, ^1 ok too
-local l = ((cnumber * sp)^2 * lpegP("l") ) / mps.lineto
-local r = ((cnumber * sp)^2 * lpegP("r") ) / mps.rlineto
-local m = ((cnumber * sp)^2 * lpegP("m") ) / mps.moveto
-local vlw = ((cnumber * sp)^1 * lpegP("vlw")) / mps.setlinewidth
-local hlw = ((cnumber * sp)^1 * lpegP("hlw")) / mps.setlinewidth
-
-local R = ((cnumber * sp)^3 * lpegP("R") ) / mps.setrgbcolor
-local C = ((cnumber * sp)^4 * lpegP("C") ) / mps.setcmykcolor
-local G = ((cnumber * sp)^1 * lpegP("G") ) / mps.setgray
-
-local lj = ((cnumber * sp)^1 * lpegP("lj") ) / mps.setlinejoin
-local ml = ((cnumber * sp)^1 * lpegP("ml") ) / mps.setmiterlimit
-local lc = ((cnumber * sp)^1 * lpegP("lc") ) / mps.setlinecap
-
-local n = lpegP("n") / mps.newpath
-local p = lpegP("p") / mps.closepath
-local S = lpegP("S") / mps.stroke
-local F = lpegP("F") / mps.fill
-local B = lpegP("B") / mps.both
-local W = lpegP("W") / mps.clip
-local P = lpegP("P") / mps.showpage
-
-local q = lpegP("q") / mps.gsave
-local Q = lpegP("Q") / mps.grestore
-
-local sd = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("sd")) / mps.setdash
-local rd = ( lpegP("rd")) / mps.resetdash
-
-local s = ( (cnumber * sp^0)^2 * lpegP("s") ) / mps.scale
-local t = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("t") ) / mps.concat
-
--- experimental
-
-local preamble = (
- prolog + setup +
- boundingbox + highresboundingbox + specials + special +
- comment
-)
-
-local procset = (
- lj + ml + lc +
- c + l + m + n + p + r +
- R + C + G +
- S + F + B + W +
- vlw + hlw +
- Q + q +
- sd + rd +
- t + s +
- fshow +
- P
-)
-
-local verbose = (
- curveto + lineto + moveto + newpath + closepath + rlineto +
- setrgbcolor + setcmykcolor + setgray +
- setlinejoin + setmiterlimit + setlinecap +
- stroke + fill + clip + both +
- setlinewidth_x + setlinewidth_y +
- gsave + grestore +
- concat + scale +
- fshow +
- setdash + -- no resetdash
- showpage
-)
-
--- order matters in terms of speed / we could check for procset first
-
-local captures_old = ( space + verbose + preamble )^0
-local captures_new = ( space + verbose + procset + preamble )^0
-
-local function parse(m_data)
- if find(m_data,"%%%%BeginResource: procset mpost") then
- lpegmatch(captures_new,m_data)
- else
- lpegmatch(captures_old,m_data)
- end
-end
-
--- main converter
-
-local a_colorspace = attributes.private('colormodel')
-
-function mptopdf.convertmpstopdf(name)
- resetall()
- local ok, m_data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load !
- if ok then
- mps.colormodel = tex.attribute[a_colorspace]
- statistics.starttiming(mptopdf)
- mptopdf.nofconverted = mptopdf.nofconverted + 1
- pdfcode(formatters["\\letterpercent\\space mptopdf begin: n=%s, file=%s"](mptopdf.nofconverted,file.basename(name)))
- pdfcode("q 1 0 0 1 0 0 cm")
- parse(m_data)
- pdfcode(pdffinishtransparencycode())
- pdfcode("Q")
- pdfcode("\\letterpercent\\space mptopdf end")
- resetall()
- statistics.stoptiming(mptopdf)
- else
- report_mptopdf("file %a not found",name)
- end
-end
-
--- status info
-
-statistics.register("mps conversion time",function()
- local n = mptopdf.nofconverted
- if n > 0 then
- return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n)
- else
- return nil
- end
-end)
+if not modules then modules = { } end modules ['meta-pdf'] = {
+ version = 1.001,
+ comment = "companion to meta-pdf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Finally we used an optimized version. The test code can be found in
+-- meta-pdh.lua but since we no longer want to overload functione we use
+-- more locals now. This module keeps changing as it is also a testbed.
+--
+-- We can make it even more efficient if needed, but as we don't use this
+-- code often in \MKIV\ it makes no sense.
+
+local concat, unpack = table.concat, table.unpack
+local gsub, find, byte, gmatch, match = string.gsub, string.find, string.byte, string.gmatch, string.match
+local lpegmatch = lpeg.match
+local round = math.round
+local formatters, format = string.formatters, string.format
+
+local report_mptopdf = logs.reporter("graphics","mptopdf")
+
+local mplib, metapost, lpdf, context = mplib, metapost, lpdf, context
+
+local pdfrgbcode = lpdf.rgbcode
+local pdfcmykcode = lpdf.cmykcode
+local pdfgraycode = lpdf.graycode
+local pdfspotcode = lpdf.spotcode
+local pdftransparencycode = lpdf.transparencycode
+local pdffinishtransparencycode = lpdf.finishtransparencycode
+local pdfliteral = nodes.pool.pdfliteral
+
+metapost.mptopdf = metapost.mptopdf or { }
+local mptopdf = metapost.mptopdf
+
+mptopdf.nofconverted = 0
+
+local f_translate = formatters["1 0 0 0 1 %f %f cm"] -- no %s due to 1e-035 issues
+local f_concat = formatters["%f %f %f %f %f %f cm"] -- no %s due to 1e-035 issues
+
+local m_path, m_stack, m_texts, m_version, m_date, m_shortcuts = { }, { }, { }, 0, 0, false
+
+local m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
+local extra_path_code, ignore_path = nil, false
+local specials = { }
+
+local function resetpath()
+ m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
+end
+
+local function resetall()
+ m_path, m_stack, m_texts, m_version, m_shortcuts = { }, { }, { }, 0, false
+ extra_path_code, ignore_path = nil, false
+ specials = { }
+ resetpath()
+end
+
+resetall()
+
+-- -- this does not work as expected (displacement of text) beware, needs another
+-- -- comment hack
+--
+-- local function pdfcode(str)
+-- context(pdfliteral(str))
+-- end
+
+local pdfcode = context.pdfliteral
+
+local function mpscode(str)
+ if ignore_path then
+ pdfcode("h W n")
+ if extra_path_code then
+ pdfcode(extra_path_code)
+ extra_path_code = nil
+ end
+ ignore_path = false
+ else
+ pdfcode(str)
+ end
+end
+
+-- auxiliary functions
+
+local function flushconcat()
+ if m_stack_concat then
+ mpscode(f_concatm(unpack(m_stack_concat)))
+ m_stack_concat = nil
+ end
+end
+
+local function flushpath(cmd)
+ if #m_stack_path > 0 then
+ local path = { }
+ if m_stack_concat then
+ local sx, sy = m_stack_concat[1], m_stack_concat[4]
+ local rx, ry = m_stack_concat[2], m_stack_concat[3]
+ local tx, ty = m_stack_concat[5], m_stack_concat[6]
+ local d = (sx*sy) - (rx*ry)
+ for k=1,#m_stack_path do
+ local v = m_stack_path[k]
+ local px, py = v[1], v[2] ; v[1], v[2] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[1],v[2])
+ if #v == 7 then
+ local px, py = v[3], v[4] ; v[3], v[4] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[3],v[4])
+ local px, py = v[5], v[6] ; v[5], v[6] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[5],v[6])
+ end
+ path[k] = concat(v," ")
+ end
+ else
+ for k=1,#m_stack_path do
+ path[k] = concat(m_stack_path[k]," ")
+ end
+ end
+ flushconcat()
+ pdfcode(concat(path," "))
+ if m_stack_close then
+ mpscode("h " .. cmd)
+ else
+ mpscode(cmd)
+ end
+ end
+ resetpath()
+end
+
+-- mp interface
+
+local mps = { }
+
+function mps.creator(a, b, c)
+ m_version = tonumber(b)
+end
+
+function mps.creationdate(a)
+ m_date = a
+end
+
+function mps.newpath()
+ m_stack_path = { }
+end
+
+function mps.boundingbox(llx, lly, urx, ury)
+ context.setMPboundingbox(llx,lly,urx,ury)
+end
+
+function mps.moveto(x,y)
+ m_stack_path[#m_stack_path+1] = { x, y, "m" }
+end
+
+function mps.curveto(ax, ay, bx, by, cx, cy)
+ m_stack_path[#m_stack_path+1] = { ax, ay, bx, by, cx, cy, "c" }
+end
+
+function mps.lineto(x,y)
+ m_stack_path[#m_stack_path+1] = { x, y, "l" }
+end
+
+function mps.rlineto(x,y)
+ local dx, dy = 0, 0
+ local topofstack = #m_stack_path
+ if topofstack > 0 then
+ local msp = m_stack_path[topofstack]
+ dx = msp[1]
+ dy = msp[2]
+ end
+ m_stack_path[topofstack+1] = {dx,dy,"l"}
+end
+
+function mps.translate(tx,ty)
+ mpscode(f_translate(tx,ty)
+end
+
+function mps.scale(sx,sy)
+ m_stack_concat = {sx,0,0,sy,0,0}
+end
+
+function mps.concat(sx, rx, ry, sy, tx, ty)
+ m_stack_concat = {sx,rx,ry,sy,tx,ty}
+end
+
+function mps.setlinejoin(d)
+ mpscode(d .. " j")
+end
+
+function mps.setlinecap(d)
+ mpscode(d .. " J")
+end
+
+function mps.setmiterlimit(d)
+ mpscode(d .. " M")
+end
+
+function mps.gsave()
+ mpscode("q")
+end
+
+function mps.grestore()
+ mpscode("Q")
+end
+
+function mps.setdash(...) -- can be made faster, operate on t = { ... }
+ local n = select("#",...)
+ mpscode("[" .. concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d")
+ -- mpscode("[" .. concat({select(1,n-1)}," ") .. "] " .. select(n,...) .. " d")
+end
+
+function mps.resetdash()
+ mpscode("[ ] 0 d")
+end
+
+function mps.setlinewidth(d)
+ mpscode(d .. " w")
+end
+
+function mps.closepath()
+ m_stack_close = true
+end
+
+function mps.fill()
+ flushpath('f')
+end
+
+function mps.stroke()
+ flushpath('S')
+end
+
+function mps.both()
+ flushpath('B')
+end
+
+function mps.clip()
+ flushpath('W n')
+end
+
+function mps.textext(font, scale, str) -- old parser
+ local dx, dy = 0, 0
+ if #m_stack_path > 0 then
+ dx, dy = m_stack_path[1][1], m_stack_path[1][2]
+ end
+ flushconcat()
+ context.MPtextext(font,scale,str,dx,dy)
+ resetpath()
+end
+
+local handlers = { }
+
+handlers[1] = function(s)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfcmykcode(mps.colormodel,s[3],s[4],s[5],s[6]))
+end
+handlers[2] = function(s)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfspotcode(mps.colormodel,s[3],s[4],s[5],s[6]))
+end
+handlers[3] = function(s)
+ pdfcode(pdfrgbcode(mps.colormodel,s[4],s[5],s[6]))
+ pdfcode(pdftransparencycode(s[2],s[3]))
+end
+handlers[4] = function(s)
+ pdfcode(pdfcmykcode(mps.colormodel,s[4],s[5],s[6],s[7]))
+ pdfcode(pdftransparencycode(s[2],s[3]))
+end
+handlers[5] = function(s)
+ pdfcode(pdfspotcode(mps.colormodel,s[4],s[5],s[6],s[7]))
+ pdfcode(pdftransparencycode(s[2],s[3]))
+end
+
+-- todo: color conversion
+
+local nofshades, tn = 0, tonumber
+
+local function linearshade(colorspace,domain,ca,cb,coordinates)
+ pdfcode(pdffinishtransparencycode())
+ nofshades = nofshades + 1
+ local name = formatters["MpsSh%s"](nofshades)
+ lpdf.linearshade(name,domain,ca,cb,1,colorspace,coordinates)
+ extra_path_code, ignore_path = formatters["/%s sh Q"](name), true
+ pdfcode("q /Pattern cs")
+end
+
+local function circularshade(colorspace,domain,ca,cb,coordinates)
+ pdfcode(pdffinishtransparencycode())
+ nofshades = nofshades + 1
+ local name = formatters["MpsSh%s"](nofshades)
+ lpdf.circularshade(name,domain,ca,cb,1,colorspace,coordinates)
+ extra_path_code, ignore_path = formatters["/%s sh Q"](name), true
+ pdfcode("q /Pattern cs")
+end
+
+handlers[30] = function(s)
+ linearshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
+ { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[10]), tn(s[11]), tn(s[12]) },
+ { tn(s[ 8]), tn(s[ 9]), tn(s[13]), tn(s[14]) } )
+end
+
+handlers[31] = function(s)
+ circularshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
+ { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[11]), tn(s[12]), tn(s[13]) },
+ { tn(s[ 8]), tn(s[ 9]), tn(s[10]), tn(s[14]), tn(s[15]), tn(s[16]) } )
+end
+
+handlers[32] = function(s)
+ linearshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
+ { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[11]), tn(s[12]), tn(s[13]), tn(s[14]) },
+ { tn(s[ 9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
+end
+
+handlers[33] = function(s)
+ circularshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
+ { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[12]), tn(s[13]), tn(s[14]), tn(s[15]) },
+ { tn(s[ 9]), tn(s[10]), tn(s[11]), tn(s[16]), tn(s[17]), tn(s[18]) } )
+end
+
+handlers[34] = function(s) -- todo (after further cleanup)
+ linearshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
+end
+
+handlers[35] = function(s) -- todo (after further cleanup)
+ circularshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
+end
+
+-- not supported in mkiv , use mplib instead
+
+handlers[10] = function() report_mptopdf("skipping special %s",10) end
+handlers[20] = function() report_mptopdf("skipping special %s",20) end
+handlers[50] = function() report_mptopdf("skipping special %s",50) end
+
+--end of not supported
+
+function mps.setrgbcolor(r,g,b) -- extra check
+ r, g = tonumber(r), tonumber(g) -- needed when we use lpeg
+ if r == 0.0123 and g < 0.1 then
+ g, b = round(g*10000), round(b*10000)
+ local s = specials[b]
+ local h = round(s[#s])
+ local handler = handlers[h]
+ if handler then
+ handler(s)
+ else
+ report_mptopdf("unknown special handler %s (1)",h)
+ end
+ elseif r == 0.123 and g < 0.1 then
+ g, b = round(g*1000), round(b*1000)
+ local s = specials[b]
+ local h = round(s[#s])
+ local handler = handlers[h]
+ if handler then
+ handler(s)
+ else
+ report_mptopdf("unknown special handler %s (2)",h)
+ end
+ else
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfrgbcode(mps.colormodel,r,g,b))
+ end
+end
+
+function mps.setcmykcolor(c,m,y,k)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfcmykcode(mps.colormodel,c,m,y,k))
+end
+
+function mps.setgray(s)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfgraycode(mps.colormodel,s))
+end
+
+function mps.specials(version,signal,factor) -- 2.0 123 1000
+end
+
+function mps.special(...) -- 7 1 0.5 1 0 0 1 3
+ local t = { ... }
+ local n = tonumber(t[#t-1])
+ specials[n] = t
+end
+
+function mps.begindata()
+end
+
+function mps.enddata()
+end
+
+function mps.showpage()
+end
+
+-- lpeg parser
+
+-- The lpeg based parser is rather optimized for the kind of output
+-- that MetaPost produces. It's my first real lpeg code, which may
+-- show. Because the parser binds to functions, we define it last.
+
+local lpegP, lpegR, lpegS, lpegC, lpegCc, lpegCs = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs
+
+local digit = lpegR("09")
+local eol = lpegS('\r\n')^1
+local sp = lpegP(' ')^1
+local space = lpegS(' \r\n')^1
+local number = lpegS('0123456789.-+')^1
+local nonspace = lpegP(1-lpegS(' \r\n'))^1
+
+local spec = digit^2 * lpegP("::::") * digit^2
+local text = lpegCc("{") * (
+ lpegP("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) +
+ lpegP(" ") / function(n) return "\\c32" end + -- never in new mp
+ lpegP(1) / function(n) return "\\c" .. byte(n) end
+ ) * lpegCc("}")
+local package = lpegCs(spec + text^0)
+
+function mps.fshow(str,font,scale) -- lpeg parser
+ mps.textext(font,scale,lpegmatch(package,str))
+end
+
+local cnumber = lpegC(number)
+local cstring = lpegC(nonspace)
+
+local specials = (lpegP("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials
+local special = (lpegP("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special
+local boundingbox = (lpegP("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
+local highresboundingbox = (lpegP("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
+
+local setup = lpegP("%%BeginSetup") * (1 - lpegP("%%EndSetup") )^1
+local prolog = lpegP("%%BeginProlog") * (1 - lpegP("%%EndProlog"))^1
+local comment = lpegP('%')^1 * (1 - eol)^1
+
+local curveto = ((cnumber * sp)^6 * lpegP("curveto") ) / mps.curveto
+local lineto = ((cnumber * sp)^2 * lpegP("lineto") ) / mps.lineto
+local rlineto = ((cnumber * sp)^2 * lpegP("rlineto") ) / mps.rlineto
+local moveto = ((cnumber * sp)^2 * lpegP("moveto") ) / mps.moveto
+local setrgbcolor = ((cnumber * sp)^3 * lpegP("setrgbcolor") ) / mps.setrgbcolor
+local setcmykcolor = ((cnumber * sp)^4 * lpegP("setcmykcolor") ) / mps.setcmykcolor
+local setgray = ((cnumber * sp)^1 * lpegP("setgray") ) / mps.setgray
+local newpath = ( lpegP("newpath") ) / mps.newpath
+local closepath = ( lpegP("closepath") ) / mps.closepath
+local fill = ( lpegP("fill") ) / mps.fill
+local stroke = ( lpegP("stroke") ) / mps.stroke
+local clip = ( lpegP("clip") ) / mps.clip
+local both = ( lpegP("gsave fill grestore")) / mps.both
+local showpage = ( lpegP("showpage") )
+local setlinejoin = ((cnumber * sp)^1 * lpegP("setlinejoin") ) / mps.setlinejoin
+local setlinecap = ((cnumber * sp)^1 * lpegP("setlinecap") ) / mps.setlinecap
+local setmiterlimit = ((cnumber * sp)^1 * lpegP("setmiterlimit") ) / mps.setmiterlimit
+local gsave = ( lpegP("gsave") ) / mps.gsave
+local grestore = ( lpegP("grestore") ) / mps.grestore
+
+local setdash = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("setdash")) / mps.setdash
+local concat = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("concat") ) / mps.concat
+local scale = ( (cnumber * sp^0)^6 * sp * lpegP("concat") ) / mps.concat
+
+local fshow = (lpegP("(") * lpegC((1-lpegP(")"))^1) * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
+local fshow = (lpegP("(") * lpegCs( ( lpegP("\\(")/"\\050" + lpegP("\\)")/"\\051" + (1-lpegP(")")) )^1 )
+ * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
+
+local setlinewidth_x = (lpegP("0") * sp * cnumber * sp * lpegP("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth
+local setlinewidth_y = (cnumber * sp * lpegP("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth
+
+local c = ((cnumber * sp)^6 * lpegP("c") ) / mps.curveto -- ^6 very inefficient, ^1 ok too
+local l = ((cnumber * sp)^2 * lpegP("l") ) / mps.lineto
+local r = ((cnumber * sp)^2 * lpegP("r") ) / mps.rlineto
+local m = ((cnumber * sp)^2 * lpegP("m") ) / mps.moveto
+local vlw = ((cnumber * sp)^1 * lpegP("vlw")) / mps.setlinewidth
+local hlw = ((cnumber * sp)^1 * lpegP("hlw")) / mps.setlinewidth
+
+local R = ((cnumber * sp)^3 * lpegP("R") ) / mps.setrgbcolor
+local C = ((cnumber * sp)^4 * lpegP("C") ) / mps.setcmykcolor
+local G = ((cnumber * sp)^1 * lpegP("G") ) / mps.setgray
+
+local lj = ((cnumber * sp)^1 * lpegP("lj") ) / mps.setlinejoin
+local ml = ((cnumber * sp)^1 * lpegP("ml") ) / mps.setmiterlimit
+local lc = ((cnumber * sp)^1 * lpegP("lc") ) / mps.setlinecap
+
+local n = lpegP("n") / mps.newpath
+local p = lpegP("p") / mps.closepath
+local S = lpegP("S") / mps.stroke
+local F = lpegP("F") / mps.fill
+local B = lpegP("B") / mps.both
+local W = lpegP("W") / mps.clip
+local P = lpegP("P") / mps.showpage
+
+local q = lpegP("q") / mps.gsave
+local Q = lpegP("Q") / mps.grestore
+
+local sd = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("sd")) / mps.setdash
+local rd = ( lpegP("rd")) / mps.resetdash
+
+local s = ( (cnumber * sp^0)^2 * lpegP("s") ) / mps.scale
+local t = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("t") ) / mps.concat
+
+-- experimental
+
+local preamble = (
+ prolog + setup +
+ boundingbox + highresboundingbox + specials + special +
+ comment
+)
+
+local procset = (
+ lj + ml + lc +
+ c + l + m + n + p + r +
+ R + C + G +
+ S + F + B + W +
+ vlw + hlw +
+ Q + q +
+ sd + rd +
+ t + s +
+ fshow +
+ P
+)
+
+local verbose = (
+ curveto + lineto + moveto + newpath + closepath + rlineto +
+ setrgbcolor + setcmykcolor + setgray +
+ setlinejoin + setmiterlimit + setlinecap +
+ stroke + fill + clip + both +
+ setlinewidth_x + setlinewidth_y +
+ gsave + grestore +
+ concat + scale +
+ fshow +
+ setdash + -- no resetdash
+ showpage
+)
+
+-- order matters in terms of speed / we could check for procset first
+
+local captures_old = ( space + verbose + preamble )^0
+local captures_new = ( space + verbose + procset + preamble )^0
+
+local function parse(m_data)
+ if find(m_data,"%%%%BeginResource: procset mpost") then
+ lpegmatch(captures_new,m_data)
+ else
+ lpegmatch(captures_old,m_data)
+ end
+end
+
+-- main converter
+
+local a_colorspace = attributes.private('colormodel')
+
+function mptopdf.convertmpstopdf(name)
+ resetall()
+ local ok, m_data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load !
+ if ok then
+ mps.colormodel = tex.attribute[a_colorspace]
+ statistics.starttiming(mptopdf)
+ mptopdf.nofconverted = mptopdf.nofconverted + 1
+ pdfcode(formatters["\\letterpercent\\space mptopdf begin: n=%s, file=%s"](mptopdf.nofconverted,file.basename(name)))
+ pdfcode("q 1 0 0 1 0 0 cm")
+ parse(m_data)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode("Q")
+ pdfcode("\\letterpercent\\space mptopdf end")
+ resetall()
+ statistics.stoptiming(mptopdf)
+ else
+ report_mptopdf("file %a not found",name)
+ end
+end
+
+-- status info
+
+statistics.register("mps conversion time",function()
+ local n = mptopdf.nofconverted
+ if n > 0 then
+ return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n)
+ else
+ return nil
+ end
+end)