Search code examples
androidpdfairair-native-extensionmupdf

Writing an AIR Native Extension using MuPDF render engine


I'm experimenting on using MuPDF as render engine for AIR targeting Android and iOS.

To achieve this, I'm trying to make an AIRNativeExtension (.ane).

The usecase for the .ane is like this:

  • open a single page .pdf

  • render a (possibly zoomed) section of the pdf to a bitmap and pass it back to AIR.

  • I don't need a full pdf reader app with ui and all the fancy extra stuff like outline and annotations etc.

So ar, I sucessfully checked out and built MuPDF for Android and coded all the 'glue' to communicate with ActionScript. But integrating MuPDF in my project is giving me a hard time. I tried to follow this thread here, but it's a bit outdated, and many of the classes/paths decribed there obviously changed over the last months and it's hard to find any guidance about the java-side implementation of the library

So I ended up exporting the hole mupdf android as jar, resulting in a 28 megs big file, which is a bit large for me to use. So I have to reduce it to the minimal.

Now the questions:

The approach above throws me an exception while executing:

Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/artifex/mupdfdemo/MuPDFCore;

How can I fix this? Or is this a bad idea, at all?

Is it possible to strip down MuPDF, so I can import only the parts I need?

Is rendering pdf for air this way possible at all? Has anybody tried this before?

Many thanks!

P.S. I'm pasting only a small part of my code here, as the other stuff is simply the basic .ane setup and nothing specific is done in here. Also, I stripped everything not needed to reproduce the error, so the code actually does nothing, really. As soon it's working, I will share this project as open source, of course.

package com.skill.ane.pdfrenderer;

import android.util.Log;

import com.adobe.fre.FREContext;
import com.adobe.fre.FREFunction;
import com.adobe.fre.FREObject;
import com.artifex.mupdfdemo.MuPDFCore;

public class OpenFileFunction implements FREFunction {

    public static final String TAG = "OpenFileFunction";


    @Override
    public FREObject call(FREContext context, FREObject[] args) 
    {

        try
        {
            String path =  args[0].getAsString();

            try
            {
                MuPDFCore core = new MuPDFCore( context.getActivity().getBaseContext() , path);
                // do all the magic stuff in here.
            }
            catch (Exception e)
            {
                Log.e(TAG, Log.getStackTraceString( e ) );
                return null;
            }

        }
        catch(Exception e)
        {
            Log.e(TAG, Log.getStackTraceString( e ) );
        }

        return null;


    }

}

Solution

  • MuPDF is at heart a C library for opening/manipulating/rendering PDF (and other format) files. We also provide various example tools that wrap this central core library to expose the functionality in useful ways. These tools include various example viewers, such as the android one.

    The key thing here is that MuPDF is a C library, not a java one. This means that the ndk is used to produce a shared library object (libmupdf.so) that the (relatively) simple java classes can call.

    The technique for making a C library accessible from java is called 'JNI' (Java Native Interface). Effectively, by declaring certain functions as being 'native' in java, they will actually call down to entries in a shared library object compiled from the native code.

    When we wrote the Android viewer, we did not do a complete mirroring of the C API into java. Instead we took the short cut route of just exposing the parts we needed. All this code can be seen in the platform/android/jni/mupdf.c file. The methods here are sufficient for our simple viewer, but may not be ideal for what you need.

    We are working on a fuller JNI reflection layer, but this isn't released yet. Come visit us in the #ghostscript irc channel, and we can let you have early access to it if it will help you.

    I suspect that you will find it a lot easier to work with a clean JNI interface rather than the limited one that's there as part of the android viewer.

    Alternatively, you can always write your own JNI layer for just the MuPDF api functions you need.