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.