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
9
10
11
12
13
14
15
16
17
18
19local tonumber = tonumber
20local concat, unpack = table.concat, table.unpack
21local gsub, find, byte, gmatch, match = string.gsub, string.find, string.byte, string.gmatch, string.match
22local lpegmatch = lpeg.match
23local round = math.round
24local formatters, format = string.formatters, string.format
25
26local mplib = mplib
27local metapost = metapost
28local lpdf = lpdf
29local context = context
30
31local report_mptopdf = logs.reporter("graphics","mptopdf")
32
33local texgetattribute = tex.getattribute
34
35local pdfrgbcode = lpdf.rgbcode
36local pdfcmykcode = lpdf.cmykcode
37local pdfgraycode = lpdf.graycode
38local pdfspotcode = lpdf.spotcode
39local pdftransparencycode = lpdf.transparencycode
40local pdffinishtransparencycode = lpdf.finishtransparencycode
41
42metapost.mptopdf = metapost.mptopdf or { }
43local mptopdf = metapost.mptopdf
44
45mptopdf.nofconverted = 0
46
47local f_translate = formatters["1 0 0 0 1 %.6N %.6N cm"]
48local f_concat = formatters["%.6N %.6N %.6N %.6N %.6N %.6N cm"]
49
50local m_path, m_stack, m_texts, m_version, m_date, m_shortcuts = { }, { }, { }, 0, 0, false
51
52local m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
53local extra_path_data, ignore_path = nil, false
54local specials = { }
55
56local function resetpath()
57 m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
58end
59
60local function resetall()
61 m_path, m_stack, m_texts, m_version, m_shortcuts = { }, { }, { }, 0, false
62 extra_path_data, ignore_path = nil, false
63 specials = { }
64 resetpath()
65end
66
67resetall()
68
69local pdfcode = context.pdfliteral
70
71local function mpscode(str)
72 if ignore_path then
73 pdfcode("h W n")
74 if extra_path_data then
75 pdfcode(extra_path_data)
76 extra_path_data = nil
77 end
78 ignore_path = false
79 else
80 pdfcode(str)
81 end
82end
83
84
85
86local function flushconcat()
87 if m_stack_concat then
88 mpscode(f_concat(unpack(m_stack_concat)))
89 m_stack_concat = nil
90 end
91end
92
93local function flushpath(cmd)
94 if #m_stack_path > 0 then
95 local path = { }
96 if m_stack_concat then
97 local sx = m_stack_concat[1]
98 local sy = m_stack_concat[4]
99 local rx = m_stack_concat[2]
100 local ry = m_stack_concat[3]
101 local tx = m_stack_concat[5]
102 local ty = m_stack_concat[6]
103 local d = (sx*sy) - (rx*ry)
104 for k=1,#m_stack_path do
105 local v = m_stack_path[k]
106 local px = v[1]
107 local py = v[2]
108 v[1] = (sy*(px-tx)-ry*(py-ty))/d
109 v[2] = (sx*(py-ty)-rx*(px-tx))/d
110 if #v == 7 then
111 px = v[3]
112 py = v[4]
113 v[3] = (sy*(px-tx)-ry*(py-ty))/d
114 v[4] = (sx*(py-ty)-rx*(px-tx))/d
115 px = v[5]
116 py = v[6]
117 v[5] = (sy*(px-tx)-ry*(py-ty))/d
118 v[6] = (sx*(py-ty)-rx*(px-tx))/d
119 end
120 path[k] = concat(v," ")
121 end
122 else
123 for k=1,#m_stack_path do
124 path[k] = concat(m_stack_path[k]," ")
125 end
126 end
127 flushconcat()
128 pdfcode(concat(path," "))
129 if m_stack_close then
130 mpscode("h " .. cmd)
131 else
132 mpscode(cmd)
133 end
134 end
135 resetpath()
136end
137
138
139
140local mps = { }
141
142function mps.creator(a, b, c)
143 m_version = tonumber(b)
144end
145
146function mps.creationdate(a)
147 m_date = a
148end
149
150function mps.newpath()
151 m_stack_path = { }
152end
153
154function mps.boundingbox(llx, lly, urx, ury)
155 context.setMPboundingbox(llx,lly,urx,ury)
156end
157
158function mps.moveto(x,y)
159 m_stack_path[#m_stack_path+1] = { x, y, "m" }
160end
161
162function mps.curveto(ax, ay, bx, by, cx, cy)
163 m_stack_path[#m_stack_path+1] = { ax, ay, bx, by, cx, cy, "c" }
164end
165
166function mps.lineto(x,y)
167 m_stack_path[#m_stack_path+1] = { x, y, "l" }
168end
169
170function mps.rlineto(x,y)
171 local dx = 0
172 local dy = 0
173 local topofstack = #m_stack_path
174 if topofstack > 0 then
175 local msp = m_stack_path[topofstack]
176 dx = msp[1]
177 dy = msp[2]
178 end
179 m_stack_path[topofstack+1] = { dx, dy, "l" }
180end
181
182function mps.translate(tx,ty)
183 mpscode(f_translate(tx,ty))
184end
185
186function mps.scale(sx,sy)
187 m_stack_concat = { sx, 0, 0, sy, 0, 0 }
188end
189
190function mps.concat(sx, rx, ry, sy, tx, ty)
191 m_stack_concat = { sx, rx, ry, sy, tx, ty }
192end
193
194function mps.setlinejoin(d)
195 mpscode(d .. " j")
196end
197
198function mps.setlinecap(d)
199 mpscode(d .. " J")
200end
201
202function mps.setmiterlimit(d)
203 mpscode(d .. " M")
204end
205
206function mps.gsave()
207 mpscode("q")
208end
209
210function mps.grestore()
211 mpscode("Q")
212end
213
214function mps.setdash(...)
215 local n = select("#",...)
216 mpscode("[" .. concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d")
217
218end
219
220function mps.resetdash()
221 mpscode("[ ] 0 d")
222end
223
224function mps.setlinewidth(d)
225 mpscode(d .. " w")
226end
227
228function mps.closepath()
229 m_stack_close = true
230end
231
232function mps.fill()
233 flushpath('f')
234end
235
236function mps.stroke()
237 flushpath('S')
238end
239
240function mps.both()
241 flushpath('B')
242end
243
244function mps.clip()
245 flushpath('W n')
246end
247
248function mps.textext(font, scale, str)
249 local dx = 0
250 local dy = 0
251 if #m_stack_path > 0 then
252 dx, dy = m_stack_path[1][1], m_stack_path[1][2]
253 end
254 flushconcat()
255 context.MPtextext(font,scale,str,dx,dy)
256 resetpath()
257end
258
259local handlers = { }
260
261handlers[1] = function(s)
262 pdfcode(pdffinishtransparencycode())
263 pdfcode(pdfcmykcode(mps.colormodel,s[3],s[4],s[5],s[6]))
264end
265handlers[2] = function(s)
266 pdfcode(pdffinishtransparencycode())
267 pdfcode(pdfspotcode(mps.colormodel,s[3],s[4],s[5],s[6]))
268end
269handlers[3] = function(s)
270 pdfcode(pdfrgbcode(mps.colormodel,s[4],s[5],s[6]))
271 pdfcode(pdftransparencycode(s[2],s[3]))
272end
273handlers[4] = function(s)
274 pdfcode(pdfcmykcode(mps.colormodel,s[4],s[5],s[6],s[7]))
275 pdfcode(pdftransparencycode(s[2],s[3]))
276end
277handlers[5] = function(s)
278 pdfcode(pdfspotcode(mps.colormodel,s[4],s[5],s[6],s[7]))
279 pdfcode(pdftransparencycode(s[2],s[3]))
280end
281
282
283
284local nofshades, tn = 0, tonumber
285
286local function linearshade(colorspace,domain,ca,cb,coordinates)
287 pdfcode(pdffinishtransparencycode())
288 nofshades = nofshades + 1
289 local name = formatters["MpsSh%s"](nofshades)
290 lpdf.linearshade(name,domain,ca,cb,1,colorspace,coordinates)
291 extra_path_data, ignore_path = formatters["/%s sh Q"](name), true
292 pdfcode("q /Pattern cs")
293end
294
295local function circularshade(colorspace,domain,ca,cb,coordinates)
296 pdfcode(pdffinishtransparencycode())
297 nofshades = nofshades + 1
298 local name = formatters["MpsSh%s"](nofshades)
299 lpdf.circularshade(name,domain,ca,cb,1,colorspace,coordinates)
300 extra_path_data, ignore_path = formatters["/%s sh Q"](name), true
301 pdfcode("q /Pattern cs")
302end
303
304handlers[30] = function(s)
305 linearshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
306 { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[10]), tn(s[11]), tn(s[12]) },
307 { tn(s[ 8]), tn(s[ 9]), tn(s[13]), tn(s[14]) } )
308end
309
310handlers[31] = function(s)
311 circularshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
312 { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[11]), tn(s[12]), tn(s[13]) },
313 { tn(s[ 8]), tn(s[ 9]), tn(s[10]), tn(s[14]), tn(s[15]), tn(s[16]) } )
314end
315
316handlers[32] = function(s)
317 linearshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
318 { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[11]), tn(s[12]), tn(s[13]), tn(s[14]) },
319 { tn(s[ 9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
320end
321
322handlers[33] = function(s)
323 circularshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
324 { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[12]), tn(s[13]), tn(s[14]), tn(s[15]) },
325 { tn(s[ 9]), tn(s[10]), tn(s[11]), tn(s[16]), tn(s[17]), tn(s[18]) } )
326end
327
328handlers[34] = function(s)
329 linearshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
330end
331
332handlers[35] = function(s)
333 circularshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
334end
335
336
337
338handlers[10] = function() report_mptopdf("skipping special %s",10) end
339handlers[20] = function() report_mptopdf("skipping special %s",20) end
340handlers[50] = function() report_mptopdf("skipping special %s",50) end
341
342
343
344function mps.setrgbcolor(r,g,b)
345 r = tonumber(r)
346 g = tonumber(g)
347 b = tonumber(b)
348 if r == 0.0123 and g < 0.1 then
349 g = round(g*10000)
350 b = round(b*10000)
351 local s = specials[b]
352 local h = round(s[#s])
353 local handler = handlers[h]
354 if handler then
355 handler(s)
356 else
357 report_mptopdf("unknown special handler %s (1)",h)
358 end
359 elseif r == 0.123 and g < 0.1 then
360 g = round(g*1000)
361 b = round(b*1000)
362 local s = specials[b]
363 local h = round(s[#s])
364 local handler = handlers[h]
365 if handler then
366 handler(s)
367 else
368 report_mptopdf("unknown special handler %s (2)",h)
369 end
370 else
371 pdfcode(pdffinishtransparencycode())
372 pdfcode(pdfrgbcode(mps.colormodel,r,g,b))
373 end
374end
375
376function mps.setcmykcolor(c,m,y,k)
377 pdfcode(pdffinishtransparencycode())
378 pdfcode(pdfcmykcode(mps.colormodel,c,m,y,k))
379end
380
381function mps.setgray(s)
382 pdfcode(pdffinishtransparencycode())
383 pdfcode(pdfgraycode(mps.colormodel,s))
384end
385
386function mps.specials(version,signal,factor)
387end
388
389function mps.special(...)
390 local t = { ... }
391 local n = tonumber(t[#t-1])
392 specials[n] = t
393end
394
395function mps.begindata()
396end
397
398function mps.enddata()
399end
400
401function mps.showpage()
402end
403
404
405
406
407
408
409
410local lpegP, lpegR, lpegS, lpegC, lpegCc, lpegCs = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs
411
412local digit = lpegR("09")
413local eol = lpegS('\r\n')^1
414local sp = lpegP(' ')^1
415local space = lpegS(' \r\n')^1
416local number = lpegS('0123456789.-+')^1
417local nonspace = lpegP(1-lpegS(' \r\n'))^1
418
419local spec = digit^2 * lpegP("::::") * digit^2
420local text = lpegCc("{") * (
421 lpegP("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) +
422 lpegP(" ") / function(n) return "\\c32" end +
423 lpegP(1) / function(n) return "\\c" .. byte(n) end
424 ) * lpegCc("}")
425local package = lpegCs(spec + text^0)
426
427function mps.fshow(str,font,scale)
428 mps.textext(font,scale,lpegmatch(package,str))
429end
430
431
432local cnumber = number/tonumber
433local cstring = lpegC(nonspace)
434
435local specials = (lpegP("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials
436local special = (lpegP("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special
437local boundingbox = (lpegP("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
438local highresboundingbox = (lpegP("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
439
440local setup = lpegP("%%BeginSetup") * (1 - lpegP("%%EndSetup") )^1
441local prolog = lpegP("%%BeginProlog") * (1 - lpegP("%%EndProlog"))^1
442local comment = lpegP('%')^1 * (1 - eol)^1
443
444local curveto = ((cnumber * sp)^6 * lpegP("curveto") ) / mps.curveto
445local lineto = ((cnumber * sp)^2 * lpegP("lineto") ) / mps.lineto
446local rlineto = ((cnumber * sp)^2 * lpegP("rlineto") ) / mps.rlineto
447local moveto = ((cnumber * sp)^2 * lpegP("moveto") ) / mps.moveto
448local setrgbcolor = ((cnumber * sp)^3 * lpegP("setrgbcolor") ) / mps.setrgbcolor
449local setcmykcolor = ((cnumber * sp)^4 * lpegP("setcmykcolor") ) / mps.setcmykcolor
450local setgray = ((cnumber * sp)^1 * lpegP("setgray") ) / mps.setgray
451local newpath = ( lpegP("newpath") ) / mps.newpath
452local closepath = ( lpegP("closepath") ) / mps.closepath
453local fill = ( lpegP("fill") ) / mps.fill
454local stroke = ( lpegP("stroke") ) / mps.stroke
455local clip = ( lpegP("clip") ) / mps.clip
456local both = ( lpegP("gsave fill grestore")) / mps.both
457local showpage = ( lpegP("showpage") )
458local setlinejoin = ((cnumber * sp)^1 * lpegP("setlinejoin") ) / mps.setlinejoin
459local setlinecap = ((cnumber * sp)^1 * lpegP("setlinecap") ) / mps.setlinecap
460local setmiterlimit = ((cnumber * sp)^1 * lpegP("setmiterlimit") ) / mps.setmiterlimit
461local gsave = ( lpegP("gsave") ) / mps.gsave
462local grestore = ( lpegP("grestore") ) / mps.grestore
463
464local setdash = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("setdash")) / mps.setdash
465local concat = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("concat") ) / mps.concat
466local scale = ( (cnumber * sp^0)^6 * sp * lpegP("concat") ) / mps.concat
467
468local fshow = (lpegP("(") * lpegC((1-lpegP(")"))^1) * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
469local fshow = (lpegP("(") * lpegCs( ( lpegP("\\(")/"\\050" + lpegP("\\)")/"\\051" + (1-lpegP(")")) )^1 )
470 * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
471
472local setlinewidth_x = (lpegP("0") * sp * cnumber * sp * lpegP("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth
473local setlinewidth_y = (cnumber * sp * lpegP("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth
474
475local c = ((cnumber * sp)^6 * lpegP("c") ) / mps.curveto
476local l = ((cnumber * sp)^2 * lpegP("l") ) / mps.lineto
477local r = ((cnumber * sp)^2 * lpegP("r") ) / mps.rlineto
478local m = ((cnumber * sp)^2 * lpegP("m") ) / mps.moveto
479local vlw = ((cnumber * sp)^1 * lpegP("vlw")) / mps.setlinewidth
480local hlw = ((cnumber * sp)^1 * lpegP("hlw")) / mps.setlinewidth
481
482local R = ((cnumber * sp)^3 * lpegP("R") ) / mps.setrgbcolor
483local C = ((cnumber * sp)^4 * lpegP("C") ) / mps.setcmykcolor
484local G = ((cnumber * sp)^1 * lpegP("G") ) / mps.setgray
485
486local lj = ((cnumber * sp)^1 * lpegP("lj") ) / mps.setlinejoin
487local ml = ((cnumber * sp)^1 * lpegP("ml") ) / mps.setmiterlimit
488local lc = ((cnumber * sp)^1 * lpegP("lc") ) / mps.setlinecap
489
490local n = lpegP("n") / mps.newpath
491local p = lpegP("p") / mps.closepath
492local S = lpegP("S") / mps.stroke
493local F = lpegP("F") / mps.fill
494local B = lpegP("B") / mps.both
495local W = lpegP("W") / mps.clip
496local P = lpegP("P") / mps.showpage
497
498local q = lpegP("q") / mps.gsave
499local Q = lpegP("Q") / mps.grestore
500
501local sd = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("sd")) / mps.setdash
502local rd = ( lpegP("rd")) / mps.resetdash
503
504local s = ( (cnumber * sp^0)^2 * lpegP("s") ) / mps.scale
505local t = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("t") ) / mps.concat
506
507
508
509local preamble = (
510 prolog + setup +
511 boundingbox + highresboundingbox + specials + special +
512 comment
513)
514
515local procset = (
516 lj + ml + lc +
517 c + l + m + n + p + r +
518 R + C + G +
519 S + F + B + W +
520 vlw + hlw +
521 Q + q +
522 sd + rd +
523 t + s +
524 fshow +
525 P
526)
527
528local verbose = (
529 curveto + lineto + moveto + newpath + closepath + rlineto +
530 setrgbcolor + setcmykcolor + setgray +
531 setlinejoin + setmiterlimit + setlinecap +
532 stroke + fill + clip + both +
533 setlinewidth_x + setlinewidth_y +
534 gsave + grestore +
535 concat + scale +
536 fshow +
537 setdash +
538 showpage
539)
540
541
542
543local captures_old = ( space + verbose + preamble )^0
544local captures_new = ( space + verbose + procset + preamble )^0
545
546local function parse(m_data)
547 if find(m_data,"%%BeginResource: procset mpost",1,true) then
548
549 lpegmatch(captures_new,m_data)
550 elseif find(m_data,"%%%%BeginProlog%s*%S+(.-)%%%%EndProlog") then
551
552 lpegmatch(captures_new,m_data)
553 else
554
555 lpegmatch(captures_old,m_data)
556 end
557end
558
559
560
561local a_colormodel = attributes.private('colormodel')
562
563function mptopdf.convertmpstopdf(name)
564 resetall()
565 local ok, m_data, n = resolvers.loadbinfile(name, 'tex')
566 if ok then
567 mps.colormodel = texgetattribute(a_colormodel)
568 statistics.starttiming(mptopdf)
569 mptopdf.nofconverted = mptopdf.nofconverted + 1
570 pdfcode(formatters["\\letterpercent\\space mptopdf begin: n=%s, file=%s"](mptopdf.nofconverted,file.basename(name)))
571 pdfcode("q 1 0 0 1 0 0 cm")
572 parse(m_data)
573 pdfcode(pdffinishtransparencycode())
574 pdfcode("Q")
575 pdfcode("\\letterpercent\\space mptopdf end")
576 resetall()
577 statistics.stoptiming(mptopdf)
578 else
579 report_mptopdf("file %a not found",name)
580 end
581end
582
583
584
585statistics.register("mps conversion time",function()
586 local n = mptopdf.nofconverted
587 if n > 0 then
588 return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n)
589 else
590 return nil
591 end
592end)
593
594
595
596interfaces.implement {
597 name = "convertmpstopdf",
598 arguments = "string",
599 actions = mptopdf.convertmpstopdf
600}
601 |