From fc4466b32ed330a956ac603b00fd145524cff49a Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 22 Apr 2016 22:14:39 +0000 Subject: context (22apr16) git-svn-id: svn://tug.org/texlive/trunk@40691 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/tex/context/base/mkiv/meta-pdf.lua | 583 +++++++++++++++++++++ 1 file changed, 583 insertions(+) create mode 100644 Master/texmf-dist/tex/context/base/mkiv/meta-pdf.lua (limited to 'Master/texmf-dist/tex/context/base/mkiv/meta-pdf.lua') diff --git a/Master/texmf-dist/tex/context/base/mkiv/meta-pdf.lua b/Master/texmf-dist/tex/context/base/mkiv/meta-pdf.lua new file mode 100644 index 00000000000..3cbff63b168 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkiv/meta-pdf.lua @@ -0,0 +1,583 @@ +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 texgetattribute = tex.getattribute + +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_concat(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, b = tonumber(r), tonumber(g), tonumber(b) -- 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 + +----- cnumber = lpegC(number) +local cnumber = number/tonumber -- we now expect numbers (feeds into %F) +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 + -- report_mptopdf("using sparse scanner, case 1") + lpegmatch(captures_new,m_data) + elseif find(m_data,"%%%%BeginProlog%s*%S+(.-)%%%%EndProlog") then + -- report_mptopdf("using sparse scanner, case 2") + lpegmatch(captures_new,m_data) + else + -- report_mptopdf("using verbose ps scanner") + 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 = texgetattribute(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) + +-- interface + +interfaces.implement { + name = "convertmpstopdf", + arguments = "string", + actions = mptopdf.convertmpstopdf +} -- cgit v1.2.3