meta-pdh.lua /size: 20 Kb    last modification: 2020-07-01 14:35
1if not modules then modules = { } end modules ['meta-pdf'] = {
2    version   = 1.001,
3    comment   = "companion to meta-pdf.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9if true then
10    return -- or os.exit()
11end
12
13-- This file contains the history of the converter. We keep it around as it
14-- relates to the development of luatex.
15
16-- This is the third version. Version 1 converted to Lua code,
17-- version 2 gsubbed the file into TeX code, and version 3 uses
18-- the new lpeg functionality and streams the result into TeX.
19
20-- We will move old stuff to edu.
21
22--~                         old           lpeg 0.4       lpeg 0.5
23--~ 100 times test graphic  2.45 (T:1.07) 0.72 (T:0.24)  0.580  (0.560  no table) -- 0.54 optimized for one space (T:0.19)
24--~ 100 times big  graphic 10.44          4.30/3.35 nogb 2.914  (2.050  no table) -- 1.99 optimized for one space (T:0.85)
25--~ 500 times test graphic                T:1.29         T:1.16 (T:1.10 no table) -- T:1.10
26
27-- only needed for mp output on disk
28
29local concat, format, find, gsub, gmatch = table.concat, string.format, string.find, string.gsub, string.gmatch
30local tostring, tonumber, select = tostring, tonumber, select
31local lpegmatch = lpeg.match
32
33metapost         = metapost or { }
34local metapost   = metapost
35local context    = context
36
37metapost.mptopdf = metapost.mptopdf or { }
38local mptopdf    = metapost.mptopdf
39
40mptopdf.parsers      = { }
41mptopdf.parser       = 'none'
42mptopdf.nofconverted = 0
43
44function mptopdf.reset()
45    mptopdf.data      = ""
46    mptopdf.path      = { }
47    mptopdf.stack     = { }
48    mptopdf.texts     = { }
49    mptopdf.version   = 0
50    mptopdf.shortcuts = false
51    mptopdf.resetpath()
52end
53
54function mptopdf.resetpath()
55    mptopdf.stack.close   = false
56    mptopdf.stack.path    = { }
57    mptopdf.stack.concat  = nil
58    mptopdf.stack.special = false
59end
60
61mptopdf.reset()
62
63function mptopdf.parsers.none()
64    -- no parser set
65end
66
67function mptopdf.parse()
68    mptopdf.parsers[mptopdf.parser]()
69end
70
71-- old code
72
73mptopdf.steps = { }
74
75mptopdf.descapes = {
76    ['('] = "\\\\char40 ",
77    [')'] = "\\\\char41 ",
78    ['"'] = "\\\\char92 "
79}
80
81function mptopdf.descape(str)
82    str = gsub(str,"\\(%d%d%d)",function(n)
83        return "\\char" .. tonumber(n,8) .. " "
84    end)
85    return gsub(str,"\\([%(%)\\])",mptopdf.descapes)
86end
87
88function mptopdf.steps.descape(str)
89    str = gsub(str,"\\(%d%d%d)",function(n)
90        return "\\\\char" .. tonumber(n,8) .. " "
91    end)
92    return gsub(str,"\\([%(%)\\])",mptopdf.descapes)
93end
94
95function mptopdf.steps.strip() -- .3 per expr
96    mptopdf.data = gsub(mptopdf.data,"^(.-)%%+Page:.-%c+(.*)%s+%a+%s+%%+EOF.*$", function(preamble, graphic)
97        local bbox = "0 0 0 0"
98        for b in gmatch(preamble,"%%%%%a+oundingBox: +(.-)%c+") do
99            bbox = b
100        end
101        local name, version = gmatch(preamble,"%%%%Creator: +(.-) +(.-) ")
102        mptopdf.version = tostring(version or "0")
103        if find(preamble,"/hlw{0 dtransform",1,true) then
104            mptopdf.shortcuts = true
105        end
106        -- the boundingbox specification needs to come before data, well, not really
107        return bbox .. " boundingbox\n" .. "\nbegindata\n" .. graphic .. "\nenddata\n"
108    end, 1)
109    mptopdf.data = gsub(mptopdf.data,"%%%%MetaPostSpecials: +(.-)%c+", "%1 specials\n", 1)
110    mptopdf.data = gsub(mptopdf.data,"%%%%MetaPostSpecial: +(.-)%c+", "%1 special\n")
111    mptopdf.data = gsub(mptopdf.data,"%%.-%c+", "")
112end
113
114function mptopdf.steps.cleanup()
115    if not mptopdf.shortcuts then
116        mptopdf.data = gsub(mptopdf.data,"gsave%s+fill%s+grestore%s+stroke", "both")
117        mptopdf.data = gsub(mptopdf.data,"([%d%.]+)%s+([%d%.]+)%s+dtransform%s+exch%s+truncate%s+exch%s+idtransform%s+pop%s+setlinewidth", function(wx,wy)
118            if tonumber(wx) > 0 then return wx .. " setlinewidth" else return wy .. " setlinewidth"  end
119        end)
120        mptopdf.data = gsub(mptopdf.data,"([%d%.]+)%s+([%d%.]+)%s+dtransform%s+truncate%s+idtransform%s+setlinewidth%s+pop", function(wx,wy)
121            if tonumber(wx) > 0 then return wx .. " setlinewidth" else return wy .. " setlinewidth"  end
122        end)
123    end
124end
125
126function mptopdf.steps.convert()
127    mptopdf.data = gsub(mptopdf.data,"%c%((.-)%) (.-) (.-) fshow", function(str,font,scale)
128        mptopdf.texts[mptopdf.texts+1] = {mptopdf.steps.descape(str), font, scale}
129        return "\n" .. #mptopdf.texts .. " textext"
130    end)
131    mptopdf.data = gsub(mptopdf.data,"%[%s*(.-)%s*%]", function(str)
132        return gsub(str,"%s+"," ")
133    end)
134    local t
135    mptopdf.data = gsub(mptopdf.data,"%s*([^%a]-)%s*(%a+)", function(args,cmd)
136        if cmd == "textext" then
137            t = mptopdf.texts[tonumber(args)]
138            return "metapost.mps.textext(" ..  "\"" .. t[2] .. "\"," .. t[3] .. ",\"" .. t[1] .. "\")\n"
139        else
140            return "metapost.mps." .. cmd .. "(" .. gsub(args," +",",") .. ")\n"
141        end
142    end)
143end
144
145function mptopdf.steps.process()
146    assert(loadstring(mptopdf.data))() -- () runs the loaded chunk
147end
148
149function mptopdf.parsers.gsub()
150    mptopdf.steps.strip()
151    mptopdf.steps.cleanup()
152    mptopdf.steps.convert()
153    mptopdf.steps.process()
154end
155
156-- end of old code
157
158-- from lua to tex
159
160function mptopdf.pdfcode(str)
161    context.pdfliteral(str) -- \\MPScode
162end
163
164function mptopdf.texcode(str)
165    context(str)
166end
167
168-- auxiliary functions
169
170function mptopdf.flushconcat()
171    if mptopdf.stack.concat then
172        mptopdf.pdfcode(concat(mptopdf.stack.concat," ") .. " cm")
173        mptopdf.stack.concat = nil
174    end
175end
176
177function mptopdf.flushpath(cmd)
178    -- faster: no local function and loop
179    if #mptopdf.stack.path > 0 then
180        local path = { }
181        if mptopdf.stack.concat then
182            local sx, sy = mptopdf.stack.concat[1], mptopdf.stack.concat[4]
183            local rx, ry = mptopdf.stack.concat[2], mptopdf.stack.concat[3]
184            local tx, ty = mptopdf.stack.concat[5], mptopdf.stack.concat[6]
185            local d = (sx*sy) - (rx*ry)
186            local function mpconcat(px, py)
187                return (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d
188            end
189            local stackpath = mptopdf.stack.path
190            for k=1,#stackpath do
191                local v = stackpath[k]
192                v[1],v[2] = mpconcat(v[1],v[2])
193                if #v == 7 then
194                    v[3],v[4] = mpconcat(v[3],v[4])
195                    v[5],v[6] = mpconcat(v[5],v[6])
196                end
197                path[#path+1] = concat(v," ")
198            end
199        else
200            local stackpath = mptopdf.stack.path
201            for k=1,#stackpath do
202                path[#path+1] = concat(stackpath[k]," ")
203            end
204        end
205        mptopdf.flushconcat()
206        mptopdf.texcode("\\MPSpath{" .. concat(path," ") .. "}")
207        if mptopdf.stack.close then
208            mptopdf.texcode("\\MPScode{h " .. cmd .. "}")
209        else
210            mptopdf.texcode("\\MPScode{" .. cmd .."}")
211        end
212    end
213    mptopdf.resetpath()
214end
215
216function mptopdf.loaded(name)
217    local ok, n
218    mptopdf.reset()
219    ok, mptopdf.data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load !
220    return ok
221end
222
223if not mptopdf.parse then
224    function mptopdf.parse() end -- forward declaration
225end
226
227function mptopdf.convertmpstopdf(name)
228    if mptopdf.loaded(name) then
229        mptopdf.nofconverted = mptopdf.nofconverted + 1
230        statistics.starttiming(mptopdf)
231        mptopdf.parse()
232        mptopdf.reset()
233        statistics.stoptiming(mptopdf)
234    else
235        context("file " .. name .. " not found")
236    end
237end
238
239-- mp interface
240
241metapost.mps = metapost.mps or { }
242local mps    = metapost.mps or { }
243
244function mps.creator(a, b, c)
245    mptopdf.version = tonumber(b)
246end
247
248function mps.creationdate(a)
249    mptopdf.date= a
250end
251
252function mps.newpath()
253    mptopdf.stack.path = { }
254end
255
256function mps.boundingbox(llx, lly, urx, ury)
257    mptopdf.texcode("\\MPSboundingbox{" .. llx .. "}{" .. lly .. "}{" .. urx .. "}{" .. ury .. "}")
258end
259
260function mps.moveto(x,y)
261    mptopdf.stack.path[#mptopdf.stack.path+1] = {x,y,"m"}
262end
263
264function mps.curveto(ax, ay, bx, by, cx, cy)
265    mptopdf.stack.path[#mptopdf.stack.path+1] = {ax,ay,bx,by,cx,cy,"c"}
266end
267
268function mps.lineto(x,y)
269    mptopdf.stack.path[#mptopdf.stack.path+1] = {x,y,"l"}
270end
271
272function mps.rlineto(x,y)
273    local dx, dy = 0, 0
274    if #mptopdf.stack.path > 0 then
275        dx, dy = mptopdf.stack.path[#mptopdf.stack.path][1], mptopdf.stack.path[#mptopdf.stack.path][2]
276    end
277    mptopdf.stack.path[#mptopdf.stack.path+1] = {dx,dy,"l"}
278end
279
280function mps.translate(tx,ty)
281    mptopdf.pdfcode("1 0 0 0 1 " .. tx .. " " .. ty .. " cm")
282end
283
284function mps.scale(sx,sy)
285    mptopdf.stack.concat = {sx,0,0,sy,0,0}
286end
287
288function mps.concat(sx, rx, ry, sy, tx, ty)
289    mptopdf.stack.concat = {sx,rx,ry,sy,tx,ty}
290end
291
292function mps.setlinejoin(d)
293    mptopdf.pdfcode(d .. " j")
294end
295
296function mps.setlinecap(d)
297    mptopdf.pdfcode(d .. " J")
298end
299
300function mps.setmiterlimit(d)
301    mptopdf.pdfcode(d .. " M")
302end
303
304function mps.gsave()
305    mptopdf.pdfcode("q")
306end
307
308function mps.grestore()
309    mptopdf.pdfcode("Q")
310end
311
312function mps.setdash(...)
313    local n = select("#",...)
314    mptopdf.pdfcode("[" .. concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d")
315end
316
317function mps.resetdash()
318    mptopdf.pdfcode("[ ] 0 d")
319end
320
321function mps.setlinewidth(d)
322    mptopdf.pdfcode(d .. " w")
323end
324
325function mps.closepath()
326    mptopdf.stack.close = true
327end
328
329function mps.fill()
330    mptopdf.flushpath('f')
331end
332
333function mps.stroke()
334    mptopdf.flushpath('S')
335end
336
337function mps.both()
338    mptopdf.flushpath('B')
339end
340
341function mps.clip()
342    mptopdf.flushpath('W n')
343end
344
345function mps.textext(font, scale, str) -- old parser
346    local dx, dy = 0, 0
347    if #mptopdf.stack.path > 0 then
348        dx, dy = mptopdf.stack.path[1][1], mptopdf.stack.path[1][2]
349    end
350    mptopdf.flushconcat()
351    mptopdf.texcode("\\MPStextext{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}")
352    mptopdf.resetpath()
353end
354
355--~ function mps.handletext(font,scale.str,dx,dy)
356--~     local one, two = string.match(str, "^(%d+)::::(%d+)")
357--~     if one and two then
358--~         mptopdf.texcode("\\MPTOPDFtextext{"..font.."}{"..scale.."}{"..one.."}{"..two.."}{"..dx.."}{"..dy.."}")
359--~     else
360--~         mptopdf.texcode("\\MPTOPDFtexcode{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}")
361--~     end
362--~ end
363
364function mps.setrgbcolor(r,g,b) -- extra check
365    r, g = tonumber(r), tonumber(g) -- needed when we use lpeg
366    if r == 0.0123 and g < 0.1 then
367        mptopdf.texcode("\\MPSspecial{" .. g*10000 .. "}{" .. b*10000 .. "}")
368    elseif r == 0.123 and g < 0.1 then
369        mptopdf.texcode("\\MPSspecial{" .. g* 1000 .. "}{" .. b* 1000 .. "}")
370    else
371        mptopdf.texcode("\\MPSrgb{" .. r .. "}{" .. g .. "}{" .. b .. "}")
372    end
373end
374
375function mps.setcmykcolor(c,m,y,k)
376    mptopdf.texcode("\\MPScmyk{" .. c .. "}{" .. m .. "}{" .. y .. "}{" .. k .. "}")
377end
378
379function mps.setgray(s)
380    mptopdf.texcode("\\MPSgray{" .. s .. "}")
381end
382
383function mps.specials(version,signal,factor) -- 2.0 123 1000
384end
385
386function mps.special(...) -- 7 1 0.5 1 0 0 1 3
387    local n = select("#",...)
388    mptopdf.texcode("\\MPSbegin\\MPSset{" .. concat({...},"}\\MPSset{",2,n) .. "}\\MPSend")
389end
390
391function mps.begindata()
392end
393
394function mps.enddata()
395end
396
397function mps.showpage()
398end
399
400mps.n   = mps.newpath       -- n
401mps.p   = mps.closepath     -- h
402mps.l   = mps.lineto        -- l
403mps.r   = mps.rlineto       -- r
404mps.m   = mps.moveto        -- m
405mps.c   = mps.curveto       -- c
406mps.hlw = mps.setlinewidth
407mps.vlw = mps.setlinewidth
408
409mps.C   = mps.setcmykcolor  -- k
410mps.G   = mps.setgray       -- g
411mps.R   = mps.setrgbcolor   -- rg
412
413mps.lj  = mps.setlinejoin   -- j
414mps.ml  = mps.setmiterlimit -- M
415mps.lc  = mps.setlinecap    -- J
416mps.sd  = mps.setdash       -- d
417mps.rd  = mps.resetdash
418
419mps.S   = mps.stroke        -- S
420mps.F   = mps.fill          -- f
421mps.B   = mps.both          -- B
422mps.W   = mps.clip          -- W
423
424mps.q   = mps.gsave         -- q
425mps.Q   = mps.grestore      -- Q
426
427mps.s   = mps.scale         -- (not in pdf)
428mps.t   = mps.concat        -- (not the same as pdf anyway)
429
430mps.P   = mps.showpage
431
432-- experimental
433
434function mps.attribute(id,value)
435    mptopdf.texcode("\\attribute " .. id .. "=" .. value .. " ")
436--  mptopdf.texcode("\\dompattribute{" .. id .. "}{" .. value .. "}")
437end
438
439-- lpeg parser
440
441-- The lpeg based parser is rather optimized for the kind of output
442-- that MetaPost produces. It's my first real lpeg code, which may
443-- show. Because the parser binds to functions, we define it last.
444
445do -- assumes \let\c\char
446
447    local byte = string.byte
448    local digit = lpeg.R("09")
449    local spec = digit^2 * lpeg.P("::::") * digit^2
450    local text = lpeg.Cc("{") * (
451        lpeg.P("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) +
452                          lpeg.P(" ")            / function(n) return "\\c32" end + -- never in new mp
453                          lpeg.P(1)              / function(n) return "\\c" .. byte(n) end
454    ) * lpeg.Cc("}")
455    local package = lpeg.Cs(spec + text^0)
456
457    function mps.fshow(str,font,scale) -- lpeg parser
458        mps.textext(font,scale,lpegmatch(package,str))
459    end
460
461end
462
463do
464
465    local eol      = lpeg.S('\r\n')^1
466    local sp       = lpeg.P(' ')^1
467    local space    = lpeg.S(' \r\n')^1
468    local number   = lpeg.S('0123456789.-+')^1
469    local nonspace = lpeg.P(1-lpeg.S(' \r\n'))^1
470
471    local cnumber = lpeg.C(number)
472    local cstring = lpeg.C(nonspace)
473
474    local specials           = (lpeg.P("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials
475    local special            = (lpeg.P("%%MetaPostSpecial:")  * sp * (cstring * sp^0)^0 * eol) / mps.special
476    local boundingbox        = (lpeg.P("%%BoundingBox:")      * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
477    local highresboundingbox = (lpeg.P("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
478
479    local setup              = lpeg.P("%%BeginSetup")  * (1 - lpeg.P("%%EndSetup") )^1
480    local prolog             = lpeg.P("%%BeginProlog") * (1 - lpeg.P("%%EndProlog"))^1
481    local comment            = lpeg.P('%')^1 * (1 - eol)^1
482
483    local curveto            = ((cnumber * sp)^6 * lpeg.P("curveto")            ) / mps.curveto
484    local lineto             = ((cnumber * sp)^2 * lpeg.P("lineto")             ) / mps.lineto
485    local rlineto            = ((cnumber * sp)^2 * lpeg.P("rlineto")            ) / mps.rlineto
486    local moveto             = ((cnumber * sp)^2 * lpeg.P("moveto")             ) / mps.moveto
487    local setrgbcolor        = ((cnumber * sp)^3 * lpeg.P("setrgbcolor")        ) / mps.setrgbcolor
488    local setcmykcolor       = ((cnumber * sp)^4 * lpeg.P("setcmykcolor")       ) / mps.setcmykcolor
489    local setgray            = ((cnumber * sp)^1 * lpeg.P("setgray")            ) / mps.setgray
490    local newpath            = (                   lpeg.P("newpath")            ) / mps.newpath
491    local closepath          = (                   lpeg.P("closepath")          ) / mps.closepath
492    local fill               = (                   lpeg.P("fill")               ) / mps.fill
493    local stroke             = (                   lpeg.P("stroke")             ) / mps.stroke
494    local clip               = (                   lpeg.P("clip")               ) / mps.clip
495    local both               = (                   lpeg.P("gsave fill grestore")) / mps.both
496    local showpage           = (                   lpeg.P("showpage")           )
497    local setlinejoin        = ((cnumber * sp)^1 * lpeg.P("setlinejoin")        ) / mps.setlinejoin
498    local setlinecap         = ((cnumber * sp)^1 * lpeg.P("setlinecap")         ) / mps.setlinecap
499    local setmiterlimit      = ((cnumber * sp)^1 * lpeg.P("setmiterlimit")      ) / mps.setmiterlimit
500    local gsave              = (                   lpeg.P("gsave")              ) / mps.gsave
501    local grestore           = (                   lpeg.P("grestore")           ) / mps.grestore
502
503    local setdash            = (lpeg.P("[") * (cnumber * sp^0)^0 * lpeg.P("]") * sp * cnumber * sp * lpeg.P("setdash")) / mps.setdash
504    local concat             = (lpeg.P("[") * (cnumber * sp^0)^6 * lpeg.P("]")                * sp * lpeg.P("concat") ) / mps.concat
505    local scale              = (              (cnumber * sp^0)^6                              * sp * lpeg.P("concat") ) / mps.concat
506
507    local fshow              = (lpeg.P("(") * lpeg.C((1-lpeg.P(")"))^1) * lpeg.P(")") * space * cstring * space * cnumber * space * lpeg.P("fshow")) / mps.fshow
508    local fshow              = (lpeg.P("(") *
509                                    lpeg.Cs( ( lpeg.P("\\(")/"\\050" + lpeg.P("\\)")/"\\051" + (1-lpeg.P(")")) )^1 )
510                                * lpeg.P(")") * space * cstring * space * cnumber * space * lpeg.P("fshow")) / mps.fshow
511
512    local setlinewidth_x     = (lpeg.P("0") * sp * cnumber * sp * lpeg.P("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth
513    local setlinewidth_y     = (cnumber * sp * lpeg.P("0 dtransform exch truncate exch idtransform pop setlinewidth")  ) / mps.setlinewidth
514
515    local c   = ((cnumber * sp)^6 * lpeg.P("c")  ) / mps.curveto -- ^6 very inefficient, ^1 ok too
516    local l   = ((cnumber * sp)^2 * lpeg.P("l")  ) / mps.lineto
517    local r   = ((cnumber * sp)^2 * lpeg.P("r")  ) / mps.rlineto
518    local m   = ((cnumber * sp)^2 * lpeg.P("m")  ) / mps.moveto
519    local vlw = ((cnumber * sp)^1 * lpeg.P("vlw")) / mps.setlinewidth
520    local hlw = ((cnumber * sp)^1 * lpeg.P("hlw")) / mps.setlinewidth
521
522    local R   = ((cnumber * sp)^3 * lpeg.P("R")  ) / mps.setrgbcolor
523    local C   = ((cnumber * sp)^4 * lpeg.P("C")  ) / mps.setcmykcolor
524    local G   = ((cnumber * sp)^1 * lpeg.P("G")  ) / mps.setgray
525
526    local lj  = ((cnumber * sp)^1 * lpeg.P("lj") ) / mps.setlinejoin
527    local ml  = ((cnumber * sp)^1 * lpeg.P("ml") ) / mps.setmiterlimit
528    local lc  = ((cnumber * sp)^1 * lpeg.P("lc") ) / mps.setlinecap
529
530    local n   = lpeg.P("n") / mps.newpath
531    local p   = lpeg.P("p") / mps.closepath
532    local S   = lpeg.P("S") / mps.stroke
533    local F   = lpeg.P("F") / mps.fill
534    local B   = lpeg.P("B") / mps.both
535    local W   = lpeg.P("W") / mps.clip
536    local P   = lpeg.P("P") / mps.showpage
537
538    local q   = lpeg.P("q") / mps.gsave
539    local Q   = lpeg.P("Q") / mps.grestore
540
541    local sd  = (lpeg.P("[") * (cnumber * sp^0)^0 * lpeg.P("]") * sp * cnumber * sp * lpeg.P("sd")) / mps.setdash
542    local rd  = (                                                                     lpeg.P("rd")) / mps.resetdash
543
544    local s   = (              (cnumber * sp^0)^2                    * lpeg.P("s") ) / mps.scale
545    local t   = (lpeg.P("[") * (cnumber * sp^0)^6 * lpeg.P("]") * sp * lpeg.P("t") ) / mps.concat
546
547    -- experimental
548
549    local attribute = ((cnumber * sp)^2 * lpeg.P("attribute")) / mps.attribute
550    local A         = ((cnumber * sp)^2 * lpeg.P("A"))         / mps.attribute
551
552    local preamble = (
553        prolog + setup +
554        boundingbox + highresboundingbox + specials + special +
555        comment
556    )
557
558    local procset = (
559        lj + ml + lc +
560        c + l + m + n + p + r +
561        A  +
562        R + C + G +
563        S + F + B + W +
564        vlw + hlw +
565        Q + q +
566        sd + rd +
567        t + s +
568        fshow +
569        P
570    )
571
572    local verbose = (
573        curveto + lineto + moveto + newpath + closepath + rlineto +
574        setrgbcolor + setcmykcolor + setgray +
575        attribute +
576        setlinejoin + setmiterlimit + setlinecap +
577        stroke + fill + clip + both +
578        setlinewidth_x + setlinewidth_y +
579        gsave + grestore +
580        concat + scale +
581        fshow +
582        setdash + -- no resetdash
583        showpage
584    )
585
586    -- order matters in terms of speed / we could check for procset first
587
588    local captures_old = ( space + verbose + preamble           )^0
589    local captures_new = ( space + procset + preamble + verbose )^0
590
591    function mptopdf.parsers.lpeg()
592        if find(mptopdf.data,"%%BeginResource: procset mpost",1,true) then
593            lpegmatch(captures_new,mptopdf.data)
594        else
595            lpegmatch(captures_old,mptopdf.data)
596        end
597    end
598
599end
600
601mptopdf.parser = 'lpeg'
602
603-- status info
604
605statistics.register("mps conversion time",function()
606    local n = mptopdf.nofconverted
607    if n > 0 then
608        return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n)
609    else
610        return nil
611    end
612end)
613