summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile4
-rw-r--r--Makerules14
-rw-r--r--android/ReadMe.txt138
-rw-r--r--android/build.properties17
-rw-r--r--android/build.xml77
-rw-r--r--android/default.properties11
-rw-r--r--android/jni/Core.mk5
-rw-r--r--android/jni/mupdf.c18
-rw-r--r--android/local.properties2
-rw-r--r--apps/pdfapp.c69
-rw-r--r--apps/pdfapp.h3
-rw-r--r--apps/pdfdraw.c31
-rw-r--r--apps/x11_main.c101
-rw-r--r--apps/xpsdraw.c31
-rw-r--r--draw/draw_affine.c158
-rw-r--r--draw/draw_device.c10
-rw-r--r--draw/draw_scale.c426
-rw-r--r--draw/draw_simple_scale.c1353
-rw-r--r--fitz/base_object.c103
-rw-r--r--fitz/dev_trace.c22
-rw-r--r--fitz/doc_outline.c49
-rw-r--r--fitz/filt_jpxd.c9
-rw-r--r--fitz/fitz.h22
-rw-r--r--fitz/stm_read.c6
-rw-r--r--ios/About.pdfbin0 -> 131735 bytes
-rw-r--r--ios/Icon-72.pngbin0 -> 5106 bytes
-rw-r--r--ios/Icon.pngbin0 -> 3599 bytes
-rw-r--r--ios/Icon@2x.pngbin0 -> 16336 bytes
-rw-r--r--ios/Info.plist52
-rw-r--r--ios/MuPDF.xcodeproj/project.pbxproj394
-rw-r--r--ios/build_libs.sh33
-rw-r--r--ios/document.c157
-rw-r--r--ios/document.h32
-rw-r--r--ios/main.m1117
-rw-r--r--pdf/data_encodings.h6
-rw-r--r--pdf/mupdf.h14
-rw-r--r--pdf/pdf_interpret.c28
-rw-r--r--pdf/pdf_outline.c67
-rw-r--r--pdf/pdf_page.c32
-rw-r--r--pdf/pdf_shade.c3
-rw-r--r--pdf/pdf_xobject.c3
-rw-r--r--scripts/cmapdump.c3
-rw-r--r--scripts/fontdump.c4
-rw-r--r--win32/libmupdf.vcproj10
-rw-r--r--win32/mupdf.vcproj4
-rw-r--r--xps/muxps.h19
-rw-r--r--xps/xps_doc.c97
-rw-r--r--xps/xps_outline.c124
-rw-r--r--xps/xps_xml.c32
50 files changed, 4416 insertions, 495 deletions
diff --git a/.gitignore b/.gitignore
index 9aabf428..d1f85a5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
*~
.*.swp
build
+DerivedData
generated
thirdparty
cscope.*
diff --git a/Makefile b/Makefile
index c798d350..9813318f 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
build ?= debug
-OUT := build/$(build)
+OUT ?= build/$(build)
GEN := generated
# --- Variables, Commands, etc... ---
@@ -41,6 +41,7 @@ $(OUT) $(GEN) :
$(OUT)/%.a :
$(AR_CMD)
+ $(RANLIB_CMD)
$(OUT)/% : $(OUT)/%.o
$(LINK_CMD)
@@ -75,7 +76,6 @@ $(MUPDF_LIB) : $(addprefix $(OUT)/, $(MUPDF_SRC:%.c=%.o))
$(MUXPS_LIB) : $(addprefix $(OUT)/, $(MUXPS_SRC:%.c=%.o))
libs: $(MUXPS_LIB) $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS)
- @ echo MuPDF/XPS and underlying libraries built
# --- Generated CMAP and FONT files ---
diff --git a/Makerules b/Makerules
index 80418031..d1e8498d 100644
--- a/Makerules
+++ b/Makerules
@@ -36,6 +36,7 @@ ifeq "$(OS)" "Darwin"
SYS_FREETYPE_INC := -I/usr/X11R6/include/freetype2
CFLAGS += -I/usr/X11R6/include
LDFLAGS += -L/usr/X11R6/lib
+RANLIB_CMD = ranlib $@
X11_LIBS := -lX11 -lXext
ifeq "$(arch)" "amd64"
CFLAGS += -m64
@@ -58,6 +59,7 @@ endif
# cygwin)
# 2) do a non cross compile build (e.g. windows in MSVC) first.
# 3) download the generated files from mupdf.com.
+
ifeq "$(OS)" "beagle-cross"
CC = arm-none-linux-gnueabi-gcc
LD = arm-none-linux-gnueabi-gcc
@@ -84,3 +86,15 @@ CFLAGS += -O3 -mcpu=arm1136jf-s -mfpu=vfp -mfloat-abi=softfp -ffast-math -fsingl
CROSSCOMPILE=yes
NOX11=yes
endif
+
+# Most variables when building for iOS are set up in ios/build_libs.sh,
+# which is called from the Xcode project as a "Run Script" build step.
+# The following section works for both device and simulator builds.
+
+ifeq "$(OS)" "ios"
+CC = $(PLATFORM_DEVELOPER_BIN_DIR)/clang
+AR = $(PLATFORM_DEVELOPER_BIN_DIR)/ar
+RANLIB_CMD = $(PLATFORM_DEVELOPER_BIN_DIR)/ranlib $@
+CROSSCOMPILE=yes
+NOX11=yes
+endif
diff --git a/android/ReadMe.txt b/android/ReadMe.txt
index c46c88b6..7d111ece 100644
--- a/android/ReadMe.txt
+++ b/android/ReadMe.txt
@@ -1,77 +1,131 @@
To build/debug android build.
-1) Download the android sdk, and install it. On windows I unpacked it as:
+1) Download the android sdk, and install it. These instructions have been
+written with r14 (the latest version at time of writing) of the SDK in mind;
+other versions may give problems. On windows r14 unpacked as:
- C:\Program Files\android-sdk-windows
+ C:\Program Files (x86)\Android\android-sdk
-on Macos as:
+on Macos an older version installed 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.
+Whatever directory it unpacks to, ensure that both the 'tools' and
+'platform-tools' directories inside it have been added to your PATH.
-2) Download the android ndk, and install in. On windows I unpacked it as:
+2) Download the android ndk, and unpack it. These instructions have been
+written with r6b (the latest version at the time of writing) of the NDK
+in mind; other versions may give problems. On windows I unpacked it as:
- C:\Program Files\android-ndk-r5
+ C:\android-ndk-r6b
-on Macos as:
+on Macos an older version unpacked as:
/Library/android-ndk-r5
-On windows add: C:/Progra~1/android-ndk-r5 to your path. On linux/macos
-add the equivalent.
+It is very important that you should unpack it to a directory with no
+spaces in the name! (Don't be tempted to put it in C:\Program Files etc)
+
+Ensure that that directory is also added to your PATH.
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.
-In the current release of the ndk (r5), when running under cygwin, there are
-bugs to do with the automatic conversion of dependencies from DOS format
-paths to cygwin format paths. The 2 fixes can be found in:
-
- <http://groups.google.com/group/android-ndk/msg/b385e47e1484c2d4>
-
-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:
+[ In version r5 of the ndk, when running under cygwin, there were ]
+[ bugs to do with the automatic conversion of dependencies from DOS ]
+[ format paths to cygwin format paths. The 2 fixes can be found in: ]
+[ ]
+[ <http://groups.google.com/group/android-ndk/msg/b385e47e1484c2d4> ]
+[ ]
+[ Use the latest version and there should not be a problem. ]
+
+4) If the SDK has not popped up a window already, bring up a shell, and run
+'android' (or android.bat on cygwin/windows). You should now have a window
+with 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) In new versions of the GUI there is a 'Tools' menu from which you can
+select 'Manage AVDs...'. In old versions, 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
+ CPU/ABI: ARM (armeabi) (If this option exists)
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.
+Click 'Create AVD' (on old versions you may have to 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
+6) You will need a copy of the JDK installed. See
+<http://www.oracle.com/technetwork/java/javase/downloads/>. When this
+installs, ensure that JAVA_HOME is set to point to the installation
+directory.
+
+7) You will need a copy of Apache ANT installed.
+See <http://ant.apache.org/>. Ensure that ANT_HOME is set to point to
+the top level directory, and that ANT_HOME/bin is on the PATH.
+
+8) 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?).
-7) You will also need a copy of mupdf-thirdparty.zip (see the source code
+9) You will also need a copy of mupdf-thirdparty.zip (see the source code
link on http://mupdf.com/). Unpack the contents of this into a 'thirdparty'
directory created within the mupdf directory (i.e. at the same level as
fitz, pdf, android etc).
-8) Finally, you will need a copy of a 'generated' directory. This is not
-currently available to download. The easiest way to obtain this is to do
-a standard windows or linux build of mupdf, which generates the required
-files as part of the build process.
+10) Finally, you will need a copy of a 'generated' directory. This is not
+currently available to download.
+
+The normal mupdf build process involves running some code on the host
+(the machine on which you are compiling), rather than the target (the
+machine/device on which you eventually want to run mupdf). This code
+repacks various bits of information (fonts, CMAPs etc) into a more
+compact and usable form.
+
+Unfortunately, the android SDK does not provide a compiler for the host
+machine, so we cannot run this step automatically as part of the android
+build. You will need to generate it by running a different build, such
+as the windows or linux native builds.
-9) Change into the android directory, and edit local.properties into your
+We do not make a snapshot of the generated directory available to
+download as the contents of this directory change frequently, and we'd
+have to keep multiple versions on the website. We assume that anyone
+capable of building for android is capable of doing a normal hosted
+build.
+
+On windows (where you are using cygwin), or on linux/macos, this can be
+as simple as running 'make' in the top level directory. Even if the
+make process fails, it should get far enough to generate you the required
+'generated' directory, and you can continue through these instructions.
+
+11) Change into the android directory, and edit local.properties into your
favourite editor. Change the sdk path there as appropriate. This should be
the only bit of localisation you need to do.
-10) Change into the android directory (note, the android directory, NOT
+12) Change into the android directory (note, the android directory, NOT
the android/jni directory!), and execute (in a Cygwin window on Windows!):
ndk-build
-This should build the native code portion. Then execute:
+This should build the native code portion.
+
+If this dies with an error in thirdparty/jbig2/os_types.h load this
+file into an editor, and change line 43 from:
+
+ #else
+
+to
+
+ #elif !defined(HAVE_STDINT_H)
+
+and this should solve the problem.
+
+13) Then execute:
ant debug
@@ -81,13 +135,13 @@ or on windows under cygwin:
This should build the java wrapper.
-11) Now start the emulator by executing:
+14) Now start the emulator by executing:
emulator -avd FroyoEm
This will take a while to full start up (be patient).
-12) We now need to give the demo file something to chew on, so let's copy
+15) 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:
@@ -96,14 +150,14 @@ done once). With the emulator running type:
(where obviously ../../MyTests/pdf_reference17.pdf is altered for your
machine). (adb lives in <sdk>/platform-tools if it's not on your path).
-13) With the emulator running (see step 11), execute
+16) With the emulator running (see step 14), execute
- ant install
+ ant debug install
-('ant.bat install' on Windows) and that will copy MuPDF into the emulator
-where you can run it from the launchpad screen.
+('ant.bat debug install' on Windows) and that will copy MuPDF into the
+emulator where you can run it from the launchpad screen.
-14) To see debug messages from the emulator (including stdout/stderr from
+17) To see debug messages from the emulator (including stdout/stderr from
our app), execute:
adb logcat
diff --git a/android/build.properties b/android/build.properties
deleted file mode 100644
index edc7f230..00000000
--- a/android/build.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-# 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.xml b/android/build.xml
index 3ba2015b..7cb2cdf8 100644
--- a/android/build.xml
+++ b/android/build.xml
@@ -1,15 +1,14 @@
<?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.
+ <!-- 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. -->
+ <loadproperties srcFile="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
@@ -17,6 +16,9 @@
out.dir
The name of the output directory. Default is 'bin'.
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
@@ -24,26 +26,25 @@
application and should be checked into Version Control Systems.
-->
- <property file="build.properties" />
+ <property file="ant.properties" />
- <!-- The default.properties file is created and updated by the 'android'
+ <!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
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" />
+ <loadproperties srcFile="project.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>
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
+ unless="sdk.dir"
+ />
- <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 -->
@@ -53,32 +54,32 @@
<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}]
+ /* 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
+ <!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
- <setup> task.
+ <import> task.
- customize it to your needs.
- - Customize the whole script.
+ - Customize the whole content of build.xml
- 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" />.
+ into this file, replacing the <import> task.
- customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
-->
- <setup />
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
</project>
diff --git a/android/default.properties b/android/default.properties
deleted file mode 100644
index 0b9250e0..00000000
--- a/android/default.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# 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/Core.mk b/android/jni/Core.mk
index 4c3e75b5..7116d57a 100644
--- a/android/jni/Core.mk
+++ b/android/jni/Core.mk
@@ -4,6 +4,8 @@ include $(CLEAR_VARS)
MY_ROOT := ../..
+LOCAL_CFLAGS += -DARCH_ARM -DARCH_THUMB -DARCH_ARM_CAN_LOAD_UNALIGNED
+
LOCAL_C_INCLUDES := \
../thirdparty/jbig2dec \
../thirdparty/openjpeg-1.4/libopenjpeg \
@@ -53,7 +55,6 @@ LOCAL_SRC_FILES := \
$(MY_ROOT)/fitz/stm_buffer.c \
$(MY_ROOT)/fitz/stm_open.c \
$(MY_ROOT)/fitz/stm_read.c \
- $(MY_ROOT)/draw/arch_arm.c \
$(MY_ROOT)/draw/arch_port.c \
$(MY_ROOT)/draw/draw_affine.c \
$(MY_ROOT)/draw/draw_blend.c \
@@ -63,7 +64,7 @@ LOCAL_SRC_FILES := \
$(MY_ROOT)/draw/draw_mesh.c \
$(MY_ROOT)/draw/draw_paint.c \
$(MY_ROOT)/draw/draw_path.c \
- $(MY_ROOT)/draw/draw_scale.c \
+ $(MY_ROOT)/draw/draw_simple_scale.c \
$(MY_ROOT)/draw/draw_unpack.c \
$(MY_ROOT)/pdf/pdf_annot.c \
$(MY_ROOT)/pdf/pdf_cmap.c \
diff --git a/android/jni/mupdf.c b/android/jni/mupdf.c
index 463899af..a3d45d8d 100644
--- a/android/jni/mupdf.c
+++ b/android/jni/mupdf.c
@@ -17,6 +17,9 @@
/* Set to 1 to enable debug log traces. */
#define DEBUG 0
+/* Enable to log rendering times (render each frame 100 times and time) */
+#undef TIME_DISPLAY_LIST
+
/* Globals */
fz_colorspace *colorspace;
fz_glyph_cache *glyphcache;
@@ -190,7 +193,22 @@ Java_com_artifex_mupdf_MuPDFCore_drawPage(JNIEnv *env, jobject thiz, jobject bit
ctm = fz_concat(ctm, fz_scale(xscale, yscale));
bbox = fz_round_rect(fz_transform_rect(ctm,currentMediabox));
dev = fz_new_draw_device(glyphcache, pix);
+#ifdef TIME_DISPLAY_LIST
+ {
+ clock_t time;
+ int i;
+
+ LOGE("Executing display list");
+ time = clock();
+ for (i=0; i<100;i++) {
+#endif
fz_execute_display_list(currentPageList, dev, ctm, bbox);
+#ifdef TIME_DISPLAY_LIST
+ }
+ time = clock() - time;
+ LOGE("100 renders in %d (%d per sec)", time, CLOCKS_PER_SEC);
+ }
+#endif
fz_free_device(dev);
fz_drop_pixmap(pix);
LOGE("Rendered");
diff --git a/android/local.properties b/android/local.properties
index d0fd99a9..d0e1f0e6 100644
--- a/android/local.properties
+++ b/android/local.properties
@@ -1,2 +1,2 @@
#sdk.dir=/Library/android-sdk-mac_x86
-sdk.dir=C:\\Program Files\\android-sdk-windows
+sdk.dir=C:\\Program Files (x86)\\Android\\android-sdk
diff --git a/apps/pdfapp.c b/apps/pdfapp.c
index 5e235ddf..fed8a025 100644
--- a/apps/pdfapp.c
+++ b/apps/pdfapp.c
@@ -145,7 +145,7 @@ static void pdfapp_open_pdf(pdfapp_t *app, char *filename, int fd)
app->outline = pdf_load_outline(app->xref);
- app->doctitle = filename;
+ app->doctitle = fz_strdup(ctx, filename);
if (strrchr(app->doctitle, '\\'))
app->doctitle = strrchr(app->doctitle, '\\') + 1;
if (strrchr(app->doctitle, '/'))
@@ -189,7 +189,7 @@ static void pdfapp_open_xps(pdfapp_t *app, char *filename, int fd)
}
fz_close(file);
- app->doctitle = filename;
+ app->doctitle = fz_strdup(app->ctx, filename);
app->pagecount = xps_count_pages(app->xps);
}
@@ -225,6 +225,22 @@ void pdfapp_open(pdfapp_t *app, char *filename, int fd, int reload)
void pdfapp_close(pdfapp_t *app)
{
+ if (app->page_list)
+ fz_free_display_list(app->ctx, app->page_list);
+ app->page_list = NULL;
+
+ if (app->page_text)
+ fz_free_text_span(app->ctx, app->page_text);
+ app->page_text = NULL;
+
+ if (app->page_links)
+ pdf_free_link(app->ctx, app->page_links);
+ app->page_links = NULL;
+
+ if (app->doctitle)
+ fz_free(app->ctx, app->doctitle);
+ app->doctitle = NULL;
+
if (app->cache)
fz_free_glyph_cache(app->ctx, app->cache);
app->cache = NULL;
@@ -234,7 +250,7 @@ void pdfapp_close(pdfapp_t *app)
app->image = NULL;
if (app->outline)
- pdf_free_outline(app->ctx, app->outline);
+ fz_free_outline(app->outline);
app->outline = NULL;
if (app->xref)
@@ -629,13 +645,8 @@ static void pdfapp_searchforward(pdfapp_t *app, enum panning *panto)
} while (app->pageno != startpage);
if (app->pageno == startpage)
- {
pdfapp_warn(app, "String '%s' not found.", app->search);
- winrepaintsearch(app);
- }
- else
- winrepaint(app);
-
+ winrepaint(app);
wincursor(app, HAND);
}
@@ -683,13 +694,9 @@ static void pdfapp_searchbackward(pdfapp_t *app, enum panning *panto)
} while (app->pageno != startpage);
if (app->pageno == startpage)
- {
pdfapp_warn(app, "String '%s' not found.", app->search);
- winrepaintsearch(app);
- }
- else
- winrepaint(app);
+ winrepaint(app);
wincursor(app, HAND);
}
@@ -726,6 +733,16 @@ void pdfapp_onkey(pdfapp_t *app, int c)
if (n > 0)
{
winrepaintsearch(app);
+
+ if (app->searchdir < 0)
+ {
+ if (app->pageno == 1)
+ app->pageno = app->pagecount;
+ else
+ app->pageno--;
+ pdfapp_showpage(app, 1, 1, 0);
+ }
+
pdfapp_onkey(app, 'n');
}
else
@@ -762,10 +779,6 @@ void pdfapp_onkey(pdfapp_t *app, int c)
switch (c)
{
- case '?':
- winhelp(app);
- break;
-
case 'q':
winclose(app);
break;
@@ -951,8 +964,18 @@ void pdfapp_onkey(pdfapp_t *app, int c)
* Searching
*/
+ case '?':
+ app->isediting = 1;
+ app->searchdir = -1;
+ app->search[0] = 0;
+ app->hit = -1;
+ app->hitlen = 0;
+ winrepaintsearch(app);
+ break;
+
case '/':
app->isediting = 1;
+ app->searchdir = 1;
app->search[0] = 0;
app->hit = -1;
app->hitlen = 0;
@@ -960,12 +983,18 @@ void pdfapp_onkey(pdfapp_t *app, int c)
break;
case 'n':
- pdfapp_searchforward(app, &panto);
+ if (app->searchdir > 0)
+ pdfapp_searchforward(app, &panto);
+ else
+ pdfapp_searchbackward(app, &panto);
loadpage = 0;
break;
case 'N':
- pdfapp_searchbackward(app, &panto);
+ if (app->searchdir > 0)
+ pdfapp_searchbackward(app, &panto);
+ else
+ pdfapp_searchforward(app, &panto);
loadpage = 0;
break;
diff --git a/apps/pdfapp.h b/apps/pdfapp.h
index 6bafbbf8..e617333d 100644
--- a/apps/pdfapp.h
+++ b/apps/pdfapp.h
@@ -31,7 +31,7 @@ struct pdfapp_s
/* current document params */
char *doctitle;
pdf_xref *xref;
- pdf_outline *outline;
+ fz_outline *outline;
xps_document *xps;
int pagecount;
@@ -81,6 +81,7 @@ struct pdfapp_s
/* search state */
int isediting;
+ int searchdir;
char search[512];
int hit;
int hitlen;
diff --git a/apps/pdfdraw.c b/apps/pdfdraw.c
index 790c7eaa..fd7cc57f 100644
--- a/apps/pdfdraw.c
+++ b/apps/pdfdraw.c
@@ -19,6 +19,7 @@ int showxml = 0;
int showtext = 0;
int showtime = 0;
int showmd5 = 0;
+int showoutline = 0;
int savealpha = 0;
int uselist = 1;
int alphabits = 8;
@@ -61,6 +62,7 @@ static void usage(void)
"\t-R -\trotate clockwise by given number of degrees\n"
"\t-G gamma\tgamma correct output\n"
"\t-I\tinvert output\n"
+ "\t-l\tprint outline\n"
"\tpages\tcomma separated list of ranges\n");
exit(1);
}
@@ -305,6 +307,16 @@ static void drawrange(pdf_xref *xref, char *range)
}
}
+static void drawoutline(pdf_xref *xref)
+{
+ fz_outline *outline = pdf_load_outline(xref);
+ if (showoutline > 1)
+ fz_debug_outline_xml(outline, 0);
+ else
+ fz_debug_outline(outline, 0);
+ fz_free_outline(outline);
+}
+
int main(int argc, char **argv)
{
char *password = "";
@@ -315,7 +327,7 @@ int main(int argc, char **argv)
int c;
fz_context *ctx;
- while ((c = fz_getopt(argc, argv, "o:p:r:R:Aab:dgmtx5G:I")) != -1)
+ while ((c = fz_getopt(argc, argv, "lo:p:r:R:Aab:dgmtx5G:I")) != -1)
{
switch (c)
{
@@ -326,6 +338,7 @@ int main(int argc, char **argv)
case 'A': accelerate = 0; break;
case 'a': savealpha = 1; break;
case 'b': alphabits = atoi(fz_optarg); break;
+ case 'l': showoutline++; break;
case 'm': showtime++; break;
case 't': showtext++; break;
case 'x': showxml++; break;
@@ -343,7 +356,7 @@ int main(int argc, char **argv)
if (fz_optind == argc)
usage();
- if (!showtext && !showxml && !showtime && !showmd5 && !output)
+ if (!showtext && !showxml && !showtime && !showmd5 && !showoutline && !output)
{
printf("nothing to do\n");
exit(0);
@@ -406,10 +419,16 @@ int main(int argc, char **argv)
if (showxml)
printf("<document name=\"%s\">\n", filename);
- if (fz_optind == argc || !isrange(argv[fz_optind]))
- drawrange(xref, "1-");
- if (fz_optind < argc && isrange(argv[fz_optind]))
- drawrange(xref, argv[fz_optind++]);
+ if (showoutline)
+ drawoutline(xref);
+
+ if (showtext || showxml || showtime || showmd5 || output)
+ {
+ if (fz_optind == argc || !isrange(argv[fz_optind]))
+ drawrange(xref, "1-");
+ if (fz_optind < argc && isrange(argv[fz_optind]))
+ drawrange(xref, argv[fz_optind++]);
+ }
if (showxml)
printf("</document>\n");
diff --git a/apps/x11_main.c b/apps/x11_main.c
index d5b7dc55..5b19a176 100644
--- a/apps/x11_main.c
+++ b/apps/x11_main.c
@@ -61,6 +61,8 @@ extern void ximage_blit(Drawable d, GC gc, int dstx, int dsty,
unsigned char *srcdata,
int srcx, int srcy, int srcw, int srch, int srcstride);
+void windrawstringxor(pdfapp_t *app, int x, int y, char *s);
+
static Display *xdpy;
static Atom XA_TARGETS;
static Atom XA_TIMESTAMP;
@@ -90,6 +92,7 @@ static char *filename;
static pdfapp_t gapp;
static int closing = 0;
static int reloading = 0;
+static int showingpage = 0;
/*
* Dialog boxes
@@ -211,6 +214,12 @@ void winclose(pdfapp_t *app)
closing = 1;
}
+static int winresolution()
+{
+ return DisplayWidth(xdpy, xscr) * 25.4 /
+ DisplayWidthMM(xdpy, xscr) + 0.5;
+}
+
void wincursor(pdfapp_t *app, int curs)
{
if (curs == ARROW)
@@ -358,6 +367,13 @@ static void winblit(pdfapp_t *app)
}
winblitsearch(app);
+
+ if (showingpage)
+ {
+ char buf[42];
+ snprintf(buf, sizeof buf, "Page %d/%d", gapp.pageno, gapp.pagecount);
+ windrawstringxor(&gapp, 10, 20, buf);
+ }
}
void winrepaint(pdfapp_t *app)
@@ -388,8 +404,6 @@ void windrawstringxor(pdfapp_t *app, int x, int y, char *s)
XGetGCValues(xdpy, xgc, GCFunction, &xgcv);
xgcv.function = prevfunction;
XChangeGC(xdpy, xgc, GCFunction, &xgcv);
-
- printf("drawstring '%s'\n", s);
}
void windrawstring(pdfapp_t *app, int x, int y, char *s)
@@ -496,7 +510,13 @@ void winopenuri(pdfapp_t *app, char *buf)
{
char *browser = getenv("BROWSER");
if (!browser)
+ {
+#ifdef __APPLE__
browser = "open";
+#else
+ browser = "xdg-open";
+#endif
+ }
if (fork() == 0)
execlp(browser, browser, buf, (char*)0);
}
@@ -509,6 +529,13 @@ static void onkey(int c)
winrepaint(&gapp);
}
+ if (!gapp.isediting && c == 'P')
+ {
+ showingpage = 1;
+ winrepaint(&gapp);
+ return;
+ }
+
pdfapp_onkey(&gapp, c);
}
@@ -547,14 +574,21 @@ int main(int argc, char **argv)
KeySym keysym;
int oldx = 0;
int oldy = 0;
- int resolution = 72;
+ int resolution = -1;
int pageno = 1;
int accelerate = 1;
int fd;
fd_set fds;
int width = -1;
int height = -1;
+<<<<<<< HEAD
fz_context *ctx;
+=======
+ struct timeval tmo_at;
+ struct timeval now;
+ struct timeval tmo;
+ struct timeval *timeout;
+>>>>>>> master
while ((c = fz_getopt(argc, argv, "p:r:b:A")) != -1)
{
@@ -568,11 +602,6 @@ int main(int argc, char **argv)
}
}
- if (resolution < MINRES)
- resolution = MINRES;
- if (resolution > MAXRES)
- resolution = MAXRES;
-
if (argc - fz_optind == 0)
usage();
@@ -593,7 +622,18 @@ int main(int argc, char **argv)
winopen();
+<<<<<<< HEAD
pdfapp_init(ctx, &gapp);
+=======
+ if (resolution == -1)
+ resolution = winresolution();
+ if (resolution < MINRES)
+ resolution = MINRES;
+ if (resolution > MAXRES)
+ resolution = MAXRES;
+
+ pdfapp_init(&gapp);
+>>>>>>> master
gapp.scrw = DisplayWidth(xdpy, xscr);
gapp.scrh = DisplayHeight(xdpy, xscr);
gapp.resolution = resolution;
@@ -606,13 +646,15 @@ int main(int argc, char **argv)
pdfapp_open(&gapp, filename, fd, 0);
FD_ZERO(&fds);
- FD_SET(x11fd, &fds);
signal(SIGHUP, signal_handler);
+ tmo_at.tv_sec = 0;
+ tmo_at.tv_usec = 0;
+
while (!closing)
{
- do
+ while (!closing && XPending(xdpy))
{
XNextEvent(xdpy, &xevt);
@@ -701,7 +743,6 @@ int main(int argc, char **argv)
break;
}
}
- while (!closing && XPending(xdpy));
if (closing)
continue;
@@ -723,10 +764,38 @@ int main(int argc, char **argv)
dirtysearch = 0;
}
+ if (showingpage && !tmo_at.tv_sec && !tmo_at.tv_usec)
+ {
+ tmo.tv_sec = 2;
+ tmo.tv_usec = 0;
+
+ gettimeofday(&now, NULL);
+ timeradd(&now, &tmo, &tmo_at);
+ }
+
if (XPending(xdpy))
continue;
- if (select(x11fd + 1, &fds, NULL, NULL, NULL) < 0)
+ timeout = NULL;
+
+ if (tmo_at.tv_sec || tmo_at.tv_usec)
+ {
+ gettimeofday(&now, NULL);
+ timersub(&tmo_at, &now, &tmo);
+ if (tmo.tv_sec <= 0)
+ {
+ tmo_at.tv_sec = 0;
+ tmo_at.tv_usec = 0;
+ timeout = NULL;
+ showingpage = 0;
+ winrepaint(&gapp);
+ }
+ else
+ timeout = &tmo;
+ }
+
+ FD_SET(x11fd, &fds);
+ if (select(x11fd + 1, &fds, NULL, NULL, timeout) < 0)
{
if (reloading)
{
@@ -734,6 +803,14 @@ int main(int argc, char **argv)
reloading = 0;
}
}
+ if (!FD_ISSET(x11fd, &fds))
+ {
+ tmo_at.tv_sec = 0;
+ tmo_at.tv_usec = 0;
+ timeout = NULL;
+ showingpage = 0;
+ winrepaint(&gapp);
+ }
}
pdfapp_close(&gapp);
diff --git a/apps/xpsdraw.c b/apps/xpsdraw.c
index c91e8702..4ac40b72 100644
--- a/apps/xpsdraw.c
+++ b/apps/xpsdraw.c
@@ -14,6 +14,7 @@ int showxml = 0;
int showtext = 0;
int showtime = 0;
int showmd5 = 0;
+int showoutline = 0;
int savealpha = 0;
int uselist = 1;
@@ -48,6 +49,7 @@ static void usage(void)
"\t-x\tshow display list\n"
"\t-d\tdisable use of display list\n"
"\t-5\tshow md5 checksums\n"
+ "\t-l\tprint outline\n"
"\tpages\tcomma separated list of ranges\n");
exit(1);
}
@@ -277,6 +279,16 @@ static void drawrange(xps_document *doc, char *range)
}
}
+static void drawoutline(xps_document *doc)
+{
+ fz_outline *outline = xps_load_outline(doc);
+ if (showoutline > 1)
+ fz_debug_outline_xml(outline, 0);
+ else
+ fz_debug_outline(outline, 0);
+ fz_free_outline(outline);
+}
+
int main(int argc, char **argv)
{
int grayscale = 0;
@@ -284,7 +296,7 @@ int main(int argc, char **argv)
xps_document *doc;
int c;
- while ((c = fz_getopt(argc, argv, "o:p:r:Aadgmtx5")) != -1)
+ while ((c = fz_getopt(argc, argv, "o:p:r:Aadglmtx5")) != -1)
{
switch (c)
{
@@ -292,6 +304,7 @@ int main(int argc, char **argv)
case 'r': resolution = atof(fz_optarg); break;
case 'A': accelerate = 0; break;
case 'a': savealpha = 1; break;
+ case 'l': showoutline++; break;
case 'm': showtime++; break;
case 't': showtext++; break;
case 'x': showxml++; break;
@@ -305,7 +318,7 @@ int main(int argc, char **argv)
if (fz_optind == argc)
usage();
- if (!showtext && !showxml && !showtime && !showmd5 && !output)
+ if (!showtext && !showxml && !showtime && !showmd5 && !showoutline && !output)
{
printf("nothing to do\n");
exit(0);
@@ -357,10 +370,16 @@ int main(int argc, char **argv)
if (showxml)
printf("<document name=\"%s\">\n", filename);
- if (fz_optind == argc || !isrange(argv[fz_optind]))
- drawrange(doc, "1-");
- if (fz_optind < argc && isrange(argv[fz_optind]))
- drawrange(doc, argv[fz_optind++]);
+ if (showoutline)
+ drawoutline(doc);
+
+ if (showtext || showxml || showtime || showmd5 || output)
+ {
+ if (fz_optind == argc || !isrange(argv[fz_optind]))
+ drawrange(doc, "1-");
+ if (fz_optind < argc && isrange(argv[fz_optind]))
+ drawrange(doc, argv[fz_optind++]);
+ }
if (showxml)
printf("</document>\n");
diff --git a/draw/draw_affine.c b/draw/draw_affine.c
index 95d28bea..c1ee88f8 100644
--- a/draw/draw_affine.c
+++ b/draw/draw_affine.c
@@ -454,6 +454,146 @@ fz_paint_affine_color_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int
}
}
+/* RJW: The following code was originally written to be sensitive to
+ * FLT_EPSILON. Given the way the 'minimum representable difference'
+ * between 2 floats changes size as we scale, we now pick a larger
+ * value to ensure idempotency even with rounding problems. The
+ * value we pick is still far smaller than would ever show up with
+ * antialiasing.
+ */
+#define MY_EPSILON 0.001
+
+void
+fz_gridfit_matrix(fz_matrix *m)
+{
+ if (fabsf(m->b) < FLT_EPSILON && fabsf(m->c) < FLT_EPSILON)
+ {
+ if (m->a > 0)
+ {
+ float f;
+ /* Adjust left hand side onto pixel boundary */
+ f = (float)(int)(m->e);
+ if (f - m->e > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves left */
+ m->a += m->e - f; /* width gets wider as f <= m->e */
+ m->e = f;
+ /* Adjust right hand side onto pixel boundary */
+ f = (float)(int)(m->a);
+ if (m->a - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves right */
+ m->a = f;
+ }
+ else if (m->a < 0)
+ {
+ float f;
+ /* Adjust right hand side onto pixel boundary */
+ f = (float)(int)(m->e);
+ if (m->e - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves right */
+ m->a += m->e - f; /* width gets wider (more -ve) */
+ m->e = f;
+ /* Adjust left hand side onto pixel boundary */
+ f = (float)(int)(m->a);
+ if (f - m->a > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves left */
+ m->a = f;
+ }
+ if (m->d > 0)
+ {
+ float f;
+ /* Adjust top onto pixel boundary */
+ f = (float)(int)(m->f);
+ if (f - m->f > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves upwards */
+ m->d += m->f - f; /* width gets wider as f <= m->f */
+ m->f = f;
+ /* Adjust bottom onto pixel boundary */
+ f = (float)(int)(m->d);
+ if (m->d - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves down */
+ m->d = f;
+ }
+ else if (m->d < 0)
+ {
+ float f;
+ /* Adjust bottom onto pixel boundary */
+ f = (float)(int)(m->f);
+ if (m->f - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves down */
+ m->d += m->f - f; /* width gets wider (more -ve) */
+ m->f = f;
+ /* Adjust top onto pixel boundary */
+ f = (float)(int)(m->d);
+ if (f - m->d > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves up */
+ m->d = f;
+ }
+ }
+ else if (fabsf(m->a) < FLT_EPSILON && fabsf(m->d) < FLT_EPSILON)
+ {
+ if (m->b > 0)
+ {
+ float f;
+ /* Adjust left hand side onto pixel boundary */
+ f = (float)(int)(m->f);
+ if (f - m->f > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves left */
+ m->b += m->f - f; /* width gets wider as f <= m->f */
+ m->f = f;
+ /* Adjust right hand side onto pixel boundary */
+ f = (float)(int)(m->b);
+ if (m->b - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves right */
+ m->b = f;
+ }
+ else if (m->b < 0)
+ {
+ float f;
+ /* Adjust right hand side onto pixel boundary */
+ f = (float)(int)(m->f);
+ if (m->f - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves right */
+ m->b += m->f - f; /* width gets wider (more -ve) */
+ m->f = f;
+ /* Adjust left hand side onto pixel boundary */
+ f = (float)(int)(m->b);
+ if (f - m->b > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves left */
+ m->b = f;
+ }
+ if (m->c > 0)
+ {
+ float f;
+ /* Adjust top onto pixel boundary */
+ f = (float)(int)(m->e);
+ if (f - m->e > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves upwards */
+ m->c += m->e - f; /* width gets wider as f <= m->e */
+ m->e = f;
+ /* Adjust bottom onto pixel boundary */
+ f = (float)(int)(m->c);
+ if (m->c - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves down */
+ m->c = f;
+ }
+ else if (m->c < 0)
+ {
+ float f;
+ /* Adjust bottom onto pixel boundary */
+ f = (float)(int)(m->e);
+ if (m->e - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves down */
+ m->c += m->e - f; /* width gets wider (more -ve) */
+ m->e = f;
+ /* Adjust top onto pixel boundary */
+ f = (float)(int)(m->c);
+ if (f - m->c > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves up */
+ m->c = f;
+ }
+ }
+}
+
/* Draw an image with an affine transform on destination */
static void
@@ -469,15 +609,7 @@ fz_paint_image_imp(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap
void (*paintfn)(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color, byte *hp);
/* grid fit the image */
- if (fz_is_rectilinear(ctm))
- {
- ctm.a = roundup(ctm.a);
- ctm.b = roundup(ctm.b);
- ctm.c = roundup(ctm.c);
- ctm.d = roundup(ctm.d);
- ctm.e = floorf(ctm.e);
- ctm.f = floorf(ctm.f);
- }
+ fz_gridfit_matrix(&ctm);
/* turn on interpolation for upscaled and non-rectilinear transforms */
dolerp = 0;
@@ -519,6 +651,14 @@ fz_paint_image_imp(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap
u = (fa * x) + (fc * y) + inv.e * 65536 + ((fa + fc) >> 1);
v = (fb * x) + (fd * y) + inv.f * 65536 + ((fb + fd) >> 1);
+ /* RJW: The following is voodoo. No idea why it works, but it gives
+ * the best match between scaled/unscaled/interpolated/non-interpolated
+ * that we have found. */
+ if (dolerp) {
+ u -= 32768;
+ v -= 32768;
+ }
+
dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n;
n = dst->n;
sp = img->samples;
diff --git a/draw/draw_device.c b/draw/draw_device.c
index a8da9e19..5407d618 100644
--- a/draw/draw_device.c
+++ b/draw/draw_device.c
@@ -861,7 +861,10 @@ fz_transform_pixmap(fz_context *ctx, fz_pixmap *image, fz_matrix *ctm, int x, in
if (ctm->a != 0 && ctm->b == 0 && ctm->c == 0 && ctm->d != 0)
{
/* Unrotated or X-flip or Y-flip or XY-flip */
- scaled = fz_scale_pixmap_gridfit(ctx, image, ctm->e, ctm->f, ctm->a, ctm->d, gridfit);
+ fz_matrix m = *ctm;
+ if (gridfit)
+ fz_gridfit_matrix(&m);
+ scaled = fz_scale_pixmap(ctx, image, m.e, m.f, m.a, m.d);
if (scaled == NULL)
return NULL;
ctm->a = scaled->w;
@@ -874,7 +877,10 @@ fz_transform_pixmap(fz_context *ctx, fz_pixmap *image, fz_matrix *ctm, int x, in
if (ctm->a == 0 && ctm->b != 0 && ctm->c != 0 && ctm->d == 0)
{
/* Other orthogonal flip/rotation cases */
- scaled = fz_scale_pixmap_gridfit(ctx, image, ctm->f, ctm->e, ctm->b, ctm->c, gridfit);
+ fz_matrix m = *ctm;
+ if (gridfit)
+ fz_gridfit_matrix(&m);
+ scaled = fz_scale_pixmap(ctx, image, m.f, m.e, m.b, m.c);
if (scaled == NULL)
return NULL;
ctm->b = scaled->w;
diff --git a/draw/draw_scale.c b/draw/draw_scale.c
index 4d1b66c5..fd3c3798 100644
--- a/draw/draw_scale.c
+++ b/draw/draw_scale.c
@@ -16,6 +16,21 @@ and then positioning it at (frac(x),frac(y)).
*/
#define SINGLE_PIXEL_SPECIALS
+/* If we're compiling as thumb code, then we need to tell the compiler
+ * to enter and exit ARM mode around our assembly sections. If we move
+ * the ARM functions to a separate file and arrange for it to be compiled
+ * without thumb mode, we can save some time on entry.
+ */
+#ifdef ARCH_ARM
+#ifdef ARCH_THUMB
+#define ENTER_ARM ".balign 4\nmov r12,pc\nbx r12\n0:.arm\n"
+#define ENTER_THUMB "9:.thumb\n"
+#else
+#define ENTER_ARM
+#define ENTER_THUMB
+#endif
+#endif
+
#ifdef DEBUG_SCALING
#ifdef WIN32
#include <windows.h>
@@ -247,10 +262,10 @@ typedef struct fz_weights_s fz_weights;
struct fz_weights_s
{
+ int flip;
int count;
int max_len;
int n;
- int flip;
int new_line;
int index[1];
};
@@ -584,6 +599,274 @@ scale_row_to_temp(int *dst, unsigned char *src, fz_weights *weights)
}
}
+#ifdef ARCH_ARM
+
+static void
+scale_row_to_temp1(int *dst, unsigned char *src, fz_weights *weights)
+__attribute__((naked));
+
+static void
+scale_row_to_temp2(int *dst, unsigned char *src, fz_weights *weights)
+__attribute__((naked));
+
+static void
+scale_row_to_temp4(int *dst, unsigned char *src, fz_weights *weights)
+__attribute__((naked));
+
+static void
+scale_row_from_temp(unsigned char *dst, int *src, fz_weights *weights, int width, int row)
+__attribute__((naked));
+
+static void
+scale_row_to_temp1(int *dst, unsigned char *src, fz_weights *weights)
+{
+ /* possible optimisation in here; unroll inner loops to avoid stall. */
+ asm volatile(
+ ENTER_ARM
+ "stmfd r13!,{r4-r5,r9,r14} \n"
+ "@ r0 = dst \n"
+ "@ r1 = src \n"
+ "@ r2 = weights \n"
+ "ldr r12,[r2],#4 @ r12= flip \n"
+ "ldr r3, [r2],#16 @ r3 = count r2 = &index\n"
+ "ldr r4, [r2] @ r4 = index[0] \n"
+ "cmp r12,#0 @ if (flip) \n"
+ "beq 4f @ { \n"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "add r0, r0, r3, LSL #2 @ dst += count \n"
+ "1: \n"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #0 @ r5 = a = 0 \n"
+ "add r4, r1, r4 @ r4 = min = &src[r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 3f @ { \n"
+ "2: \n"
+ "ldr r12,[r2], #4 @ r12 = *contrib++ \n"
+ "ldrb r14,[r4], #1 @ r14 = *min++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "@stall on r14 \n"
+ "mla r5, r12,r14,r5 @ g += r14 * r12 \n"
+ "bgt 2b @ } \n"
+ "3: \n"
+ "str r5,[r0, #-4]! @ *--dst=a \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 1b @ \n"
+ "ldmfd r13!,{r4-r5,r9,PC} @ pop, return to thumb \n"
+ "4:"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "5:"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #0 @ r5 = a = 0 \n"
+ "add r4, r1, r4 @ r4 = min = &src[r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 7f @ { \n"
+ "6: \n"
+ "ldr r12,[r2], #4 @ r12 = *contrib++ \n"
+ "ldrb r14,[r4], #1 @ r14 = *min++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "@stall on r14 \n"
+ "mla r5, r12,r14,r5 @ a += r14 * r12 \n"
+ "bgt 6b @ } \n"
+ "7: \n"
+ "str r5, [r0], #4 @ *dst++=a \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 5b @ \n"
+ "ldmfd r13!,{r4-r5,r9,PC} @ pop, return to thumb \n"
+ ENTER_THUMB
+ );
+}
+
+static void
+scale_row_to_temp2(int *dst, unsigned char *src, fz_weights *weights)
+{
+ asm volatile(
+ ENTER_ARM
+ "stmfd r13!,{r4-r6,r9-r11,r14} \n"
+ "@ r0 = dst \n"
+ "@ r1 = src \n"
+ "@ r2 = weights \n"
+ "ldr r12,[r2],#4 @ r12= flip \n"
+ "ldr r3, [r2],#16 @ r3 = count r2 = &index\n"
+ "ldr r4, [r2] @ r4 = index[0] \n"
+ "cmp r12,#0 @ if (flip) \n"
+ "beq 4f @ { \n"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "add r0, r0, r3, LSL #3 @ dst += 2*count \n"
+ "1: \n"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #0 @ r5 = g = 0 \n"
+ "mov r6, #0 @ r6 = a = 0 \n"
+ "add r4, r1, r4, LSL #1 @ r4 = min = &src[2*r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 3f @ { \n"
+ "2: \n"
+ "ldr r14,[r2], #4 @ r14 = *contrib++ \n"
+ "ldrb r11,[r4], #1 @ r11 = *min++ \n"
+ "ldrb r12,[r4], #1 @ r12 = *min++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "mla r5, r14,r11,r5 @ g += r11 * r14 \n"
+ "mla r6, r14,r12,r6 @ a += r12 * r14 \n"
+ "bgt 2b @ } \n"
+ "3: \n"
+ "stmdb r0!,{r5,r6} @ *--dst=a;*--dst=g; \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 1b @ \n"
+ "ldmfd r13!,{r4-r6,r9-r11,PC} @ pop, return to thumb \n"
+ "4:"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "5:"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #0 @ r5 = g = 0 \n"
+ "mov r6, #0 @ r6 = a = 0 \n"
+ "add r4, r1, r4, LSL #1 @ r4 = min = &src[2*r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 7f @ { \n"
+ "6: \n"
+ "ldr r14,[r2], #4 @ r10 = *contrib++ \n"
+ "ldrb r11,[r4], #1 @ r11 = *min++ \n"
+ "ldrb r12,[r4], #1 @ r12 = *min++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "mla r5, r14,r11,r5 @ g += r11 * r14 \n"
+ "mla r6, r14,r12,r6 @ a += r12 * r14 \n"
+ "bgt 6b @ } \n"
+ "7: \n"
+ "stmia r0!,{r5,r6} @ *dst++=r;*dst++=g; \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 5b @ \n"
+ "ldmfd r13!,{r4-r6,r9-r11,PC} @ pop, return to thumb \n"
+ ENTER_THUMB
+ );
+}
+
+static void
+scale_row_to_temp4(int *dst, unsigned char *src, fz_weights *weights)
+{
+ asm volatile(
+ ENTER_ARM
+ "stmfd r13!,{r4-r11,r14} \n"
+ "@ r0 = dst \n"
+ "@ r1 = src \n"
+ "@ r2 = weights \n"
+ "ldr r12,[r2],#4 @ r12= flip \n"
+ "ldr r3, [r2],#16 @ r3 = count r2 = &index\n"
+ "ldr r4, [r2] @ r4 = index[0] \n"
+ "cmp r12,#0 @ if (flip) \n"
+ "beq 4f @ { \n"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "add r0, r0, r3, LSL #4 @ dst += 4*count \n"
+ "1: \n"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #0 @ r5 = r = 0 \n"
+ "mov r6, #0 @ r6 = g = 0 \n"
+ "mov r7, #0 @ r7 = b = 0 \n"
+ "mov r8, #0 @ r8 = a = 0 \n"
+ "add r4, r1, r4, LSL #2 @ r4 = min = &src[4*r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 3f @ { \n"
+ "2: \n"
+ "ldr r10,[r2], #4 @ r10 = *contrib++ \n"
+ "ldrb r11,[r4], #1 @ r11 = *min++ \n"
+ "ldrb r12,[r4], #1 @ r12 = *min++ \n"
+ "ldrb r14,[r4], #1 @ r14 = *min++ \n"
+ "mla r5, r10,r11,r5 @ r += r11 * r10 \n"
+ "ldrb r11,[r4], #1 @ r11 = *min++ \n"
+ "mla r6, r10,r12,r6 @ g += r12 * r10 \n"
+ "mla r7, r10,r14,r7 @ b += r14 * r10 \n"
+ "mla r8, r10,r11,r8 @ a += r11 * r10 \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "bgt 2b @ } \n"
+ "3: \n"
+ "stmdb r0!,{r5,r6,r7,r8} @ *--dst=a;*--dst=b; \n"
+ " @ *--dst=g;*--dst=r; \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 1b @ \n"
+ "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n"
+ "4:"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "5:"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #0 @ r5 = r = 0 \n"
+ "mov r6, #0 @ r6 = g = 0 \n"
+ "mov r7, #0 @ r7 = b = 0 \n"
+ "mov r8, #0 @ r8 = a = 0 \n"
+ "add r4, r1, r4, LSL #2 @ r4 = min = &src[4*r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 7f @ { \n"
+ "6: \n"
+ "ldr r10,[r2], #4 @ r10 = *contrib++ \n"
+ "ldrb r11,[r4], #1 @ r11 = *min++ \n"
+ "ldrb r12,[r4], #1 @ r12 = *min++ \n"
+ "ldrb r14,[r4], #1 @ r14 = *min++ \n"
+ "mla r5, r10,r11,r5 @ r += r11 * r10 \n"
+ "ldrb r11,[r4], #1 @ r11 = *min++ \n"
+ "mla r6, r10,r12,r6 @ g += r12 * r10 \n"
+ "mla r7, r10,r14,r7 @ b += r14 * r10 \n"
+ "mla r8, r10,r11,r8 @ a += r11 * r10 \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "bgt 6b @ } \n"
+ "7: \n"
+ "stmia r0!,{r5,r6,r7,r8} @ *dst++=r;*dst++=g; \n"
+ " @ *dst++=b;*dst++=a; \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 5b @ \n"
+ "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n"
+ ENTER_THUMB
+ );
+}
+
+static void
+scale_row_from_temp(unsigned char *dst, int *src, fz_weights *weights, int width, int row)
+{
+ asm volatile(
+ ENTER_ARM
+ "ldr r12,[r13] @ r12= row \n"
+ "add r2, r2, #20 @ r2 = weights->index \n"
+ "stmfd r13!,{r4-r11,r14} \n"
+ "@ r0 = dst \n"
+ "@ r1 = src \n"
+ "@ r2 = &weights->index[0] \n"
+ "@ r3 = width \n"
+ "@ r12= row \n"
+ "ldr r4, [r2, r12, LSL #2] @ r4 = index[row] \n"
+ "add r2, r2, #4 @ r2 = &index[1] \n"
+ "mov r6, r3 @ r6 = x = width \n"
+ "ldr r14,[r2, r4, LSL #2]! @ r2 = contrib = index[index[row]+1]\n"
+ " @ r14= len = *contrib \n"
+ "1: \n"
+ "mov r5, r1 @ r5 = min = src \n"
+ "mov r7, #1<<15 @ r7 = val = 1<<15 \n"
+ "movs r8, r14 @ r8 = len2 = len \n"
+ "add r9, r2, #4 @ r9 = contrib2 \n"
+ "ble 3f @ while (len2-- > 0) { \n"
+ "2: \n"
+ "ldr r10,[r9], #4 @ r10 = *contrib2++ \n"
+ "ldr r12,[r5], r3, LSL #2 @ r12 = *min r5 = min += width\n"
+ "subs r8, r8, #1 @ len2-- \n"
+ "@ stall r12 \n"
+ "mla r7, r10,r12,r7 @ val += r12 * r10 \n"
+ "bgt 2b @ } \n"
+ "3: \n"
+ "movs r7, r7, asr #16 @ r7 = val >>= 16 \n"
+ "movlt r7, #0 @ if (r7 < 0) r7 = 0 \n"
+ "cmp r7, #255 @ if (r7 > 255) \n"
+ "add r1, r1, #4 @ src++ \n"
+ "movgt r7, #255 @ r7 = 255 \n"
+ "subs r6, r6, #1 @ x-- \n"
+ "strb r7, [r0], #1 @ *dst++ = val \n"
+ "bgt 1b @ \n"
+ "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n"
+ ENTER_THUMB
+ );
+}
+
+#else
+
static void
scale_row_to_temp1(int *dst, unsigned char *src, fz_weights *weights)
{
@@ -672,54 +955,13 @@ static void
scale_row_to_temp4(int *dst, unsigned char *src, fz_weights *weights)
{
int *contrib = &weights->index[weights->index[0]];
-#ifndef ARCH_ARM
int len, i;
unsigned char *min;
-#endif
assert(weights->n == 4);
if (weights->flip)
{
dst += 4*weights->count;
-#ifdef ARCH_ARM
- asm volatile(
- "1:"
- "ldr r4, [%2], #4 @ r4 = *contrib++ \n"
- "ldr r9, [%2], #4 @ r9 = len = *contrib++ \n"
- "mov r5, #0 @ r5 = r = 0 \n"
- "mov r6, #0 @ r6 = g = 0 \n"
- "mov r7, #0 @ r7 = b = 0 \n"
- "mov r8, #0 @ r8 = a = 0 \n"
- "add r4, %1, r4, LSL #2 @ r4 = min = &src[4*r4] \n"
- "cmp r9, #0 @ while (len-- > 0) \n"
- "beq 3f @ { \n"
- "2: \n"
- "ldr r10,[%2], #4 @ r10 = *contrib++ \n"
- "ldrb r11,[r4], #1 @ r11 = *min++ \n"
- "ldrb r12,[r4], #1 @ r12 = *min++ \n"
- "ldrb r14,[r4], #1 @ r14 = *min++ \n"
- "mla r5, r10,r11,r5 @ r += r11 * r10 \n"
- "ldrb r11,[r4], #1 @ r11 = *min++ \n"
- "mla r6, r10,r12,r6 @ g += r12 * r10 \n"
- "mla r7, r10,r14,r7 @ b += r14 * r10 \n"
- "mla r8, r10,r11,r8 @ a += r11 * r10 \n"
- "subs r9, r9, #1 @ r9 = len-- \n"
- "bgt 2b @ } \n"
- "stmdb %0!,{r5,r6,r7,r8} @ *--dst=a;*--dst=b; \n"
- "3: @ *--dst=g;*--dst=r; \n"
- "subs %3, %3, #1 @ i-- \n"
- "bgt 1b @ \n"
- :
- :
- "r" (dst),
- "r" (src),
- "r" (contrib),
- "r" (weights->count)
- :
- "r4","r5","r6","r7","r8","r9","r10","r11","r12","r14",
- "memory","cc"
- );
-#else
for (i=weights->count; i > 0; i--)
{
int r = 0;
@@ -740,49 +982,9 @@ scale_row_to_temp4(int *dst, unsigned char *src, fz_weights *weights)
*--dst = g;
*--dst = r;
}
-#endif
}
else
{
-#ifdef ARCH_ARM
- asm volatile(
- "1:"
- "ldr r4, [%2], #4 @ r4 = *contrib++ \n"
- "ldr r9, [%2], #4 @ r9 = len = *contrib++ \n"
- "mov r5, #0 @ r5 = r = 0 \n"
- "mov r6, #0 @ r6 = g = 0 \n"
- "mov r7, #0 @ r7 = b = 0 \n"
- "mov r8, #0 @ r8 = a = 0 \n"
- "add r4, %1, r4, LSL #2 @ r4 = min = &src[4*r4] \n"
- "cmp r9, #0 @ while (len-- > 0) \n"
- "beq 3f @ { \n"
- "2: \n"
- "ldr r10,[%2], #4 @ r10 = *contrib++ \n"
- "ldrb r11,[r4], #1 @ r11 = *min++ \n"
- "ldrb r12,[r4], #1 @ r12 = *min++ \n"
- "ldrb r14,[r4], #1 @ r14 = *min++ \n"
- "mla r5, r10,r11,r5 @ r += r11 * r10 \n"
- "ldrb r11,[r4], #1 @ r11 = *min++ \n"
- "mla r6, r10,r12,r6 @ g += r12 * r10 \n"
- "mla r7, r10,r14,r7 @ b += r14 * r10 \n"
- "mla r8, r10,r11,r8 @ a += r11 * r10 \n"
- "subs r9, r9, #1 @ r9 = len-- \n"
- "bgt 2b @ } \n"
- "stmia %0!,{r5,r6,r7,r8} @ *dst++=r;*dst++=g; \n"
- "3: @ *dst++=b;*dst++=a; \n"
- "subs %3, %3, #1 @ i-- \n"
- "bgt 1b @ \n"
- :
- :
- "r" (dst),
- "r" (src),
- "r" (contrib),
- "r" (weights->count)
- :
- "r4","r5","r6","r7","r8","r9","r10","r11","r12","r14",
- "memory","cc"
- );
-#else
for (i=weights->count; i > 0; i--)
{
int r = 0;
@@ -803,7 +1005,6 @@ scale_row_to_temp4(int *dst, unsigned char *src, fz_weights *weights)
*dst++ = b;
*dst++ = a;
}
-#endif
}
}
@@ -836,6 +1037,7 @@ scale_row_from_temp(unsigned char *dst, int *src, fz_weights *weights, int width
src++;
}
}
+#endif
#ifdef SINGLE_PIXEL_SPECIALS
static void
@@ -1004,64 +1206,6 @@ scale_single_col(unsigned char *dst, unsigned char *src, fz_weights *weights, in
}
#endif /* SINGLE_PIXEL_SPECIALS */
-fz_pixmap *
-fz_scale_pixmap_gridfit(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h, int gridfit)
-{
- if (gridfit) {
- float n;
- if (w > 0) {
- /* Adjust the left hand edge, leftwards to a pixel boundary */
- n = (float)(int)x; /* n is now on a pixel boundary */
- if (n > x) /* Ensure it's the pixel boundary BELOW x */
- n -= 1.0f;
- w += x-n; /* width gets wider as x >= n */
- x = n;
- /* Adjust the right hand edge rightwards to a pixel boundary */
- n = (float)(int)w; /* n is now the integer width <= w */
- if (n != w) /* If w isn't an integer already, bump it */
- w = 1.0f + n;/* up to the next integer. */
- } else {
- /* Adjust the right hand edge, rightwards to a pixel boundary */
- n = (float)(int)x; /* n is now on a pixel boundary */
- if (n > x) /* Ensure it's the pixel boundary <= x */
- n -= 1.0f;
- if (n != x) /* If x isn't on a pixel boundary already, */
- n += 1.0f; /* make n be the pixel boundary above x. */
- w -= n-x; /* Expand width (more negative!) as n >= x */
- x = n;
- /* Adjust the left hand edge leftwards to a pixel boundary */
- n = (float)(int)w;
- if (n != w)
- w = n - 1.0f;
- }
- if (h > 0) {
- /* Adjust the bottom edge, downwards to a pixel boundary */
- n = (float)(int)y; /* n is now on a pixel boundary */
- if (n > y) /* Ensure it's the pixel boundary BELOW y */
- n -= 1.0f;
- h += y-n; /* height gets larger as y >= n */
- y = n;
- /* Adjust the top edge upwards to a pixel boundary */
- n = (float)(int)h; /* n is now the integer height <= h */
- if (n != h) /* If h isn't an integer already, bump it */
- h = 1.0f + n;/* up to the next integer. */
- } else {
- /* Adjust the top edge, upwards to a pixel boundary */
- n = (float)(int)y; /* n is now on a pixel boundary */
- if (n > y) /* Ensure it's the pixel boundary <= y */
- n -= 1.0f;
- if (n != y) /* If y isn't on a pixel boundary already, */
- n += 1.0f; /* make n be the pixel boundary above y. */
- h -= n-y; /* Expand height (more negative!) as n >= y */
- y = n;
- /* Adjust the bottom edge downwards to a pixel boundary */
- n = (float)(int)h;
- if (n != h)
- h = n - 1.0f;
- }
- }
- return fz_scale_pixmap(ctx, src, x, y, w, h);
-}
fz_pixmap *
fz_scale_pixmap(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h)
diff --git a/draw/draw_simple_scale.c b/draw/draw_simple_scale.c
new file mode 100644
index 00000000..dcd5cd68
--- /dev/null
+++ b/draw/draw_simple_scale.c
@@ -0,0 +1,1353 @@
+/*
+This code does smooth scaling of a pixmap.
+
+This function returns a new pixmap representing the area starting at (0,0)
+given by taking the source pixmap src, scaling it to width w, and height h,
+and then positioning it at (frac(x),frac(y)).
+
+This is a cut-down version of draw_scale.c that only copes with filters
+that return values strictly in the 0..1 range, and uses bytes for
+intermediate results rather than ints.
+*/
+
+#include "fitz.h"
+
+/* Do we special case handling of single pixel high/wide images? The
+ * 'purest' handling is given by not special casing them, but certain
+ * files that use such images 'stack' them to give full images. Not
+ * special casing them results in then being fainter and giving noticable
+ * rounding errors.
+ */
+#define SINGLE_PIXEL_SPECIALS
+
+/* If we're compiling as thumb code, then we need to tell the compiler
+ * to enter and exit ARM mode around our assembly sections. If we move
+ * the ARM functions to a separate file and arrange for it to be compiled
+ * without thumb mode, we can save some time on entry.
+ */
+#ifdef ARCH_ARM
+#ifdef ARCH_THUMB
+#define ENTER_ARM ".balign 4\nmov r12,pc\nbx r12\n0:.arm\n"
+#define ENTER_THUMB "9:.thumb\n"
+#else
+#define ENTER_ARM
+#define ENTER_THUMB
+#endif
+#endif
+
+#ifdef DEBUG_SCALING
+#ifdef WIN32
+#include <windows.h>
+static void debug_print(const char *fmt, ...)
+{
+ va_list args;
+ char text[256];
+ va_start(args, fmt);
+ vsprintf(text, fmt, args);
+ va_end(args);
+ OutputDebugStringA(text);
+ printf(text);
+}
+#else
+static void debug_print(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
+#endif
+#endif
+#ifdef DEBUG_SCALING
+#define DBUG(A) debug_print A
+#else
+#define DBUG(A) do {} while(0==1)
+#endif
+
+/*
+Consider a row of source samples, src, of width src_w, positioned at x,
+scaled to width dst_w.
+
+src[i] is centred at: x + (i + 0.5)*dst_w/src_w
+
+Therefore the distance between the centre of the jth output pixel and
+the centre of the ith source sample is:
+
+dist[j,i] = j + 0.5 - (x + (i + 0.5)*dst_w/src_w)
+
+When scaling up, therefore:
+
+dst[j] = SUM(filter(dist[j,i]) * src[i])
+ (for all ints i)
+
+This can be simplified by noticing that filters are only non zero within
+a given filter width (henceforth called W). So:
+
+dst[j] = SUM(filter(dist[j,i]) * src[i])
+ (for ints i, s.t. (j*src_w/dst_w)-W < i < (j*src_w/dst_w)+W)
+
+When scaling down, each filtered source sample is stretched to be wider
+to avoid aliasing issues. This effectively reduces the distance between
+centres.
+
+dst[j] = SUM(filter(dist[j,i] * F) * F * src[i])
+ (where F = dst_w/src_w)
+ (for ints i, s.t. (j-W)/F < i < (j+W)/F)
+
+*/
+
+typedef struct fz_scale_filter_s fz_scale_filter;
+
+struct fz_scale_filter_s
+{
+ int width;
+ float (*fn)(fz_scale_filter *, float);
+};
+
+/* Image scale filters */
+
+static float
+triangle(fz_scale_filter *filter, float f)
+{
+ if (f >= 1)
+ return 0;
+ return 1-f;
+}
+
+static float
+box(fz_scale_filter *filter, float f)
+{
+ if (f >= 0.5f)
+ return 0;
+ return 1;
+}
+
+static float
+simple(fz_scale_filter *filter, float x)
+{
+ if (x >= 1)
+ return 0;
+ return 1 + (2*x - 3)*x*x;
+}
+
+fz_scale_filter fz_scale_filter_box = { 1, box };
+fz_scale_filter fz_scale_filter_triangle = { 1, triangle };
+fz_scale_filter fz_scale_filter_simple = { 1, simple };
+
+/*
+We build ourselves a set of tables to contain the precalculated weights
+for a given set of scale settings.
+
+The first dst_w entries in index are the index into index of the
+sets of weight for each destination pixel.
+
+Each of the sets of weights is a set of values consisting of:
+ the minimum source pixel index used for this destination pixel
+ the number of weights used for this destination pixel
+ the weights themselves
+
+So to calculate dst[i] we do the following:
+
+ weights = &index[index[i]];
+ min = *weights++;
+ len = *weights++;
+ dst[i] = 0;
+ while (--len > 0)
+ dst[i] += src[min++] * *weights++
+
+in addition, we guarantee that at the end of this process weights will now
+point to the weights value for dst pixel i+1.
+
+In the simplest version of this algorithm, we would scale the whole image
+horizontally first into a temporary buffer, then scale that temporary
+buffer again vertically to give us our result. Using such a simple
+algorithm would mean that could use the same style of weights for both
+horizontal and vertical scaling.
+
+Unfortunately, this would also require a large temporary buffer,
+particularly in the case where we are scaling up.
+
+We therefore modify the algorithm as follows; we scale scanlines from the
+source image horizontally into a temporary buffer, until we have all the
+contributors for a given output scanline. We then produce that output
+scanline from the temporary buffer. In this way we restrict the height
+of the temporary buffer to a small fraction of the final size.
+
+Unfortunately, this means that the pseudo code for recombining a
+scanline of fully scaled pixels is as follows:
+
+ weights = &index[index[y]];
+ min = *weights++;
+ len = *weights++;
+ for (x=0 to dst_w)
+ min2 = min
+ len2 = len
+ weights2 = weights
+ dst[x] = 0;
+ while (--len2 > 0)
+ dst[x] += temp[x][(min2++) % tmp_buf_height] * *weights2++
+
+i.e. it requires a % operation for every source pixel - this is typically
+expensive.
+
+To avoid this, we alter the order in which vertical weights are stored,
+so that they are ordered in the same order as the temporary buffer lines
+would appear. This simplifies the algorithm to:
+
+ weights = &index[index[y]];
+ min = *weights++;
+ len = *weights++;
+ for (x=0 to dst_w)
+ min2 = 0
+ len2 = len
+ weights2 = weights
+ dst[x] = 0;
+ while (--len2 > 0)
+ dst[x] += temp[i][min2++] * *weights2++
+
+This means that len may be larger than it needs to be (due to the
+possible inclusion of a zero weight row or two), but in practise this
+is only an increase of 1 or 2 at worst.
+
+We implement this by generating the weights as normal (but ensuring we
+leave enough space) and then reordering afterwards.
+
+*/
+
+typedef struct fz_weights_s fz_weights;
+
+struct fz_weights_s
+{
+ int flip;
+ int count;
+ int max_len;
+ int n;
+ int new_line;
+ int index[1];
+};
+
+static fz_weights *
+new_weights(fz_context *ctx, fz_scale_filter *filter, int src_w, float dst_w, int dst_w_i, int n, int flip)
+{
+ int max_len;
+ fz_weights *weights;
+
+ if (src_w > dst_w)
+ {
+ /* Scaling down, so there will be a maximum of
+ * 2*filterwidth*src_w/dst_w src pixels
+ * contributing to each dst pixel. */
+ max_len = (int)ceilf((2 * filter->width * src_w)/dst_w);
+ if (max_len > src_w)
+ max_len = src_w;
+ }
+ else
+ {
+ /* Scaling up, so there will be a maximum of
+ * 2*filterwidth src pixels contributing to each dst pixel.
+ */
+ max_len = 2 * filter->width;
+ }
+ /* We need the size of the struct,
+ * plus dst_w*sizeof(int) for the index
+ * plus (2+max_len)*sizeof(int) for the weights
+ * plus room for an extra set of weights for reordering.
+ */
+ weights = fz_malloc(ctx, sizeof(*weights)+(max_len+3)*(dst_w_i+1)*sizeof(int));
+ if (weights == NULL)
+ return NULL;
+ weights->count = -1;
+ weights->max_len = max_len;
+ weights->index[0] = dst_w_i;
+ weights->n = n;
+ weights->flip = flip;
+ return weights;
+}
+
+static void
+init_weights(fz_weights *weights, int j)
+{
+ int index;
+
+ assert(weights->count == j-1);
+ weights->count++;
+ weights->new_line = 1;
+ if (j == 0)
+ index = weights->index[0];
+ else
+ {
+ index = weights->index[j-1];
+ index += 2 + weights->index[index+1];
+ }
+ weights->index[j] = index; /* row pointer */
+ weights->index[index] = 0; /* min */
+ weights->index[index+1] = 0; /* len */
+}
+
+static void
+add_weight(fz_weights *weights, int j, int i, fz_scale_filter *filter,
+ float x, float F, float G, int src_w, float dst_w)
+{
+ float dist = j - x + 0.5f - ((i + 0.5f)*dst_w/src_w);
+ float f;
+ int min, len, index, weight;
+
+ dist *= G;
+ if (dist < 0)
+ dist = -dist;
+ f = filter->fn(filter, dist)*F;
+ weight = (int)(256*f+0.5f);
+ if (weight == 0)
+ return;
+
+ /* Ensure i is in range */
+ if (i < 0)
+ {
+ i = 0;
+ weight = 0;
+ }
+ else if (i >= src_w)
+ {
+ i = src_w-1;
+ weight = 0;
+ }
+ if (weight == 0)
+ return;
+
+ DBUG(("add_weight[%d][%d] = %d(%g) dist=%g\n",j,i,weight,f,dist));
+
+ if (weights->new_line)
+ {
+ /* New line */
+ weights->new_line = 0;
+ index = weights->index[j]; /* row pointer */
+ weights->index[index] = i; /* min */
+ weights->index[index+1] = 0; /* len */
+ }
+ index = weights->index[j];
+ min = weights->index[index++];
+ len = weights->index[index++];
+ while (i < min)
+ {
+ /* This only happens in rare cases, but we need to insert
+ * one earlier. In exceedingly rare cases we may need to
+ * insert more than one earlier. */
+ int k;
+
+ for (k = len; k > 0; k--)
+ {
+ weights->index[index+k] = weights->index[index+k-1];
+ }
+ weights->index[index] = 0;
+ min--;
+ len++;
+ weights->index[index-2] = min;
+ weights->index[index-1] = len;
+ }
+ if (i-min >= len)
+ {
+ /* The usual case */
+ while (i-min >= ++len)
+ {
+ weights->index[index+len-1] = 0;
+ }
+ assert(len-1 == i-min);
+ weights->index[index+i-min] = weight;
+ weights->index[index-1] = len;
+ assert(len <= weights->max_len);
+ }
+ else
+ {
+ /* Infrequent case */
+ weights->index[index+i-min] += weight;
+ }
+}
+
+static void
+reorder_weights(fz_weights *weights, int j, int src_w)
+{
+ int idx = weights->index[j];
+ int min = weights->index[idx++];
+ int len = weights->index[idx++];
+ int max = weights->max_len;
+ int tmp = idx+max;
+ int i, off;
+
+ /* Copy into the temporary area */
+ memcpy(&weights->index[tmp], &weights->index[idx], sizeof(int)*len);
+
+ /* Pad out if required */
+ assert(len <= max);
+ assert(min+len <= src_w);
+ off = 0;
+ if (len < max)
+ {
+ memset(&weights->index[tmp+len], 0, sizeof(int)*(max-len));
+ len = max;
+ if (min + len > src_w)
+ {
+ off = min + len - src_w;
+ min = src_w - len;
+ weights->index[idx-2] = min;
+ }
+ weights->index[idx-1] = len;
+ }
+
+ /* Copy back into the proper places */
+ for (i = 0; i < len; i++)
+ {
+ weights->index[idx+((min+i+off) % max)] = weights->index[tmp+i];
+ }
+}
+
+/* Due to rounding and edge effects, the sums for the weights sometimes don't
+ * add up to 256. This causes visible rendering effects. Therefore, we take
+ * pains to ensure that they 1) never exceed 256, and 2) add up to exactly
+ * 256 for all pixels that are completely covered. See bug #691629. */
+static void
+check_weights(fz_weights *weights, int j, int w, float x, float wf)
+{
+ int idx, len;
+ int sum = 0;
+ int max = -256;
+ int maxidx = 0;
+ int i;
+
+ idx = weights->index[j];
+ idx++; /* min */
+ len = weights->index[idx++];
+
+ for(i=0; i < len; i++)
+ {
+ int v = weights->index[idx++];
+ sum += v;
+ if (v > max)
+ {
+ max = v;
+ maxidx = idx;
+ }
+ }
+ /* If we aren't the first or last pixel, OR if the sum is too big
+ * then adjust it. */
+ if (((j != 0) && (j != w-1)) || (sum > 256))
+ weights->index[maxidx-1] += 256-sum;
+ /* Otherwise, if we are the first pixel, and it's fully covered, then
+ * adjust it. */
+ else if ((j == 0) && (x < 0.0001F) && (sum != 256))
+ weights->index[maxidx-1] += 256-sum;
+ /* Finally, if we are the last pixel, and it's fully covered, then
+ * adjust it. */
+ else if ((j == w-1) && ((float)w-wf < 0.0001F) && (sum != 256))
+ weights->index[maxidx-1] += 256-sum;
+ DBUG(("total weight %d = %d\n", j, sum));
+}
+
+static fz_weights *
+make_weights(fz_context *ctx, int src_w, float x, float dst_w, fz_scale_filter *filter, int vertical, int dst_w_int, int n, int flip)
+{
+ fz_weights *weights;
+ float F, G;
+ float window;
+ int j;
+
+ if (dst_w < src_w)
+ {
+ /* Scaling down */
+ F = dst_w / src_w;
+ G = 1;
+ }
+ else
+ {
+ /* Scaling up */
+ F = 1;
+ G = src_w / dst_w;
+ }
+ window = filter->width / F;
+ DBUG(("make_weights src_w=%d x=%g dst_w=%g dst_w_int=%d F=%g window=%g\n", src_w, x, dst_w, dst_w_int, F, window));
+ weights = new_weights(ctx, filter, src_w, dst_w, dst_w_int, n, flip);
+ if (weights == NULL)
+ return NULL;
+ for (j = 0; j < dst_w_int; j++)
+ {
+ /* find the position of the centre of dst[j] in src space */
+ float centre = (j - x + 0.5f)*src_w/dst_w - 0.5f;
+ int l, r;
+ l = ceilf(centre - window);
+ r = floorf(centre + window);
+ DBUG(("%d: centre=%g l=%d r=%d\n", j, centre, l, r));
+ init_weights(weights, j);
+ for (; l <= r; l++)
+ {
+ add_weight(weights, j, l, filter, x, F, G, src_w, dst_w);
+ }
+ check_weights(weights, j, dst_w_int, x, dst_w);
+ if (vertical)
+ {
+ reorder_weights(weights, j, src_w);
+ }
+ }
+ weights->count++; /* weights->count = dst_w_int now */
+ return weights;
+}
+
+static void
+scale_row_to_temp(unsigned char *dst, unsigned char *src, fz_weights *weights)
+{
+ int *contrib = &weights->index[weights->index[0]];
+ int len, i, j, n;
+ unsigned char *min;
+ int tmp[FZ_MAX_COLORS];
+ int *t = tmp;
+
+ n = weights->n;
+ for (j = 0; j < n; j++)
+ tmp[j] = 128;
+ if (weights->flip)
+ {
+ dst += (weights->count-1)*n;
+ for (i=weights->count; i > 0; i--)
+ {
+ min = &src[n * *contrib++];
+ len = *contrib++;
+ while (len-- > 0)
+ {
+ for (j = n; j > 0; j--)
+ *t++ += *min++ * *contrib;
+ t -= n;
+ contrib++;
+ }
+ for (j = n; j > 0; j--)
+ {
+ *dst++ = (unsigned char)(*t>>8);
+ *t++ = 128;
+ }
+ t -= n;
+ dst -= n*2;
+ }
+ }
+ else
+ {
+ for (i=weights->count; i > 0; i--)
+ {
+ min = &src[n * *contrib++];
+ len = *contrib++;
+ while (len-- > 0)
+ {
+ for (j = n; j > 0; j--)
+ *t++ += *min++ * *contrib;
+ t -= n;
+ contrib++;
+ }
+ for (j = n; j > 0; j--)
+ {
+ *dst++ = (unsigned char)(*t>>8);
+ *t++ = 128;
+ }
+ t -= n;
+ }
+ }
+}
+
+#ifdef ARCH_ARM
+
+static void
+scale_row_to_temp1(unsigned char *dst, unsigned char *src, fz_weights *weights)
+__attribute__((naked));
+
+static void
+scale_row_to_temp2(unsigned char *dst, unsigned char *src, fz_weights *weights)
+__attribute__((naked));
+
+static void
+scale_row_to_temp4(unsigned char *dst, unsigned char *src, fz_weights *weights)
+__attribute__((naked));
+
+static void
+scale_row_from_temp(unsigned char *dst, unsigned char *src, fz_weights *weights, int width, int row)
+__attribute__((naked));
+
+static void
+scale_row_to_temp1(unsigned char *dst, unsigned char *src, fz_weights *weights)
+{
+ /* possible optimisation in here; unroll inner loops to avoid stall. */
+ asm volatile(
+ ENTER_ARM
+ "stmfd r13!,{r4-r5,r9,r14} \n"
+ "@ r0 = dst \n"
+ "@ r1 = src \n"
+ "@ r2 = weights \n"
+ "ldr r12,[r2],#4 @ r12= flip \n"
+ "ldr r3, [r2],#16 @ r3 = count r2 = &index\n"
+ "ldr r4, [r2] @ r4 = index[0] \n"
+ "cmp r12,#0 @ if (flip) \n"
+ "beq 4f @ { \n"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "add r0, r0, r3 @ dst += count \n"
+ "1: \n"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #128 @ r5 = a = 128 \n"
+ "add r4, r1, r4 @ r4 = min = &src[r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 3f @ { \n"
+ "2: \n"
+ "ldr r12,[r2], #4 @ r12 = *contrib++ \n"
+ "ldrb r14,[r4], #1 @ r14 = *min++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "@stall on r14 \n"
+ "mla r5, r12,r14,r5 @ g += r14 * r12 \n"
+ "bgt 2b @ } \n"
+ "3: \n"
+ "mov r5, r5, lsr #8 @ g >>= 8 \n"
+ "strb r5,[r0, #-1]! @ *--dst=a \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 1b @ \n"
+ "ldmfd r13!,{r4-r5,r9,PC} @ pop, return to thumb \n"
+ "4:"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "5:"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #128 @ r5 = a = 128 \n"
+ "add r4, r1, r4 @ r4 = min = &src[r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 7f @ { \n"
+ "6: \n"
+ "ldr r12,[r2], #4 @ r12 = *contrib++ \n"
+ "ldrb r14,[r4], #1 @ r14 = *min++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "@stall on r14 \n"
+ "mla r5, r12,r14,r5 @ a += r14 * r12 \n"
+ "bgt 6b @ } \n"
+ "7: \n"
+ "mov r5, r5, LSR #8 @ a >>= 8 \n"
+ "strb r5, [r0], #1 @ *dst++=a \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 5b @ \n"
+ "ldmfd r13!,{r4-r5,r9,PC} @ pop, return to thumb \n"
+ ENTER_THUMB
+ );
+}
+
+static void
+scale_row_to_temp2(unsigned char *dst, unsigned char *src, fz_weights *weights)
+{
+ asm volatile(
+ ENTER_ARM
+ "stmfd r13!,{r4-r6,r9-r11,r14} \n"
+ "@ r0 = dst \n"
+ "@ r1 = src \n"
+ "@ r2 = weights \n"
+ "ldr r12,[r2],#4 @ r12= flip \n"
+ "ldr r3, [r2],#16 @ r3 = count r2 = &index\n"
+ "ldr r4, [r2] @ r4 = index[0] \n"
+ "cmp r12,#0 @ if (flip) \n"
+ "beq 4f @ { \n"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "add r0, r0, r3, LSL #1 @ dst += 2*count \n"
+ "1: \n"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #128 @ r5 = g = 128 \n"
+ "mov r6, #128 @ r6 = a = 128 \n"
+ "add r4, r1, r4, LSL #1 @ r4 = min = &src[2*r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 3f @ { \n"
+ "2: \n"
+ "ldr r14,[r2], #4 @ r14 = *contrib++ \n"
+ "ldrb r11,[r4], #1 @ r11 = *min++ \n"
+ "ldrb r12,[r4], #1 @ r12 = *min++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "mla r5, r14,r11,r5 @ g += r11 * r14 \n"
+ "mla r6, r14,r12,r6 @ a += r12 * r14 \n"
+ "bgt 2b @ } \n"
+ "3: \n"
+ "mov r5, r5, lsr #8 @ g >>= 8 \n"
+ "mov r6, r6, lsr #8 @ a >>= 8 \n"
+ "strb r5, [r0, #-2]! @ *--dst=a \n"
+ "strb r6, [r0, #1] @ *--dst=g \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 1b @ \n"
+ "ldmfd r13!,{r4-r6,r9-r11,PC} @ pop, return to thumb \n"
+ "4:"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "5:"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r5, #128 @ r5 = g = 128 \n"
+ "mov r6, #128 @ r6 = a = 128 \n"
+ "add r4, r1, r4, LSL #1 @ r4 = min = &src[2*r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 7f @ { \n"
+ "6: \n"
+ "ldr r14,[r2], #4 @ r10 = *contrib++ \n"
+ "ldrb r11,[r4], #1 @ r11 = *min++ \n"
+ "ldrb r12,[r4], #1 @ r12 = *min++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "mla r5, r14,r11,r5 @ g += r11 * r14 \n"
+ "mla r6, r14,r12,r6 @ a += r12 * r14 \n"
+ "bgt 6b @ } \n"
+ "7: \n"
+ "mov r5, r5, lsr #8 @ g >>= 8 \n"
+ "mov r6, r6, lsr #8 @ a >>= 8 \n"
+ "strb r5, [r0], #1 @ *dst++=g \n"
+ "strb r6, [r0], #1 @ *dst++=a \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 5b @ \n"
+ "ldmfd r13!,{r4-r6,r9-r11,PC} @ pop, return to thumb \n"
+ ENTER_THUMB
+ );
+}
+
+static void
+scale_row_to_temp4(unsigned char *dst, unsigned char *src, fz_weights *weights)
+{
+ asm volatile(
+ ENTER_ARM
+ "stmfd r13!,{r4-r11,r14} \n"
+ "@ r0 = dst \n"
+ "@ r1 = src \n"
+ "@ r2 = weights \n"
+ "ldr r12,[r2],#4 @ r12= flip \n"
+ "ldr r3, [r2],#16 @ r3 = count r2 = &index\n"
+ "ldr r4, [r2] @ r4 = index[0] \n"
+ "ldr r5,=0x00800080 @ r5 = rounding \n"
+ "ldr r6,=0x00FF00FF @ r7 = 0x00FF00FF \n"
+ "cmp r12,#0 @ if (flip) \n"
+ "beq 4f @ { \n"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "add r0, r0, r3, LSL #2 @ dst += 4*count \n"
+ "1: \n"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r7, r5 @ r7 = b = rounding \n"
+ "mov r8, r5 @ r8 = a = rounding \n"
+ "add r4, r1, r4, LSL #2 @ r4 = min = &src[4*r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 3f @ { \n"
+ "2: \n"
+ "ldr r11,[r4], #4 @ r11 = *min++ \n"
+ "ldr r10,[r2], #4 @ r10 = *contrib++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "and r12,r6, r11 @ r12 = __22__00 \n"
+ "and r11,r6, r11,LSR #8 @ r11 = __33__11 \n"
+ "mla r7, r10,r12,r7 @ b += r14 * r10 \n"
+ "mla r8, r10,r11,r8 @ a += r11 * r10 \n"
+ "bgt 2b @ } \n"
+ "3: \n"
+ "and r7, r6, r7, lsr #8 @ r7 = __22__00 \n"
+ "bic r8, r8, r6 @ r8 = 33__11__ \n"
+ "orr r7, r7, r8 @ r7 = 33221100 \n"
+ "str r7, [r0, #-4]! @ *--dst=r \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 1b @ \n"
+ "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n"
+ "4: \n"
+ "add r2, r2, r4, LSL #2 @ r2 = &index[index[0]] \n"
+ "5: \n"
+ "ldr r4, [r2], #4 @ r4 = *contrib++ \n"
+ "ldr r9, [r2], #4 @ r9 = len = *contrib++ \n"
+ "mov r7, r5 @ r7 = b = rounding \n"
+ "mov r8, r5 @ r8 = a = rounding \n"
+ "add r4, r1, r4, LSL #2 @ r4 = min = &src[4*r4] \n"
+ "cmp r9, #0 @ while (len-- > 0) \n"
+ "beq 7f @ { \n"
+ "6: \n"
+ "ldr r11,[r4], #4 @ r11 = *min++ \n"
+ "ldr r10,[r2], #4 @ r10 = *contrib++ \n"
+ "subs r9, r9, #1 @ r9 = len-- \n"
+ "and r12,r6, r11 @ r12 = __22__00 \n"
+ "and r11,r6, r11,LSR #8 @ r11 = __33__11 \n"
+ "mla r7, r10,r12,r7 @ b += r14 * r10 \n"
+ "mla r8, r10,r11,r8 @ a += r11 * r10 \n"
+ "bgt 6b @ } \n"
+ "7: \n"
+ "and r7, r6, r7, lsr #8 @ r7 = __22__00 \n"
+ "bic r8, r8, r6 @ r8 = 33__11__ \n"
+ "orr r7, r7, r8 @ r7 = 33221100 \n"
+ "str r7, [r0], #4 @ *dst++=r \n"
+ "subs r3, r3, #1 @ i-- \n"
+ "bgt 5b @ \n"
+ "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n"
+ ENTER_THUMB
+ );
+}
+
+static void
+scale_row_from_temp(unsigned char *dst, unsigned char *src, fz_weights *weights, int width, int row)
+{
+ asm volatile(
+ ENTER_ARM
+ "ldr r12,[r13] @ r12= row \n"
+ "add r2, r2, #20 @ r2 = weights->index \n"
+ "stmfd r13!,{r4-r11,r14} \n"
+ "@ r0 = dst \n"
+ "@ r1 = src \n"
+ "@ r2 = &weights->index[0] \n"
+ "@ r3 = width \n"
+ "@ r12= row \n"
+ "ldr r4, [r2, r12, LSL #2] @ r4 = index[row] \n"
+ "add r2, r2, #4 @ r2 = &index[1] \n"
+ "subs r6, r3, #4 @ r6 = x = width-4 \n"
+ "ldr r14,[r2, r4, LSL #2]! @ r2 = contrib = index[index[row]+1]\n"
+ " @ r14= len = *contrib \n"
+ "blt 4f @ while (x >= 0) { \n"
+#ifndef ARCH_ARM_CAN_LOAD_UNALIGNED
+ "tst r3, #3 @ if (r3 & 3) \n"
+ "blt 4f @ can't do fast code\n"
+#endif
+ "ldr r9, =0x00FF00FF @ r9 = 0x00FF00FF \n"
+ "1: \n"
+ "ldr r5, =0x00800080 @ r5 = val0 = round \n"
+ "stmfd r13!,{r1,r2} @ stash r1,r2,r14 \n"
+ " @ r1 = min = src \n"
+ " @ r2 = contrib2-4 \n"
+ "movs r8, r14 @ r8 = len2 = len \n"
+ "mov r7, r5 @ r7 = val1 = round \n"
+ "ble 3f @ while (len2-- > 0) { \n"
+ "2: \n"
+ "ldr r12,[r1], r3 @ r12 = *min r5 = min += width\n"
+ "ldr r10,[r2, #4]! @ r10 = *contrib2++ \n"
+ "subs r8, r8, #1 @ len2-- \n"
+ "and r11,r9, r12 @ r11= __22__00 \n"
+ "and r12,r9, r12,LSR #8 @ r12= __33__11 \n"
+ "mla r5, r10,r11,r5 @ r5 = val0 += r11 * r10\n"
+ "mla r7, r10,r12,r7 @ r7 = val1 += r12 * r10\n"
+ "bgt 2b @ } \n"
+ "3: \n"
+ "ldmfd r13!,{r1,r2} @ restore r1,r2,r14 \n"
+ "and r5, r9, r5, LSR #8 @ r5 = __22__00 \n"
+ "and r7, r7, r9, LSL #8 @ r7 = 33__11__ \n"
+ "orr r5, r5, r7 @ r5 = 33221100 \n"
+ "subs r6, r6, #4 @ x-- \n"
+ "add r1, r1, #4 @ src++ \n"
+ "str r5, [r0], #4 @ *dst++ = val \n"
+ "bge 1b @ \n"
+ "4: @ } (Less than 4 to go) \n"
+ "adds r6, r6, #4 @ r6 = x += 4 \n"
+ "beq 8f @ if (x == 0) done \n"
+ "5: \n"
+ "mov r5, r1 @ r5 = min = src \n"
+ "mov r7, #128 @ r7 = val = 128 \n"
+ "movs r8, r14 @ r8 = len2 = len \n"
+ "add r9, r2, #4 @ r9 = contrib2 \n"
+ "ble 7f @ while (len2-- > 0) { \n"
+ "6: \n"
+ "ldr r10,[r9], #4 @ r10 = *contrib2++ \n"
+ "ldrb r12,[r5], r3 @ r12 = *min r5 = min += width\n"
+ "subs r8, r8, #1 @ len2-- \n"
+ "@ stall r12 \n"
+ "mla r7, r10,r12,r7 @ val += r12 * r10 \n"
+ "bgt 6b @ } \n"
+ "7: \n"
+ "mov r7, r7, asr #8 @ r7 = val >>= 8 \n"
+ "subs r6, r6, #1 @ x-- \n"
+ "add r1, r1, #1 @ src++ \n"
+ "strb r7, [r0], #1 @ *dst++ = val \n"
+ "bgt 5b @ \n"
+ "8: \n"
+ "ldmfd r13!,{r4-r11,PC} @ pop, return to thumb \n"
+ ENTER_THUMB
+ );
+}
+#else
+
+static void
+scale_row_to_temp1(unsigned char *dst, unsigned char *src, fz_weights *weights)
+{
+ int *contrib = &weights->index[weights->index[0]];
+ int len, i;
+ unsigned char *min;
+
+ assert(weights->n == 1);
+ if (weights->flip)
+ {
+ dst += weights->count;
+ for (i=weights->count; i > 0; i--)
+ {
+ int val = 128;
+ min = &src[*contrib++];
+ len = *contrib++;
+ while (len-- > 0)
+ {
+ val += *min++ * *contrib++;
+ }
+ *--dst = (unsigned char)(val>>8);
+ }
+ }
+ else
+ {
+ for (i=weights->count; i > 0; i--)
+ {
+ int val = 128;
+ min = &src[*contrib++];
+ len = *contrib++;
+ while (len-- > 0)
+ {
+ val += *min++ * *contrib++;
+ }
+ *dst++ = (unsigned char)(val>>8);
+ }
+ }
+}
+
+static void
+scale_row_to_temp2(unsigned char *dst, unsigned char *src, fz_weights *weights)
+{
+ int *contrib = &weights->index[weights->index[0]];
+ int len, i;
+ unsigned char *min;
+
+ assert(weights->n == 2);
+ if (weights->flip)
+ {
+ dst += 2*weights->count;
+ for (i=weights->count; i > 0; i--)
+ {
+ int c1 = 128;
+ int c2 = 128;
+ min = &src[2 * *contrib++];
+ len = *contrib++;
+ while (len-- > 0)
+ {
+ c1 += *min++ * *contrib;
+ c2 += *min++ * *contrib++;
+ }
+ *--dst = (unsigned char)(c2>>8);
+ *--dst = (unsigned char)(c1>>8);
+ }
+ }
+ else
+ {
+ for (i=weights->count; i > 0; i--)
+ {
+ int c1 = 128;
+ int c2 = 128;
+ min = &src[2 * *contrib++];
+ len = *contrib++;
+ while (len-- > 0)
+ {
+ c1 += *min++ * *contrib;
+ c2 += *min++ * *contrib++;
+ }
+ *dst++ = (unsigned char)(c1>>8);
+ *dst++ = (unsigned char)(c2>>8);
+ }
+ }
+}
+
+static void
+scale_row_to_temp4(unsigned char *dst, unsigned char *src, fz_weights *weights)
+{
+ int *contrib = &weights->index[weights->index[0]];
+ int len, i;
+ unsigned char *min;
+
+ assert(weights->n == 4);
+ if (weights->flip)
+ {
+ dst += 4*weights->count;
+ for (i=weights->count; i > 0; i--)
+ {
+ int r = 128;
+ int g = 128;
+ int b = 128;
+ int a = 128;
+ min = &src[4 * *contrib++];
+ len = *contrib++;
+ while (len-- > 0)
+ {
+ r += *min++ * *contrib;
+ g += *min++ * *contrib;
+ b += *min++ * *contrib;
+ a += *min++ * *contrib++;
+ }
+ *--dst = (unsigned char)(a>>8);
+ *--dst = (unsigned char)(b>>8);
+ *--dst = (unsigned char)(g>>8);
+ *--dst = (unsigned char)(r>>8);
+ }
+ }
+ else
+ {
+ for (i=weights->count; i > 0; i--)
+ {
+ int r = 128;
+ int g = 128;
+ int b = 128;
+ int a = 128;
+ min = &src[4 * *contrib++];
+ len = *contrib++;
+ while (len-- > 0)
+ {
+ r += *min++ * *contrib;
+ g += *min++ * *contrib;
+ b += *min++ * *contrib;
+ a += *min++ * *contrib++;
+ }
+ *dst++ = (unsigned char)(r>>8);
+ *dst++ = (unsigned char)(g>>8);
+ *dst++ = (unsigned char)(b>>8);
+ *dst++ = (unsigned char)(a>>8);
+ }
+ }
+}
+
+static void
+scale_row_from_temp(unsigned char *dst, unsigned char *src, fz_weights *weights, int width, int row)
+{
+ int *contrib = &weights->index[weights->index[row]];
+ int len, x;
+
+ contrib++; /* Skip min */
+ len = *contrib++;
+ for (x=width; x > 0; x--)
+ {
+ unsigned char *min = src;
+ int val = 128;
+ int len2 = len;
+ int *contrib2 = contrib;
+
+ while (len2-- > 0)
+ {
+ val += *min * *contrib2++;
+ min += width;
+ }
+ *dst++ = (unsigned char)(val>>8);
+ src++;
+ }
+}
+#endif
+
+#ifdef SINGLE_PIXEL_SPECIALS
+static void
+duplicate_single_pixel(unsigned char *dst, unsigned char *src, int n, int w, int h)
+{
+ int i;
+
+ for (i = n; i > 0; i--)
+ *dst++ = *src++;
+ for (i = (w*h-1)*n; i > 0; i--)
+ {
+ *dst = dst[-n];
+ dst++;
+ }
+}
+
+static void
+scale_single_row(unsigned char *dst, unsigned char *src, fz_weights *weights, int src_w, int h)
+{
+ int *contrib = &weights->index[weights->index[0]];
+ int min, len, i, j, n;
+ int tmp[FZ_MAX_COLORS];
+
+ n = weights->n;
+ /* Scale a single row */
+ for (j = 0; j < n; j++)
+ tmp[j] = 128;
+ if (weights->flip)
+ {
+ dst += (weights->count-1)*n;
+ for (i=weights->count; i > 0; i--)
+ {
+ min = *contrib++;
+ len = *contrib++;
+ min *= n;
+ while (len-- > 0)
+ {
+ for (j = 0; j < n; j++)
+ tmp[j] += src[min++] * *contrib;
+ contrib++;
+ }
+ for (j = 0; j < n; j++)
+ {
+ *dst++ = (unsigned char)(tmp[j]>>8);
+ tmp[j] = 128;
+ }
+ dst -= 2*n;
+ }
+ dst += n * (weights->count+1);
+ }
+ else
+ {
+ for (i=weights->count; i > 0; i--)
+ {
+ min = *contrib++;
+ len = *contrib++;
+ min *= n;
+ while (len-- > 0)
+ {
+ for (j = 0; j < n; j++)
+ tmp[j] += src[min++] * *contrib;
+ contrib++;
+ }
+ for (j = 0; j < n; j++)
+ {
+ *dst++ = (unsigned char)(tmp[j]>>8);
+ tmp[j] = 128;
+ }
+ }
+ }
+ /* And then duplicate it h times */
+ n *= weights->count;
+ while (--h > 0)
+ {
+ memcpy(dst, dst-n, n);
+ dst += n;
+ }
+}
+
+static void
+scale_single_col(unsigned char *dst, unsigned char *src, fz_weights *weights, int src_w, int n, int w, int flip_y)
+{
+ int *contrib = &weights->index[weights->index[0]];
+ int min, len, i, j;
+ int tmp[FZ_MAX_COLORS];
+
+ for (j = 0; j < n; j++)
+ tmp[j] = 128;
+ if (flip_y)
+ {
+ src_w = (src_w-1)*n;
+ w = (w-1)*n;
+ for (i=weights->count; i > 0; i--)
+ {
+ /* Scale the next pixel in the column */
+ min = *contrib++;
+ len = *contrib++;
+ min = src_w-min*n;
+ while (len-- > 0)
+ {
+ for (j = 0; j < n; j++)
+ tmp[j] += src[src_w-min+j] * *contrib;
+ contrib++;
+ }
+ for (j = 0; j < n; j++)
+ {
+ *dst++ = (unsigned char)(tmp[j]>>8);
+ tmp[j] = 128;
+ }
+ /* And then duplicate it across the row */
+ for (j = w; j > 0; j--)
+ {
+ *dst = dst[-n];
+ dst++;
+ }
+ }
+ }
+ else
+ {
+ w = (w-1)*n;
+ for (i=weights->count; i > 0; i--)
+ {
+ /* Scale the next pixel in the column */
+ min = *contrib++;
+ len = *contrib++;
+ min *= n;
+ while (len-- > 0)
+ {
+ for (j = 0; j < n; j++)
+ tmp[j] += src[min++] * *contrib;
+ contrib++;
+ }
+ for (j = 0; j < n; j++)
+ {
+ *dst++ = (unsigned char)(tmp[j]>>8);
+ tmp[j] = 128;
+ }
+ /* And then duplicate it across the row */
+ for (j = w; j > 0; j--)
+ {
+ *dst = dst[-n];
+ dst++;
+ }
+ }
+ }
+}
+#endif /* SINGLE_PIXEL_SPECIALS */
+
+fz_pixmap *
+fz_scale_pixmap(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h)
+{
+ fz_scale_filter *filter = &fz_scale_filter_simple;
+ fz_weights *contrib_rows = NULL;
+ fz_weights *contrib_cols = NULL;
+ fz_pixmap *output = NULL;
+ unsigned char *temp = NULL;
+ int max_row, temp_span, temp_rows, row;
+ int dst_w_int, dst_h_int, dst_x_int, dst_y_int;
+ int flip_x, flip_y;
+
+ DBUG(("Scale: (%d,%d) to (%g,%g) at (%g,%g)\n",src->w,src->h,w,h,x,y));
+
+ /* Find the destination bbox, width/height, and sub pixel offset,
+ * allowing for whether we're flipping or not. */
+ /* Note that the x and y sub pixel offsets here are different.
+ * The (x,y) position given describes where the bottom left corner
+ * of the source image should be mapped to (i.e. where (0,h) in image
+ * space ends up, not the more logical and sane (0,0)). Also there
+ * are differences in the way we scale horizontally and vertically.
+ * When scaling rows horizontally, we always read forwards through
+ * the source, and store either forwards or in reverse as required.
+ * When scaling vertically, we always store out forwards, but may
+ * feed source rows in in a different order.
+ *
+ * Consider the image rectange 'r' to which the image is mapped,
+ * and the (possibly) larger rectangle 'R', given by expanding 'r' to
+ * complete pixels.
+ *
+ * x can either be r.xmin-R.xmin or R.xmax-r.xmax depending on whether
+ * the image is x flipped or not. Whatever happens 0 <= x < 1.
+ * y is always R.ymax - r.ymax.
+ */
+ /* dst_x_int is calculated to be the left of the scaled image, and
+ * x (the sub_pixel_offset) is the distance in from either the left
+ * or right pixel expanded edge. */
+ flip_x = (w < 0);
+ if (flip_x)
+ {
+ float tmp;
+ w = -w;
+ dst_x_int = floor(x-w);
+ tmp = ceilf(x);
+ dst_w_int = (int)tmp;
+ x = tmp - x;
+ dst_w_int -= dst_x_int;
+ }
+ else
+ {
+ dst_x_int = floor(x);
+ x -= (float)dst_x_int;
+ dst_w_int = (int)ceilf(x + w);
+ }
+ flip_y = (h < 0);
+ /* dst_y_int is calculated to be the bottom of the scaled image, but
+ * y (the sub pixel offset) has to end up being the value at the top.
+ */
+ if (flip_y)
+ {
+ h = -h;
+ dst_y_int = floor(y-h);
+ dst_h_int = (int)ceilf(y) - dst_y_int;
+ } else {
+ dst_y_int = floor(y);
+ y += h;
+ dst_h_int = (int)ceilf(y) - dst_y_int;
+ }
+ /* y is the top edge position in floats. We want it to be the
+ * distance down from the next pixel boundary. */
+ y = ceilf(y) - y;
+
+ DBUG(("Result image: (%d,%d) at (%d,%d) (subpix=%g,%g)\n", dst_w_int, dst_h_int, dst_x_int, dst_y_int, x, y));
+
+ /* Step 1: Calculate the weights for columns and rows */
+#ifdef SINGLE_PIXEL_SPECIALS
+ if (src->w == 1)
+ {
+ contrib_cols = NULL;
+ }
+ else
+#endif /* SINGLE_PIXEL_SPECIALS */
+ {
+ contrib_cols = make_weights(ctx, src->w, x, w, filter, 0, dst_w_int, src->n, flip_x);
+ if (contrib_cols == NULL)
+ goto cleanup;
+ }
+#ifdef SINGLE_PIXEL_SPECIALS
+ if (src->h == 1)
+ {
+ contrib_rows = NULL;
+ }
+ else
+#endif /* SINGLE_PIXEL_SPECIALS */
+ {
+ contrib_rows = make_weights(ctx, src->h, y, h, filter, 1, dst_h_int, src->n, flip_y);
+ if (contrib_rows == NULL)
+ goto cleanup;
+ }
+
+ assert(contrib_cols == NULL || contrib_cols->count == dst_w_int);
+ assert(contrib_rows == NULL || contrib_rows->count == dst_h_int);
+ output = fz_new_pixmap(ctx, src->colorspace, dst_w_int, dst_h_int);
+ output->x = dst_x_int;
+ output->y = dst_y_int;
+
+ /* Step 2: Apply the weights */
+#ifdef SINGLE_PIXEL_SPECIALS
+ if (contrib_rows == NULL)
+ {
+ /* Only 1 source pixel high. */
+ if (contrib_cols == NULL)
+ {
+ /* Only 1 pixel in the entire image! */
+ duplicate_single_pixel(output->samples, src->samples, src->n, dst_w_int, dst_h_int);
+ }
+ else
+ {
+ /* Scale the row once, then copy it. */
+ scale_single_row(output->samples, src->samples, contrib_cols, src->w, dst_h_int);
+ }
+ }
+ else if (contrib_cols == NULL)
+ {
+ /* Only 1 source pixel wide. Scale the col and duplicate. */
+ scale_single_col(output->samples, src->samples, contrib_rows, src->h, src->n, dst_w_int, flip_y);
+ }
+ else
+#endif /* SINGLE_PIXEL_SPECIALS */
+ {
+ void (*row_scale)(unsigned char *dst, unsigned char *src, fz_weights *weights);
+
+ temp_span = contrib_cols->count * src->n;
+ temp_rows = contrib_rows->max_len;
+ if (temp_span <= 0 || temp_rows > INT_MAX / temp_span)
+ goto cleanup;
+ temp = fz_calloc(ctx, temp_span*temp_rows, sizeof(unsigned char));
+ if (temp == NULL)
+ goto cleanup;
+ switch (src->n)
+ {
+ default:
+ row_scale = scale_row_to_temp;
+ break;
+ case 1: /* Image mask case */
+ row_scale = scale_row_to_temp1;
+ break;
+ case 2: /* Greyscale with alpha case */
+ row_scale = scale_row_to_temp2;
+ break;
+ case 4: /* RGBA */
+ row_scale = scale_row_to_temp4;
+ break;
+ }
+ max_row = 0;
+ for (row = 0; row < contrib_rows->count; row++)
+ {
+ /*
+ Which source rows do we need to have scaled into the
+ temporary buffer in order to be able to do the final
+ scale?
+ */
+ int row_index = contrib_rows->index[row];
+ int row_min = contrib_rows->index[row_index++];
+ int row_len = contrib_rows->index[row_index++];
+ while (max_row < row_min+row_len)
+ {
+ /* Scale another row */
+ assert(max_row < src->h);
+ DBUG(("scaling row %d to temp\n", max_row));
+ (*row_scale)(&temp[temp_span*(max_row % temp_rows)], &src->samples[(flip_y ? (src->h-1-max_row): max_row)*src->w*src->n], contrib_cols);
+ max_row++;
+ }
+
+ DBUG(("scaling row %d from temp\n", row));
+ scale_row_from_temp(&output->samples[row*output->w*output->n], temp, contrib_rows, temp_span, row);
+ }
+ fz_free(ctx, temp);
+ }
+
+cleanup:
+ fz_free(ctx, contrib_rows);
+ fz_free(ctx, contrib_cols);
+ return output;
+}
diff --git a/fitz/base_object.c b/fitz/base_object.c
index 6307e5e0..b1feeb2c 100644
--- a/fitz/base_object.c
+++ b/fitz/base_object.c
@@ -396,6 +396,18 @@ fz_new_array(fz_context *ctx, int initialcap)
return obj;
}
+static void
+fz_array_grow(fz_obj *obj)
+{
+ int i;
+
+ obj->u.a.cap = (obj->u.a.cap * 3) / 2;
+ obj->u.a.items = fz_resize_array(obj->ctx, obj->u.a.items, obj->u.a.cap, sizeof(fz_obj*));
+
+ for (i = obj->u.a.len ; i < obj->u.a.cap; i++)
+ obj->u.a.items[i] = NULL;
+}
+
fz_obj *
fz_copy_array(fz_context *ctx, fz_obj *obj)
{
@@ -466,13 +478,7 @@ fz_array_push(fz_obj *obj, fz_obj *item)
else
{
if (obj->u.a.len + 1 > obj->u.a.cap)
- {
- int i;
- obj->u.a.cap = (obj->u.a.cap * 3) / 2;
- obj->u.a.items = fz_resize_array(obj->ctx, obj->u.a.items, obj->u.a.cap, sizeof(fz_obj*));
- for (i = obj->u.a.len ; i < obj->u.a.cap; i++)
- obj->u.a.items[i] = NULL;
- }
+ fz_array_grow(obj);
obj->u.a.items[obj->u.a.len] = fz_keep_obj(item);
obj->u.a.len++;
}
@@ -488,13 +494,7 @@ fz_array_insert(fz_obj *obj, fz_obj *item)
else
{
if (obj->u.a.len + 1 > obj->u.a.cap)
- {
- int i;
- obj->u.a.cap = (obj->u.a.cap * 3) / 2;
- obj->u.a.items = fz_resize_array(obj->ctx, obj->u.a.items, obj->u.a.cap, sizeof(fz_obj*));
- for (i = obj->u.a.len ; i < obj->u.a.cap; i++)
- obj->u.a.items[i] = NULL;
- }
+ fz_array_grow(obj);
memmove(obj->u.a.items + 1, obj->u.a.items, obj->u.a.len * sizeof(fz_obj*));
obj->u.a.items[0] = fz_keep_obj(item);
obj->u.a.len++;
@@ -521,7 +521,7 @@ fz_new_dict(fz_context *ctx, int initialcap)
obj->refs = 1;
obj->kind = FZ_DICT;
- obj->u.d.sorted = 1;
+ obj->u.d.sorted = 0;
obj->u.d.len = 0;
obj->u.d.cap = initialcap > 1 ? initialcap : 10;
@@ -535,6 +535,21 @@ fz_new_dict(fz_context *ctx, int initialcap)
return obj;
}
+static void
+fz_dict_grow(fz_obj *obj)
+{
+ int i;
+
+ obj->u.d.cap = (obj->u.d.cap * 3) / 2;
+ obj->u.d.items = fz_resize_array(obj->ctx, obj->u.d.items, obj->u.d.cap, sizeof(struct keyval));
+
+ for (i = obj->u.d.len; i < obj->u.d.cap; i++)
+ {
+ obj->u.d.items[i].k = NULL;
+ obj->u.d.items[i].v = NULL;
+ }
+}
+
fz_obj *
fz_copy_dict(fz_context *ctx, fz_obj *obj)
{
@@ -590,12 +605,20 @@ fz_dict_get_val(fz_obj *obj, int i)
}
static int
-fz_dict_finds(fz_obj *obj, char *key)
+fz_dict_finds(fz_obj *obj, char *key, int *location)
{
if (obj->u.d.sorted)
{
int l = 0;
int r = obj->u.d.len - 1;
+
+ if (strcmp(fz_to_name(obj->u.d.items[r].k), key) < 0)
+ {
+ if (location)
+ *location = r + 1;
+ return -1;
+ }
+
while (l <= r)
{
int m = (l + r) >> 1;
@@ -606,6 +629,9 @@ fz_dict_finds(fz_obj *obj, char *key)
l = m + 1;
else
return m;
+
+ if (location)
+ *location = l;
}
}
@@ -615,6 +641,9 @@ fz_dict_finds(fz_obj *obj, char *key)
for (i = 0; i < obj->u.d.len; i++)
if (strcmp(fz_to_name(obj->u.d.items[i].k), key) == 0)
return i;
+
+ if (location)
+ *location = obj->u.d.len;
}
return -1;
@@ -630,7 +659,7 @@ fz_dict_gets(fz_obj *obj, char *key)
if (!fz_is_dict(obj))
return NULL;
- i = fz_dict_finds(obj, key);
+ i = fz_dict_finds(obj, key, NULL);
if (i >= 0)
return obj->u.d.items[i].v;
@@ -658,6 +687,7 @@ fz_dict_getsa(fz_obj *obj, char *key, char *abbrev)
void
fz_dict_put(fz_obj *obj, fz_obj *key, fz_obj *val)
{
+ int location;
char *s;
int i;
@@ -683,33 +713,30 @@ fz_dict_put(fz_obj *obj, fz_obj *key, fz_obj *val)
return;
}
- i = fz_dict_finds(obj, s);
- if (i >= 0)
+ if (obj->u.d.len > 100 && !obj->u.d.sorted)
+ fz_sort_dict(obj);
+
+ i = fz_dict_finds(obj, s, &location);
+ if (i >= 0 && i < obj->u.d.len)
{
fz_drop_obj(obj->u.d.items[i].v);
obj->u.d.items[i].v = fz_keep_obj(val);
- return;
}
-
- if (obj->u.d.len + 1 > obj->u.d.cap)
+ else
{
- obj->u.d.cap = (obj->u.d.cap * 3) / 2;
- obj->u.d.items = fz_resize_array(obj->ctx, obj->u.d.items, obj->u.d.cap, sizeof(struct keyval));
- for (i = obj->u.d.len; i < obj->u.d.cap; i++)
- {
- obj->u.d.items[i].k = NULL;
- obj->u.d.items[i].v = NULL;
- }
- }
+ if (obj->u.d.len + 1 > obj->u.d.cap)
+ fz_dict_grow(obj);
- /* borked! */
- if (obj->u.d.len)
- if (strcmp(fz_to_name(obj->u.d.items[obj->u.d.len - 1].k), s) > 0)
- obj->u.d.sorted = 0;
+ i = location;
+ if (obj->u.d.sorted)
+ memmove(&obj->u.d.items[i + 1],
+ &obj->u.d.items[i],
+ (obj->u.d.len - i) * sizeof(struct keyval));
- obj->u.d.items[obj->u.d.len].k = fz_keep_obj(key);
- obj->u.d.items[obj->u.d.len].v = fz_keep_obj(val);
- obj->u.d.len ++;
+ obj->u.d.items[i].k = fz_keep_obj(key);
+ obj->u.d.items[i].v = fz_keep_obj(val);
+ obj->u.d.len ++;
+ }
}
void
@@ -729,7 +756,7 @@ fz_dict_dels(fz_obj *obj, char *key)
fz_warn(obj->ctx, "assert: not a dict (%s)", fz_objkindstr(obj));
else
{
- int i = fz_dict_finds(obj, key);
+ int i = fz_dict_finds(obj, key, NULL);
if (i >= 0)
{
fz_drop_obj(obj->u.d.items[i].k);
diff --git a/fitz/dev_trace.c b/fitz/dev_trace.c
index 4db215a0..62661a3d 100644
--- a/fitz/dev_trace.c
+++ b/fitz/dev_trace.c
@@ -8,6 +8,13 @@ fz_trace_matrix(fz_matrix ctm)
}
static void
+fz_trace_trm(fz_matrix trm)
+{
+ printf("trm=\"%g %g %g %g\" ",
+ trm.a, trm.b, trm.c, trm.d);
+}
+
+static void
fz_trace_color(fz_colorspace *colorspace, float *color, float alpha)
{
int i;
@@ -135,7 +142,8 @@ fz_trace_fill_text(fz_device *dev, fz_text *text, fz_matrix ctm,
{
printf("<fill_text font=\"%s\" wmode=\"%d\" ", text->font->name, text->wmode);
fz_trace_color(colorspace, color, alpha);
- fz_trace_matrix(fz_concat(ctm, text->trm));
+ fz_trace_matrix(ctm);
+ fz_trace_trm(text->trm);
printf(">\n");
fz_debug_text(text, 0);
printf("</fill_text>\n");
@@ -147,7 +155,8 @@ fz_trace_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, fz_
{
printf("<stroke_text font=\"%s\" wmode=\"%d\" ", text->font->name, text->wmode);
fz_trace_color(colorspace, color, alpha);
- fz_trace_matrix(fz_concat(ctm, text->trm));
+ fz_trace_matrix(ctm);
+ fz_trace_trm(text->trm);
printf(">\n");
fz_debug_text(text, 0);
printf("</stroke_text>\n");
@@ -158,7 +167,8 @@ fz_trace_clip_text(fz_device *dev, fz_text *text, fz_matrix ctm, int accumulate)
{
printf("<clip_text font=\"%s\" wmode=\"%d\" ", text->font->name, text->wmode);
printf("accumulate=\"%d\" ", accumulate);
- fz_trace_matrix(fz_concat(ctm, text->trm));
+ fz_trace_matrix(ctm);
+ fz_trace_trm(text->trm);
printf(">\n");
fz_debug_text(text, 0);
printf("</clip_text>\n");
@@ -168,7 +178,8 @@ static void
fz_trace_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm)
{
printf("<clip_stroke_text font=\"%s\" wmode=\"%d\" ", text->font->name, text->wmode);
- fz_trace_matrix(fz_concat(ctm, text->trm));
+ fz_trace_matrix(ctm);
+ fz_trace_trm(text->trm);
printf(">\n");
fz_debug_text(text, 0);
printf("</clip_stroke_text>\n");
@@ -178,7 +189,8 @@ static void
fz_trace_ignore_text(fz_device *dev, fz_text *text, fz_matrix ctm)
{
printf("<ignore_text font=\"%s\" wmode=\"%d\" ", text->font->name, text->wmode);
- fz_trace_matrix(fz_concat(ctm, text->trm));
+ fz_trace_matrix(ctm);
+ fz_trace_trm(text->trm);
printf(">\n");
fz_debug_text(text, 0);
printf("</ignore_text>\n");
diff --git a/fitz/doc_outline.c b/fitz/doc_outline.c
new file mode 100644
index 00000000..f6d07405
--- /dev/null
+++ b/fitz/doc_outline.c
@@ -0,0 +1,49 @@
+#include "fitz.h"
+
+void
+fz_free_outline(fz_outline *outline)
+{
+ while (outline)
+ {
+ fz_outline *next = outline->next;
+ fz_free_outline(outline->down);
+ fz_free(outline->ctx, outline->title);
+ fz_free(outline->ctx, outline);
+ outline = next;
+ }
+}
+
+void
+fz_debug_outline_xml(fz_outline *outline, int level)
+{
+ while (outline)
+ {
+ printf("<outline title=\"%s\" page=\"%d\"", outline->title, outline->page);
+ if (outline->down)
+ {
+ printf(">\n");
+ fz_debug_outline_xml(outline->down, level + 1);
+ printf("</outline>\n");
+ }
+ else
+ {
+ printf(" />\n");
+ }
+ outline = outline->next;
+ }
+}
+
+void
+fz_debug_outline(fz_outline *outline, int level)
+{
+ int i;
+ while (outline)
+ {
+ for (i = 0; i < level; i++)
+ putchar('\t');
+ printf("%s\t%d\n", outline->title, outline->page);
+ if (outline->down)
+ fz_debug_outline(outline->down, level + 1);
+ outline = outline->next;
+ }
+}
diff --git a/fitz/filt_jpxd.c b/fitz/filt_jpxd.c
index 5700a664..64197a08 100644
--- a/fitz/filt_jpxd.c
+++ b/fitz/filt_jpxd.c
@@ -5,17 +5,19 @@
static void fz_opj_error_callback(const char *msg, void *client_data)
{
- fprintf(stderr, "openjpeg error: %s", msg);
+ fz_context *ctx = (fz_context *)client_data;
+ fz_warn(ctx, "openjpeg error: %s", msg);
}
static void fz_opj_warning_callback(const char *msg, void *client_data)
{
- fprintf(stderr, "openjpeg warning: %s", msg);
+ fz_context *ctx = (fz_context *)client_data;
+ fz_warn(ctx, "openjpeg warning: %s", msg);
}
static void fz_opj_info_callback(const char *msg, void *client_data)
{
- /* fprintf(stderr, "openjpeg info: %s", msg); */
+ /* fz_warn("openjpeg info: %s", msg); */
}
fz_pixmap *
@@ -50,6 +52,7 @@ fz_load_jpx_image(fz_context *ctx, unsigned char *data, int size, fz_colorspace
opj_set_default_decoder_parameters(&params);
info = opj_create_decompress(format);
+ info->client_data = (void *)ctx;
opj_set_event_mgr((opj_common_ptr)info, &evtmgr, stderr);
opj_setup_decoder(info, &params);
diff --git a/fitz/fitz.h b/fitz/fitz.h
index 44d976cb..0a3875c7 100644
--- a/fitz/fitz.h
+++ b/fitz/fitz.h
@@ -348,6 +348,8 @@ fz_point fz_transform_vector(fz_matrix m, fz_point p);
fz_rect fz_transform_rect(fz_matrix m, fz_rect r);
fz_bbox fz_transform_bbox(fz_matrix m, fz_bbox b);
+void fz_gridfit_matrix(fz_matrix *m);
+
/*
* Basic crypto functions.
* Independent of the rest of fitz.
@@ -705,7 +707,6 @@ void fz_invert_pixmap(fz_pixmap *pix);
void fz_gamma_pixmap(fz_pixmap *pix, float gamma);
fz_pixmap *fz_scale_pixmap(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h);
-fz_pixmap *fz_scale_pixmap_gridfit(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h, int gridfit);
void fz_write_pnm(fz_context *ctx, fz_pixmap *pixmap, char *filename);
void fz_write_pam(fz_context *ctx, fz_pixmap *pixmap, char *filename, int savealpha);
@@ -1141,6 +1142,25 @@ fz_device *fz_new_list_device(fz_context *ctx, fz_display_list *list);
void fz_execute_display_list(fz_display_list *list, fz_device *dev, fz_matrix ctm, fz_bbox area);
/*
+ * Document interface.
+ */
+
+typedef struct fz_outline_s fz_outline;
+
+struct fz_outline_s
+{
+ fz_context *ctx;
+ char *title;
+ int page;
+ fz_outline *next;
+ fz_outline *down;
+};
+
+void fz_debug_outline_xml(fz_outline *outline, int level);
+void fz_debug_outline(fz_outline *outline, int level);
+void fz_free_outline(fz_outline *outline);
+
+/*
* Plotting functions.
*/
diff --git a/fitz/stm_read.c b/fitz/stm_read.c
index 58379887..5622a24f 100644
--- a/fitz/stm_read.c
+++ b/fitz/stm_read.c
@@ -175,10 +175,10 @@ fz_seek(fz_stream *stm, int offset, int whence)
}
if (whence == 0)
{
- unsigned char *p = stm->wp - (stm->pos - offset);
- if (p >= stm->bp && p <= stm->wp)
+ int dist = stm->pos - offset;
+ if (dist >= 0 && dist <= stm->wp - stm->bp)
{
- stm->rp = p;
+ stm->rp = stm->wp - dist;
stm->eof = 0;
return;
}
diff --git a/ios/About.pdf b/ios/About.pdf
new file mode 100644
index 00000000..763c467e
--- /dev/null
+++ b/ios/About.pdf
Binary files differ
diff --git a/ios/Icon-72.png b/ios/Icon-72.png
new file mode 100644
index 00000000..fb8c12f0
--- /dev/null
+++ b/ios/Icon-72.png
Binary files differ
diff --git a/ios/Icon.png b/ios/Icon.png
new file mode 100644
index 00000000..4bb46ae1
--- /dev/null
+++ b/ios/Icon.png
Binary files differ
diff --git a/ios/Icon@2x.png b/ios/Icon@2x.png
new file mode 100644
index 00000000..c62a076d
--- /dev/null
+++ b/ios/Icon@2x.png
Binary files differ
diff --git a/ios/Info.plist b/ios/Info.plist
new file mode 100644
index 00000000..116bacee
--- /dev/null
+++ b/ios/Info.plist
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFiles</key>
+ <array/>
+ <key>CFBundleIdentifier</key>
+ <string>com.artifex.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>0.9</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>0.9</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UIFileSharingEnabled</key>
+ <true/>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UIPrerenderedIcon</key>
+ <true/>
+</dict>
+</plist>
diff --git a/ios/MuPDF.xcodeproj/project.pbxproj b/ios/MuPDF.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..61542780
--- /dev/null
+++ b/ios/MuPDF.xcodeproj/project.pbxproj
@@ -0,0 +1,394 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 9644E9A0146ACEC000E5B70A /* document.c in Sources */ = {isa = PBXBuildFile; fileRef = 9644E99E146ACEC000E5B70A /* document.c */; };
+ 9683F619145F4F84000E1607 /* About.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9683F618145F4F84000E1607 /* About.pdf */; };
+ 968F2E9C14539C880085264E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2E9B14539C880085264E /* UIKit.framework */; };
+ 968F2E9E14539C880085264E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2E9D14539C880085264E /* Foundation.framework */; };
+ 968F2EA014539C880085264E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2E9F14539C880085264E /* CoreGraphics.framework */; };
+ 968F2EB014539CDA0085264E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 968F2E9014539BEB0085264E /* main.m */; };
+ 968F2EBB14539F350085264E /* libfitz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2EB314539F350085264E /* libfitz.a */; };
+ 968F2EBC14539F350085264E /* libfreetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2EB414539F350085264E /* libfreetype.a */; };
+ 968F2EBD14539F350085264E /* libjbig2dec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2EB514539F350085264E /* libjbig2dec.a */; };
+ 968F2EBE14539F350085264E /* libjpeg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2EB614539F350085264E /* libjpeg.a */; };
+ 968F2EBF14539F350085264E /* libmupdf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2EB714539F350085264E /* libmupdf.a */; };
+ 968F2EC014539F350085264E /* libmuxps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2EB814539F350085264E /* libmuxps.a */; };
+ 968F2EC114539F350085264E /* libopenjpeg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2EB914539F350085264E /* libopenjpeg.a */; };
+ 968F2EC214539F350085264E /* libz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2EBA14539F350085264E /* libz.a */; };
+ 96BD2B38145AC485001CEBC3 /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 96BD2B35145AC485001CEBC3 /* Icon-72.png */; };
+ 96BD2B39145AC485001CEBC3 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 96BD2B36145AC485001CEBC3 /* Icon.png */; };
+ 96F2341514603FBA004A8A22 /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 96F2341414603FBA004A8A22 /* Icon@2x.png */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 968461EE14642E3A0012AE09 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 96A3B27614539BAD00D0A895 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 968461E114642DB00012AE09;
+ remoteInfo = Libraries;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 9644E99E146ACEC000E5B70A /* document.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = document.c; sourceTree = "<group>"; };
+ 9644E99F146ACEC000E5B70A /* document.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = document.h; sourceTree = "<group>"; };
+ 9683F618145F4F84000E1607 /* About.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = About.pdf; sourceTree = "<group>"; };
+ 968461E214642DB00012AE09 /* libLibraries.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLibraries.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 968F2E8E14539BEB0085264E /* build_libs.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build_libs.sh; sourceTree = "<group>"; };
+ 968F2E8F14539BEB0085264E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 968F2E9014539BEB0085264E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 968F2E9714539C880085264E /* MuPDF.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MuPDF.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 968F2E9B14539C880085264E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
+ 968F2E9D14539C880085264E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
+ 968F2E9F14539C880085264E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
+ 968F2EB314539F350085264E /* libfitz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libfitz.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 968F2EB414539F350085264E /* libfreetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libfreetype.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 968F2EB514539F350085264E /* libjbig2dec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libjbig2dec.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 968F2EB614539F350085264E /* libjpeg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libjpeg.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 968F2EB714539F350085264E /* libmupdf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmupdf.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 968F2EB814539F350085264E /* libmuxps.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmuxps.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 968F2EB914539F350085264E /* libopenjpeg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libopenjpeg.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 968F2EBA14539F350085264E /* libz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libz.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 96BD2B35145AC485001CEBC3 /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-72.png"; sourceTree = "<group>"; };
+ 96BD2B36145AC485001CEBC3 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = "<group>"; };
+ 96F2341414603FBA004A8A22 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon@2x.png"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 968F2E9414539C880085264E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 968F2E9C14539C880085264E /* UIKit.framework in Frameworks */,
+ 968F2E9E14539C880085264E /* Foundation.framework in Frameworks */,
+ 968F2EA014539C880085264E /* CoreGraphics.framework in Frameworks */,
+ 968F2EBB14539F350085264E /* libfitz.a in Frameworks */,
+ 968F2EBC14539F350085264E /* libfreetype.a in Frameworks */,
+ 968F2EBD14539F350085264E /* libjbig2dec.a in Frameworks */,
+ 968F2EBE14539F350085264E /* libjpeg.a in Frameworks */,
+ 968F2EBF14539F350085264E /* libmupdf.a in Frameworks */,
+ 968F2EC014539F350085264E /* libmuxps.a in Frameworks */,
+ 968F2EC114539F350085264E /* libopenjpeg.a in Frameworks */,
+ 968F2EC214539F350085264E /* libz.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 968F2E9214539BF10085264E /* Sources */ = {
+ isa = PBXGroup;
+ children = (
+ 9683F618145F4F84000E1607 /* About.pdf */,
+ 96BD2B36145AC485001CEBC3 /* Icon.png */,
+ 96F2341414603FBA004A8A22 /* Icon@2x.png */,
+ 96BD2B35145AC485001CEBC3 /* Icon-72.png */,
+ 968F2E8F14539BEB0085264E /* Info.plist */,
+ 968F2E8E14539BEB0085264E /* build_libs.sh */,
+ 9644E99E146ACEC000E5B70A /* document.c */,
+ 9644E99F146ACEC000E5B70A /* document.h */,
+ 968F2E9014539BEB0085264E /* main.m */,
+ );
+ name = Sources;
+ sourceTree = "<group>";
+ };
+ 968F2E9814539C880085264E /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 968F2E9714539C880085264E /* MuPDF.app */,
+ 968461E214642DB00012AE09 /* libLibraries.a */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 968F2E9A14539C880085264E /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 968F2E9B14539C880085264E /* UIKit.framework */,
+ 968F2E9D14539C880085264E /* Foundation.framework */,
+ 968F2E9F14539C880085264E /* CoreGraphics.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 968F2EB214539F230085264E /* Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ 968F2EB314539F350085264E /* libfitz.a */,
+ 968F2EB414539F350085264E /* libfreetype.a */,
+ 968F2EB514539F350085264E /* libjbig2dec.a */,
+ 968F2EB614539F350085264E /* libjpeg.a */,
+ 968F2EB714539F350085264E /* libmupdf.a */,
+ 968F2EB814539F350085264E /* libmuxps.a */,
+ 968F2EB914539F350085264E /* libopenjpeg.a */,
+ 968F2EBA14539F350085264E /* libz.a */,
+ );
+ name = Libraries;
+ sourceTree = "<group>";
+ };
+ 96A3B27414539BAD00D0A895 = {
+ isa = PBXGroup;
+ children = (
+ 968F2E9214539BF10085264E /* Sources */,
+ 968F2EB214539F230085264E /* Libraries */,
+ 968F2E9A14539C880085264E /* Frameworks */,
+ 968F2E9814539C880085264E /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 968461E114642DB00012AE09 /* Libraries */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 968461EA14642DB00012AE09 /* Build configuration list for PBXNativeTarget "Libraries" */;
+ buildPhases = (
+ 968461ED14642DE50012AE09 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Libraries;
+ productName = Libraries;
+ productReference = 968461E214642DB00012AE09 /* libLibraries.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ 968F2E9614539C880085264E /* MuPDF */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 968F2EAD14539C880085264E /* Build configuration list for PBXNativeTarget "MuPDF" */;
+ buildPhases = (
+ 968F2E9314539C880085264E /* Sources */,
+ 968F2E9414539C880085264E /* Frameworks */,
+ 968F2E9514539C880085264E /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 968461EF14642E3A0012AE09 /* PBXTargetDependency */,
+ );
+ name = MuPDF;
+ productName = MuPDF;
+ productReference = 968F2E9714539C880085264E /* MuPDF.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 96A3B27614539BAD00D0A895 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0420;
+ };
+ buildConfigurationList = 96A3B27914539BAD00D0A895 /* Build configuration list for PBXProject "MuPDF" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 96A3B27414539BAD00D0A895;
+ productRefGroup = 968F2E9814539C880085264E /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 968F2E9614539C880085264E /* MuPDF */,
+ 968461E114642DB00012AE09 /* Libraries */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 968F2E9514539C880085264E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 96BD2B38145AC485001CEBC3 /* Icon-72.png in Resources */,
+ 96BD2B39145AC485001CEBC3 /* Icon.png in Resources */,
+ 9683F619145F4F84000E1607 /* About.pdf in Resources */,
+ 96F2341514603FBA004A8A22 /* Icon@2x.png in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 968461ED14642DE50012AE09 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/libmupdf.a",
+ "$(DERIVED_FILE_DIR)/libfitz.a",
+ "$(DERIVED_FILE_DIR)/libmuxps.a",
+ "$(DERIVED_FILE_DIR)/libfreetype.a",
+ "$(DERIVED_FILE_DIR)/libjbig2dec.a",
+ "$(DERIVED_FILE_DIR)/libjpeg.a",
+ "$(DERIVED_FILE_DIR)/libopenjpeg.a",
+ "$(DERIVED_FILE_DIR)/libz.a",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "bash build_libs.sh";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 968F2E9314539C880085264E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 968F2EB014539CDA0085264E /* main.m in Sources */,
+ 9644E9A0146ACEC000E5B70A /* document.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 968461EF14642E3A0012AE09 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 968461E114642DB00012AE09 /* Libraries */;
+ targetProxy = 968461EE14642E3A0012AE09 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 968461EB14642DB00012AE09 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+ PRODUCT_NAME = Libraries;
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ 968461EC14642DB00012AE09 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+ PRODUCT_NAME = Libraries;
+ SDKROOT = iphoneos;
+ };
+ name = Release;
+ };
+ 968F2EAE14539C880085264E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = ..;
+ INFOPLIST_FILE = Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+ LIBRARY_SEARCH_PATHS = "$(inherited)";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 968F2EAF14539C880085264E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = ..;
+ INFOPLIST_FILE = Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+ LIBRARY_SEARCH_PATHS = "$(inherited)";
+ OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ 96A3B27B14539BAD00D0A895 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ 96A3B27C14539BAD00D0A895 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 968461EA14642DB00012AE09 /* Build configuration list for PBXNativeTarget "Libraries" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 968461EB14642DB00012AE09 /* Debug */,
+ 968461EC14642DB00012AE09 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 968F2EAD14539C880085264E /* Build configuration list for PBXNativeTarget "MuPDF" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 968F2EAE14539C880085264E /* Debug */,
+ 968F2EAF14539C880085264E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 96A3B27914539BAD00D0A895 /* Build configuration list for PBXProject "MuPDF" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 96A3B27B14539BAD00D0A895 /* Debug */,
+ 96A3B27C14539BAD00D0A895 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 96A3B27614539BAD00D0A895 /* Project object */;
+}
diff --git a/ios/build_libs.sh b/ios/build_libs.sh
new file mode 100644
index 00000000..beea9cde
--- /dev/null
+++ b/ios/build_libs.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Call this script from a "Run Script" target in the Xcode project to
+# cross compile MuPDF and third party libraries using the regular Makefile.
+# Also see "iOS" section in Makerules.
+
+echo Generating cmap and font files
+make -C .. generate || exit 1
+
+export OS=ios
+export build=$(echo $CONFIGURATION | tr A-Z a-z)
+
+case $ARCHS in
+ armv6) ARCHFLAGS="-arch armv6 -mno-thumb" ;;
+ armv7) ARCHFLAGS="-arch armv7 -mthumb" ;;
+ i386) ARCHFLAGS="-arch i386" ;;
+ *) echo "Unknown architecture!"; exit 1 ;;
+esac
+
+export CFLAGS="$ARCHFLAGS -isysroot $SDKROOT"
+export LDFLAGS="$ARCHFLAGS -isysroot $SDKROOT"
+export OUT=build/$build-$OS-$ARCHS
+
+echo Building libraries for $ARCHS.
+make -C .. libs || exit 1
+
+echo Copying files into $BUILT_PRODUCTS_DIR.
+
+mkdir -p "$BUILT_PRODUCTS_DIR"
+cp ../$OUT/lib*.a $BUILT_PRODUCTS_DIR
+ranlib $BUILT_PRODUCTS_DIR/lib*.a
+
+echo Done.
diff --git a/ios/document.c b/ios/document.c
new file mode 100644
index 00000000..6d30a0bc
--- /dev/null
+++ b/ios/document.c
@@ -0,0 +1,157 @@
+#include "fitz/fitz.h"
+#include "pdf/mupdf.h"
+#include "xps/muxps.h"
+#include "document.h"
+
+struct document *
+open_document(char *filename)
+{
+ fz_error error;
+
+ if (strstr(filename, ".pdf") || strstr(filename, ".PDF")) {
+ struct document *doc = fz_malloc(sizeof *doc);
+ memset(doc, 0, sizeof *doc);
+ doc->number = -1;
+ error = pdf_open_xref(&doc->pdf, filename, "");
+ if (error) {
+ fz_free(doc);
+ fz_rethrow(error, "cannot open pdf document");
+ return NULL;
+ }
+ error = pdf_load_page_tree(doc->pdf);
+ if (error) {
+ pdf_free_xref(doc->pdf);
+ fz_free(doc);
+ fz_rethrow(error, "cannot open pdf document");
+ return NULL;
+ }
+ return doc;
+ } else if (strstr(filename, ".xps") || strstr(filename, ".XPS")) {
+ struct document *doc = fz_malloc(sizeof *doc);
+ memset(doc, 0, sizeof *doc);
+ doc->number = -1;
+ error = xps_open_file(&doc->xps, filename);
+ if (error) {
+ fz_free(doc);
+ fz_rethrow(error, "cannot open xps document");
+ return NULL;
+ }
+ return doc;
+ } else {
+ fz_throw("unknown document format");
+ return NULL;
+ }
+}
+
+fz_outline *
+load_outline(struct document *doc)
+{
+ if (doc->pdf)
+ return pdf_load_outline(doc->pdf);
+ else if (doc->xps)
+ return xps_load_outline(doc->xps);
+ else
+ return NULL;
+}
+
+int
+count_pages(struct document *doc)
+{
+ if (doc->pdf)
+ return pdf_count_pages(doc->pdf);
+ else if (doc->xps)
+ return xps_count_pages(doc->xps);
+ else
+ return 1;
+}
+
+static void
+load_page(struct document *doc, int number)
+{
+ fz_error error;
+ if (doc->number == number)
+ return;
+ doc->number = number;
+ if (doc->pdf) {
+ if (doc->pdf_page) {
+ pdf_age_store(doc->pdf->store, 1);
+ pdf_free_page(doc->pdf_page);
+ }
+ doc->pdf_page = NULL;
+printf("load pdf page %d\n", number);
+ error = pdf_load_page(&doc->pdf_page, doc->pdf, number);
+ if (error)
+ fz_catch(error, "cannot load page %d", number);
+ }
+ if (doc->xps) {
+ if (doc->xps_page)
+ xps_free_page(doc->xps, doc->xps_page);
+ doc->xps_page = NULL;
+printf("load xps page %d\n", number);
+ error = xps_load_page(&doc->xps_page, doc->xps, number);
+ if (error)
+ fz_catch(error, "cannot load page %d", number);
+ }
+}
+
+void
+measure_page(struct document *doc, int number, float *w, float *h)
+{
+ load_page(doc, number);
+ if (doc->pdf_page) {
+ pdf_page *page = doc->pdf_page;
+ fz_rect mediabox = fz_transform_rect(fz_rotate(page->rotate), page->mediabox);
+ *w = mediabox.x1 - mediabox.x0;
+ *h = mediabox.y1 - mediabox.y0;
+ }
+ else if (doc->xps_page) {
+ xps_page *page = doc->xps_page;
+ *w = page->width * 72.0f / 96.0f;
+ *h = page->height * 72.0f / 96.0f;
+ }
+ else {
+ *w = *h = 72;
+ }
+ fz_flush_warnings();
+}
+
+void
+draw_page(struct document *doc, int number, fz_device *dev, fz_matrix ctm)
+{
+ load_page(doc, number);
+ if (doc->pdf_page) {
+ pdf_page *page = doc->pdf_page;
+ fz_matrix page_ctm = fz_concat(fz_rotate(-page->rotate), fz_scale(1, -1));
+ fz_rect mediabox = fz_transform_rect(page_ctm, page->mediabox);
+ page_ctm = fz_concat(page_ctm, fz_translate(-mediabox.x0, -mediabox.y0));
+ ctm = fz_concat(page_ctm, ctm);
+ pdf_run_page(doc->pdf, page, dev, ctm);
+ } else if (doc->xps_page) {
+ xps_page *page = doc->xps_page;
+ fz_matrix page_ctm = fz_scale(72.0f / 96.0f, 72.0f / 96.0f);
+ ctm = fz_concat(page_ctm, ctm);
+ doc->xps->dev = dev;
+ xps_parse_fixed_page(doc->xps, ctm, page);
+ doc->xps->dev = NULL;
+ }
+ fz_flush_warnings();
+}
+
+void
+close_document(struct document *doc)
+{
+ if (doc->pdf) {
+ if (doc->pdf_page)
+ pdf_free_page(doc->pdf_page);
+ if (doc->pdf->store)
+ pdf_free_store(doc->pdf->store);
+ doc->pdf->store = NULL;
+ pdf_free_xref(doc->pdf);
+ }
+ if (doc->xps) {
+ if (doc->xps_page)
+ xps_free_page(doc->xps, doc->xps_page);
+ xps_free_context(doc->xps);
+ }
+ fz_flush_warnings();
+}
diff --git a/ios/document.h b/ios/document.h
new file mode 100644
index 00000000..1f59ccdd
--- /dev/null
+++ b/ios/document.h
@@ -0,0 +1,32 @@
+#ifndef _DOCUMENT_H_
+#define _DOCUMENT_H_
+
+#ifndef _FITZ_H_
+#error "fitz.h must be included before document.h"
+#endif
+
+#ifndef _MUPDF_H_
+#error "mupdf.h must be included before document.h"
+#endif
+
+#ifndef _MUXPS_H_
+#error "muxps.h must be included before document.h"
+#endif
+
+struct document
+{
+ pdf_xref *pdf;
+ xps_context *xps;
+ int number;
+ pdf_page *pdf_page;
+ xps_page *xps_page;
+};
+
+struct document *open_document(char *filename);
+fz_outline *load_outline(struct document *doc);
+int count_pages(struct document *doc);
+void measure_page(struct document *doc, int number, float *w, float *h);
+void draw_page(struct document *doc, int number, fz_device *dev, fz_matrix ctm);
+void close_document(struct document *doc);
+
+#endif
diff --git a/ios/main.m b/ios/main.m
new file mode 100644
index 00000000..3a6a45f3
--- /dev/null
+++ b/ios/main.m
@@ -0,0 +1,1117 @@
+#import <UIKit/UIKit.h>
+
+#undef ABS
+#undef MIN
+#undef MAX
+
+#include "fitz/fitz.h"
+#include "pdf/mupdf.h"
+#include "xps/muxps.h"
+
+#include "document.h"
+
+#define GAP 20
+#define INDICATOR_Y -44-24
+
+static dispatch_queue_t queue;
+static fz_glyph_cache *glyphcache = NULL;
+static float screenScale = 1;
+
+@interface MuLibraryController : UITableViewController
+{
+ NSArray *files;
+ NSTimer *timer;
+}
+- (void) openDocument: (NSString*)filename;
+- (void) reload;
+@end
+
+@interface MuOutlineController : UITableViewController
+{
+ id target;
+ NSMutableArray *titles;
+ NSMutableArray *pages;
+}
+- (id) initWithTarget: (id)aTarget titles: (NSMutableArray*)aTitles pages: (NSMutableArray*)aPages;
+@end
+
+@interface MuPageView : UIScrollView <UIScrollViewDelegate>
+{
+ struct document *doc;
+ int number;
+ UIActivityIndicatorView *loadingView;
+ UIImageView *imageView;
+ UIImageView *tileView;
+ CGRect tileFrame;
+ float tileScale;
+ BOOL cancel;
+}
+- (id) initWithFrame: (CGRect)frame document: (struct document*)aDoc page: (int)aNumber;
+- (void) displayImage: (UIImage*)image;
+- (void) resizeImage;
+- (void) loadPage;
+- (void) loadTile;
+- (void) willRotate;
+- (void) resetZoomAnimated: (BOOL)animated;
+- (int) number;
+@end
+
+@interface MuDocumentController : UIViewController <UIScrollViewDelegate, UIGestureRecognizerDelegate>
+{
+ struct document *doc;
+ NSString *key;
+ NSMutableSet *visiblePages;
+ NSMutableSet *recycledPages;
+ MuOutlineController *outline;
+ UIScrollView *canvas;
+ UILabel *indicator;
+ UISlider *slider;
+ UIBarButtonItem *wrapper; // for slider
+ int width; // current screen size
+ int height;
+ int current; // currently visible page
+ int scroll_animating; // stop view updates during scrolling animations
+}
+- (id) initWithFile: (NSString*)filename;
+- (void) createPageView: (int)number;
+- (void) gotoPage: (int)number animated: (BOOL)animated;
+- (void) onShowOutline: (id)sender;
+- (void) onSlide: (id)sender;
+- (void) onTap: (UITapGestureRecognizer*)sender;
+- (void) showNavigationBar;
+- (void) hideNavigationBar;
+@end
+
+@interface MuAppDelegate : NSObject <UIApplicationDelegate, UINavigationControllerDelegate>
+{
+ UIWindow *window;
+ UINavigationController *navigator;
+ MuLibraryController *library;
+}
+@end
+
+#pragma mark -
+
+static void showAlert(NSString *msg)
+{
+ char msgbuf[160 * 30];
+ int i;
+
+ fz_strlcpy(msgbuf, "", sizeof msgbuf);
+ for (i = 0; i < fz_get_error_count(); i++)
+ {
+ char *s = fz_get_error_line(i);
+ s = strstr(s, "(): ") + 4;
+ fz_strlcat(msgbuf, s, sizeof msgbuf);
+ fz_strlcat(msgbuf, "\n", sizeof msgbuf);
+ }
+
+ UIAlertView *alert = [[UIAlertView alloc]
+ initWithTitle: msg
+ message: [NSString stringWithUTF8String: msgbuf]
+ delegate: nil
+ cancelButtonTitle: @"Okay"
+ otherButtonTitles: nil];
+ [alert show];
+ [alert release];
+}
+
+static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_outline *outline, int level)
+{
+ char indent[8*4+1];
+ if (level > 8)
+ level = 8;
+ memset(indent, ' ', level * 4);
+ indent[level * 4] = 0;
+ while (outline)
+ {
+ if (outline->page >= 0 && outline->title) {
+ NSString *title = [NSString stringWithUTF8String: outline->title];
+ [titles addObject: [NSString stringWithFormat: @"%s%@", indent, title]];
+ [pages addObject: [NSNumber numberWithInt: outline->page]];
+ }
+ flattenOutline(titles, pages, outline->down, level + 1);
+ outline = outline->next;
+ }
+}
+
+static void loadOutline(NSMutableArray *titles, NSMutableArray *pages, struct document *doc)
+{
+}
+
+static void releasePixmap(void *info, const void *data, size_t size)
+{
+ fz_drop_pixmap(info);
+}
+
+static UIImage *newImageWithPixmap(fz_pixmap *pix)
+{
+ CGDataProviderRef cgdata = CGDataProviderCreateWithData(pix, pix->samples, pix->w * 4 * pix->h, releasePixmap);
+ CGImageRef cgimage = CGImageCreate(pix->w, pix->h, 8, 32, 4 * pix->w,
+ CGColorSpaceCreateDeviceRGB(),
+ kCGBitmapByteOrderDefault,
+ cgdata, NULL, NO, kCGRenderingIntentDefault);
+ UIImage *image = [[UIImage alloc]
+ initWithCGImage: cgimage
+ scale: screenScale
+ orientation: UIImageOrientationUp];
+ CGDataProviderRelease(cgdata);
+ CGImageRelease(cgimage);
+ return image;
+}
+
+static CGSize fitPageToScreen(CGSize page, CGSize screen)
+{
+ float hscale = screen.width / page.width;
+ float vscale = screen.height / page.height;
+ float scale = MIN(hscale, vscale);
+ hscale = floorf(page.width * scale) / page.width;
+ vscale = floorf(page.height * scale) / page.height;
+ return CGSizeMake(hscale, vscale);
+}
+
+static UIImage *renderPage(struct document *doc, int number, CGSize screenSize)
+{
+ CGSize pageSize;
+ fz_bbox bbox;
+ fz_matrix ctm;
+ fz_device *dev;
+ fz_pixmap *pix;
+ CGSize scale;
+
+ screenSize.width *= screenScale;
+ screenSize.height *= screenScale;
+
+ measure_page(doc, number, &pageSize.width, &pageSize.height);
+ scale = fitPageToScreen(pageSize, screenSize);
+ ctm = fz_scale(scale.width, scale.height);
+ bbox = (fz_bbox){0, 0, pageSize.width * scale.width, pageSize.height * scale.height};
+
+ pix = fz_new_pixmap_with_rect(fz_device_rgb, bbox);
+ fz_clear_pixmap_with_color(pix, 255);
+
+ dev = fz_new_draw_device(glyphcache, pix);
+ draw_page(doc, number, dev, ctm);
+ fz_free_device(dev);
+
+ return newImageWithPixmap(pix);
+}
+
+static UIImage *renderTile(struct document *doc, int number, CGSize screenSize, CGRect tileRect, float zoom)
+{
+ CGSize pageSize;
+ fz_rect rect;
+ fz_bbox bbox;
+ fz_matrix ctm;
+ fz_device *dev;
+ fz_pixmap *pix;
+ CGSize scale;
+
+ screenSize.width *= screenScale;
+ screenSize.height *= screenScale;
+ tileRect.origin.x *= screenScale;
+ tileRect.origin.y *= screenScale;
+ tileRect.size.width *= screenScale;
+ tileRect.size.height *= screenScale;
+
+ measure_page(doc, number, &pageSize.width, &pageSize.height);
+ scale = fitPageToScreen(pageSize, screenSize);
+ ctm = fz_scale(scale.width * zoom, scale.height * zoom);
+
+ rect.x0 = tileRect.origin.x;
+ rect.y0 = tileRect.origin.y;
+ rect.x1 = tileRect.origin.x + tileRect.size.width;
+ rect.y1 = tileRect.origin.y + tileRect.size.height;
+ bbox = fz_round_rect(rect);
+
+ pix = fz_new_pixmap_with_rect(fz_device_rgb, bbox);
+ fz_clear_pixmap_with_color(pix, 255);
+
+ dev = fz_new_draw_device(glyphcache, pix);
+ draw_page(doc, number, dev, ctm);
+ fz_free_device(dev);
+
+ return newImageWithPixmap(pix);
+}
+
+#pragma mark -
+
+@implementation MuLibraryController
+
+- (void) viewWillAppear: (BOOL)animated
+{
+ [self setTitle: @"PDF and XPS Documents"];
+ [self reload];
+ printf("library viewWillAppear (starting reload timer)\n");
+ timer = [NSTimer timerWithTimeInterval: 1
+ target: self selector: @selector(reload) userInfo: nil
+ repeats: YES];
+ [[NSRunLoop currentRunLoop] addTimer: timer forMode: NSDefaultRunLoopMode];
+}
+
+- (void) viewWillDisappear: (BOOL)animated
+{
+ printf("library viewWillDisappear (stopping reload timer)\n");
+ [timer invalidate];
+ timer = nil;
+}
+
+- (void) reload
+{
+ NSError *error = nil;
+
+ if (files) {
+ [files release];
+ files = nil;
+ }
+
+ NSString *docdir = [NSString stringWithFormat: @"%@/Documents", NSHomeDirectory()];
+ files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath: docdir error: &error];
+ if (error)
+ files = [NSArray arrayWithObjects: @"...error loading directory...", nil];
+ [files retain];
+
+ [[self tableView] reloadData];
+}
+
+- (void) dealloc
+{
+ [files release];
+ [super dealloc];
+}
+
+- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)o
+{
+ return YES;
+}
+
+- (NSInteger) numberOfSectionsInTableView: (UITableView*)tableView
+{
+ return 1;
+}
+
+- (NSInteger) tableView: (UITableView*)tableView numberOfRowsInSection: (NSInteger)section
+{
+ return [files count] + 1;
+}
+
+- (UITableViewCell*) tableView: (UITableView*)tableView cellForRowAtIndexPath: (NSIndexPath*)indexPath
+{
+ static NSString *cellid = @"MuCellIdent";
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellid];
+ if (!cell)
+ cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: cellid] autorelease];
+ int row = [indexPath row];
+ if (row == 0) {
+ [[cell textLabel] setText: @"About MuPDF"];
+ [[cell textLabel] setFont: [UIFont systemFontOfSize: 20]];
+// [[cell textLabel] setFont: [UIFont italicSystemFontOfSize: 20]];
+ } else {
+ [[cell textLabel] setText: [files objectAtIndex: row - 1]];
+ [[cell textLabel] setFont: [UIFont systemFontOfSize: 20]];
+ }
+ return cell;
+}
+
+- (void) tableView: (UITableView*)tableView didSelectRowAtIndexPath: (NSIndexPath*)indexPath
+{
+ int row = [indexPath row];
+ if (row == 0)
+ [self openDocument: @"../MuPDF.app/About.pdf"];
+ else
+ [self openDocument: [files objectAtIndex: row - 1]];
+}
+
+- (void) openDocument: (NSString*)filename
+{
+ MuDocumentController *document = [[MuDocumentController alloc] initWithFile: filename];
+ if (document) {
+ [self setTitle: @"Library"];
+ [[self navigationController] pushViewController: document animated: YES];
+ [document release];
+ }
+}
+
+@end
+
+#pragma mark -
+
+@implementation MuOutlineController
+
+- (id) initWithTarget: (id)aTarget titles: (NSMutableArray*)aTitles pages: (NSMutableArray*)aPages
+{
+ self = [super initWithStyle: UITableViewStylePlain];
+ if (self) {
+ [self setTitle: @"Table of Contents"];
+ target = aTarget; // only keep a weak reference, to avoid retain cycles
+ titles = [aTitles retain];
+ pages = [aPages retain];
+ [[self tableView] setSeparatorStyle: UITableViewCellSeparatorStyleNone];
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ [titles release];
+ [pages release];
+ [super dealloc];
+}
+
+- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)o
+{
+ return YES;
+}
+
+- (NSInteger) numberOfSectionsInTableView: (UITableView*)tableView
+{
+ return 1;
+}
+
+- (NSInteger) tableView: (UITableView*)tableView numberOfRowsInSection: (NSInteger)section
+{
+ return [titles count];
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ return 28;
+}
+
+- (UITableViewCell*) tableView: (UITableView*)tableView cellForRowAtIndexPath: (NSIndexPath*)indexPath
+{
+ static NSString *cellid = @"MuCellIdent";
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellid];
+ if (!cell)
+ cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleValue1 reuseIdentifier: cellid] autorelease];
+ NSString *title = [titles objectAtIndex: [indexPath row]];
+ NSString *page = [pages objectAtIndex: [indexPath row]];
+ [[cell textLabel] setFont: [UIFont systemFontOfSize: 16]];
+ [[cell detailTextLabel] setFont: [UIFont systemFontOfSize: 16]];
+ [[cell textLabel] setText: title];
+ [[cell detailTextLabel] setText: [NSString stringWithFormat: @"%d", [page intValue]+1]];
+ return cell;
+}
+
+- (void) tableView: (UITableView*)tableView didSelectRowAtIndexPath: (NSIndexPath*)indexPath
+{
+ NSNumber *page = [pages objectAtIndex: [indexPath row]];
+ [target gotoPage: [page intValue] animated: NO];
+ [[self navigationController] popViewControllerAnimated: YES];
+}
+
+@end
+
+#pragma mark -
+
+@implementation MuPageView
+
+- (id) initWithFrame: (CGRect)frame document: (struct document*)aDoc page: (int)aNumber
+{
+ self = [super initWithFrame: frame];
+ if (self) {
+ doc = aDoc;
+ number = aNumber;
+ cancel = NO;
+
+ [self setShowsVerticalScrollIndicator: NO];
+ [self setShowsHorizontalScrollIndicator: NO];
+ [self setDecelerationRate: UIScrollViewDecelerationRateFast];
+ [self setDelegate: self];
+
+ // zoomDidFinish/Begin events fire before bounce animation completes,
+ // making a mess when we rearrange views during the animation.
+ [self setBouncesZoom: NO];
+
+ [self resetZoomAnimated: NO];
+
+ // TODO: use a one shot timer to delay the display of this?
+ loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
+ [loadingView startAnimating];
+ [self addSubview: loadingView];
+
+ [self loadPage];
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ // dealloc can trigger in background thread when the queued block is
+ // our last owner, and releases us on completion.
+ // Send the dealloc back to the main thread so we don't mess up UIKit.
+ if (dispatch_get_current_queue() != dispatch_get_main_queue()) {
+ __block id block_self = self; // don't auto-retain self!
+ dispatch_async(dispatch_get_main_queue(), ^{ [block_self dealloc]; });
+ } else {
+ [tileView release];
+ [loadingView release];
+ [imageView release];
+ [super dealloc];
+ }
+}
+
+- (int) number
+{
+ return number;
+}
+
+- (void) resetZoomAnimated: (BOOL)animated
+{
+ // discard tile and any pending tile jobs
+ tileFrame = CGRectZero;
+ tileScale = 1;
+ if (tileView) {
+ [tileView removeFromSuperview];
+ [tileView release];
+ tileView = nil;
+ }
+
+ [self setMinimumZoomScale: 1];
+ [self setMaximumZoomScale: 5];
+ [self setZoomScale: 1 animated: animated];
+}
+
+- (void) removeFromSuperview
+{
+ cancel = YES;
+ [super removeFromSuperview];
+}
+
+- (void) loadPage
+{
+ if (number < 0 || number >= count_pages(doc))
+ return;
+ dispatch_async(queue, ^{
+ if (!cancel) {
+ printf("render page %d\n", number);
+ UIImage *image = renderPage(doc, number, self.bounds.size);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self displayImage: image];
+ [image release];
+ });
+ } else {
+ printf("cancel page %d\n", number);
+ }
+ });
+}
+
+- (void) displayImage: (UIImage*)image
+{
+ if (loadingView) {
+ [loadingView removeFromSuperview];
+ [loadingView release];
+ loadingView = nil;
+ }
+
+ if (!imageView) {
+ imageView = [[UIImageView alloc] initWithImage: image];
+ imageView.opaque = YES;
+ [self addSubview: imageView];
+ } else {
+ [imageView setImage: image];
+ }
+
+ [self resizeImage];
+}
+
+- (void) resizeImage
+{
+ if (imageView) {
+ CGSize imageSize = imageView.image.size;
+ CGSize scale = fitPageToScreen(imageSize, self.bounds.size);
+ if (fabs(scale.width - 1) > 0.1) {
+ CGRect frame = [imageView frame];
+ frame.size.width = imageSize.width * scale.width;
+ frame.size.height = imageSize.height * scale.height;
+ [imageView setFrame: frame];
+
+ printf("resized view; queuing up a reload (%d)\n", number);
+ dispatch_async(queue, ^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ CGSize scale = fitPageToScreen(imageView.image.size, self.bounds.size);
+ if (fabs(scale.width - 1) > 0.1)
+ [self loadPage];
+ });
+ });
+ } else {
+ [imageView sizeToFit];
+ }
+
+ [self setContentSize: imageView.frame.size];
+
+ [self layoutIfNeeded];
+ }
+}
+
+- (void) willRotate
+{
+ if (imageView) {
+ [self resetZoomAnimated: NO];
+ [self resizeImage];
+ }
+}
+
+- (void) layoutSubviews
+{
+ [super layoutSubviews];
+
+ // center the image as it becomes smaller than the size of the screen
+
+ CGSize boundsSize = self.bounds.size;
+ CGRect frameToCenter = loadingView ? loadingView.frame : imageView.frame;
+
+ // center horizontally
+ if (frameToCenter.size.width < boundsSize.width)
+ frameToCenter.origin.x = floor((boundsSize.width - frameToCenter.size.width) / 2);
+ else
+ frameToCenter.origin.x = 0;
+
+ // center vertically
+ if (frameToCenter.size.height < boundsSize.height)
+ frameToCenter.origin.y = floor((boundsSize.height - frameToCenter.size.height) / 2);
+ else
+ frameToCenter.origin.y = 0;
+
+ if (loadingView)
+ loadingView.frame = frameToCenter;
+ else
+ imageView.frame = frameToCenter;
+}
+
+- (UIView*) viewForZoomingInScrollView: (UIScrollView*)scrollView
+{
+ return imageView;
+}
+
+- (void) loadTile
+{
+ CGSize pageSize = self.bounds.size;
+
+ tileFrame.origin = self.contentOffset;
+ tileFrame.size = self.bounds.size;
+ tileFrame = CGRectIntersection(tileFrame, imageView.frame);
+ tileScale = self.zoomScale;
+
+ CGRect frame = tileFrame;
+ float scale = tileScale;
+
+ CGRect viewFrame = frame;
+ if (self.contentOffset.x < imageView.frame.origin.x)
+ viewFrame.origin.x = 0;
+ if (self.contentOffset.y < imageView.frame.origin.y)
+ viewFrame.origin.y = 0;
+
+ if (scale < 1.01)
+ return;
+
+ dispatch_async(queue, ^{
+ __block BOOL isValid;
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ isValid = CGRectEqualToRect(frame, tileFrame) && scale == tileScale;
+ });
+ if (!isValid) {
+ printf("cancel tile\n");
+ return;
+ }
+
+ printf("render tile\n");
+ UIImage *image = renderTile(doc, number, pageSize, viewFrame, scale);
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ isValid = CGRectEqualToRect(frame, tileFrame) && scale == tileScale;
+ if (isValid) {
+ tileFrame = CGRectZero;
+ tileScale = 1;
+ if (tileView) {
+ [tileView removeFromSuperview];
+ [tileView release];
+ tileView = nil;
+ }
+
+ tileView = [[UIImageView alloc] initWithFrame: frame];
+ [tileView setImage: image];
+ [self addSubview: tileView];
+ } else {
+ printf("discard tile\n");
+ }
+ [image release];
+ });
+ });
+}
+
+- (void) scrollViewDidScrollToTop:(UIScrollView *)scrollView { [self loadTile]; }
+- (void) scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { [self loadTile]; }
+- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView { [self loadTile]; }
+- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
+{
+ if (!decelerate)
+ [self loadTile];
+}
+
+- (void) scrollViewWillBeginZooming: (UIScrollView*)scrollView withView: (UIView*)view
+{
+ // discard tile and any pending tile jobs
+ tileFrame = CGRectZero;
+ tileScale = 1;
+ if (tileView) {
+ [tileView removeFromSuperview];
+ [tileView release];
+ tileView = nil;
+ }
+}
+
+- (void) scrollViewDidEndZooming: (UIScrollView*)scrollView withView: (UIView*)view atScale: (float)scale
+{
+ [self loadTile];
+}
+
+@end
+
+#pragma mark -
+
+@implementation MuDocumentController
+
+- (id) initWithFile: (NSString*)nsfilename
+{
+ char filename[PATH_MAX];
+
+ self = [super init];
+ if (!self)
+ return nil;
+
+ key = [nsfilename retain];
+
+ dispatch_sync(queue, ^{});
+
+ strcpy(filename, [NSHomeDirectory() UTF8String]);
+ strcat(filename, "/Documents/");
+ strcat(filename, [nsfilename UTF8String]);
+
+ printf("open document '%s'\n", filename);
+
+ doc = open_document(filename);
+ if (!doc) {
+ showAlert(@"Cannot open document");
+ [self release];
+ return nil;
+ }
+
+ NSMutableArray *titles = [[NSMutableArray alloc] init];
+ NSMutableArray *pages = [[NSMutableArray alloc] init];
+ fz_outline *root = load_outline(doc);
+ if (root) {
+ flattenOutline(titles, pages, root, 0);
+ fz_free_outline(root);
+ }
+ if ([titles count]) {
+ outline = [[MuOutlineController alloc] initWithTarget: self titles: titles pages: pages];
+ [[self navigationItem] setRightBarButtonItem:
+ [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem: UIBarButtonSystemItemBookmarks
+ target:self action:@selector(onShowOutline:)]];
+ }
+ [titles release];
+ [pages release];
+
+ return self;
+}
+
+- (void) loadView
+{
+ [[NSUserDefaults standardUserDefaults] setObject: key forKey: @"OpenDocumentKey"];
+
+ current = [[NSUserDefaults standardUserDefaults] integerForKey: key];
+ if (current < 0 || current >= count_pages(doc))
+ current = 0;
+
+ UIView *view = [[UIView alloc] initWithFrame: CGRectZero];
+ [view setAutoresizingMask: UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
+ [view setAutoresizesSubviews: YES];
+
+ visiblePages = [[NSMutableSet alloc] init];
+ recycledPages = [[NSMutableSet alloc] init];
+
+ canvas = [[UIScrollView alloc] initWithFrame: CGRectMake(0,0,GAP,0)];
+ [canvas setAutoresizingMask: UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
+ [canvas setPagingEnabled: YES];
+ [canvas setShowsHorizontalScrollIndicator: NO];
+ [canvas setShowsVerticalScrollIndicator: NO];
+ [canvas setDelegate: self];
+
+ [canvas addGestureRecognizer: [[[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(onTap:)] autorelease]];
+
+ scroll_animating = NO;
+
+ indicator = [[UILabel alloc] initWithFrame: CGRectZero];
+ [indicator setAutoresizingMask: UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin];
+ [indicator setText: @"0000 of 9999"];
+ [indicator sizeToFit];
+ [indicator setCenter: CGPointMake(0, INDICATOR_Y)];
+ [indicator setTextAlignment: UITextAlignmentCenter];
+ [indicator setBackgroundColor: [[UIColor blackColor] colorWithAlphaComponent: 0.5]];
+ [indicator setTextColor: [UIColor whiteColor]];
+
+ slider = [[UISlider alloc] initWithFrame: CGRectZero];
+ [slider setMinimumValue: 0];
+ [slider setMaximumValue: count_pages(doc) - 1];
+ [slider addTarget: self action: @selector(onSlide:) forControlEvents: UIControlEventValueChanged];
+
+ [view addSubview: canvas];
+ [view addSubview: indicator];
+
+ wrapper = [[UIBarButtonItem alloc] initWithCustomView: slider];
+ [self setToolbarItems: [NSArray arrayWithObjects: wrapper, nil]];
+
+ [self setView: view];
+ [view release];
+}
+
+- (void) viewDidUnload
+{
+ [visiblePages release]; visiblePages = nil;
+ [recycledPages release]; recycledPages = nil;
+ [indicator release]; indicator = nil;
+ [slider release]; slider = nil;
+ [wrapper release]; wrapper = nil;
+ [canvas release]; canvas = nil;
+}
+
+- (void) dealloc
+{
+ if (doc) {
+ struct document *self_doc = doc; // don't auto-retain self here!
+ dispatch_async(queue, ^{
+ printf("close document\n");
+ close_document(self_doc);
+ });
+ }
+ [outline release];
+ [key release];
+ [super dealloc];
+}
+
+- (void) viewWillAppear: (BOOL)animated
+{
+ CGSize size = [canvas frame].size;
+ width = size.width;
+ height = size.height;
+
+ [self setTitle: [key lastPathComponent]];
+
+ [slider setValue: current];
+
+ [indicator setText: [NSString stringWithFormat: @" %d of %d ", current+1, count_pages(doc)]];
+
+ [canvas setContentInset: UIEdgeInsetsZero];
+ [canvas setContentSize: CGSizeMake(count_pages(doc) * width, height)];
+ [canvas setContentOffset: CGPointMake(current * width, 0)];
+
+ [wrapper setWidth: width - GAP - 24];
+
+ [[self navigationController] setToolbarHidden: NO animated: animated];
+}
+
+- (void) viewDidAppear: (BOOL)animated
+{
+ [self scrollViewDidScroll: canvas];
+}
+
+- (void) viewWillDisappear: (BOOL)animated
+{
+ [self setTitle: @"Resume"];
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey: @"OpenDocumentKey"];
+ [[self navigationController] setToolbarHidden: YES animated: animated];
+}
+
+- (void) showNavigationBar
+{
+ if ([[self navigationController] isNavigationBarHidden]) {
+ [[self navigationController] setNavigationBarHidden: NO];
+ [[self navigationController] setToolbarHidden: NO];
+ [indicator setHidden: NO];
+
+ [UIView beginAnimations: @"MuNavBar" context: NULL];
+
+ [[[self navigationController] navigationBar] setAlpha: 1];
+ [[[self navigationController] toolbar] setAlpha: 1];
+ [indicator setAlpha: 1];
+
+ [UIView commitAnimations];
+ }
+}
+
+- (void) hideNavigationBar
+{
+ if (![[self navigationController] isNavigationBarHidden]) {
+ [UIView beginAnimations: @"MuNavBar" context: NULL];
+ [UIView setAnimationDelegate: self];
+ [UIView setAnimationDidStopSelector: @selector(onHideNavigationBarFinished)];
+
+ [[[self navigationController] navigationBar] setAlpha: 0];
+ [[[self navigationController] toolbar] setAlpha: 0];
+ [indicator setAlpha: 0];
+
+ [UIView commitAnimations];
+ }
+}
+
+- (void) onHideNavigationBarFinished
+{
+ [[self navigationController] setNavigationBarHidden: YES];
+ [[self navigationController] setToolbarHidden: YES];
+ [indicator setHidden: YES];
+}
+
+- (void) onShowOutline: (id)sender
+{
+ [[self navigationController] pushViewController: outline animated: YES];
+}
+
+- (void) onSlide: (id)sender
+{
+ int number = [slider value];
+ if ([slider isTracking])
+ [indicator setText: [NSString stringWithFormat: @" %d of %d ", number+1, count_pages(doc)]];
+ else
+ [self gotoPage: number animated: NO];
+}
+
+- (void) onTap: (UITapGestureRecognizer*)sender
+{
+ CGPoint p = [sender locationInView: canvas];
+ CGPoint ofs = [canvas contentOffset];
+ float x0 = (width - GAP) / 5;
+ float x1 = (width - GAP) - x0;
+ p.x -= ofs.x;
+ p.y -= ofs.y;
+ if (p.x < x0) {
+ [self gotoPage: current-1 animated: YES];
+ } else if (p.x > x1) {
+ [self gotoPage: current+1 animated: YES];
+ } else {
+ if ([[self navigationController] isNavigationBarHidden])
+ [self showNavigationBar];
+ else
+ [self hideNavigationBar];
+ }
+}
+
+- (void) scrollViewWillBeginDragging: (UIScrollView *)scrollView
+{
+ [self hideNavigationBar];
+}
+
+- (void) scrollViewDidScroll: (UIScrollView*)scrollview
+{
+ if (width == 0)
+ return; // not visible yet
+
+ if (scroll_animating)
+ return; // don't mess with layout during animations
+
+ float x = [canvas contentOffset].x + width * 0.5f;
+ current = x / width;
+
+ [[NSUserDefaults standardUserDefaults] setInteger: current forKey: key];
+
+ [indicator setText: [NSString stringWithFormat: @" %d of %d ", current+1, count_pages(doc)]];
+ [slider setValue: current];
+
+ // swap the page views in and out
+
+ for (MuPageView *view in visiblePages) {
+ if ([view number] != current)
+ [view resetZoomAnimated: YES];
+ if ([view number] < current - 2 || [view number] > current + 2) {
+ [recycledPages addObject: view];
+ [view removeFromSuperview];
+ }
+ }
+ [visiblePages minusSet: recycledPages];
+ [recycledPages removeAllObjects]; // don't bother recycling them...
+
+ [self createPageView: current];
+ [self createPageView: current - 1];
+ [self createPageView: current + 1];
+}
+
+- (void) createPageView: (int)number
+{
+ if (number < 0 || number >= count_pages(doc))
+ return;
+ int found = 0;
+ for (MuPageView *view in [canvas subviews])
+ if ([view number] == number)
+ found = 1;
+ if (!found) {
+ MuPageView *view = [[MuPageView alloc] initWithFrame: CGRectMake(number * width, 0, width-GAP, height) document: doc page: number];
+ [visiblePages addObject: view];
+ [canvas addSubview: view];
+ [view release];
+ }
+}
+
+- (void) gotoPage: (int)number animated: (BOOL)animated
+{
+ if (number < 0)
+ number = 0;
+ if (number >= count_pages(doc))
+ number = count_pages(doc) - 1;
+ if (animated) {
+ // setContentOffset:animated: does not use the normal animation
+ // framework. It also doesn't play nice with the tap gesture
+ // recognizer. So we do our own page flipping animation here.
+ // We must set the scroll_animating flag so that we don't create
+ // or remove subviews until after the animation, or they'll
+ // swoop in from origo during the animation.
+
+ scroll_animating = YES;
+ [UIView beginAnimations: @"MuScroll" context: NULL];
+ [UIView setAnimationDuration: 0.4];
+ [UIView setAnimationBeginsFromCurrentState: YES];
+ [UIView setAnimationDelegate: self];
+ [UIView setAnimationDidStopSelector: @selector(onGotoPageFinished)];
+
+ for (MuPageView *view in visiblePages)
+ [view resetZoomAnimated: NO];
+
+ [canvas setContentOffset: CGPointMake(number * width, 0)];
+ [slider setValue: number];
+ [indicator setText: [NSString stringWithFormat: @" %d of %d ", number+1, count_pages(doc)]];
+
+ [UIView commitAnimations];
+ } else {
+ for (MuPageView *view in visiblePages)
+ [view resetZoomAnimated: NO];
+ [canvas setContentOffset: CGPointMake(number * width, 0)];
+ }
+ current = number;
+}
+
+- (void) onGotoPageFinished
+{
+ scroll_animating = NO;
+ [self scrollViewDidScroll: canvas];
+}
+
+- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)o
+{
+ return YES;
+}
+
+- (void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
+{
+ CGSize size = [canvas frame].size;
+ int max_width = MAX(width, size.width);
+
+ width = size.width;
+ height = size.height;
+
+ [wrapper setWidth: width - GAP - 24];
+ [[[self navigationController] toolbar] setNeedsLayout]; // force layout!
+
+ // use max_width so we don't clamp the content offset too early during animation
+ [canvas setContentSize: CGSizeMake(count_pages(doc) * max_width, height)];
+ [canvas setContentOffset: CGPointMake(current * width, 0)];
+
+ for (MuPageView *view in visiblePages) {
+ if ([view number] == current) {
+ [view setFrame: CGRectMake([view number] * width, 0, width-GAP, height)];
+ [view willRotate];
+ }
+ }
+ for (MuPageView *view in visiblePages) {
+ if ([view number] != current) {
+ [view setFrame: CGRectMake([view number] * width, 0, width-GAP, height)];
+ [view willRotate];
+ }
+ }
+}
+
+- (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation)o
+{
+ [canvas setContentSize: CGSizeMake(count_pages(doc) * width, height)];
+ [canvas setContentOffset: CGPointMake(current * width, 0)];
+}
+
+@end
+
+#pragma mark -
+
+@implementation MuAppDelegate
+
+- (BOOL) application: (UIApplication*)application didFinishLaunchingWithOptions: (NSDictionary*)launchOptions
+{
+ queue = dispatch_queue_create("com.artifex.mupdf.queue", NULL);
+
+ glyphcache = fz_new_glyph_cache();
+
+ screenScale = [[UIScreen mainScreen] scale];
+
+ library = [[MuLibraryController alloc] initWithStyle: UITableViewStylePlain];
+
+ navigator = [[UINavigationController alloc] initWithRootViewController: library];
+ [[navigator navigationBar] setTranslucent: YES];
+ [[navigator toolbar] setTranslucent: YES];
+ [navigator setDelegate: self];
+
+ window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
+ [window setBackgroundColor: [UIColor scrollViewTexturedBackgroundColor]];
+ [window addSubview: [navigator view]];
+ [window makeKeyAndVisible];
+
+ NSString *filename = [[NSUserDefaults standardUserDefaults] objectForKey: @"OpenDocumentKey"];
+ if (filename)
+ [library openDocument: filename];
+
+ return YES;
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application
+{
+ printf("applicationDidEnterBackground!\n");
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application
+{
+ printf("applicationWillEnterForeground!\n");
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application
+{
+ printf("applicationDidBecomeActive!\n");
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application
+{
+ printf("applicationWillTerminate!\n");
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
+{
+ printf("applicationDidReceiveMemoryWarning\n");
+}
+
+- (void) dealloc
+{
+ dispatch_release(queue);
+ [library release];
+ [navigator release];
+ [window release];
+ [super dealloc];
+}
+
+@end
+
+#pragma mark -
+
+int main(int argc, char *argv[])
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ int retVal = UIApplicationMain(argc, argv, nil, @"MuAppDelegate");
+ [pool release];
+ return retVal;
+}
diff --git a/pdf/data_encodings.h b/pdf/data_encodings.h
index 4a86e514..96b06769 100644
--- a/pdf/data_encodings.h
+++ b/pdf/data_encodings.h
@@ -2,9 +2,9 @@
const unsigned short pdf_doc_encoding[256] =
{
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0009, 0x000A, 0x0000, 0x0000, 0x000D, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0, ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', 0x9, 0xA, ' ', ' ', 0xD, ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
diff --git a/pdf/mupdf.h b/pdf/mupdf.h
index 727d28e2..d3eb72e6 100644
--- a/pdf/mupdf.h
+++ b/pdf/mupdf.h
@@ -410,7 +410,6 @@ void pdf_debug_font(pdf_font_desc *fontdesc);
typedef struct pdf_link_s pdf_link;
typedef struct pdf_annot_s pdf_annot;
-typedef struct pdf_outline_s pdf_outline;
typedef enum pdf_link_kind_e
{
@@ -438,22 +437,11 @@ struct pdf_annot_s
pdf_annot *next;
};
-struct pdf_outline_s
-{
- char *title;
- pdf_link *link;
- int count;
- pdf_outline *child;
- pdf_outline *next;
-};
-
fz_obj *pdf_lookup_dest(pdf_xref *xref, fz_obj *needle);
fz_obj *pdf_lookup_name(pdf_xref *xref, char *which, fz_obj *needle);
fz_obj *pdf_load_name_tree(pdf_xref *xref, char *which);
-pdf_outline *pdf_load_outline(pdf_xref *xref);
-void pdf_debug_outline(fz_context *ctx, pdf_outline *outline, int level);
-void pdf_free_outline(fz_context *ctx, pdf_outline *outline);
+fz_outline *pdf_load_outline(pdf_xref *xref);
pdf_link *pdf_load_link(pdf_xref *xref, fz_obj *dict);
void pdf_load_links(pdf_link **, pdf_xref *, fz_obj *annots);
diff --git a/pdf/pdf_interpret.c b/pdf/pdf_interpret.c
index e7555e24..21039f8c 100644
--- a/pdf/pdf_interpret.c
+++ b/pdf/pdf_interpret.c
@@ -82,6 +82,8 @@ struct pdf_csi_s
/* path object state */
fz_path *path;
+ int clip;
+ int clip_even_odd;
/* text object state */
fz_text *text;
@@ -241,14 +243,6 @@ pdf_show_image(pdf_csi *csi, fz_pixmap *image)
pdf_end_group(csi);
}
-static void pdf_show_clip(pdf_csi *csi, int even_odd)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
-
- gstate->clip_depth++;
- fz_clip_path(csi->dev, csi->path, NULL, even_odd, gstate->ctm);
-}
-
static void
pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd)
{
@@ -271,6 +265,12 @@ pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd)
if (dofill || dostroke)
pdf_begin_group(csi, bbox);
+ if (csi->clip)
+ {
+ gstate->clip_depth++;
+ fz_clip_path(csi->dev, path, NULL, csi->clip_even_odd, gstate->ctm);
+ }
+
if (dofill)
{
switch (gstate->fill.kind)
@@ -674,7 +674,9 @@ pdf_new_csi(pdf_xref *xref, fz_device *dev, fz_matrix ctm, char *target)
csi->xbalance = 0;
csi->in_text = 0;
- csi->path = fz_new_path(dev->ctx);
+ csi->path = fz_new_path(xref->ctx);
+ csi->clip = 0;
+ csi->clip_even_odd = 0;
csi->text = NULL;
csi->tlm = fz_identity;
@@ -1090,7 +1092,7 @@ pdf_run_xobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj, fz_matrix tr
fz_lineto(ctx, csi->path, xobj->bbox.x1, xobj->bbox.y1);
fz_lineto(ctx, csi->path, xobj->bbox.x0, xobj->bbox.y1);
fz_closepath(ctx, csi->path);
- pdf_show_clip(csi, 0);
+ csi->clip = 1;
pdf_show_path(csi, 0, 0, 0, 0);
/* run contents */
@@ -1702,12 +1704,14 @@ static void pdf_run_TJ(pdf_csi *csi)
static void pdf_run_W(pdf_csi *csi)
{
- pdf_show_clip(csi, 0);
+ csi->clip = 1;
+ csi->clip_even_odd = 0;
}
static void pdf_run_Wstar(pdf_csi *csi)
{
- pdf_show_clip(csi, 1);
+ csi->clip = 1;
+ csi->clip_even_odd = 1;
}
static void pdf_run_b(pdf_csi *csi)
diff --git a/pdf/pdf_outline.c b/pdf/pdf_outline.c
index 677a701f..9353d892 100644
--- a/pdf/pdf_outline.c
+++ b/pdf/pdf_outline.c
@@ -1,37 +1,39 @@
#include "fitz.h"
#include "mupdf.h"
-static pdf_outline *
+static fz_outline *
pdf_load_outline_imp(pdf_xref *xref, fz_obj *dict)
{
fz_context *ctx = xref->ctx;
- pdf_outline *node;
+ pdf_link *link;
+ fz_outline *node;
fz_obj *obj;
if (fz_is_null(dict))
return NULL;
- node = fz_malloc(ctx, sizeof(pdf_outline));
+ node = fz_malloc(ctx, sizeof(fz_outline));
+ node->ctx = ctx;
node->title = NULL;
- node->link = NULL;
- node->child = NULL;
+ node->page = 0;
+ node->down = NULL;
node->next = NULL;
- node->count = 0;
obj = fz_dict_gets(dict, "Title");
if (obj)
node->title = pdf_to_utf8(ctx, obj);
- obj = fz_dict_gets(dict, "Count");
- if (obj)
- node->count = fz_to_int(obj);
-
if (fz_dict_gets(dict, "Dest") || fz_dict_gets(dict, "A"))
- node->link = pdf_load_link(xref, dict);
+ {
+ link = pdf_load_link(xref, dict);
+ if (link->kind == PDF_LINK_GOTO)
+ node->page = pdf_find_page_number(xref, fz_array_get(link->dest, 0));
+ pdf_free_link(xref->ctx, link);
+ }
obj = fz_dict_gets(dict, "First");
if (obj)
- node->child = pdf_load_outline_imp(xref, obj);
+ node->down = pdf_load_outline_imp(xref, obj);
obj = fz_dict_gets(dict, "Next");
if (obj)
@@ -40,7 +42,7 @@ pdf_load_outline_imp(pdf_xref *xref, fz_obj *dict)
return node;
}
-pdf_outline *
+fz_outline *
pdf_load_outline(pdf_xref *xref)
{
fz_obj *root, *obj, *first;
@@ -53,42 +55,3 @@ pdf_load_outline(pdf_xref *xref)
return NULL;
}
-
-void
-pdf_free_outline(fz_context *ctx, pdf_outline *outline)
-{
- if (outline->child)
- pdf_free_outline(ctx, outline->child);
- if (outline->next)
- pdf_free_outline(ctx, outline->next);
- if (outline->link)
- pdf_free_link(ctx, outline->link);
- fz_free(ctx, outline->title);
- fz_free(ctx, outline);
-}
-
-void
-pdf_debug_outline(fz_context *ctx, pdf_outline *outline, int level)
-{
- int i;
- while (outline)
- {
- for (i = 0; i < level; i++)
- putchar(' ');
-
- if (outline->title)
- printf("%s ", outline->title);
- else
- printf("<NULL> ");
-
- if (outline->link)
- fz_debug_obj(outline->link->dest);
- else
- printf("<NULL>\n");
-
- if (outline->child)
- pdf_debug_outline(ctx, outline->child, level + 2);
-
- outline = outline->next;
- }
-}
diff --git a/pdf/pdf_page.c b/pdf/pdf_page.c
index c97eb9b4..fdc48ff3 100644
--- a/pdf/pdf_page.c
+++ b/pdf/pdf_page.c
@@ -269,7 +269,7 @@ pdf_load_page(pdf_xref *xref, int number)
pdf_annot *annot;
fz_obj *pageobj, *pageref;
fz_obj *obj;
- fz_bbox bbox;
+ fz_rect mediabox, cropbox;
fz_context *ctx = xref->ctx;
if (number < 0 || number >= xref->page_len)
@@ -289,28 +289,24 @@ pdf_load_page(pdf_xref *xref, int number)
page->links = NULL;
page->annots = NULL;
- obj = fz_dict_gets(pageobj, "MediaBox");
- bbox = fz_round_rect(pdf_to_rect(ctx, obj));
- if (fz_is_empty_rect(pdf_to_rect(ctx, obj)))
+ mediabox = pdf_to_rect(ctx, fz_dict_gets(pageobj, "MediaBox"));
+ if (fz_is_empty_rect(mediabox))
{
fz_warn(ctx, "cannot find page size for page %d", number + 1);
- bbox.x0 = 0;
- bbox.y0 = 0;
- bbox.x1 = 612;
- bbox.y1 = 792;
+ mediabox.x0 = 0;
+ mediabox.y0 = 0;
+ mediabox.x1 = 612;
+ mediabox.y1 = 792;
}
- obj = fz_dict_gets(pageobj, "CropBox");
- if (fz_is_array(obj))
- {
- fz_bbox cropbox = fz_round_rect(pdf_to_rect(ctx, obj));
- bbox = fz_intersect_bbox(bbox, cropbox);
- }
+ cropbox = pdf_to_rect(ctx, fz_dict_gets(pageobj, "CropBox"));
+ if (!fz_is_empty_rect(cropbox))
+ mediabox = fz_intersect_rect(mediabox, cropbox);
- page->mediabox.x0 = MIN(bbox.x0, bbox.x1);
- page->mediabox.y0 = MIN(bbox.y0, bbox.y1);
- page->mediabox.x1 = MAX(bbox.x0, bbox.x1);
- page->mediabox.y1 = MAX(bbox.y0, bbox.y1);
+ page->mediabox.x0 = MIN(mediabox.x0, mediabox.x1);
+ page->mediabox.y0 = MIN(mediabox.y0, mediabox.y1);
+ page->mediabox.x1 = MAX(mediabox.x0, mediabox.x1);
+ page->mediabox.y1 = MAX(mediabox.y0, mediabox.y1);
if (page->mediabox.x1 - page->mediabox.x0 < 1 || page->mediabox.y1 - page->mediabox.y0 < 1)
{
diff --git a/pdf/pdf_shade.c b/pdf/pdf_shade.c
index ca39e1f8..b281542b 100644
--- a/pdf/pdf_shade.c
+++ b/pdf/pdf_shade.c
@@ -1029,7 +1029,8 @@ pdf_load_shading_dict(pdf_xref *xref, fz_obj *dict, fz_matrix transform)
for (i = 0; i < funcs; i++)
{
func[i] = pdf_load_function(xref, fz_array_get(obj, i));
- fz_throw(ctx, "cannot load shading function (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
+ if (func[i] == NULL)
+ fz_throw(ctx, "cannot load shading function (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
}
}
diff --git a/pdf/pdf_xobject.c b/pdf/pdf_xobject.c
index ceec02d0..81ef52fa 100644
--- a/pdf/pdf_xobject.c
+++ b/pdf/pdf_xobject.c
@@ -51,7 +51,8 @@ pdf_load_xobject(pdf_xref *xref, fz_obj *dict)
if (obj)
{
form->colorspace = pdf_load_colorspace(xref, obj);
- fz_throw(ctx, "cannot load xobject colorspace");
+ if (form->colorspace == NULL)
+ fz_throw(ctx, "cannot load xobject colorspace");
}
}
diff --git a/scripts/cmapdump.c b/scripts/cmapdump.c
index 9827fff5..9f75a401 100644
--- a/scripts/cmapdump.c
+++ b/scripts/cmapdump.c
@@ -157,7 +157,8 @@ main(int argc, char **argv)
fprintf(fo, "};\n");
- printf("\t{\"%s\",&cmap_%s},\n", cmap->cmap_name, name);
+ if (getenv("verbose"))
+ printf("\t{\"%s\",&cmap_%s},\n", cmap->cmap_name, name);
fz_close(fi);
}
diff --git a/scripts/fontdump.c b/scripts/fontdump.c
index e40bf4ab..34d996a4 100644
--- a/scripts/fontdump.c
+++ b/scripts/fontdump.c
@@ -1,6 +1,7 @@
/* fontdump.c -- an "xxd -i" workalike for dumping binary fonts as source code */
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
static int
@@ -88,7 +89,8 @@ main(int argc, char **argv)
len = ftell(fi);
fseek(fi, 0, SEEK_SET);
- printf("\t{\"%s\",pdf_font_%s,%d},\n", origname, fontname, len);
+ if (getenv("verbose"))
+ printf("\t{\"%s\",pdf_font_%s,%d},\n", origname, fontname, len);
fprintf(fo, "\n#ifdef HAVE_INCBIN\n");
fprintf(fo, "extern const unsigned char pdf_font_%s[%d];\n", fontname, len);
diff --git a/win32/libmupdf.vcproj b/win32/libmupdf.vcproj
index 3abe1fa2..52977dca 100644
--- a/win32/libmupdf.vcproj
+++ b/win32/libmupdf.vcproj
@@ -411,6 +411,10 @@
>
</File>
<File
+ RelativePath="..\fitz\doc_outline.c"
+ >
+ </File>
+ <File
RelativePath="..\fitz\filt_basic.c"
>
</File>
@@ -539,7 +543,7 @@
>
</File>
<File
- RelativePath="..\draw\draw_scale.c"
+ RelativePath="..\draw\draw_simple_scale.c"
>
</File>
<File
@@ -579,6 +583,10 @@
>
</File>
<File
+ RelativePath="..\xps\xps_outline.c"
+ >
+ </File>
+ <File
RelativePath="..\xps\xps_path.c"
>
</File>
diff --git a/win32/mupdf.vcproj b/win32/mupdf.vcproj
index 9de7ca25..c61cb34d 100644
--- a/win32/mupdf.vcproj
+++ b/win32/mupdf.vcproj
@@ -241,6 +241,10 @@
>
</File>
<File
+ RelativePath="..\apps\pdfapp.h"
+ >
+ </File>
+ <File
RelativePath="..\apps\win_main.c"
>
</File>
diff --git a/xps/muxps.h b/xps/muxps.h
index 48ad2ae0..18ca528e 100644
--- a/xps/muxps.h
+++ b/xps/muxps.h
@@ -15,6 +15,8 @@ typedef struct xps_document_s xps_document;
#define REL_START_PART \
"http://schemas.microsoft.com/xps/2005/06/fixedrepresentation"
+#define REL_DOC_STRUCTURE \
+ "http://schemas.microsoft.com/xps/2005/06/documentstructure"
#define REL_REQUIRED_RESOURCE \
"http://schemas.microsoft.com/xps/2005/06/required-resource"
#define REL_REQUIRED_RESOURCE_RECURSIVE \
@@ -70,22 +72,32 @@ void xps_free_part(xps_document *doc, xps_part *part);
typedef struct xps_fixdoc_s xps_fixdoc;
typedef struct xps_page_s xps_page;
+typedef struct xps_target_s xps_target;
struct xps_fixdoc_s
{
char *name;
+ char *outline;
xps_fixdoc *next;
};
struct xps_page_s
{
char *name;
+ int number;
int width;
int height;
xml_element *root;
xps_page *next;
};
+struct xps_target_s
+{
+ char *name;
+ int page;
+ xps_target *next;
+};
+
int xps_read_page_list(xps_document *doc);
void xps_debug_page_list(xps_document *doc);
void xps_free_page_list(xps_document *doc);
@@ -94,6 +106,10 @@ int xps_count_pages(xps_document *doc);
xps_page *xps_load_page(xps_document *doc, int number);
void xps_free_page(xps_document *doc, xps_page *page);
+fz_outline *xps_load_outline(xps_document *doc);
+
+int xps_find_link_target(xps_document *doc, char *target_uri);
+
/*
* Images, fonts, and colorspaces.
*/
@@ -207,6 +223,9 @@ struct xps_document_s
xps_fixdoc *last_fixdoc; /* last fixed document */
xps_page *first_page; /* first page of document */
xps_page *last_page; /* last page of document */
+ int page_count;
+
+ xps_target *target; /* link targets */
char *base_uri; /* base uri for parsing XML and resolving relative paths */
char *part_uri; /* part uri for parsing metadata relations */
diff --git a/xps/xps_doc.c b/xps/xps_doc.c
index dca1868c..6b6c0c08 100644
--- a/xps/xps_doc.c
+++ b/xps/xps_doc.c
@@ -1,6 +1,20 @@
#include "fitz.h"
#include "muxps.h"
+static void
+xps_rels_for_part(char *buf, char *name, int buflen)
+{
+ char *p, *basename;
+ p = strrchr(name, '/');
+ basename = p ? p + 1 : name;
+ fz_strlcpy(buf, name, buflen);
+ p = strrchr(buf, '/');
+ if (p) *p = 0;
+ fz_strlcat(buf, "/_rels/", buflen);
+ fz_strlcat(buf, basename, buflen);
+ fz_strlcat(buf, ".rels", buflen);
+}
+
/*
* The FixedDocumentSequence and FixedDocument parts determine
* which parts correspond to actual pages, and the page order.
@@ -23,7 +37,7 @@ xps_debug_page_list(xps_document *doc)
while (page)
{
- printf("page %s w=%d h=%d\n", page->name, page->width, page->height);
+ printf("page[%d] %s w=%d h=%d\n", page->number, page->name, page->width, page->height);
page = page->next;
}
}
@@ -40,6 +54,7 @@ xps_add_fixed_document(xps_document *doc, char *name)
fixdoc = fz_malloc(doc->ctx, sizeof(xps_fixdoc));
fixdoc->name = fz_strdup(doc->ctx, name);
+ fixdoc->outline = NULL;
fixdoc->next = NULL;
if (!doc->first_fixdoc)
@@ -66,6 +81,7 @@ xps_add_fixed_page(xps_document *doc, char *name, int width, int height)
page = fz_malloc(doc->ctx, sizeof(xps_page));
page->name = fz_strdup(doc->ctx, name);
+ page->number = doc->page_count++;
page->width = width;
page->height = height;
page->root = NULL;
@@ -84,6 +100,42 @@ xps_add_fixed_page(xps_document *doc, char *name, int width, int height)
}
static void
+xps_add_link_target(xps_document *doc, char *name)
+{
+ xps_page *page = doc->last_page;
+ xps_target *target = fz_malloc(doc->ctx, sizeof *target);
+ target->name = fz_strdup(doc->ctx, name);
+ target->page = page->number;
+ target->next = doc->target;
+ doc->target = target;
+}
+
+int
+xps_find_link_target(xps_document *doc, char *target_uri)
+{
+ xps_target *target;
+ char *needle = strrchr(target_uri, '#');
+ needle = needle ? needle + 1 : target_uri;
+ for (target = doc->target; target; target = target->next)
+ if (!strcmp(target->name, needle))
+ return target->page;
+ return 0;
+}
+
+static void
+xps_free_link_targets(xps_document *doc)
+{
+ xps_target *target = doc->target, *next;
+ while (target)
+ {
+ next = target->next;
+ fz_free(doc->ctx, target->name);
+ fz_free(doc->ctx, target);
+ target = next;
+ }
+}
+
+static void
xps_free_fixed_pages(xps_document *doc)
{
xps_page *page = doc->first_page;
@@ -119,6 +171,7 @@ xps_free_page_list(xps_document *doc)
{
xps_free_fixed_documents(doc);
xps_free_fixed_pages(doc);
+ xps_free_link_targets(doc);
}
/*
@@ -126,12 +179,10 @@ xps_free_page_list(xps_document *doc)
*/
static void
-xps_parse_metadata_imp(xps_document *doc, xml_element *item)
+xps_parse_metadata_imp(xps_document *doc, xml_element *item, xps_fixdoc *fixdoc)
{
while (item)
{
- xps_parse_metadata_imp(doc, xml_down(item));
-
if (!strcmp(xml_tag(item), "Relationship"))
{
char *target = xml_att(item, "Target");
@@ -142,6 +193,8 @@ xps_parse_metadata_imp(xps_document *doc, xml_element *item)
xps_absolute_path(tgtbuf, doc->base_uri, target, sizeof tgtbuf);
if (!strcmp(type, REL_START_PART))
doc->start_part = fz_strdup(doc->ctx, tgtbuf);
+ if (!strcmp(type, REL_DOC_STRUCTURE) && fixdoc)
+ fixdoc->outline = fz_strdup(doc->ctx, tgtbuf);
}
}
@@ -171,12 +224,21 @@ xps_parse_metadata_imp(xps_document *doc, xml_element *item)
}
}
+ if (!strcmp(xml_tag(item), "LinkTarget"))
+ {
+ char *name = xml_att(item, "Name");
+ if (name)
+ xps_add_link_target(doc, name);
+ }
+
+ xps_parse_metadata_imp(doc, xml_down(item), fixdoc);
+
item = xml_next(item);
}
}
static int
-xps_parse_metadata(xps_document *doc, xps_part *part)
+xps_parse_metadata(xps_document *doc, xps_part *part, xps_fixdoc *fixdoc)
{
xml_element *root;
char buf[1024];
@@ -203,7 +265,7 @@ xps_parse_metadata(xps_document *doc, xps_part *part)
if (!root)
return fz_error_note(-1, "cannot parse metadata part '%s'", part->name);
- xps_parse_metadata_imp(doc, root);
+ xps_parse_metadata_imp(doc, root, fixdoc);
xml_free_element(doc->ctx, root);
@@ -214,7 +276,7 @@ xps_parse_metadata(xps_document *doc, xps_part *part)
}
static int
-xps_read_and_process_metadata_part(xps_document *doc, char *name)
+xps_read_and_process_metadata_part(xps_document *doc, char *name, xps_fixdoc *fixdoc)
{
xps_part *part;
int code;
@@ -223,7 +285,7 @@ xps_read_and_process_metadata_part(xps_document *doc, char *name)
if (!part)
return fz_error_note(-1, "cannot read zip part '%s'", name);
- code = xps_parse_metadata(doc, part);
+ code = xps_parse_metadata(doc, part, fixdoc);
if (code)
return fz_error_note(code, "cannot process metadata part '%s'", name);
@@ -238,20 +300,27 @@ xps_read_page_list(xps_document *doc)
xps_fixdoc *fixdoc;
int code;
- code = xps_read_and_process_metadata_part(doc, "/_rels/.rels");
+ code = xps_read_and_process_metadata_part(doc, "/_rels/.rels", NULL);
if (code)
return fz_error_note(code, "cannot process root relationship part");
if (!doc->start_part)
return fz_error_make("cannot find fixed document sequence start part");
- code = xps_read_and_process_metadata_part(doc, doc->start_part);
+ code = xps_read_and_process_metadata_part(doc, doc->start_part, NULL);
if (code)
return fz_error_note(code, "cannot process FixedDocumentSequence part");
for (fixdoc = doc->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
{
- code = xps_read_and_process_metadata_part(doc, fixdoc->name);
+ char relbuf[1024];
+ xps_rels_for_part(relbuf, fixdoc->name, sizeof relbuf);
+
+ code = xps_read_and_process_metadata_part(doc, relbuf, fixdoc);
+ if (code)
+ fz_error_handle(code, "cannot process FixedDocument rels part");
+
+ code = xps_read_and_process_metadata_part(doc, fixdoc->name, fixdoc);
if (code)
return fz_error_note(code, "cannot process FixedDocument part");
}
@@ -262,11 +331,7 @@ xps_read_page_list(xps_document *doc)
int
xps_count_pages(xps_document *doc)
{
- xps_page *page;
- int n = 0;
- for (page = doc->first_page; page; page = page->next)
- n ++;
- return n;
+ return doc->page_count;
}
static int
diff --git a/xps/xps_outline.c b/xps/xps_outline.c
new file mode 100644
index 00000000..48eb100b
--- /dev/null
+++ b/xps/xps_outline.c
@@ -0,0 +1,124 @@
+#include "fitz.h"
+#include "muxps.h"
+
+/*
+ * Parse the document structure / outline parts referenced from fixdoc relationships.
+ */
+
+static fz_outline *
+xps_find_last_outline_at_level(fz_outline *node, int level, int target_level)
+{
+ while (node->next)
+ node = node->next;
+ if (level == target_level || !node->down)
+ return node;
+ return xps_find_last_outline_at_level(node->down, level + 1, target_level);
+}
+
+static fz_outline *
+xps_parse_document_outline(xps_document *doc, xml_element *root)
+{
+ xml_element *node;
+ fz_outline *head = NULL, *entry, *tail;
+ int last_level = 1, this_level;
+ for (node = xml_down(root); node; node = xml_next(node))
+ {
+ if (!strcmp(xml_tag(node), "OutlineEntry"))
+ {
+ char *level = xml_att(node, "OutlineLevel");
+ char *target = xml_att(node, "OutlineTarget");
+ char *description = xml_att(node, "Description");
+ if (!target || !description)
+ continue;
+
+ entry = fz_malloc(doc->ctx, sizeof *entry);
+ entry->title = fz_strdup(doc->ctx, description);
+ entry->page = xps_find_link_target(doc, target);
+ entry->down = NULL;
+ entry->next = NULL;
+
+ this_level = level ? atoi(level) : 1;
+
+ if (!head)
+ {
+ head = entry;
+ }
+ else
+ {
+ tail = xps_find_last_outline_at_level(head, 1, this_level);
+ if (this_level > last_level)
+ tail->down = entry;
+ else
+ tail->next = entry;
+ }
+
+ last_level = this_level;
+ }
+ }
+ return head;
+}
+
+static fz_outline *
+xps_parse_document_structure(xps_document *doc, xml_element *root)
+{
+ xml_element *node;
+ if (!strcmp(xml_tag(root), "DocumentStructure"))
+ {
+ node = xml_down(root);
+ if (!strcmp(xml_tag(node), "DocumentStructure.Outline"))
+ {
+ node = xml_down(node);
+ if (!strcmp(xml_tag(node), "DocumentOutline"))
+ return xps_parse_document_outline(doc, node);
+ }
+ }
+ return NULL;
+}
+
+static fz_outline *
+xps_load_document_structure(xps_document *doc, xps_fixdoc *fixdoc)
+{
+ xps_part *part;
+ xml_element *root;
+ fz_outline *outline;
+
+ part = xps_read_part(doc, fixdoc->outline);
+ if (!part)
+ return NULL;
+
+ root = xml_parse_document(doc->ctx, part->data, part->size);
+ if (!root) {
+ fz_error_handle(-1, "cannot parse document structure part '%s'", part->name);
+ xps_free_part(doc, part);
+ return NULL;
+ }
+
+ outline = xps_parse_document_structure(doc, root);
+
+ xml_free_element(doc->ctx, root);
+ xps_free_part(doc, part);
+
+ return outline;
+
+}
+
+fz_outline *
+xps_load_outline(xps_document *doc)
+{
+ xps_fixdoc *fixdoc;
+ fz_outline *head = NULL, *tail, *outline;
+
+ for (fixdoc = doc->first_fixdoc; fixdoc; fixdoc = fixdoc->next) {
+ if (fixdoc->outline) {
+ outline = xps_load_document_structure(doc, fixdoc);
+ if (outline) {
+ if (!head)
+ head = outline;
+ else
+ tail->next = outline;
+ tail = outline;
+ }
+ }
+ }
+ return head;
+}
diff --git a/xps/xps_xml.c b/xps/xps_xml.c
index 0c3f591b..d92804af 100644
--- a/xps/xps_xml.c
+++ b/xps/xps_xml.c
@@ -18,7 +18,7 @@ struct element
struct parser
{
struct element *head;
- fz_context *doc;
+ fz_context *ctx;
};
static inline void indent(int n)
@@ -71,26 +71,26 @@ char *xml_att(struct element *item, const char *name)
return NULL;
}
-static void xml_free_attribute(fz_context *doc, struct attribute *att)
+static void xml_free_attribute(fz_context *ctx, struct attribute *att)
{
while (att) {
struct attribute *next = att->next;
if (att->value)
- fz_free(doc, att->value);
- fz_free(doc, att);
+ fz_free(ctx, att->value);
+ fz_free(ctx, att);
att = next;
}
}
-void xml_free_element(fz_context *doc, struct element *item)
+void xml_free_element(fz_context *ctx, struct element *item)
{
while (item) {
struct element *next = item->next;
if (item->atts)
- xml_free_attribute(doc, item->atts);
+ xml_free_attribute(ctx, item->atts);
if (item->down)
- xml_free_element(doc, item->down);
- fz_free(doc, item);
+ xml_free_element(ctx, item->down);
+ fz_free(ctx, item);
item = next;
}
}
@@ -134,7 +134,7 @@ static void xml_emit_open_tag(struct parser *parser, char *a, char *b)
{
struct element *head, *tail;
- head = fz_malloc(parser->doc, sizeof(struct element));
+ head = fz_malloc(parser->ctx, sizeof(struct element));
if (b - a > sizeof(head->name))
b = a + sizeof(head->name);
memcpy(head->name, a, b - a);
@@ -163,7 +163,7 @@ static void xml_emit_att_name(struct parser *parser, char *a, char *b)
struct element *head = parser->head;
struct attribute *att;
- att = fz_malloc(parser->doc, sizeof(struct attribute));
+ att = fz_malloc(parser->ctx, sizeof(struct attribute));
if (b - a > sizeof(att->name))
b = a + sizeof(att->name);
memcpy(att->name, a, b - a);
@@ -181,7 +181,7 @@ static void xml_emit_att_value(struct parser *parser, char *a, char *b)
int c;
/* entities are all longer than UTFmax so runetochar is safe */
- s = att->value = fz_malloc(parser->doc, b - a + 1);
+ s = att->value = fz_malloc(parser->ctx, b - a + 1);
while (a < b) {
if (*a == '&') {
a += xml_parse_entity(&c, a);
@@ -362,7 +362,7 @@ static char *convert_to_utf8(fz_context *doc, unsigned char *s, int n)
}
struct element *
-xml_parse_document(fz_context *doc, unsigned char *s, int n)
+xml_parse_document(fz_context *ctx, unsigned char *s, int n)
{
struct parser parser;
struct element root;
@@ -372,18 +372,18 @@ xml_parse_document(fz_context *doc, unsigned char *s, int n)
memset(&root, 0, sizeof(root));
parser.head = &root;
- parser.doc = doc;
+ parser.ctx = ctx;
- p = convert_to_utf8(doc, s, n);
+ p = convert_to_utf8(ctx, s, n);
error = xml_parse_document_imp(&parser, p);
if (error) {
- fz_error_handle(-1, error);
+ fz_throw(ctx, "%s", error);
return NULL;
}
if (p != (char*)s)
- fz_free(doc, p);
+ fz_free(ctx, p);
return root.down;
}