diff options
Diffstat (limited to 'ios')
-rw-r--r-- | ios/About.pdf | bin | 0 -> 131735 bytes | |||
-rw-r--r-- | ios/Icon-72.png | bin | 0 -> 5106 bytes | |||
-rw-r--r-- | ios/Icon.png | bin | 0 -> 3599 bytes | |||
-rw-r--r-- | ios/Icon@2x.png | bin | 0 -> 16336 bytes | |||
-rw-r--r-- | ios/Info.plist | 52 | ||||
-rw-r--r-- | ios/MuPDF.xcodeproj/project.pbxproj | 394 | ||||
-rw-r--r-- | ios/build_libs.sh | 33 | ||||
-rw-r--r-- | ios/document.c | 157 | ||||
-rw-r--r-- | ios/document.h | 32 | ||||
-rw-r--r-- | ios/main.m | 1117 |
10 files changed, 1785 insertions, 0 deletions
diff --git a/ios/About.pdf b/ios/About.pdf Binary files differnew file mode 100644 index 00000000..763c467e --- /dev/null +++ b/ios/About.pdf diff --git a/ios/Icon-72.png b/ios/Icon-72.png Binary files differnew file mode 100644 index 00000000..fb8c12f0 --- /dev/null +++ b/ios/Icon-72.png diff --git a/ios/Icon.png b/ios/Icon.png Binary files differnew file mode 100644 index 00000000..4bb46ae1 --- /dev/null +++ b/ios/Icon.png diff --git a/ios/Icon@2x.png b/ios/Icon@2x.png Binary files differnew file mode 100644 index 00000000..c62a076d --- /dev/null +++ b/ios/Icon@2x.png 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; +} |