Perhaps I should be moving to gentoo

I'm reading PDFs quite a bit, most of them in my beloved zathura. So, I was dismayed when today I paged through a book that showed in zathura as on the left side of this figure:

Renderings of a PDF in poppler and mupdf.

The metrics are off so badly that readability suffers.

Rather than try to fix the PDF, I remembered I had for a long time wanted to look into using mupdf as a backend for zathura rather than its default poppler, if only because poppler used to have a shocking amount of rather serious bugs a couple of years ago (now that I think of it: It's been a while since I last heard of any – hm).

Bringing up the PDF in mupdf looked a lot better (the right panel in the above figure). Which then led to a bout of yak shaving, because there is a plugin for zathura that promised to do what I wanted, zathura-pdf-mupdf, but of course nobody has bothered to package it up for Debian yet. So… let's try to build it.

It's probably not a good sign that the package's README talks about make to build the thing, whereas the web page talks about a build system with commands meson and ninja (that, frankly, I had never heard about before, but at least it's in Debian). But, never mind, let's do meson build && cd build && ninjia (oh wow).

Of course, building fails with something like:

../zathura-pdf-mupdf/index.c: In function ‘build_index’:
../zathura-pdf-mupdf/index.c:68:7: error: unknown type name ‘fz_location’; did you mean ‘fz_catch’?
       fz_location location = fz_resolve_link(ctx, document, outline->uri, &x, &y);
       ^~~~~~~~~~~

A quick web search shows that this fz_location is part of the mupdf API and has indeed undergone an API change. So, I backported libmupdf from Debian testing (I'm on Debian stable almost always), and because that needs mujs, I backported that, too. Mujs sounds a lot like javascript in PDF, and that's where I first think gentoo: with its USE flags it would proabably make it easier to just keep javascript out of my PDF rendering engines altogether. Which is something I'd consider an excellent idea.

Anyway, with a bit of hacking around – I don't have a libmupdf-third library that the meson build file mentions but perhaps doesn't need any more – I then got the plugin to build.

Regrettably, zathura still would not use mupdf to render, saying:

error: Could not load plugin '/usr/lib/i386-linux-gnu/zathura/libpdf-mupdf.so'
(/usr/lib/i386-linux-gnu/zathura/libpdf-mupdf.so:
undefined symbol: jpeg_resync_to_restart).

Again asking a search engine about typical scenearios that would lead to this failure when loading a plugin, there's quite a bit of speculation, one of it being about using libjpeg-turbo instead of libjpeg. Which made me see what this plugin links again. Fasten your seat belts:

$ ldd /usr/lib/i386-linux-gnu/zathura/libpdf-mupdf.so
        linux-gate.so.1 (0xf7fa7000)
        libgirara-gtk3.so.3 => /usr/lib/i386-linux-gnu/libgirara-gtk3.so.3 (0xf5d23000)
        libcairo.so.2 => /usr/lib/i386-linux-gnu/libcairo.so.2 (0xf5bd3000)
        libglib-2.0.so.0 => /usr/lib/i386-linux-gnu/libglib-2.0.so.0 (0xf5a9a000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf58bc000)
        libgtk-3.so.0 => /usr/lib/i386-linux-gnu/libgtk-3.so.0 (0xf50bc000)
        libgdk-3.so.0 => /usr/lib/i386-linux-gnu/libgdk-3.so.0 (0xf4fae000)
        libpango-1.0.so.0 => /usr/lib/i386-linux-gnu/libpango-1.0.so.0 (0xf4f5f000)
        libgio-2.0.so.0 => /usr/lib/i386-linux-gnu/libgio-2.0.so.0 (0xf4d57000)
        libgobject-2.0.so.0 => /usr/lib/i386-linux-gnu/libgobject-2.0.so.0 (0xf4cf2000)
        libjson-c.so.3 => /usr/lib/i386-linux-gnu/libjson-c.so.3 (0xf4ce5000)
        libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xf4cc2000)
        libpixman-1.so.0 => /usr/lib/i386-linux-gnu/libpixman-1.so.0 (0xf4c12000)
        libfontconfig.so.1 => /usr/lib/i386-linux-gnu/libfontconfig.so.1 (0xf4bc5000)
        libfreetype.so.6 => /usr/lib/i386-linux-gnu/libfreetype.so.6 (0xf4b02000)
        libpng16.so.16 => /usr/lib/i386-linux-gnu/libpng16.so.16 (0xf4ac3000)
        libxcb-shm.so.0 => /usr/lib/i386-linux-gnu/libxcb-shm.so.0 (0xf4abe000)
        libxcb.so.1 => /usr/lib/i386-linux-gnu/libxcb.so.1 (0xf4a90000)
        libxcb-render.so.0 => /usr/lib/i386-linux-gnu/libxcb-render.so.0 (0xf4a81000)
        libXrender.so.1 => /usr/lib/i386-linux-gnu/libXrender.so.1 (0xf4a75000)
        libX11.so.6 => /usr/lib/i386-linux-gnu/libX11.so.6 (0xf4926000)
        libXext.so.6 => /usr/lib/i386-linux-gnu/libXext.so.6 (0xf4911000)
        libz.so.1 => /lib/i386-linux-gnu/libz.so.1 (0xf48f0000)
        librt.so.1 => /lib/i386-linux-gnu/librt.so.1 (0xf48e5000)
        libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xf47df000)
        libpcre.so.3 => /lib/i386-linux-gnu/libpcre.so.3 (0xf4766000)
        /lib/ld-linux.so.2 (0xf7fa9000)
        libgmodule-2.0.so.0 => /usr/lib/i386-linux-gnu/libgmodule-2.0.so.0 (0xf4760000)
        libpangocairo-1.0.so.0 => /usr/lib/i386-linux-gnu/libpangocairo-1.0.so.0 (0xf4750000)
        libXi.so.6 => /usr/lib/i386-linux-gnu/libXi.so.6 (0xf473d000)
        libXcomposite.so.1 => /usr/lib/i386-linux-gnu/libXcomposite.so.1 (0xf4739000)
        libXdamage.so.1 => /usr/lib/i386-linux-gnu/libXdamage.so.1 (0xf4734000)
        libXfixes.so.3 => /usr/lib/i386-linux-gnu/libXfixes.so.3 (0xf472d000)
        libcairo-gobject.so.2 => /usr/lib/i386-linux-gnu/libcairo-gobject.so.2 (0xf4721000)
        libgdk_pixbuf-2.0.so.0 => /usr/lib/i386-linux-gnu/libgdk_pixbuf-2.0.so.0 (0xf46f4000)
        libatk-1.0.so.0 => /usr/lib/i386-linux-gnu/libatk-1.0.so.0 (0xf46cb000)
        libatk-bridge-2.0.so.0 => /usr/lib/i386-linux-gnu/libatk-bridge-2.0.so.0 (0xf4693000)
        libxkbcommon.so.0 => /usr/lib/i386-linux-gnu/libxkbcommon.so.0 (0xf464d000)
        libwayland-cursor.so.0 => /usr/lib/i386-linux-gnu/libwayland-cursor.so.0 (0xf4644000)
        libwayland-egl.so.1 => /usr/lib/i386-linux-gnu/libwayland-egl.so.1 (0xf463f000)
        libwayland-client.so.0 => /usr/lib/i386-linux-gnu/libwayland-client.so.0 (0xf4630000)
        libepoxy.so.0 => /usr/lib/i386-linux-gnu/libepoxy.so.0 (0xf451e000)
        libharfbuzz.so.0 => /usr/lib/i386-linux-gnu/libharfbuzz.so.0 (0xf4407000)
        libpangoft2-1.0.so.0 => /usr/lib/i386-linux-gnu/libpangoft2-1.0.so.0 (0xf43ee000)
        libXinerama.so.1 => /usr/lib/i386-linux-gnu/libXinerama.so.1 (0xf43e7000)
        libXrandr.so.2 => /usr/lib/i386-linux-gnu/libXrandr.so.2 (0xf43da000)
        libXcursor.so.1 => /usr/lib/i386-linux-gnu/libXcursor.so.1 (0xf43cd000)
        libthai.so.0 => /usr/lib/i386-linux-gnu/libthai.so.0 (0xf43c1000)
        libfribidi.so.0 => /usr/lib/i386-linux-gnu/libfribidi.so.0 (0xf43a5000)
        libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xf439f000)
        libmount.so.1 => /lib/i386-linux-gnu/libmount.so.1 (0xf4333000)
        libselinux.so.1 => /lib/i386-linux-gnu/libselinux.so.1 (0xf4306000)
        libresolv.so.2 => /lib/i386-linux-gnu/libresolv.so.2 (0xf42ec000)
        libffi.so.6 => /usr/lib/i386-linux-gnu/libffi.so.6 (0xf42e2000)
        libexpat.so.1 => /lib/i386-linux-gnu/libexpat.so.1 (0xf42a5000)
        libuuid.so.1 => /lib/i386-linux-gnu/libuuid.so.1 (0xf429b000)
        libXau.so.6 => /usr/lib/i386-linux-gnu/libXau.so.6 (0xf4296000)
        libXdmcp.so.6 => /usr/lib/i386-linux-gnu/libXdmcp.so.6 (0xf428f000)
        libdbus-1.so.3 => /lib/i386-linux-gnu/libdbus-1.so.3 (0xf4230000)
        libatspi.so.0 => /usr/lib/i386-linux-gnu/libatspi.so.0 (0xf41fb000)
        libgraphite2.so.3 => /usr/lib/i386-linux-gnu/libgraphite2.so.3 (0xf41cd000)
        libdatrie.so.1 => /usr/lib/i386-linux-gnu/libdatrie.so.1 (0xf41c3000)
        libblkid.so.1 => /lib/i386-linux-gnu/libblkid.so.1 (0xf4163000)
        libbsd.so.0 => /usr/lib/i386-linux-gnu/libbsd.so.0 (0xf4144000)
        libsystemd.so.0 => /lib/i386-linux-gnu/libsystemd.so.0 (0xf4099000)
        liblzma.so.5 => /lib/i386-linux-gnu/liblzma.so.5 (0xf406d000)
        liblz4.so.1 => /usr/lib/i386-linux-gnu/liblz4.so.1 (0xf404d000)
        libgcrypt.so.20 => /lib/i386-linux-gnu/libgcrypt.so.20 (0xf3f6a000)
        libgpg-error.so.0 => /lib/i386-linux-gnu/libgpg-error.so.0 (0xf3f45000)

Now, I appreciate that glueing two pieces of relatively complex code together can be a bit involved, but: 69 libraries!? Among them Xrandr, Xinerama, wayland (which I don't use), systemd (even if I used it: what would that plugin have to do with it?), gpg-error, selinux, and then some things I've never heard about.

I'm sorry, but this is wrong. Which makes me think hard if gentoo's USE flags might not really be the way to go in this day and age of exploding dependencies.

Holy cow.

In case you came here from a search engine that hit on one of the error messages: After writing this, I was hungry and let it sit. The one thing I can tell you is that the elusive jpeg_resync_to_restart is in libjpeg, indeed. What I don't know yet is why that library hasn't made it into the heap of libraries that the plugin links to.

I'd suspect that zathura and mupdf are built against different libjegs – but then I'd have to explain how that would have happened. Hm.

Update: Well, I couldn't let it sit, so here's what I needed to do (and I suspect I'd have found it in one of the upstream branches):

  1. The libjpeg thing really is that the libjpeg library needs to be linked into the plugin in Debian; the details I can't quite work out, because I'd say the linker should be able to work that out, but clearly it's not, because the situation is actually being cared for in the plugin's meson file. However, you need to manually flip a switch: this would be a ./configure run in autoconf, but here, the command line is:

    meson setup --wipe -D link-external=true  build
    
  2. However, one link-time dependency is missing with the mupdf from Debian bullseye, and that's the spooky mujs. To fix this, patch the build file like so:

    diff --git a/meson.build b/meson.build
    index 23cdc6a..24929ca 100644
    --- a/meson.build
    +++ b/meson.build
    @@ -19,8 +19,8 @@ zathura = dependency('zathura', version: '>=0.3.9')
     girara = dependency('girara-gtk3')
     glib = dependency('glib-2.0')
     cairo = dependency('cairo')
    +mujs = dependency('mujs')
     mupdf = cc.find_library('mupdf')
    -mupdfthird = cc.find_library('mupdf-third')
    
     build_dependencies = [
       zathura,
    @@ -28,7 +28,7 @@ build_dependencies = [
       glib,
       cairo,
       mupdf,
    -  mupdfthird
    +  mujs,
     ]
    
     if get_option('link-external')
    
  3. After a sudo ninja install, zathura will still prefer the poppler plugin (well, it did that for me). I've not attempted to figure out a smart way to change this preference and for now just move moved libpdf-poppler.so in /usr/lib/i386-linux-gnu/zathura out of the way (to libpdf-poppler.no-so, if you want to know).

Kategorie: edv