by Paul Murrell
http://orcid.org/0000-0002-3224-8858
Version 1: Wednesday 03 October 2018

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.
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.
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;
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;
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).
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.
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).
Murrell, P. (2018). "Building an mplib Shared Library" Technical Report 2018-10, Department of Statistics, The University of Auckland. [ bib ]

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