Building an mplib Shared Library

by Paul Murrell http://orcid.org/0000-0002-3224-8858

Version 1: Wednesday 03 October 2018


Creative Commons License
This document by Paul Murrell is licensed under a Creative Commons Attribution 4.0 International License.


This report records the steps that were used to build a shared library for the MetaPost graphics system.

Table of Contents:

1. Introduction

MetaPost (Hobby, 1998) is a graphics language that provides some very useful features for describing curves. For example, the following MetaPost code describes an infinity symbol from a MetaPost "path" that consists of only four points (plus constraints that dictate the direction that the curve should take at each of the points). This code is saved in a file called infinity.mp.

  outputtemplate := "%j.svg";
  outputformat := "svg";
  beginfig(1);
  z0 = (0, 0);
  z1 = (20, 10);
  z2 = (20, 0);
  z3 = (0, 10);
  draw z0{dir 0}..z1{dir 0}..z2{dir 180}..z3{dir 180}..cycle;
  endfig;
  end

The MetaPost description of a path is processed by a MetaPost compiler, mpost, which calculates a set of cubic Bezier curves that draw the final curve. The command below processes MetaPost code in the file infinity.mp and produces a file infinity.log.

  mpost -s tracingchoices=1 infinity.mp

The file infinity.log shows the Bezier control points that mpost has calculated (starting from the line "Path at line 8, after choices:").

  This is MetaPost, version 1.999 (TeX Live 2015/Debian) (kpathsea version 6.2.1)  3 OCT 2018 23:31
  **infinity.mp
  (/usr/share/texlive/texmf-dist/metapost/base/mpost.mp
  (/usr/share/texlive/texmf-dist/metapost/base/plain.mp
  Preloading the plain mem file, version 1.005) ) (./infinity.mp
  Path at line 8, before choices:
  (0,0){4096,0}
   ..{4096,0}(20,10){4096,0}
   ..{-4096,0}(20,0){-4096,0}
   ..{-4096,0}(0,10){-4096,0}
   ..{4096,0}cycle
  
  Path at line 8, after choices:
  (0,0)..controls (7.86894,0) and (12.13106,10)
   ..(20,10)..controls (26.66667,10) and (26.66667,0)
   ..(20,0)..controls (12.13106,0) and (7.86894,10)
   ..(0,10)..controls (-6.66667,10) and (-6.66667,0)
   ..cycle
  
   [1] )
  1 output file written: infinity.svg

The MetaPost compiler also produces PostScript, PNG, or SVG output to render the Bezier curves. The file infinity.mp produces the SVG file infinity.svg, which is shown below.

The MetaPost compiler is built on a MetaPost compiler library called mplib (Hobby and the MetaPost development team, 2018 p. 3), which means that other programs can include the MetaPost compiler. For example, there is a Lua (Ierusalimschy et al., 1996) binding for the mplib library that allows MetaPost code to be embedded directly within a LuaTeX document (The LuaTeX development team, 2017). The ConTeXt document processing system also builds on this Lua mplib binding to allow embedding of MetaPost code within ConTeXt documents.

The mplib library provides C functions that allow us to define MetaPost paths, and solve them, in C code. For example, the C code below, which is saved in a file called infinity.c, uses the mplib API to describe and solve the infinity symbol that we described with MetaPost code in the file infinity.mp.

The main action occurs in the main function; the mp_dump_solved_path function is just there to print out the answer (the code is based on an example in Hoekwater and Scarso, 2018). There are calls to the function mp_append_knot to create points on a path, calls to mp_set_knot_direction to define directions at each point, and a call to mp_solve_path that calculates Bezier curves to draw the final curve.

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include "mplib.h"
  
  void mp_dump_solved_path (MP mp, mp_knot h) {
      mp_knot p, q;
      if (h == NULL) return;
      p = h;
      do {
          q=mp_knot_next(mp,p);
          printf ("(%g,%g)..controls (%g,%g) and (%g,%g)",
                  mp_number_as_double(mp,p->x_coord),
                  mp_number_as_double(mp,p->y_coord),
                  mp_number_as_double(mp,p->right_x),
                  mp_number_as_double(mp,p->right_y),
                  mp_number_as_double(mp,q->left_x),
                  mp_number_as_double(mp,q->left_y));
          p=q;
          if ( p!=h || h->data.types.left_type!=mp_endpoint) {
              printf ("\n ..");
          }
      } while (p!=h);
      if ( h->data.types.left_type!=mp_endpoint )
          printf("cycle");
      printf (";\n");
  }
  
  int main (int argc, char ** argv) {
      MP mp;
      mp_knot p, first, q;
      int result;
      MP_options * opt = mp_options();
      opt -> command_line = NULL;
      opt -> noninteractive = 1;
      mp = mp_initialize(opt);
      first = p = mp_append_knot(mp, NULL, 0, 0);
      mp_set_knot_direction(mp, p, 1, 0);
      q = mp_append_knot(mp, p, 20, 10);
      mp_set_knot_direction(mp, q, 1, 0);
      p = mp_append_knot(mp, q, 20, 0);
      mp_set_knot_direction(mp, p, -1, 0);
      q = mp_append_knot(mp, p, 0, 10);
      mp_set_knot_direction(mp, q, -1, 0);
      mp_close_path_cycle(mp, q, first);
      if (mp_solve_path(mp, first)) {
          mp_dump_solved_path(mp, first);
      }
      mp_free_path(mp, first);
      mp_finish(mp);
      free(opt);
      return 0;
  }

Although there are several mentions in TUGboat articles of developing the mplib library as a system library that can be used by any application program (Hoekwater, 2006, Hoekwater and Hagen, 2007, Hoekwater, 2008), and there is an "MPLib API Manual" (Hoekwater and Scarso, 2018), only the mpost standalone program is included as part of large TeX distributions such as TeX Live and MikTeX. Furthermore, there are no "development" distributions of these packages that include header files, let alone pre-built libraries for mplib. Both LuaTeX and ConTeXt are part of these large distributions, so have access to mplib when they are built.

This means that, if we want to create a new program, like the file infinity.c, that links to the mplib library, we must start with a copy of the MetaPost source code and do a bit more of the work ourselves.

This report describes a series of steps for making use of the mplib library within a new C program, with demonstrations of building both static and dynamic mplib libraries.

This report will focus solely on solutions with TeX Live in an Ubuntu Linux environment. The information should have some relevance to other Linux distributions, but may not be very helpful with respect to Windows or Mac OS environments.

2. mplib as a static library

It is straightforward to obtain the MetaPost compiler program mpost as part of TeX Live (on Ubuntu, this comes as part of the texlive-binaries package), but the TeX Live distribution does not contain the mplib library.

Fortunately, it is also straightforward to obtain and build the TeX Live source code (https://www.tug.org/texlive/) and, even better, we can just get the source code relating to MetaPost and build that (https://serveur-svn.lri.fr/svn/modhel/metapost; user/pw = anonsvn/anonsvn).

Building MetaPost simply involves a subversion checkout of the MetaPost source files followed by a call to the shell script build.sh that is included in the root directory of the MetaPost source code.

The Dockerfile below builds a Docker container (Merkel, 2014) based on Ubuntu 16.04 with (the development version of) MetaPost downloaded and built in /opt/MetaPost/trunk.

  
  # Base image
  FROM ubuntu:16.04
  MAINTAINER Paul Murrell <paul@stat.auckland.ac.nz>
  
  RUN apt-get update && \
      apt-get install -y \
          subversion
  
  # Checkout MetaPost source
  RUN mkdir /opt/MetaPost && \
      cd /opt/MetaPost && \
      svn co --username anonsvn --password anonsvn --trust-server-cert https://serveur-svn.lri.fr/svn/modhel/metapost/trunk 
  
  # Build MetaPost
  RUN apt-get update && \
      apt-get install -y \
          gcc \
          g++ \
          make \
          texinfo
  RUN cd /opt/MetaPost/trunk/ && \
      ./build.sh

In order to compile a new C program against this mplib build, we need the location of the mplib.h header file and we need the correct set of libraries to link against.

The header file is easy enough to find ...

  find /opt/MetaPost/trunk -name mplib.h
  /opt/MetaPost/trunk/build/texk/web2c/mplib.h

We can also see that the build generated several static mplib library files ...

  find /opt/MetaPost/trunk/build/texk/web2c -name *libmp*.a
  /opt/MetaPost/trunk/build/texk/web2c/libmputil.a
  /opt/MetaPost/trunk/build/texk/web2c/libmplibextramath.a
  /opt/MetaPost/trunk/build/texk/web2c/libmplibbackends.a
  /opt/MetaPost/trunk/build/texk/web2c/libmplibcore.a

In addition, the build generates a number of other libraries that the mplib library depends on.

  ls /opt/MetaPost/trunk/build/libs/*/*.a
  /opt/MetaPost/trunk/build/libs/cairo/libcairo.a
  /opt/MetaPost/trunk/build/libs/gmp/libgmp.a
  /opt/MetaPost/trunk/build/libs/libpng/libpng.a
  /opt/MetaPost/trunk/build/libs/mpfr/libmpfr.a
  /opt/MetaPost/trunk/build/libs/pixman/libpixman.a
  /opt/MetaPost/trunk/build/libs/zlib/libz.a

These can be used to statically link mplib as part of a new program. The following code compiles an executable from infinity.c (shown earlier in this report), using the static mplib library files ...

  gcc -c -I/opt/MetaPost/trunk/build/texk/web2c/ -o infinity-static.o infinity.c
  gcc -o infinity-static infinity-static.o
      -L/opt/MetaPost/trunk/build/texk/web2c -lmplibcore -lmplibbackends -lmputil -lmplibextramath
      -L/opt/MetaPost/trunk/build/libs/libpng -lpng
      -L/opt/MetaPost/trunk/build/libs/cairo -lcairo
      -L/opt/MetaPost/trunk/build/libs/pixman -lpixman
      -L/opt/MetaPost/trunk/build/libs/mpfr -lmpfr
      -L/opt/MetaPost/trunk/build/libs/gmp -lgmp
      -L/opt/MetaPost/trunk/build/libs/zlib -lz
      -L/opt/MetaPost/trunk/build/texk/kpathsea/.libs -lkpathsea -lm

This produces an executable file, infinity-static, and when we run this program, the result is the solved MetaPost path for the infinity symbol curve. This path should correspond to the result that we saw earlier in the log file that was generated by mpost.

  ./infinity-static
  (0,0)..controls (7.86894,0) and (12.1311,10)
   ..(20,10)..controls (26.6667,10) and (26.6667,0)
   ..(20,0)..controls (12.1311,0) and (7.86894,10)
   ..(0,10)..controls (-6.66667,10) and (-6.66667,0)
   ..cycle;

3. mplib as a shared library

Building a shared library version of mplib is very similar to the static build. The only difficulty is that the build.sh script only generates statically linked libraries (see the INSTALL file; user/pw = anonsvn/anonsvn). However, if we specify CFLAGS=-fpic before calling the build.sh script, the build produces object files that can be used to create a shared library.

Another difference is that we build libmplib.so using the mplib object files (.o files rather than .a files) so that they are part of the shared library itself.

  find /opt/MetaPost/trunk/build/texk/web2c -name libmp*.o
  /opt/MetaPost/trunk/build/texk/web2c/mplibdir/libmputil_a-avl.o
  /opt/MetaPost/trunk/build/texk/web2c/mplibdir/libmputil_a-decContext.o
  /opt/MetaPost/trunk/build/texk/web2c/mplibdir/libmputil_a-decNumber.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibextramath_a-mpmathbinary.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibcore_a-mpmath.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibcore_a-mpmathdecimal.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibcore_a-mpstrings.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibbackends_a-pngout.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibcore_a-tfmin.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibcore_a-mp.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibcore_a-psout.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibcore_a-mpmathdouble.o
  /opt/MetaPost/trunk/build/texk/web2c/libmplibbackends_a-svgout.o

The Dockerfile below shows these minor changes. It also takes further steps, installing the shared library libmplib.so in /usr/lib, and installing the mplib.h header file in /usr/include.

  
  # Base image
  FROM ubuntu:16.04
  MAINTAINER Paul Murrell <paul@stat.auckland.ac.nz>
  
  RUN apt-get update && \
      apt-get install -y \
          subversion
  
  # Checkout MetaPost source
  RUN mkdir /opt/MetaPost && \
      cd /opt/MetaPost && \
      svn co --username anonsvn --password anonsvn --trust-server-cert https://serveur-svn.lri.fr/svn/modhel/metapost/trunk 
  
  # Build MetaPost
  RUN apt-get update && \
      apt-get install -y \
          gcc \
          g++ \
          make \
          texinfo
  RUN cd /opt/MetaPost/trunk/ && \
      CFLAGS=-fpic ./build.sh
  
  # Build MetaPost shared library
  RUN cd /opt/MetaPost/trunk/build/texk/web2c && \
      gcc -shared -o libmplib.so libmplib*.o mplibdir/libmputil*.o \
          -L/opt/MetaPost/trunk/build/libs/libpng -lpng \
          -L/opt/MetaPost/trunk/build/libs/cairo -lcairo \
          -L/opt/MetaPost/trunk/build/libs/pixman -lpixman \
          -L/opt/MetaPost/trunk/build/libs/mpfr -lmpfr \
          -L/opt/MetaPost/trunk/build/libs/gmp -lgmp \ 
          -L/opt/MetaPost/trunk/build/texk/kpathsea/.libs -lkpathsea \
          -L/opt/MetaPost/trunk/build/libs/zlib -lz \
          -lm
  
  # INSTALL MetaPost shared library 
  RUN cp /opt/MetaPost/trunk/build/texk/web2c/libmplib.so /usr/lib && \
      cp /opt/MetaPost/trunk/build/texk/web2c/mplib.h /usr/include

With this set up, building our test program is as simple as the following ...

  gcc -c -fpic -o infinity-shared.o infinity.c
  gcc -o infinity-shared infinity-shared.o -lmplib
  ./infinity-shared
  (0,0)..controls (7.86894,0) and (12.1311,10)
   ..(20,10)..controls (26.6667,10) and (26.6667,0)
   ..(20,0)..controls (12.1311,0) and (7.86894,10)
   ..(0,10)..controls (-6.66667,10) and (-6.66667,0)
   ..cycle;

Building from TeX Live source

A similar approach can be used to build MetaPost libraries from a full set of TeX Live source code (rather than just the MetaPost subset). One difference is that the build script is called Build instead of build.sh, and another is that the build files are generated within a directory called Work instead of a directory called build, but otherwise the process is very similar. An obvious disadvantage to using the complete TeX Live source is that the download is much larger and the build is longer and produces more files. However, a complete TeX Live build may be useful for supporting a wider range of MetaPost images (e.g., MetaPost images that include TeX-formatted text labels).

4. Summary

This report describes a series of steps that can be used to build a shared library for MetaPost. This makes it possible to write a new program that can make use of the powerful curve description facilities of MetaPost. The series of steps is provided in code form as a Dockerfile so that a Docker container that contains the MetaPost shared library can be generated easily.

5. Technical requirements

The examples and discussion in this document relate to the subversion archive of the development version of the MetaPost source code, plus a Dockerfile that describes the construction of a static MetaPost library and a Dockerfile that describes the construction of a shared MetaPost library.

This report was generated within a Docker container (see Resources section below).

6. Resources

How to cite this document

Murrell, P. (2018). "Building an mplib Shared Library" Technical Report 2018-10, Department of Statistics, The University of Auckland. [ bib ]

7. References

[Hobby, 1998]
Hobby, J. (1998). A User's Manual for MetaPost. [ bib ]
[Hobby and the MetaPost development team, 2018]
Hobby, J. D. and the MetaPost development team (2018). METAPOST a user’s manual. [ bib | .pdf ]
[Hoekwater, 2006]
Hoekwater, T. (2006). MetaPost developments - autumn 2006. TUGboat, 27(1):18--21. [ bib | .pdf ]
[Hoekwater, 2008]
Hoekwater, T. (2008). MetaPost developments: Mplib project report. TUGboat, 29(3):380--382. [ bib | .pdf ]
[Hoekwater and Hagen, 2007]
Hoekwater, T. and Hagen, H. (2007). MPlib: MetaPost as a reusable component. TUGboat, 28(3):317--318. [ bib | .pdf ]
[Hoekwater and Scarso, 2018]
Hoekwater, T. and Scarso, L. (2018). MPlib API documentation, version 2.00. [ bib | .pdf ]
[Ierusalimschy et al., 1996]
Ierusalimschy, R., de Figueiredo, L. H., and Filho, W. C. (1996). Lua - an extensible extension language. Softw. Pract. Exper., 26(6):635--652. [ bib | DOI | http ]
[Merkel, 2014]
Merkel, D. (2014). Docker: Lightweight linux containers for consistent development and deployment. Linux J., 2014(239). [ bib | http ]
[The LuaTeX development team, 2017]
The LuaTeX development team (2017). LuaTeX Reference Manual. [ bib | .pdf ]

Creative Commons License
This document by Paul Murrell is licensed under a Creative Commons Attribution 4.0 International License.