Introduction
MuPDF Android App Kit
MuPDF iOS App Kit
Download App Kit

PDF Viewing

Presenting a Document

There are two fundamental ways of presenting a document to the screen. One way is to use the Default UI which includes an in-built user interface. The alternative is to load the document into a dedicated document view and provide your own Custom UI with listener methods for your document control.

File access

In order for an Android App to be able to work with files on the device then read & write permissions need to be enabled.

To do this your application manifest should include the following:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

AndroidManifest.xml

Further to this your application should ask for permission from the user before attempting to present a document.

Default UI

The Default UI is an App Kit UI created by Artifex which includes a user-interface for typical document features and actions. It is presented at the top of the document view and accommodates for both tablet and phone layout.

The Default UI aims to deliver a handy way of allowing for document viewing & manipulation without the need to provide your own Custom UI.

Default UI and NUIActivity

When using the Default UI it must be launched in it's own Activity which extends NUIActivity. This ensures that the UI's icon buttons operate as expected.

At a minimum your NUIActivity should be responsible for setting up the Activity lifecyle interfaces previously explained.

Kotlin
import android.os.Bundle
import com.artifex.sonui.editor.NUIActivity
import com.artifex.sonui.editor.Utilities
import com.artifex.solib.*

class DefaultUIActivity : NUIActivity() {

    public override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        Utilities.setDataLeakHandlers(MyOwnDataLeakHandlers())
        Utilities.setPersistentStorage(MyOwnPersistentStorage())
        ArDkLib.setClipboardHandler(MyOwnClipboardHandler())
        ArDkLib.setSecureFS(MyOwnSecureFS())
        FileUtils.init(this)
    }

}
Java
import android.os.Bundle;
import com.artifex.sonui.editor.NUIActivity;
import com.artifex.sonui.editor.Utilities;
import com.artifex.solib.*;

public class DefaultUIActivity extends NUIActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Utilities.setDataLeakHandlers(new MyOwnDataLeakHandlers());
        Utilities.setPersistentStorage(new MyOwnPersistentStorage());
        ArDkLib.setClipboardHandler(new MyOwnClipboardHandler());
        ArDkLib.setSecureFS(new MyOwnSecureFS());
        FileUtils.init(this);
    }

}

Once your version of NUIActivity is ready an application developer should set the document's Uri in the intent's data payload and start their activity.

Kotlin
fun presentDocument(documentUri:Uri) {
    val intent:Intent = Intent(this, DefaultUIActivity::class.java)
    intent.action = Intent.ACTION_VIEW
    intent.data = documentUri
    startActivity(intent)
}
Java
public void presentDocument(Uri documentUri) {
    Intent intent =  new Intent(this, DefaultUIActivity.class);
    intent.setAction(Intent.ACTION_VIEW);
    intent.setData(uri);
    startActivity(intent);
}

Once configured, then your document view activity, powered by NUIActivity, will appear as follows:

default-ui default-ui
The default UI for File operations The default UI for Annotate operations

Application theme

When using the Default UI the theme for the application should at least extend Theme.AppCompat.DayNight.NoActionBar. This will ensure the correct layout for the UI icons.

Therefore ensure to have a base theme setup in the themes.xml file as follows:

<style name="AppBaseTheme" parent="Theme.AppCompat.DayNight.NoActionBar"></style>

res/themes/themes.xml

Then ensure to reference this in your main application manifest:

android {
    <application
        android:theme="@style/AppBaseTheme
        ...
        ">
}

AndroidManifest.xml

Obviously an application developer can then extend the theme as required.

Configuration options

When using the Default UI an application developer can optionally set certain configurable features. This should be done by instantiating a ConfigOptions object and then setting the required values against the keys within. Once complete the newly created ConfigOptions object should be set against ArDkLib.

NOTE
The configuration options should be set before a document is opened.

Kotlin
import com.artifex.solib.ConfigOptions

// Set a thin purple line for annotation ink drawing
val configOptions = ConfigOptions()
configOptions.defaultPdfInkAnnotationDefaultLineThickness = 2.0f
configOptions.defaultPdfInkAnnotationDefaultLineColor = 0xffff00ff.toInt()
ArDkLib.setAppConfigOptions(configOptions)
Java
import com.artifex.solib.ConfigOptions;

// Set a thin purple line for annotation ink drawing
ConfigOptions configOptions = new ConfigOptions();
configOptions.setDefaultPdfInkAnnotationDefaultLineThickness(2.0f);
configOptions.setDefaultPdfInkAnnotationDefaultLineColor(0xffff00ff);
ArDkLib.setAppConfigOptions(configOptions);

App Config options

Key Value type Default value Details
setAllowAutoOpen boolean true Records the state of an open doc, in case the app exits. If it does, the app can 'auto-open' the last document which was being worked on.
setAnimationFeatureEnabled boolean true
setAppAuthEnabled boolean false Determines if user authentication is required.
setAppAuthTimeout int 30 Timeout value (seconds) in relation to user authentication.
setCustomSaveEnabled boolean false Related to the custom save button
setDefaultPdfInkAnnotationDefaultLineColor int 0 Sets the ink annotation line color
setDefaultPdfInkAnnotationDefaultLineThickness float 0 Sets the ink annotation line thickness. Note, if set to zero then the DocumentView will automatically readjust this and set it to 4.5.
setDocAuthEntryEnabled boolean true enables or disables the ability to modify a document's author. Used with Track Changes.
setEditingEnabled boolean true enables or disables editing mode
setExtClipboardInEnabled boolean true enables or disables the ability to paste from the external clipboard
setExtClipboardOutEnabled boolean true enables or disables the ability to copy to the external clipboard
setFeatureTracker FeatureTracker null
setFormFillingEnabled boolean false enables or disables form filling on a PDF
setFormSigningFeatureEnabled boolean false enables or disables form signing on a PDF
setFullscreenEnabled boolean false Sets whether full screen mode is allowed or not
setImageInsertEnabled boolean true enables or disables image insertion on a PDF
setInvertContentInDarkModeEnabled boolean false enables or disables the PDF content to invert in dark mode - this also affects images as well as text and page background colors
setLaunchUrlEnabled boolean true enables or disables opening of URLs from PDFs
setNonRepudiationCertOnlyFilterEnabled boolean false
setOpenInEnabled boolean true enables or disables "Open In" for external applications
setOpenPdfInEnabled boolean true enables or disables "Open PDF In" for external applications
setPDFAnnotationEnabled boolean true enables or disables PDF annotations
setPhotoInsertEnabled boolean true enables or disables photo insertion
setPrintingEnabled boolean true enables or disables printing
setRedactionsEnabled boolean false enables or disables redactions
setSaveAsEnabled boolean true enables or disables "Save As"
setSaveAsPdfEnabled boolean true enables or disables "Save As PDF"
setSaveEnabled boolean true enables or disables "Save"
setSecurePrintingEnabled boolean false enables or disables secure printing
setShareEnabled boolean true enables or disables document sharing
setShowUI boolean true shows the Default UI when the document opens or not
setTrackChangesFeatureEnabled boolean false Enables or disables the Track Changes feature.
setUsePersistentFileState boolean true Enables or disables a feature where the App Kit keeps track of the state of your editing session, and restores it when you re-open the document, or when recovering from a crash.

Custom UI

Providing a custom UI means that the application developer is responsible for providing their own UI and functionality. This approach involves document presentation within an instance of DocumentView.

Considering that you have a valid document Uri from a Uri File instance, an application developer should initialize the document as explained in Starting a Document View.

NOTE
Don't forget to attach the DocumentView to your view hierarchy and set it's metrics to fit the area you need for display.

Starting a Document View

A DocumentView is the central class which MuPDF uses for document display and is required to be imported and instantiated.

An application developer will typically set this up in their relevant Activity layout as follows:

<com.artifex.sonui.editor.DocumentView
        android:id="@+id/doc_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
</com.artifex.sonui.editor.DocumentView>

The start() method for DocumentView allows 3 parameters as follows:

  • uri: Uri document URI
  • page: Int the document page to start viewing from (Note: if this value is out of bounds then the document will render to the nearest available page)
  • showUI: Bool indicates whether to render and use the Default UI on top of the document view or not - for the Custom UI this should always be set to false
Kotlin
import com.artifex.sonui.editor.DocumentView

fun presentDocument(documentUri:Uri) {
    val documentView:DocumentView = findViewById(R.id.doc_view)
    documentView.start(documentUri, 0, false)
}
Java
import com.artifex.sonui.editor.DocumentView;

public void presentDocument(Uri documentUri) {
    DocumentView documentView = findViewById(R.id.doc_view);
    documentView.start(documentUri, 0, false);
}

NOTE
Don't forget that your Activity should also be responsible for setting up the Activity lifecyle interfaces previously explained.

You should also adhere to the document listeners which allow for feedback against document events.

File editing

If your custom UI requires document editing features then you must ensure that your activity or fragment has implemented the Activity lifecyle events for the document view to support text and annotation selection.

NOTE
Without these activity events being present then any attempt to edit a document will result in an application exception.

Going to a page

Once a document is loaded an application developer can view pages either by scrolling the document view or by using the App Kit API as follows:

Kotlin
// note: page number is zero-indexed,
// thus this would show page 8 of your document
documentView.goToPage(7)
Java
// note: page number is zero-indexed,
// thus this would show page 8 of your document
documentView.goToPage(7);

In the code sample above documentView refers to the instance of your DocumentView. Furthermore this API should only be called after the document has initially loaded and had it's first render (see Document Listeners - Document completed).

Viewing full-screen

In order to view a document in full-screen, it is up to the application developer to hide any UI which has been presented, and set the frame of the document view to fill the screen. Once that's done, calling enterFullScreen and passing a Runnable to it will invoke the full-screen mode. When the user taps to exit full-screen mode, the Runnable will be invoked, at which time the UI and frame should be restored to their previous state.

Kotlin
documentView?.enterFullScreen({
    // restore our UI
})
Java
if (documentView != null) {
    documentView.enterFullScreen(new Runnable() {
        @Override
        public void run() {
            // restore our UI

        }
    });
}

Viewing the page list

A handy way of showing or hiding the page navigator in your custom UI can be utilized with the following methods:

Kotlin
// show
documentView?.showPageList()

// hide
documentView?.hidePageList()
Java
// show
if (documentView != null) {
    documentView.showPageList();
}

// hide
if (documentView != null) {
    documentView.hidePageList();
}