summaryrefslogtreecommitdiff
path: root/ios
diff options
context:
space:
mode:
Diffstat (limited to 'ios')
-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
10 files changed, 1785 insertions, 0 deletions
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;
+}