{"id":3683,"date":"2014-10-25T16:48:19","date_gmt":"2014-10-25T16:48:19","guid":{"rendered":"http:\/\/www.readytext.co.uk\/?p=3683"},"modified":"2015-12-02T14:22:48","modified_gmt":"2015-12-02T14:22:48","slug":"metapost-direct-to-pdf-via-mplib","status":"publish","type":"post","link":"https:\/\/www.readytext.co.uk\/?p=3683","title":{"rendered":"MetaPost: Direct to PDF via MPlib"},"content":{"rendered":"<h1>Introduction<\/h1>\n<p>Using the <a href=\"http:\/\/cairographics.org\/\">Cairo graphics library<\/a> (under Windows\/Visual Studio) I have, with some caveats, been able to create a direct-to-PDF backend for MetaPost via the brilliant <a href=\"https:\/\/foundry.supelec.fr\/projects\/metapost\">MPlib C library<\/a>. Of course, Cairo does not support the CMYK colour space which is a real shame, despite there being <a href=\"http:\/\/libregraphicsworld.org\/blog\/entry\/color-managed-cairo-is-slipping-away\">a lot of discussion on the need for that<\/a>. I might look at using <a href=\"http:\/\/libharu.org\/\">LibHaru<\/a> or possibly <a href=\"http:\/\/podofo.sourceforge.net\/\">PoDoFo<\/a>, both of which I&#8217;ve managed to build on Windows &ndash; although I found PoDoFo somewhat difficult to build as a native Windows library. In addition, I have not yet added support for including text in the MetaPost graphics which is, of course, a pretty big omission! That&#8217;s on the &#8220;TODO&#8221; list. An example PDF is included in this post, based on the MetaPost code available on <a href=\"http:\/\/www.ursoswald.ch\/metapost\/tutorial.htm\">this site<\/a>. If you look at the example PDF you will see it is created with Cairo 1.12.16, the latest release available at the time I wrote this post (25 October 2014).<\/p>\n<p><a href=\"http:\/\/readytext.co.uk\/files\/tester2.pdf\">Download PDF<\/a><\/p>\n<p><iframe src=\"https:\/\/docs.google.com\/gview?url=http:\/\/readytext.co.uk\/files\/tester2.pdf&#038;embedded=true\" style=\"width:100%; height:600px;\" frameborder=\"0\"><\/iframe><\/p>\n<p><H2>Quick overview of the process<\/H2><\/p>\n<p>At the moment, the PDF backend seems to work well, at least with the MetaPost code I&#8217;ve tried it with (minus text, of course!). The lack of CMYK support in Cairo is a nuisance and at the moment I do a very simple, and wholly inadequate, &#8220;conversion&#8221; of CMYK to RGB, which really makes me cringe. Perhaps I might put in a &#8220;callback&#8221; feature to use other PDF libraries at the appropriate points in my C code. MPlib itself is a superb C library and the <a href=\"http:\/\/www.tug.org\/metapost\/src\/manual\/mplibapi.pdf\">API documentation<\/a> (version 1.800) that&#8217;s currently available was a helpful start but as <em>very<\/em> non-expert MetaPost user I did need to resort to <a href=\"http:\/\/readytext.co.uk\/files\/mp064.pdf\">John Hobby&#8217;s original work<\/a> in order to understand just a little more about some MetaPost internals. In writing the PDF backend I pretty much had to go through the PostScript backend and replace PostScript output with the appropriate Cairo API calls. The trickiest part, at least for me, was implementing management of the graphics state (as MetaPost sees it). In the end, I chose to use MPlib&#8217;s ability to register a userdata pointer (void*) with the MetaPost interpreter. In the PostScript backend the graphics state is managed internally by the MetaPost interpreter (MPlib). Can&#8217;t quite recall why I chose to externalise the graphics state code but I think it was to give me a bit more flexibility; either way, so far it basically works well. I chose to build MPlib as a static Windows .lib file &ndash; no particular reason, just that&#8217;s what I prefer to do &ndash; although building a DLL is no more difficult. Much of MPlib is released as a set of CWEB files so you will need to extract the C code via CTANGLE.EXE. I use Windows and Visual Studio so, not surprisingly, I found that the MPlib C code would not compile immediately &#8220;out of the box&#8221; but a few minor (pretty trivial) adjustments to the header files (and some manual #defines) soon resolved the problems and it compiled fine after that.<\/p>\n<h2>A little deeper<\/h2>\n<p>Assuming you have a working compilation of MPlib, how do you actually use it? I won&#8217;t repeat the information available in the the <a href=\"http:\/\/www.tug.org\/metapost\/src\/manual\/mplibapi.pdf\">MPlib API documentation<\/a> but will give a brief summary of additional considerations that might be helpful to others. Firstly, in my implementation I instantiate an instance of the MP interpreter like this:<\/p>\n<pre class=\"brush: plain; light: false; title: ; toolbar: true; notranslate\" title=\"\">\r\n\tMP mp = init_metapost((void*)create_mp_graphics_state());\r\n\tif ( ! mp ) exit ( EXIT_FAILURE ) ;\r\n<\/pre>\n<p> where <code>(void*)create_mp_graphics_state()<\/code> is a function to create a new graphics state and register this as the userdata item stored in the MPlib instance &ndash; see the code for <code>init_metapost(void* userdata)<\/code> below (Note: this is a work-in-progress and the error checking is very minimal!!! :-)). Providing the initialization succeeds you will get a new MetaPost interpreter instance returned to you. As part of the initialization you have to provide a callback that tells MetaPost how to find input files &ndash; my callback is called <code>file_finder<\/code> which uses recursive directory searching: no kpathsea involved at all. One very important setting in <code>MP-options<\/code> is <code>math_mode<\/code> which affects how MetaPost performs its internal calculations: later versions of MPlib (after 1.800) support all 4 of the possible options. As part of the initialization I also preload the <code>plain.mp<\/code> macro collection.<\/p>\n<pre class=\"brush: plain; light: false; title: ; toolbar: true; notranslate\" title=\"\">\r\nMP init_metapost(void* userdata)\r\n{\r\n\r\n\tMP mp;\r\n\tMP_options * opt = mp_options () ;\r\n\topt -&gt; command_line = NULL;\r\n\topt -&gt; noninteractive = 1 ;\r\n\topt-&gt;find_file = file_finder;\r\n\topt-&gt;print_found_names = 1;\r\n\topt-&gt;userdata = userdata;\r\n\r\n\t\/*\r\n\ttypedef enum{\r\n\tmp_math_scaled_mode= 0,\r\n\tmp_math_double_mode= 1,\r\n\tmp_math_binary_mode= 2,\r\n\tmp_math_decimal_mode= 3\r\n\t}mp_math_mode;\r\n\t*\/\r\n\r\n\topt-&gt;math_mode =mp_math_scaled_mode;\r\n        opt-&gt;ini_version = 1;\r\n\tmp = mp_initialize ( opt ) ;\r\n\tif ( ! mp ) \r\n\t\t\/\/exit ( EXIT_FAILURE )\r\n\t\treturn NULL;\r\n\telse\r\n\t{\r\n\t\tchar * input= &quot;let dump = endinput ; input plain; &quot;;\r\n\t\tmp_execute(mp, input, strlen(input));\r\n\t\tmp_run_data * res = mp_rundata(mp);\r\n\t\t\r\n\t\tif(mp-&gt;history &gt; 0)\r\n\t\t{\r\n\t\t\tprintf(&quot;Error text (%s\\n)&quot;, res-&gt;term_out.data);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t\telse{\r\n\t\t\r\n\t\t\treturn mp;\r\n\t\t}\r\n\t}\r\n\r\n}\r\n<\/pre>\n<p><H2>Got a working instance, now what?<\/H2><\/p>\n<p>In you get a working MP instance the next task is, of course, to feed it with some MetaPost code (using <code>mp_execute(mp, your_code, strlen(your_code))<\/code>\ud83d\ude09 and checking to see if MetaPost successfully interpreted <code>your_code<\/code>. Now I&#8217;m not going to give full details of the checks you need to perform as this is pretty routine and the API documentation contains enough help already. In essence, if MPlib was able to run your MetaPost code successfully, it stores the individual graphics (produced from <code>your_code<\/code>) as a linked list of so-called <em>edge structures<\/em> (<code>mp_edge_object<\/code>s). Each edge structure (<code>mp_edge_object<\/code>) is a graphic that you want to output and, in essence, each edge structure results from the successful execution of the code contained in each <code>beginfig(x) ... endfig;<\/code> pair. In turn, each edge structure (individual graphic to output) is itself made up from smaller building blocks of 8 types of fundamental graphics object (<code>mp_graphic_object<\/code>). Each <code>mp_graphic_object<\/code> has a <code>type<\/code> to tell you what sort of graphic object it is so you can call the appropriate function to render it &ndash; as the equivalent PostScript, PDF, PNG, SVG etc.<\/p>\n<h2>In summary<\/H2><\/p>\n<p>If your MetaPost interpreter instance is called, say, mp, then to gain access to the linked list of edge structures you do something like this:<\/p>\n<pre class=\"brush: plain; light: false; title: ; toolbar: true; notranslate\" title=\"\"> \r\nmp_run_data * res = mp_rundata(mp);\r\nmp_edge_object* graphics = res-&gt;edges;\r\n<\/pre>\n<p>Note that the edge structures form a <em>simple linked list<\/em> but the list of components within each individual edge structure (the <code>mp_graphic_object<\/code> objects) form a <em>circularly-linked list<\/em>, so you have to be careful to check when you get to the end of the circular list of the <code>mp_graphic_object<\/code> objects: see the API docs for an example. In closing, here&#8217;s the loop from my code to process an individual edge structure into PDF &ndash; not including all the additional functions to process the various types of the <code>mp_graphic_object<\/code> objects.<\/p>\n<pre class=\"brush: plain; light: false; title: ; toolbar: true; notranslate\" title=\"\">\r\nint draw_mp_graphic_on_pdf(mp_edge_object* single_graphic, cairo_t *cr)\r\n{\r\n\r\n \t\tmp_graphic_object*p;\r\n \t\tMP mp = single_graphic-&gt;parent;\r\n\r\n\t\t p=single_graphic-&gt;body;\r\n\t\t\r\n\t\t\/\/ Inherited this weirdness from core MP engine...\r\n\t\tinit_graphics_state(mp, 0);\r\n\t\t\/\/ Here we are looping over all the objects in a single graphics\r\n\t\t\/\/ resulting from a beginfig(x) ... endfig pair\r\n\t\t while (p != NULL) \r\n\t\t {\r\n\t\t\tmp_gr_fix_graphics_state(mp,p,cr);\r\n \t\t\tswitch (gr_type(p)) \r\n\t\t\t {\r\n\t\t\t\t case mp_fill_code:\r\n\r\n\t\t\t\t {\r\n\t\t\t\t\r\n\t\t\t\t if(gr_pen_p((mp_fill_object*)p)==NULL)\r\n\t\t\t\t\t {\r\n\t\t\t\t\t\t\/\/mp_dump_solved_path(gr_path_p((mp_fill_object*)p));\r\n\t\t\t\t\t\tcairo_gr_pdf_fill_out(mp,gr_path_p((mp_fill_object*)p),cr); \r\n\t\t\t\t\t}\r\n\t\t\t\t\t else if(pen_is_elliptical(gr_pen_p((mp_fill_object*)p)))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t\/\/mp_dump_solved_path(gr_path_p((mp_fill_object*)p));\r\n\t\t\t\t\t\tcairo_gr_stroke_ellipse(mp,p,true,cr);\r\n\t\t\t\t\t }else{\r\n\t\t\t\t\t\t\/\/mp_dump_solved_path(gr_path_p((mp_stroked_object*)p));\r\n\t\t\t\t\t\tcairo_gr_pdf_fill_out(mp,gr_path_p((mp_fill_object*)p),cr);\r\n\t\t\t\t\t\tcairo_gr_pdf_fill_out(mp,gr_htap(p),cr); \r\n\t\t\t\t\t }\r\n\t\r\n\t\t\t\t\t if(   ((mp_fill_object*)p)-&gt;post_script != NULL)\r\n\t\t\t\t\t {\r\n\t\t\t\t\t        \/\/ just something I'm experimenting with\r\n\t\t\t\t\t\t\/\/ondraw(cr, ((mp_fill_object*)p)-&gt;post_script);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t\t case mp_stroked_code:\r\n\t\t\t\t {\r\n\t\t\t\t\t\r\n\t\t\t\t\t mp_dump_solved_path(gr_path_p((mp_stroked_object*)p));\t\r\n\t\t\t\t\tif(pen_is_elliptical(gr_pen_p((mp_stroked_object*)p)))\r\n\t\t\t\t\t\tcairo_gr_stroke_ellipse(mp, p, false, cr);\r\n \t\t\t\t\telse\r\n \t\t\t\t\t{\r\n\t\t\t\t\t\t\/\/mp_dump_solved_path(gr_path_p((mp_stroked_object*)p));\r\n\t\t\t\t\t\tcairo_gr_pdf_fill_out(mp,gr_path_p((mp_stroked_object*)p),cr);\r\n \t\t\t\t\t}\r\n\r\n\t\t\t\t\t if(((mp_stroked_object*)p)-&gt;post_script != NULL)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tondraw(cr, ((mp_stroked_object*)p)-&gt;post_script);\r\n \t\t\t\t\t}\r\n \t\t\t\t}\r\n \t\t\t\tbreak;\r\n\r\n  \t\t\t         case mp_text_code: \/\/ not yet implemented\r\n\t\t\t\t {\r\n\t\t\t\t\t mp_text_object* to;\r\n\t\t\t\t\tto = (mp_text_object*)p;\r\n\t\t\t\t\tchar * po = to-&gt;post_script;\r\n\t\t\t\t\tchar * ps = to-&gt;pre_script;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t\tcase mp_start_clip_code:\r\n\t\t\t\t\tcairo_save(cr);\r\n\t\t\t\t\tcairo_gr_pdf_path_out(mp,gr_path_p((mp_clip_object*)p),cr);\r\n\t\t\t\t\tcairo_clip(cr);\r\n\t\t\t\tbreak;\r\n\t\t\t\t\r\n\t\t\t\tcase mp_stop_clip_code:\r\n\t\t\t\t\tcairo_restore(cr);\r\n\t\t\t\tbreak;\t\t\r\n\t\t\t\t\r\n\t\t\t\tcase mp_start_bounds_code: \/\/ ignored\r\n\t\t\t\t\t\/\/mp_bounds_object *sbo;\r\n\t\t\t\t\t\/\/sbo = (mp_bounds_object *)gr;\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t\tcase mp_stop_bounds_code: \/\/ignored\r\n\t\t\t\t\t\/\/mp_special_object\r\n\t\t\t\tbreak;\r\n\t\t\t\tcase mp_special_code: \/\/just more experimenting, ignore\r\n\t\t\t\t\t\r\n\t\t\t\t\tmp_special_object *speco;\r\n\t\t\t\t\tspeco = (mp_special_object *)p;\r\n\t\t\t\t\tprintf(&quot;%s&quot;, speco-&gt;pre_script);\r\n\t\t\t\t\tondraw(cr, speco-&gt;pre_script);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\t\tp= gr_link(p);\r\n\t\t}\r\n\t\treturn 0;\r\n\t}\r\n<\/pre>\n<p><H1>Conclusion<\/H1><\/p>\n<p>I wish I could switch on the commenting feature but, sadly, spammers make this impossible. So, I just hope the above is a useful starting point for anyone wanting to explore the marvellous MPlib C library.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Using the Cairo graphics library (under Windows\/Visual Studio) I have, with some caveats, been able to create a direct-to-PDF backend for MetaPost via the brilliant MPlib C library. Of course, Cairo does not support the CMYK colour space which is a real shame, despite there being a lot of discussion on the need for [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[31],"tags":[],"class_list":["post-3683","post","type-post","status-publish","format-standard","hentry","category-metapostmplib"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/3683","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3683"}],"version-history":[{"count":26,"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/3683\/revisions"}],"predecessor-version":[{"id":3785,"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/3683\/revisions\/3785"}],"wp:attachment":[{"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3683"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3683"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.readytext.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3683"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}