summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2010-12-02 18:52:55 +0000
committerRobin Watts <robin.watts@artifex.com>2010-12-02 18:52:55 +0000
commitcd7c8b147ad173f7071d3b1d115e913fc08c5cc3 (patch)
treea99ff136ac5458d54b1d1ee5f8856c8c46a3905e
parentb31a16f7bb711764fab3722898e1b459ca79a1e1 (diff)
downloadmupdf-cd7c8b147ad173f7071d3b1d115e913fc08c5cc3.tar.xz
Import Android demo.
-rw-r--r--android/AndroidManifest.xml23
-rw-r--r--android/ReadMe.txt94
-rw-r--r--android/build.properties17
-rw-r--r--android/build.sh1
-rw-r--r--android/build.xml84
-rw-r--r--android/default.properties11
-rw-r--r--android/jni/Android.mk21
-rw-r--r--android/jni/Application.mk3
-rw-r--r--android/jni/Core.mk110
-rw-r--r--android/jni/ThirdParty.mk150
-rw-r--r--android/jni/mupdf.c239
-rw-r--r--android/local.properties11
-rw-r--r--android/res/drawable-hdpi/icon.pngbin0 -> 2945 bytes
-rw-r--r--android/res/drawable-ldpi/icon.pngbin0 -> 1456 bytes
-rw-r--r--android/res/drawable-mdpi/icon.pngbin0 -> 1916 bytes
-rw-r--r--android/res/layout/main.xml8
-rw-r--r--android/res/values/strings.xml4
-rw-r--r--android/res/values/theme.xml7
-rw-r--r--android/src/com/artifex/mupdf/MuPDFActivity.java114
-rw-r--r--android/src/com/artifex/mupdf/PixmapView.java577
20 files changed, 1474 insertions, 0 deletions
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
new file mode 100644
index 00000000..f58ba5ca
--- /dev/null
+++ b/android/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.artifex.mupdf"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <supports-screens android:smallScreens="true"
+ android:normalScreens="true"
+ android:largeScreens="true"
+ android:anyDensity="true" />
+ <uses-sdk android:minSdkVersion="3" />
+ <application android:label="@string/app_name"
+ android:icon="@drawable/icon"
+ android:debuggable="true">
+ <activity android:name="MuPDFActivity"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.NoBackground.NoTitle">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/android/ReadMe.txt b/android/ReadMe.txt
new file mode 100644
index 00000000..2678d732
--- /dev/null
+++ b/android/ReadMe.txt
@@ -0,0 +1,94 @@
+To build/debug android build.
+
+1) Download the android sdk, and install it. On windows I unpacked it as:
+
+ C:\Program Files\android-sdk-windows
+
+on Macos as:
+
+ /Library/android-sdk-mac_x86
+
+On windows add: C:/Progra~1/android-sdk-windows/tools to your path.
+On linux/macos add the equivalent.
+
+2) Download the android ndk, and install in. On windows I unpacked it as:
+
+ C:\Program Files\android-ndk-r4b
+
+on Macos as:
+
+ /Library/android-ndk-r4b
+
+On windows add: C:/Progra~1/android-ndk-r4b to your patyh. On linux/macos
+add the equivalent.
+
+3) On windows, to use the ndk, you *must* be running under cygwin. This means
+you need to install Cygwin 1.7 or greater now.
+
+4) Bring up a shell, and run 'android'. This will bring up a graphical
+gui for the sdk. From here you can install the different SDK components
+for the different flavours of android. Download them all - bandwidth and disk
+space are cheap, right?
+
+5) Now go to the Virtual Devices entry on the right hand side. You need to
+create yourself an emulator image to use. Click 'New...' on the right hand
+side and a window will appear. Fill in the entries as follows:
+
+ Name: FroyoEm
+ Target: Android 2.2 - API Level 8
+ SD card: Size: 1024MiB
+ Skin: Resolution: 480x756 (756 just fits my macbook screen, but 800 may
+ be 'more standard')
+
+Click 'Create AVD' and wait for a minute or so while it is prepared. Now
+you can exit the GUI.
+
+6) Now we are ready to build mupdf for Android. Check out a copy of MuPDF
+(but you've done that already, cos you're reading this, right?). Get the
+thirdparty package from mupdf.com and unpack that into mupdf/thirdparty.
+Also get the pregen package from the same place and unpack that into
+mupdf/pregen.
+
+7) Load local.properties into your favourite editor, and edit the sdk
+path there as appropriate. This should be the only bit of localisation
+you need to do.
+
+8) Change into the android directory, and execute (in a Cygwin window on
+Windows!):
+
+ ndk-build
+
+This should build the native code portion. Then execute:
+
+ ant debug
+
+This should build the java wrapper.
+
+9) Now start the emulator by executing:
+
+ emulator -avd FroyoEm
+
+This will take a while to full start up (be patient).
+
+10) We now need to give the demo file something to chew on, so let's copy
+a file into the SD card image of the emulator (this should only need to be
+done once). With the emulator running type:
+
+ adb push ../../MyTests/pdf_reference17.pdf /mnt/sdcard/Download/test.pdf
+
+(where obviously ../../MyTests/pdf_reference17.pdf is altered for your
+machine).
+
+11) With the emulator running (see step 9), execute
+
+ ant install
+
+and that will copy MuPDF into the emulator where you can run it from the
+launchpad screen.
+
+12) To see debug messages from the emulator (including stdout/stderr from
+our app), execute:
+
+ adb logcat
+
+Good luck!
diff --git a/android/build.properties b/android/build.properties
new file mode 100644
index 00000000..edc7f230
--- /dev/null
+++ b/android/build.properties
@@ -0,0 +1,17 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
diff --git a/android/build.sh b/android/build.sh
new file mode 100644
index 00000000..36ad883f
--- /dev/null
+++ b/android/build.sh
@@ -0,0 +1 @@
+ndk-build && ant.bat install
diff --git a/android/build.xml b/android/build.xml
new file mode 100644
index 00000000..3ba2015b
--- /dev/null
+++ b/android/build.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="MuPDF" default="help">
+
+<!-- The local.properties file is created and updated by the 'android'
+ tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The build.properties file can be created by you and is never touched
+ by the 'android' tool. This is the place to change some of the
+ default property values used by the Ant rules.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="build.properties" />
+
+ <!-- The default.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <property file="default.properties" />
+
+ <!-- Custom Android task to deal with the project target, and import the
+ proper rules.
+ This requires ant 1.6.0 or above. -->
+ <path id="android.antlibs">
+ <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
+ </path>
+
+ <taskdef name="setup"
+ classname="com.android.ant.SetupTask"
+ classpathref="android.antlibs" />
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+ in between standard targets -->
+<!--
+ <target name="-pre-build">
+ </target>
+ <target name="-pre-compile">
+ </target>
+
+ [This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir}]
+ <target name="-post-compile">
+ </target>
+-->
+
+
+ <!-- Execute the Android Setup task that will setup some properties
+ specific to the target, and import the build rules files.
+
+ The rules file is imported from
+ <SDK>/platforms/<target_platform>/ant/ant_rules_r#.xml
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <setup> task.
+ - customize it to your needs.
+ - Customize the whole script.
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, *after* the <setup> task
+ - disable the import of the rules by changing the setup task
+ below to <setup import="false" />.
+ - customize to your needs.
+ -->
+ <setup />
+
+</project>
diff --git a/android/default.properties b/android/default.properties
new file mode 100644
index 00000000..0b9250e0
--- /dev/null
+++ b/android/default.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-8
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
new file mode 100644
index 00000000..66c69c9c
--- /dev/null
+++ b/android/jni/Android.mk
@@ -0,0 +1,21 @@
+TOP_LOCAL_PATH := $(call my-dir)
+
+MUPDF_ROOT := ../..
+
+include $(TOP_LOCAL_PATH)/Core.mk
+include $(TOP_LOCAL_PATH)/ThirdParty.mk
+
+LOCAL_PATH = $(TOP_LOCAL_PATH)
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/draw \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/fitz \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/mupdf
+LOCAL_MODULE := mupdf
+LOCAL_SRC_FILES := mupdf.c
+LOCAL_STATIC_LIBRARIES := mupdfcore mupdfthirdparty
+
+LOCAL_LDLIBS := -lm -llog -ljnigraphics
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/android/jni/Application.mk b/android/jni/Application.mk
new file mode 100644
index 00000000..16de5796
--- /dev/null
+++ b/android/jni/Application.mk
@@ -0,0 +1,3 @@
+# The ARMv7 is significanly faster due to the use of the hardware FPU
+APP_ABI := armeabi armeabi-v7a
+APP_OPTIM := debug
diff --git a/android/jni/Core.mk b/android/jni/Core.mk
new file mode 100644
index 00000000..02ad14c2
--- /dev/null
+++ b/android/jni/Core.mk
@@ -0,0 +1,110 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+MY_ROOT := ../..
+
+LOCAL_CFLAGS := \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/jbig2dec \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/openjpeg/libopenjpeg \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/jpeg \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/zlib \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/freetype/include \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/draw \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/fitz \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/mupdf
+
+LOCAL_MODULE := mupdfcore
+LOCAL_SRC_FILES := \
+ $(MY_ROOT)/fitz/base_error.c \
+ $(MY_ROOT)/fitz/base_geometry.c \
+ $(MY_ROOT)/fitz/base_getopt.c \
+ $(MY_ROOT)/fitz/base_hash.c \
+ $(MY_ROOT)/fitz/base_memory.c \
+ $(MY_ROOT)/fitz/base_string.c \
+ $(MY_ROOT)/fitz/base_time.c \
+ $(MY_ROOT)/fitz/crypt_aes.c \
+ $(MY_ROOT)/fitz/crypt_arc4.c \
+ $(MY_ROOT)/fitz/crypt_md5.c \
+ $(MY_ROOT)/fitz/dev_bbox.c \
+ $(MY_ROOT)/fitz/dev_draw.c \
+ $(MY_ROOT)/fitz/dev_list.c \
+ $(MY_ROOT)/fitz/dev_null.c \
+ $(MY_ROOT)/fitz/dev_text.c \
+ $(MY_ROOT)/fitz/dev_trace.c \
+ $(MY_ROOT)/fitz/filt_basic.c \
+ $(MY_ROOT)/fitz/filt_dctd.c \
+ $(MY_ROOT)/fitz/filt_faxd.c \
+ $(MY_ROOT)/fitz/filt_flate.c \
+ $(MY_ROOT)/fitz/filt_jbig2d.c \
+ $(MY_ROOT)/fitz/filt_jpxd.c \
+ $(MY_ROOT)/fitz/filt_lzwd.c \
+ $(MY_ROOT)/fitz/filt_predict.c \
+ $(MY_ROOT)/fitz/obj_array.c \
+ $(MY_ROOT)/fitz/obj_dict.c \
+ $(MY_ROOT)/fitz/obj_print.c \
+ $(MY_ROOT)/fitz/obj_simple.c \
+ $(MY_ROOT)/fitz/res_colorspace.c \
+ $(MY_ROOT)/fitz/res_font.c \
+ $(MY_ROOT)/fitz/res_path.c \
+ $(MY_ROOT)/fitz/res_pixmap.c \
+ $(MY_ROOT)/fitz/res_shade.c \
+ $(MY_ROOT)/fitz/res_text.c \
+ $(MY_ROOT)/fitz/stm_buffer.c \
+ $(MY_ROOT)/fitz/stm_open.c \
+ $(MY_ROOT)/fitz/stm_read.c \
+ $(MY_ROOT)/draw/archport.c \
+ $(MY_ROOT)/draw/blendmodes.c \
+ $(MY_ROOT)/draw/glyphcache.c \
+ $(MY_ROOT)/draw/imagedraw.c \
+ $(MY_ROOT)/draw/imagescale.c \
+ $(MY_ROOT)/draw/imagesmooth.c \
+ $(MY_ROOT)/draw/imageunpack.c \
+ $(MY_ROOT)/draw/meshdraw.c \
+ $(MY_ROOT)/draw/pathfill.c \
+ $(MY_ROOT)/draw/pathscan.c \
+ $(MY_ROOT)/draw/pathstroke.c \
+ $(MY_ROOT)/draw/porterduff.c \
+ $(MY_ROOT)/mupdf/pdf_annot.c \
+ $(MY_ROOT)/mupdf/pdf_build.c \
+ $(MY_ROOT)/mupdf/pdf_cmap.c \
+ $(MY_ROOT)/mupdf/pdf_cmap_load.c \
+ $(MY_ROOT)/mupdf/pdf_cmap_parse.c \
+ $(MY_ROOT)/mupdf/pdf_cmap_table.c \
+ $(MY_ROOT)/mupdf/pdf_colorspace.c \
+ $(MY_ROOT)/mupdf/pdf_crypt.c \
+ $(MY_ROOT)/mupdf/pdf_debug.c \
+ $(MY_ROOT)/mupdf/pdf_font.c \
+ $(MY_ROOT)/mupdf/pdf_fontagl.c \
+ $(MY_ROOT)/mupdf/pdf_fontenc.c \
+ $(MY_ROOT)/mupdf/pdf_fontfile.c \
+ $(MY_ROOT)/mupdf/pdf_fontmtx.c \
+ $(MY_ROOT)/mupdf/pdf_function.c \
+ $(MY_ROOT)/mupdf/pdf_image.c \
+ $(MY_ROOT)/mupdf/pdf_interpret.c \
+ $(MY_ROOT)/mupdf/pdf_lex.c \
+ $(MY_ROOT)/mupdf/pdf_nametree.c \
+ $(MY_ROOT)/mupdf/pdf_outline.c \
+ $(MY_ROOT)/mupdf/pdf_page.c \
+ $(MY_ROOT)/mupdf/pdf_pagetree.c \
+ $(MY_ROOT)/mupdf/pdf_parse.c \
+ $(MY_ROOT)/mupdf/pdf_pattern.c \
+ $(MY_ROOT)/mupdf/pdf_repair.c \
+ $(MY_ROOT)/mupdf/pdf_shade.c \
+ $(MY_ROOT)/mupdf/pdf_store.c \
+ $(MY_ROOT)/mupdf/pdf_stream.c \
+ $(MY_ROOT)/mupdf/pdf_type3.c \
+ $(MY_ROOT)/mupdf/pdf_unicode.c \
+ $(MY_ROOT)/mupdf/pdf_xobject.c \
+ $(MY_ROOT)/mupdf/pdf_xref.c \
+ $(MY_ROOT)/pregen/cmap_unicode.c \
+ $(MY_ROOT)/pregen/cmap_cns.c \
+ $(MY_ROOT)/pregen/cmap_gb.c \
+ $(MY_ROOT)/pregen/cmap_japan.c \
+ $(MY_ROOT)/pregen/cmap_korea.c \
+ $(MY_ROOT)/pregen/font_base14.c \
+ $(MY_ROOT)/pregen/font_cjk.c
+
+LOCAL_LDLIBS := -lm -llog -ljnigraphics
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/android/jni/ThirdParty.mk b/android/jni/ThirdParty.mk
new file mode 100644
index 00000000..6fa7ecf8
--- /dev/null
+++ b/android/jni/ThirdParty.mk
@@ -0,0 +1,150 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+MY_ROOT := ../..
+
+LOCAL_CFLAGS := \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/jbig2dec \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/openjpeg/libopenjpeg \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/jpeg \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/zlib \
+ -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/freetype/include \
+ -DFT2_BUILD_LIBRARY -DDARWIN_NO_CARBON -DHAVE_STDINT_H
+
+LOCAL_MODULE := mupdfthirdparty
+LOCAL_SRC_FILES := \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_arith.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_arith_int.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_arith_iaid.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_huffman.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_segment.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_page.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_symbol_dict.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_text.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_generic.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_refinement.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_mmr.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_image.c \
+ $(MY_ROOT)/thirdparty/jbig2dec/jbig2_metadata.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/bio.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/cio.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/dwt.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/event.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/image.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/j2k.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/j2k_lib.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/jp2.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/jpt.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/mct.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/mqc.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/openjpeg.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/pi.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/raw.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/t1.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/t2.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/tcd.c \
+ $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/tgt.c \
+ $(MY_ROOT)/thirdparty/jpeg/jaricom.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcapimin.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcapistd.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcarith.c \
+ $(MY_ROOT)/thirdparty/jpeg/jccoefct.c \
+ $(MY_ROOT)/thirdparty/jpeg/jccolor.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcdctmgr.c \
+ $(MY_ROOT)/thirdparty/jpeg/jchuff.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcinit.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcmainct.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcmarker.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcmaster.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcomapi.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcparam.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcprepct.c \
+ $(MY_ROOT)/thirdparty/jpeg/jcsample.c \
+ $(MY_ROOT)/thirdparty/jpeg/jctrans.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdapimin.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdapistd.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdarith.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdatadst.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdatasrc.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdcoefct.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdcolor.c \
+ $(MY_ROOT)/thirdparty/jpeg/jddctmgr.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdhuff.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdinput.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdmainct.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdmarker.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdmaster.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdmerge.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdpostct.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdsample.c \
+ $(MY_ROOT)/thirdparty/jpeg/jdtrans.c \
+ $(MY_ROOT)/thirdparty/jpeg/jerror.c \
+ $(MY_ROOT)/thirdparty/jpeg/jfdctflt.c \
+ $(MY_ROOT)/thirdparty/jpeg/jfdctfst.c \
+ $(MY_ROOT)/thirdparty/jpeg/jfdctint.c \
+ $(MY_ROOT)/thirdparty/jpeg/jidctflt.c \
+ $(MY_ROOT)/thirdparty/jpeg/jidctfst.c \
+ $(MY_ROOT)/thirdparty/jpeg/jidctint.c \
+ $(MY_ROOT)/thirdparty/jpeg/jquant1.c \
+ $(MY_ROOT)/thirdparty/jpeg/jquant2.c \
+ $(MY_ROOT)/thirdparty/jpeg/jutils.c \
+ $(MY_ROOT)/thirdparty/jpeg/jmemmgr.c \
+ $(MY_ROOT)/thirdparty/jpeg/jmemansi.c \
+ $(MY_ROOT)/thirdparty/zlib/adler32.c \
+ $(MY_ROOT)/thirdparty/zlib/compress.c \
+ $(MY_ROOT)/thirdparty/zlib/crc32.c \
+ $(MY_ROOT)/thirdparty/zlib/deflate.c \
+ $(MY_ROOT)/thirdparty/zlib/gzclose.c \
+ $(MY_ROOT)/thirdparty/zlib/gzlib.c \
+ $(MY_ROOT)/thirdparty/zlib/gzread.c \
+ $(MY_ROOT)/thirdparty/zlib/gzwrite.c \
+ $(MY_ROOT)/thirdparty/zlib/infback.c \
+ $(MY_ROOT)/thirdparty/zlib/inffast.c \
+ $(MY_ROOT)/thirdparty/zlib/inflate.c \
+ $(MY_ROOT)/thirdparty/zlib/inftrees.c \
+ $(MY_ROOT)/thirdparty/zlib/trees.c \
+ $(MY_ROOT)/thirdparty/zlib/uncompr.c \
+ $(MY_ROOT)/thirdparty/zlib/zutil.c \
+ $(MY_ROOT)/thirdparty/freetype/src/autofit/autofit.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftbase.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftbbox.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftbdf.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftbitmap.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftdebug.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftgasp.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftglyph.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftgxval.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftinit.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftlcdfil.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftmm.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftotval.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftpfr.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftstroke.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftsynth.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftsystem.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/fttype1.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftwinfnt.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftxf86.c \
+ $(MY_ROOT)/thirdparty/freetype/src/base/ftpatent.c \
+ $(MY_ROOT)/thirdparty/freetype/src/bdf/bdf.c \
+ $(MY_ROOT)/thirdparty/freetype/src/cache/ftcache.c \
+ $(MY_ROOT)/thirdparty/freetype/src/cff/cff.c \
+ $(MY_ROOT)/thirdparty/freetype/src/cid/type1cid.c \
+ $(MY_ROOT)/thirdparty/freetype/src/gzip/ftgzip.c \
+ $(MY_ROOT)/thirdparty/freetype/src/lzw/ftlzw.c \
+ $(MY_ROOT)/thirdparty/freetype/src/pcf/pcf.c \
+ $(MY_ROOT)/thirdparty/freetype/src/pfr/pfr.c \
+ $(MY_ROOT)/thirdparty/freetype/src/psaux/psaux.c \
+ $(MY_ROOT)/thirdparty/freetype/src/pshinter/pshinter.c \
+ $(MY_ROOT)/thirdparty/freetype/src/psnames/psnames.c \
+ $(MY_ROOT)/thirdparty/freetype/src/raster/raster.c \
+ $(MY_ROOT)/thirdparty/freetype/src/smooth/smooth.c \
+ $(MY_ROOT)/thirdparty/freetype/src/sfnt/sfnt.c \
+ $(MY_ROOT)/thirdparty/freetype/src/truetype/truetype.c \
+ $(MY_ROOT)/thirdparty/freetype/src/type1/type1.c \
+ $(MY_ROOT)/thirdparty/freetype/src/type42/type42.c \
+ $(MY_ROOT)/thirdparty/freetype/src/winfonts/winfnt.c
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/android/jni/mupdf.c b/android/jni/mupdf.c
new file mode 100644
index 00000000..ea11be2d
--- /dev/null
+++ b/android/jni/mupdf.c
@@ -0,0 +1,239 @@
+#include <jni.h>
+#include <time.h>
+#include <android/log.h>
+#include <android/bitmap.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "fitz.h"
+#include "mupdf.h"
+
+#define LOG_TAG "libmupdf"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+
+/* Set to 1 to enable debug log traces. */
+#define DEBUG 0
+
+/* Globals */
+fz_colorspace *colorspace;
+fz_glyphcache *glyphcache;
+pdf_xref *xref;
+int pagenum = 1;
+int resolution = 160;
+pdf_page *currentPage;
+float pageWidth = 100;
+float pageHeight = 100;
+
+JNIEXPORT int JNICALL Java_com_artifex_mupdf_PixmapView_mupdfOpenFile(JNIEnv * env, jobject thiz, jstring jfilename)
+{
+ const char *filename;
+ char *password = "";
+ int accelerate = 1;
+ fz_error error;
+
+ filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
+ if (filename == NULL)
+ {
+ LOGE("Failed to get filename");
+ return 0;
+ }
+
+ if (accelerate)
+ fz_accelerate();
+ glyphcache = fz_newglyphcache();
+ colorspace = fz_devicergb;
+
+ LOGE("Opening document...");
+ error = pdf_openxref(&xref, filename, password);
+ if (error)
+ {
+ LOGE("Cannot open document: '%s'\n", filename);
+ return 0;
+ }
+
+ LOGE("Loading page tree...");
+ error = pdf_loadpagetree(xref);
+ if (error)
+ {
+ LOGE("Cannot load page tree: '%s'\n", filename);
+ return 0;
+ }
+ LOGE("Done! %d pages", pdf_getpagecount(xref));
+
+ return pdf_getpagecount(xref);
+}
+
+JNIEXPORT void JNICALL Java_com_artifex_mupdf_PixmapView_mupdfGotoPage(
+ JNIEnv *env,
+ jobject thiz,
+ int page)
+{
+ float zoom;
+ fz_matrix ctm;
+ fz_obj *pageobj;
+ fz_bbox bbox;
+ fz_error error;
+
+ /* In the event of an error, ensure we give a non-empty page */
+ pageWidth = 100;
+ pageHeight = 100;
+
+ /* Free any current page */
+ if (currentPage != NULL)
+ {
+ pdf_freepage(currentPage);
+ currentPage = NULL;
+ }
+
+ LOGE("Goto page %d...", page);
+ pagenum = page;
+ pageobj = pdf_getpageobject(xref, pagenum);
+ if (pageobj == NULL)
+ return;
+ error = pdf_loadpage(&currentPage, xref, pageobj);
+ if (error)
+ return;
+ zoom = resolution / 72;
+ ctm = fz_translate(0, -currentPage->mediabox.y1);
+ ctm = fz_concat(ctm, fz_scale(zoom, -zoom));
+ ctm = fz_concat(ctm, fz_rotate(currentPage->rotate));
+ bbox = fz_roundrect(fz_transformrect(ctm, currentPage->mediabox));
+ pageWidth = bbox.x1-bbox.x0;
+ pageHeight = bbox.y1-bbox.y0;
+ LOGE("Success [w=%g, h=%g]", pageWidth, pageHeight);
+}
+
+JNIEXPORT float JNICALL Java_com_artifex_mupdf_PixmapView_mupdfPageWidth(
+ JNIEnv *env,
+ jobject thiz)
+{
+ LOGE("PageWidth=%g", pageWidth);
+ return pageWidth;
+}
+
+JNIEXPORT float JNICALL Java_com_artifex_mupdf_PixmapView_mupdfPageHeight(
+ JNIEnv *env,
+ jobject thiz)
+{
+ LOGE("PageHeight=%g", pageHeight);
+ return pageHeight;
+}
+
+
+JNIEXPORT jboolean JNICALL Java_com_artifex_mupdf_PixmapView_mupdfDrawPage(
+ JNIEnv *env,
+ jobject thiz,
+ jobject bitmap,
+ int pageW,
+ int pageH,
+ int patchX,
+ int patchY,
+ int patchW,
+ int patchH)
+{
+ AndroidBitmapInfo info;
+ void* pixels;
+ int ret, i, c;
+
+ LOGI("In native method\n");
+ if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
+ LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
+ return 0;
+ }
+
+ LOGI("Checking format\n");
+ if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
+ LOGE("Bitmap format is not RGBA_8888 !");
+ return 0;
+ }
+
+ LOGI("locking pixels\n");
+ if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
+ LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
+ return 0;
+ }
+
+ LOGI("render page\n");
+ /* Call mupdf to render page */
+ {
+ fz_error error;
+ fz_displaylist *list;
+ fz_device *dev;
+
+ /* Render to list */
+ LOGI("make list\n");
+ list = fz_newdisplaylist();
+ LOGI("make device\n");
+ dev = fz_newlistdevice(list);
+ LOGI("render to device\n");
+ error = pdf_runpage(xref, currentPage, dev, fz_identity);
+ if (error)
+ {
+ LOGE("cannot draw page %d", pagenum);
+ return 0;
+ }
+ LOGI("free device\n");
+ fz_freedevice(dev);
+
+ /* Render to screen */
+ LOGE("Rendering page=%dx%d patch=[%d,%d,%d,%d]",
+ pageW, pageH, patchX, patchY, patchW, patchH);
+ {
+ float zoom;
+ fz_matrix ctm;
+ fz_bbox bbox;
+ fz_pixmap *pix;
+ float xscale, yscale;
+
+ zoom = resolution / 72;
+ ctm = fz_translate(0, -currentPage->mediabox.y1);
+ ctm = fz_concat(ctm, fz_scale(zoom, -zoom));
+ ctm = fz_concat(ctm, fz_rotate(currentPage->rotate));
+ bbox = fz_roundrect(fz_transformrect(ctm,currentPage->mediabox));
+
+ LOGE("mediabox=%g %g %g %g zoom=%g rotate=%d",
+ currentPage->mediabox.x0,
+ currentPage->mediabox.y0,
+ currentPage->mediabox.x1,
+ currentPage->mediabox.y1,
+ zoom,
+ currentPage->rotate);
+ LOGE("ctm = [%g %g %g %g %g %g] to [%d %d %d %d]", ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
+ /* Now, adjust ctm so that it would give the correct page width
+ * heights. */
+ xscale = (float)pageW/(float)(bbox.x1-bbox.x0);
+ yscale = (float)pageH/(float)(bbox.y1-bbox.y0);
+ ctm = fz_concat(ctm, fz_scale(xscale, yscale));
+ bbox = fz_roundrect(fz_transformrect(ctm,currentPage->mediabox));
+ LOGE("ctm = [%g %g %g %g %g %g] to [%d %d %d %d]", ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
+
+ pix = fz_newpixmapwithdata(colorspace,
+ patchX,
+ patchY,
+ patchW,
+ patchH,
+ pixels);
+ LOGE("Clearing");
+ fz_clearpixmapwithcolor(pix, 0xff);
+ LOGE("Cleared");
+ dev = fz_newdrawdevice(glyphcache, pix);
+ fz_executedisplaylist(list, dev, ctm);
+ fz_freedevice(dev);
+ fz_droppixmap(pix);
+ }
+ LOGE("Rendered");
+ fz_freedisplaylist(list);
+ }
+
+ AndroidBitmap_unlockPixels(env, bitmap);
+
+ return 1;
+}
+
+void android_log(char *err, int n)
+{
+ LOGE(err, n);
+}
diff --git a/android/local.properties b/android/local.properties
new file mode 100644
index 00000000..93292dc5
--- /dev/null
+++ b/android/local.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must *NOT* be checked in Version Control Systems,
+# as it contains information specific to your local configuration.
+
+# location of the SDK. This is only used by Ant
+# For customization when using a Version Control System, please read the
+# header note.
+#sdk.dir=/Library/android-sdk-mac_x86
+sdk.dir=C:\\Program Files\\android-sdk-windows
diff --git a/android/res/drawable-hdpi/icon.png b/android/res/drawable-hdpi/icon.png
new file mode 100644
index 00000000..1d47b235
--- /dev/null
+++ b/android/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/android/res/drawable-ldpi/icon.png b/android/res/drawable-ldpi/icon.png
new file mode 100644
index 00000000..f9fe4d1c
--- /dev/null
+++ b/android/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/android/res/drawable-mdpi/icon.png b/android/res/drawable-mdpi/icon.png
new file mode 100644
index 00000000..4bb3e7cd
--- /dev/null
+++ b/android/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/android/res/layout/main.xml b/android/res/layout/main.xml
new file mode 100644
index 00000000..0da95fc5
--- /dev/null
+++ b/android/res/layout/main.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+</LinearLayout>
+
diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
new file mode 100644
index 00000000..ec3f9ae6
--- /dev/null
+++ b/android/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">MuPDF</string>
+</resources>
diff --git a/android/res/values/theme.xml b/android/res/values/theme.xml
new file mode 100644
index 00000000..68c55644
--- /dev/null
+++ b/android/res/values/theme.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="Theme.NoBackground.NoTitle" parent="android:Theme">
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ </style>
+</resources>
diff --git a/android/src/com/artifex/mupdf/MuPDFActivity.java b/android/src/com/artifex/mupdf/MuPDFActivity.java
new file mode 100644
index 00000000..f1dd1cc8
--- /dev/null
+++ b/android/src/com/artifex/mupdf/MuPDFActivity.java
@@ -0,0 +1,114 @@
+package com.artifex.mupdf;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.*;
+import android.view.View.OnClickListener;
+import android.widget.*;
+import android.widget.LinearLayout.*;
+import java.io.File;
+
+import com.artifex.mupdf.PixmapView;
+
+public class MuPDFActivity extends Activity
+{
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ String state = Environment.getExternalStorageState();
+
+ if (Environment.MEDIA_MOUNTED.equals(state))
+ {
+ System.out.println("Media mounted read/write");
+ }
+ else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
+ {
+ System.out.println("Media mounted read only");
+ }
+ else
+ {
+ System.out.println("No media at all! Bale!\n");
+ return;
+ }
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ File file = new File(path, "test.pdf");
+ System.out.println("Trying to open "+file.toString());
+ PixmapView pixmapView = new PixmapView(this, file.toString());
+ super.onCreate(savedInstanceState);
+
+ /* Now create the UI */
+ RelativeLayout layout;
+ LinearLayout bar;
+ MyButtonHandler bh = new MyButtonHandler(pixmapView);
+
+ bar = new LinearLayout(this);
+ bar.setOrientation(LinearLayout.HORIZONTAL);
+ bh.buttonStart = new Button(this);
+ bh.buttonStart.setText("<<");
+ bh.buttonStart.setOnClickListener(bh);
+ bar.addView(bh.buttonStart);
+ bh.buttonPrev = new Button(this);
+ bh.buttonPrev.setText("<");
+ bh.buttonPrev.setOnClickListener(bh);
+ bar.addView(bh.buttonPrev);
+ bh.buttonNext = new Button(this);
+ bh.buttonNext.setText(">");
+ bh.buttonNext.setOnClickListener(bh);
+ bar.addView(bh.buttonNext);
+ bh.buttonEnd = new Button(this);
+ bh.buttonEnd.setText(">>");
+ bh.buttonEnd.setOnClickListener(bh);
+ bar.addView(bh.buttonEnd);
+
+ layout = new RelativeLayout(this);
+ layout.setLayoutParams(new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.FILL_PARENT,
+ RelativeLayout.LayoutParams.FILL_PARENT));
+ layout.setGravity(Gravity.FILL);
+
+ RelativeLayout.LayoutParams barParams =
+ new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.FILL_PARENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
+ barParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+ bar.setId(100);
+ layout.addView(bar, barParams);
+
+ RelativeLayout.LayoutParams pixmapParams =
+ new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.FILL_PARENT,
+ RelativeLayout.LayoutParams.FILL_PARENT);
+ pixmapParams.addRule(RelativeLayout.ABOVE,100);
+ layout.addView(pixmapView, pixmapParams);
+
+ setContentView(layout);
+ }
+
+ private class MyButtonHandler implements OnClickListener
+ {
+ Button buttonStart;
+ Button buttonPrev;
+ Button buttonNext;
+ Button buttonEnd;
+ PixmapView pixmapView;
+
+ public MyButtonHandler(PixmapView pixmapView)
+ {
+ this.pixmapView = pixmapView;
+ }
+
+ public void onClick(View v)
+ {
+ if (v == buttonStart)
+ pixmapView.changePage(Integer.MIN_VALUE);
+ else if (v == buttonPrev)
+ pixmapView.changePage(-1);
+ else if (v == buttonNext)
+ pixmapView.changePage(+1);
+ else if (v == buttonEnd)
+ pixmapView.changePage(Integer.MAX_VALUE);
+ }
+ }
+}
diff --git a/android/src/com/artifex/mupdf/PixmapView.java b/android/src/com/artifex/mupdf/PixmapView.java
new file mode 100644
index 00000000..80fcb5f1
--- /dev/null
+++ b/android/src/com/artifex/mupdf/PixmapView.java
@@ -0,0 +1,577 @@
+package com.artifex.mupdf;
+
+import android.app.*;
+import android.os.*;
+import android.content.*;
+import android.content.res.*;
+import android.graphics.*;
+import android.util.*;
+import android.view.*;
+import android.widget.*;
+import java.net.*;
+import java.io.*;
+
+public class PixmapView extends SurfaceView implements SurfaceHolder.Callback
+{
+ private SurfaceHolder holder;
+ private MuPDFThread thread = null;
+ private boolean threadStarted = false;
+
+ /* Constructor */
+ public PixmapView(Context context, String filename)
+ {
+ super(context);
+ System.out.println("PixmapView construct");
+ holder = getHolder();
+ holder.addCallback(this);
+ thread = new MuPDFThread(holder, filename);
+ setFocusable(true); // need to get the key events
+ }
+
+ /* load our native library */
+ static {
+ System.loadLibrary("mupdf");
+ }
+
+ /* Handlers for keys - so we can actually do stuff */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ if (thread.onKeyDown(keyCode, event))
+ return true;
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event)
+ {
+ if (thread.onKeyUp(keyCode, event))
+ return true;
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event)
+ {
+ if (thread.onTouchEvent(event))
+ return true;
+ return super.onTouchEvent(event);
+ }
+
+ public void changePage(int delta)
+ {
+ thread.changePage(delta);
+ }
+
+ /* Handlers for SurfaceHolder callbacks; these are called when the
+ * surface is created/destroyed/changed. We need to ensure that we only
+ * draw into the surface between the created and destroyed calls.
+ * Therefore, we start/stop the thread that actually runs MuPDF on
+ * creation/destruction. */
+ public void surfaceCreated(SurfaceHolder holder)
+ {
+ }
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
+ {
+ thread.newScreenSize(width, height);
+ if (!threadStarted)
+ {
+ threadStarted = true;
+ thread.setRunning(true);
+ thread.start();
+ }
+ }
+
+ public void surfaceDestroyed(SurfaceHolder holder)
+ {
+ boolean retry = true;
+ thread.setRunning(false);
+ while (retry)
+ {
+ try
+ {
+ thread.join();
+ retry = false;
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ threadStarted = false;
+ }
+
+ class MuPDFThread extends Thread
+ {
+ private SurfaceHolder holder;
+ private boolean running = false;
+ private int keycode = -1;
+ private String filename;
+ private int screenWidth;
+ private int screenHeight;
+ private int screenGeneration;
+ private Bitmap bitmap;
+ private int numPages;
+
+ /* The following variables deal with the size of the current page;
+ * specifically, its position on the screen, its raw size, its
+ * current scale, and its current scaled size (in terms of whole
+ * pixels).
+ */
+ private int pageOriginX;
+ private int pageOriginY;
+ private float pageRawWidth;
+ private float pageRawHeight;
+ private float pageScale;
+ private int pageWidth;
+ private int pageHeight;
+
+ /* The following variables deal with the multitouch handling */
+ private final int NONE = 0;
+ private final int DRAG = 1;
+ private final int ZOOM = 2;
+ private int touchMode = NONE;
+ private float touchInitialSpacing;
+ private float touchDragStartX;
+ private float touchDragStartY;
+ private float touchInitialOriginX;
+ private float touchInitialOriginY;
+ private float touchInitialScale;
+ private PointF touchZoomMidpoint;
+
+ /* The following control the inner loop; other events etc cause
+ * action to be set. The inner loop runs around a tight loop
+ * performing the action requested of it.
+ */
+ private boolean wakeMe = false;
+ private int action;
+ private final int SLEEP = 0;
+ private final int REDRAW = 1;
+ private final int DIE = 2;
+ private final int GOTOPAGE = 3;
+ private int actionPageNum;
+
+ /* Members for blitting, declared here to avoid causing gcs */
+ private Rect srcRect;
+ private RectF dstRect;
+
+ public MuPDFThread(SurfaceHolder holder, String filename)
+ {
+ this.holder = holder;
+ this.filename = filename;
+ touchZoomMidpoint = new PointF(0,0);
+ srcRect = new Rect(0,0,0,0);
+ dstRect = new RectF(0,0,0,0);
+ }
+
+ public void setRunning(boolean running)
+ {
+ this.running = running;
+ }
+
+ public void newScreenSize(int width, int height)
+ {
+ this.screenWidth = width;
+ this.screenHeight = height;
+ this.screenGeneration++;
+ }
+
+ public boolean onKeyDown(int keyCode, KeyEvent msg)
+ {
+ keycode = keyCode;
+ return false;
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent msg)
+ {
+ return false;
+ }
+
+ public synchronized void changePage(int delta)
+ {
+ action = GOTOPAGE;
+ if (delta == Integer.MIN_VALUE)
+ actionPageNum = 1;
+ else if (delta == Integer.MAX_VALUE)
+ actionPageNum = numPages;
+ else
+ {
+ actionPageNum += delta;
+ if (actionPageNum < 1)
+ actionPageNum = 1;
+ if (actionPageNum > numPages)
+ actionPageNum = numPages;
+ }
+ if (wakeMe)
+ {
+ wakeMe = false;
+ this.notify();
+ }
+ }
+
+ private float spacing(MotionEvent event)
+ {
+ float x = event.getX(0) - event.getX(1);
+ float y = event.getY(0) - event.getY(1);
+ return FloatMath.sqrt(x*x+y*y);
+ }
+
+ private void midpoint(PointF point, MotionEvent event)
+ {
+ float x = event.getX(0) + event.getX(1);
+ float y = event.getY(0) + event.getY(1);
+ point.set(x/2, y/2);
+ }
+
+ private synchronized void forceRedraw()
+ {
+ if (wakeMe)
+ {
+ wakeMe = false;
+ this.notify();
+ }
+ action = REDRAW;
+ }
+
+ public synchronized void setPageOriginTo(int x, int y)
+ {
+ /* Adjust the coordinates so that the page always covers the
+ * centre of the screen. */
+ if (x + pageWidth < screenWidth/2)
+ {
+ x = screenWidth/2 - pageWidth;
+ }
+ else if (x > screenWidth/2)
+ {
+ x = screenWidth/2;
+ }
+ if (y + pageHeight < screenHeight/2)
+ {
+ y = screenHeight/2 - pageHeight;
+ }
+ else if (y > screenHeight/2)
+ {
+ y = screenHeight/2;
+ }
+ if ((x != pageOriginX) || (y != pageOriginY))
+ {
+ pageOriginX = x;
+ pageOriginY = y;
+ }
+ forceRedraw();
+ }
+
+ public void setPageScaleTo(float scale, PointF midpoint)
+ {
+ float x, y;
+ /* Convert midpoint (in screen coords) to page coords */
+ x = (midpoint.x - pageOriginX)/pageScale;
+ y = (midpoint.y - pageOriginY)/pageScale;
+ /* Find new scaled page sizes */
+ synchronized(this)
+ {
+ pageWidth = (int)(pageRawWidth*scale+0.5);
+ if (pageWidth < screenWidth/2)
+ {
+ scale = screenWidth/2/pageRawWidth;
+ pageWidth = (int)(pageRawWidth*scale+0.5);
+ }
+ pageHeight = (int)(pageRawHeight*scale+0.5);
+ if (pageHeight < screenHeight/2)
+ {
+ scale = screenHeight/2/pageRawHeight;
+ pageWidth = (int)(pageRawWidth *scale+0.5);
+ pageHeight = (int)(pageRawHeight*scale+0.5);
+ }
+ pageScale = scale;
+ /* Now given this new scale, calculate page origins so that
+ * x and y are at midpoint */
+ float xscale = (float)pageWidth /(float)pageRawWidth;
+ float yscale = (float)pageHeight/(float)pageRawHeight;
+ setPageOriginTo((int)(midpoint.x - x*xscale + 0.5),
+ (int)(midpoint.y - y*yscale + 0.5));
+ }
+ }
+
+ public void scalePageToScreen()
+ {
+ float scaleX, scaleY;
+ scaleX = (float)screenWidth/pageRawWidth;
+ scaleY = (float)screenHeight/pageRawHeight;
+ synchronized(this)
+ {
+ if (scaleX < scaleY)
+ pageScale = scaleX;
+ else
+ pageScale = scaleY;
+ pageWidth = (int)(pageRawWidth * pageScale + 0.5);
+ pageHeight = (int)(pageRawHeight * pageScale + 0.5);
+ pageOriginX = (screenWidth - pageWidth)/2;
+ pageOriginY = (screenHeight - pageHeight)/2;
+ forceRedraw();
+ }
+ System.out.println("scalePageToScreen: Raw="+
+ pageRawWidth+"x"+pageRawHeight+" scaled="+
+ pageWidth+","+pageHeight+" pageScale="+
+ pageScale);
+ }
+
+ public boolean onTouchEvent(MotionEvent event)
+ {
+ int action = event.getAction();
+ boolean done = false;
+ switch (action & MotionEvent.ACTION_MASK)
+ {
+ case MotionEvent.ACTION_DOWN:
+ touchMode = DRAG;
+ touchDragStartX = event.getX();
+ touchDragStartY = event.getY();
+ touchInitialOriginX = pageOriginX;
+ touchInitialOriginY = pageOriginY;
+ System.out.println("Starting dragging from: "+touchDragStartX+","+touchDragStartY+" ("+pageOriginX+","+pageOriginY+")");
+ done = true;
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ touchInitialSpacing = spacing(event);
+ if (touchInitialSpacing > 10f)
+ {
+ System.out.println("Started zooming: spacing="+touchInitialSpacing);
+ touchInitialScale = pageScale;
+ touchMode = ZOOM;
+ done = true;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ if (touchMode != NONE)
+ {
+ System.out.println("Released!");
+ touchMode = NONE;
+ done = true;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (touchMode == DRAG)
+ {
+ float x = touchInitialOriginX+event.getX()-touchDragStartX;
+ float y = touchInitialOriginY+event.getY()-touchDragStartY;
+ System.out.println("Dragged to "+x+","+y);
+ setPageOriginTo((int)(x+0.5),(int)(y+0.5));
+ done = true;
+ }
+ else if (touchMode == ZOOM)
+ {
+ float newSpacing = spacing(event);
+ if (newSpacing > 10f)
+ {
+ float newScale = touchInitialScale*newSpacing/touchInitialSpacing;
+ System.out.println("Zoomed to "+newSpacing);
+ midpoint(touchZoomMidpoint,event);
+ setPageScaleTo(newScale,touchZoomMidpoint);
+ done = true;
+ }
+ }
+ }
+ return done;
+ }
+
+ public void run()
+ {
+ boolean redraw = false;
+ int patchW = 0;
+ int patchH = 0;
+ int patchX = 0;
+ int patchY = 0;
+ int localPageW = 0;
+ int localPageH = 0;
+ int localScreenGeneration = screenGeneration;
+ int localAction;
+ int localActionPageNum = 1;
+ numPages = mupdfOpenFile(filename);
+ if (numPages <= 0)
+ {
+ /* Error whilst loading file */
+ }
+ System.out.println("File loaded: "+numPages+" pages");
+ /* Set up our default action */
+ action = GOTOPAGE;
+ actionPageNum = 1;
+ while (action != DIE)
+ {
+ synchronized(this)
+ {
+ while (action == SLEEP)
+ {
+ wakeMe = true;
+ try
+ {
+ System.out.println("Render thread sleeping");
+ this.wait();
+ System.out.println("Render thread woken");
+ }
+ catch (java.lang.InterruptedException e)
+ {
+ System.out.println("Render thread exception:"+e);
+ }
+ }
+ /* Now we do as little as we can get away with while
+ * synchronised. In general this means copying any action
+ * or global variables into local ones so that when we
+ * unsynchronoise, other people can alter them again.
+ */
+ switch (action)
+ {
+ case GOTOPAGE:
+ localActionPageNum = actionPageNum;
+ break;
+ case REDRAW:
+ /* Figure out what area of the page we want to
+ * redraw (in local variables, in docspace).
+ * We'll always draw a screensized lump, unless
+ * that's too big. */
+ System.out.println("page="+pageWidth+","+pageHeight+" ("+pageRawWidth+","+pageRawHeight+"@"+pageScale+") @ "+pageOriginX+","+pageOriginY);
+ localPageW = pageWidth;
+ localPageH = pageHeight;
+ patchW = pageWidth;
+ patchH = pageHeight;
+ patchX = -pageOriginX;
+ patchY = -pageOriginY;
+ if (patchX < 0)
+ patchX = 0;
+ if (patchW > screenWidth)
+ patchW = screenWidth;
+ srcRect.left = 0;
+ if (patchX+patchW > pageWidth)
+ {
+ srcRect.left += patchX+patchW-pageWidth;
+ patchX = pageWidth-patchW;
+ }
+ if (patchY < 0)
+ patchY = 0;
+ if (patchH > screenHeight)
+ patchH = screenHeight;
+ srcRect.top = 0;
+ if (patchY+patchH > pageHeight)
+ {
+ srcRect.top += patchY+patchH-pageHeight;
+ patchY = pageHeight-patchH;
+ }
+ dstRect.left = pageOriginX;
+ if (dstRect.left < 0)
+ dstRect.left = 0;
+ dstRect.top = pageOriginY;
+ if (dstRect.top < 0)
+ dstRect.top = 0;
+ dstRect.right = dstRect.left + patchW;
+ srcRect.right = srcRect.left + patchW;
+ if (srcRect.right > screenWidth)
+ {
+ dstRect.right -= srcRect.right-screenWidth;
+ srcRect.right = screenWidth;
+ }
+ if (dstRect.right > screenWidth)
+ {
+ srcRect.right -= dstRect.right-screenWidth;
+ dstRect.right = screenWidth;
+ }
+ dstRect.bottom = dstRect.top + patchH;
+ srcRect.bottom = srcRect.top + patchH;
+ if (srcRect.bottom > screenHeight)
+ {
+ dstRect.bottom -=srcRect.bottom-screenHeight;
+ srcRect.bottom = screenHeight;
+ }
+ if (dstRect.bottom > screenHeight)
+ {
+ srcRect.bottom -=dstRect.bottom-screenHeight;
+ dstRect.bottom = screenHeight;
+ }
+ System.out.println("patch=["+patchX+","+patchY+","+patchW+","+patchH+"]");
+ break;
+ }
+ localAction = action;
+ action = SLEEP;
+ }
+ /* In the redraw case:
+ * pW, pH, pX, pY, localPageW, localPageH are now all set
+ * in local variables, and we are safe from the global vars
+ * being altered in calls from other threads. This is all
+ * the information we need to actually do our render.
+ */
+ switch (localAction)
+ {
+ case GOTOPAGE:
+ mupdfGotoPage(localActionPageNum);
+ pageRawWidth = mupdfPageWidth();
+ pageRawHeight = mupdfPageHeight();
+ scalePageToScreen();
+ action = REDRAW;
+ break;
+ case REDRAW:
+ if ((bitmap == null) ||
+ (bitmap.getWidth() != patchW) ||
+ (bitmap.getHeight() != patchH))
+ {
+ /* make bitmap of required size */
+ bitmap = Bitmap.createBitmap(patchW, patchH,
+ Bitmap.Config.ARGB_8888);
+ }
+ System.out.println("Calling redraw native method");
+ mupdfDrawPage(bitmap, localPageW, localPageH,
+ patchX, patchY, patchW, patchH);
+ System.out.println("Called native method");
+ {
+ Canvas c = null;
+ try
+ {
+ c = holder.lockCanvas(null);
+ synchronized(holder)
+ {
+ if (localScreenGeneration == screenGeneration)
+ {
+ doDraw(c);
+ }
+ else
+ {
+ /* Someone has changed the screen
+ * under us! Better redraw again...
+ */
+ action = REDRAW;
+ }
+ }
+ }
+ finally
+ {
+ if (c != null)
+ holder.unlockCanvasAndPost(c);
+ }
+ }
+ }
+ }
+ }
+
+ protected void doDraw(Canvas canvas)
+ {
+ if ((canvas == null) || (bitmap == null))
+ return;
+ /* Clear the screen */
+ canvas.drawRGB(128,128,128);
+ /* Draw our bitmap on top */
+ System.out.println("Blitting bitmap from "+srcRect.left+","+srcRect.top+","+srcRect.right+","+srcRect.bottom+" to "+dstRect.left+","+dstRect.top+","+dstRect.right+","+dstRect.bottom);
+ canvas.drawBitmap(bitmap, srcRect, dstRect, (Paint)null);
+ }
+ }
+
+ /* These should be native functions */
+ private static native int mupdfOpenFile(String filename);
+ private static native void mupdfGotoPage(int localActionPageNum);
+ private static native float mupdfPageWidth();
+ private static native float mupdfPageHeight();
+ private static native void mupdfDrawPage(Bitmap bitmap,
+ int pageW,
+ int pageH,
+ int patchX,
+ int patchY,
+ int patchW,
+ int patchH);
+}