mupdf/ffi.lisp
libmupdf could not be loaded. The Nix environment puts ~
it on LD_library_PATH and sets luv_MUPDF_LIBDIR; outside it, pass a directory ~
to load-mupdf.
Loading
The same pinning libav uses and for the same reason: CFFI explodes
LD_library_PATH itself before consulting the system loader, so an
unqualified soname is free to find some other MuPDF that happens to be
installed.
Names to try, most portable first. MuPDF does not version its soname the way the libav libraries do, so there is no major number to build one from.
Load libmupdf, from directory or luv_MUPDF_LIBDIR or the platform search.
The two layouts
A transform and a rectangle, passed and returned by value. MuPDF's matrix is the usual six-element affine one, laid out so that a point maps to (a*x + c*y + e, b*x + d*y + f).
Contexts
FZ_NEW_context is a macro that pins the header's version string into the
call, so the library can refuse a caller compiled against a different
one. There is no function to ask the library what version it is -- the
string only exists as a macro -- so the caller has to say it out loud,
and the mismatch comes back as a null context rather than as a throw,
which is the one MuPDF failure this binding can see coming.
The version is read off the pinned library directory, which in the Nix
environment is a store path with the version in its name. When that
fails, *MUPDF-VERSION* is the place to say it by hand.
The version in a store path like .../by3piv…-mupdf-1.27.2/lib.
Documents and pages
Pixels
Structured text
The one part of MuPDF whose layout this system has to know, because it is a linked structure walked field by field rather than an opaque handle. Those records gain fields between releases, so the offsets are groveled from the headers in ABI.LISP rather than written down here.
Buffers and output
MuPDF will serialize a structured-text page to XML itself, and that is what this binding reads instead of walking the FZ_STEXT_* records field by field. Those records gain fields between releases and would have to be groveled; the XML is a documented output format, and asking for it keeps every C object in this system opaque. No headers, no offsets.
(pathname &key context)The library band: eyebrow, site name, and the three doors.
(&optional directory)Names to try, most portable first. MuPDF does not version its soname the way the libav libraries do, so there is no major number to build one from.
Logical disjunction of tests and raw truth values.
Logical negation of one test or raw truth value.
A MuPDF context: its allocator, its store, and its error stack.
The FZ_VERSION string to claim, or NIL to read it from the library path.
(directory)Addition over compatible quantities.
An open document and the context it was opened in.
(&optional (text *page*) (name "test"))
Loading MuPDF, and the slice of its C ABI this system speaks.
Two things about MuPDF shape everything here.
The first is that its objects are opaque. A context, a document, a page, a pixmap are all pointers you never look inside; everything you want from them comes back through an accessor function. That is why this binding declares almost no C structure layouts: only FZ-MATRIX and FZ-RECT, which are passed and returned by value and are six floats and four.
The second is that it reports failure by longjmp. FZ_THROW unwinds to the nearest FZ_TRY, and a C library's setjmp is not something a Lisp can stand in the middle of, so this binding does not try. With no handler on the stack MuPDF prints the error and aborts the process -- which means a malformed document takes the image with it.
open-documenttherefore refuses anything that does not begin with a PDF header, which is a cheap guard against the common case rather than a real answer. The real answer is a C shim holding FZ_TRY, and it is not written yet.