diff --git a/Night Reader/Night Reader/Icon-72.png b/Night Reader/Night Reader/Icon-72.png new file mode 100644 index 0000000..ab99e37 Binary files /dev/null and b/Night Reader/Night Reader/Icon-72.png differ diff --git a/Night Reader/Night Reader/Icon-72@2x.png b/Night Reader/Night Reader/Icon-72@2x.png new file mode 100644 index 0000000..1903a5a Binary files /dev/null and b/Night Reader/Night Reader/Icon-72@2x.png differ diff --git a/Night Reader/Night Reader/Icon-Small-50.png b/Night Reader/Night Reader/Icon-Small-50.png new file mode 100644 index 0000000..87534c4 Binary files /dev/null and b/Night Reader/Night Reader/Icon-Small-50.png differ diff --git a/Night Reader/Night Reader/Icon-Small-50@2x.png b/Night Reader/Night Reader/Icon-Small-50@2x.png new file mode 100644 index 0000000..0a34d12 Binary files /dev/null and b/Night Reader/Night Reader/Icon-Small-50@2x.png differ diff --git a/Night Reader/Night Reader/Icon-Small.png b/Night Reader/Night Reader/Icon-Small.png new file mode 100644 index 0000000..244c0b9 Binary files /dev/null and b/Night Reader/Night Reader/Icon-Small.png differ diff --git a/Night Reader/Night Reader/Icon-Small@2x.png b/Night Reader/Night Reader/Icon-Small@2x.png new file mode 100644 index 0000000..365d3c7 Binary files /dev/null and b/Night Reader/Night Reader/Icon-Small@2x.png differ diff --git a/Night Reader/Night Reader/Icon.png b/Night Reader/Night Reader/Icon.png new file mode 100644 index 0000000..9d3187e Binary files /dev/null and b/Night Reader/Night Reader/Icon.png differ diff --git a/Night Reader/Night Reader/Icon@2x.png b/Night Reader/Night Reader/Icon@2x.png new file mode 100644 index 0000000..6dd9618 Binary files /dev/null and b/Night Reader/Night Reader/Icon@2x.png differ diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/project.pbxproj b/Night Reader/Night Reader/Night Reader.xcodeproj/project.pbxproj new file mode 100644 index 0000000..544e862 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader.xcodeproj/project.pbxproj @@ -0,0 +1,530 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + F09BBB9717B7C6C400900F01 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F09BBB9617B7C6C300900F01 /* UIKit.framework */; }; + F09BBB9917B7C6C400900F01 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F09BBB9817B7C6C400900F01 /* Foundation.framework */; }; + F09BBB9B17B7C6C400900F01 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F09BBB9A17B7C6C400900F01 /* CoreGraphics.framework */; }; + F09BBBA117B7C6C400900F01 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F09BBB9F17B7C6C400900F01 /* InfoPlist.strings */; }; + F09BBBA317B7C6C400900F01 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBA217B7C6C400900F01 /* main.m */; }; + F09BBBA717B7C6C400900F01 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBA617B7C6C400900F01 /* AppDelegate.m */; }; + F09BBBA917B7C6C400900F01 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBBA817B7C6C400900F01 /* Default.png */; }; + F09BBBAB17B7C6C400900F01 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBBAA17B7C6C400900F01 /* Default@2x.png */; }; + F09BBBAD17B7C6C400900F01 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBBAC17B7C6C400900F01 /* Default-568h@2x.png */; }; + F09BBBBD17B7C6DC00900F01 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F09BBBBC17B7C6DC00900F01 /* QuartzCore.framework */; }; + F09BBBBF17B7C6E600900F01 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F09BBBBE17B7C6E600900F01 /* ImageIO.framework */; }; + F09BBBC117B7C6F100900F01 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F09BBBC017B7C6F100900F01 /* MessageUI.framework */; }; + F09BBBF417B7C8BC00900F01 /* CGPDFDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBCD17B7C8BC00900F01 /* CGPDFDocument.m */; }; + F09BBBF517B7C8BC00900F01 /* ReaderConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBCF17B7C8BC00900F01 /* ReaderConstants.m */; }; + F09BBBF617B7C8BC00900F01 /* ReaderContentPage.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBD117B7C8BC00900F01 /* ReaderContentPage.m */; }; + F09BBBF717B7C8BC00900F01 /* ReaderContentTile.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBD317B7C8BC00900F01 /* ReaderContentTile.m */; }; + F09BBBF817B7C8BC00900F01 /* ReaderContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBD517B7C8BC00900F01 /* ReaderContentView.m */; }; + F09BBBF917B7C8BC00900F01 /* ReaderDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBD717B7C8BC00900F01 /* ReaderDocument.m */; }; + F09BBBFA17B7C8BC00900F01 /* ReaderDocumentOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBD917B7C8BC00900F01 /* ReaderDocumentOutline.m */; }; + F09BBBFB17B7C8BC00900F01 /* ReaderMainPagebar.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBDB17B7C8BC00900F01 /* ReaderMainPagebar.m */; }; + F09BBBFC17B7C8BC00900F01 /* ReaderMainToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBDD17B7C8BC00900F01 /* ReaderMainToolbar.m */; }; + F09BBBFD17B7C8BC00900F01 /* ReaderThumbCache.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBDF17B7C8BC00900F01 /* ReaderThumbCache.m */; }; + F09BBBFE17B7C8BC00900F01 /* ReaderThumbFetch.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBE117B7C8BC00900F01 /* ReaderThumbFetch.m */; }; + F09BBBFF17B7C8BC00900F01 /* ReaderThumbQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBE317B7C8BC00900F01 /* ReaderThumbQueue.m */; }; + F09BBC0017B7C8BC00900F01 /* ReaderThumbRender.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBE517B7C8BC00900F01 /* ReaderThumbRender.m */; }; + F09BBC0117B7C8BC00900F01 /* ReaderThumbRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBE717B7C8BC00900F01 /* ReaderThumbRequest.m */; }; + F09BBC0217B7C8BC00900F01 /* ReaderThumbsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBE917B7C8BC00900F01 /* ReaderThumbsView.m */; }; + F09BBC0317B7C8BC00900F01 /* ReaderThumbView.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBEB17B7C8BC00900F01 /* ReaderThumbView.m */; }; + F09BBC0417B7C8BC00900F01 /* ReaderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBED17B7C8BC00900F01 /* ReaderViewController.m */; }; + F09BBC0517B7C8BC00900F01 /* ThumbsMainToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBEF17B7C8BC00900F01 /* ThumbsMainToolbar.m */; }; + F09BBC0617B7C8BC00900F01 /* ThumbsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBF117B7C8BC00900F01 /* ThumbsViewController.m */; }; + F09BBC0717B7C8BC00900F01 /* UIXToolbarView.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBBF317B7C8BC00900F01 /* UIXToolbarView.m */; }; + F09BBC0E17B7C8EA00900F01 /* ReaderAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBC0917B7C8EA00900F01 /* ReaderAppDelegate.m */; }; + F09BBC0F17B7C8EA00900F01 /* ReaderBookDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBC0B17B7C8EA00900F01 /* ReaderBookDelegate.m */; }; + F09BBC1017B7C8EA00900F01 /* ReaderDemoController.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBC0D17B7C8EA00900F01 /* ReaderDemoController.m */; }; + F09BBC1D17B7D62B00900F01 /* NightReaderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F09BBC1B17B7D62A00900F01 /* NightReaderViewController.m */; }; + F09BBC1E17B7D62B00900F01 /* NightReaderViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC1C17B7D62B00900F01 /* NightReaderViewController.xib */; }; + F09BBC2317B8D4EB00900F01 /* NightReaderViewController-ipad.xib in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2117B8D4EB00900F01 /* NightReaderViewController-ipad.xib */; }; + F09BBC2717B8DA2900900F01 /* 84-lightbulb.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2417B8DA2900900F01 /* 84-lightbulb.png */; }; + F09BBC2817B8DA2900900F01 /* 84-lightbulb@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2517B8DA2900900F01 /* 84-lightbulb@2x.png */; }; + F09BBC2917B8DA2900900F01 /* redslider2.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2617B8DA2900900F01 /* redslider2.png */; }; + F09BBC3417B8E34100900F01 /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2A17B8E34000900F01 /* Icon-72.png */; }; + F09BBC3517B8E34100900F01 /* Icon-72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2B17B8E34000900F01 /* Icon-72@2x.png */; }; + F09BBC3617B8E34100900F01 /* Icon-Small-50.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2C17B8E34000900F01 /* Icon-Small-50.png */; }; + F09BBC3717B8E34100900F01 /* Icon-Small-50@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2D17B8E34000900F01 /* Icon-Small-50@2x.png */; }; + F09BBC3817B8E34100900F01 /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2E17B8E34000900F01 /* Icon-Small.png */; }; + F09BBC3917B8E34100900F01 /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC2F17B8E34000900F01 /* Icon-Small@2x.png */; }; + F09BBC3A17B8E34100900F01 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC3017B8E34000900F01 /* Icon.png */; }; + F09BBC3B17B8E34100900F01 /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC3117B8E34100900F01 /* Icon@2x.png */; }; + F09BBC3C17B8E34100900F01 /* iTunesArtwork in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC3217B8E34100900F01 /* iTunesArtwork */; }; + F09BBC3D17B8E34100900F01 /* iTunesArtwork@2x in Resources */ = {isa = PBXBuildFile; fileRef = F09BBC3317B8E34100900F01 /* iTunesArtwork@2x */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + F09BBB9317B7C6C300900F01 /* Night Reader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Night Reader.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + F09BBB9617B7C6C300900F01 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + F09BBB9817B7C6C400900F01 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + F09BBB9A17B7C6C400900F01 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + F09BBB9E17B7C6C400900F01 /* Night Reader-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Night Reader-Info.plist"; sourceTree = ""; }; + F09BBBA017B7C6C400900F01 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + F09BBBA217B7C6C400900F01 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + F09BBBA417B7C6C400900F01 /* Night Reader-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Night Reader-Prefix.pch"; sourceTree = ""; }; + F09BBBA517B7C6C400900F01 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + F09BBBA617B7C6C400900F01 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + F09BBBA817B7C6C400900F01 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; + F09BBBAA17B7C6C400900F01 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; + F09BBBAC17B7C6C400900F01 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + F09BBBBC17B7C6DC00900F01 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + F09BBBBE17B7C6E600900F01 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; }; + F09BBBC017B7C6F100900F01 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; + F09BBBCC17B7C8BC00900F01 /* CGPDFDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CGPDFDocument.h; sourceTree = ""; }; + F09BBBCD17B7C8BC00900F01 /* CGPDFDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CGPDFDocument.m; sourceTree = ""; }; + F09BBBCE17B7C8BC00900F01 /* ReaderConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderConstants.h; sourceTree = ""; }; + F09BBBCF17B7C8BC00900F01 /* ReaderConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderConstants.m; sourceTree = ""; }; + F09BBBD017B7C8BC00900F01 /* ReaderContentPage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderContentPage.h; sourceTree = ""; }; + F09BBBD117B7C8BC00900F01 /* ReaderContentPage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderContentPage.m; sourceTree = ""; }; + F09BBBD217B7C8BC00900F01 /* ReaderContentTile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderContentTile.h; sourceTree = ""; }; + F09BBBD317B7C8BC00900F01 /* ReaderContentTile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderContentTile.m; sourceTree = ""; }; + F09BBBD417B7C8BC00900F01 /* ReaderContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderContentView.h; sourceTree = ""; }; + F09BBBD517B7C8BC00900F01 /* ReaderContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderContentView.m; sourceTree = ""; }; + F09BBBD617B7C8BC00900F01 /* ReaderDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderDocument.h; sourceTree = ""; }; + F09BBBD717B7C8BC00900F01 /* ReaderDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderDocument.m; sourceTree = ""; }; + F09BBBD817B7C8BC00900F01 /* ReaderDocumentOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderDocumentOutline.h; sourceTree = ""; }; + F09BBBD917B7C8BC00900F01 /* ReaderDocumentOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderDocumentOutline.m; sourceTree = ""; }; + F09BBBDA17B7C8BC00900F01 /* ReaderMainPagebar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderMainPagebar.h; sourceTree = ""; }; + F09BBBDB17B7C8BC00900F01 /* ReaderMainPagebar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderMainPagebar.m; sourceTree = ""; }; + F09BBBDC17B7C8BC00900F01 /* ReaderMainToolbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderMainToolbar.h; sourceTree = ""; }; + F09BBBDD17B7C8BC00900F01 /* ReaderMainToolbar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderMainToolbar.m; sourceTree = ""; }; + F09BBBDE17B7C8BC00900F01 /* ReaderThumbCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderThumbCache.h; sourceTree = ""; }; + F09BBBDF17B7C8BC00900F01 /* ReaderThumbCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderThumbCache.m; sourceTree = ""; }; + F09BBBE017B7C8BC00900F01 /* ReaderThumbFetch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderThumbFetch.h; sourceTree = ""; }; + F09BBBE117B7C8BC00900F01 /* ReaderThumbFetch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderThumbFetch.m; sourceTree = ""; }; + F09BBBE217B7C8BC00900F01 /* ReaderThumbQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderThumbQueue.h; sourceTree = ""; }; + F09BBBE317B7C8BC00900F01 /* ReaderThumbQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderThumbQueue.m; sourceTree = ""; }; + F09BBBE417B7C8BC00900F01 /* ReaderThumbRender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderThumbRender.h; sourceTree = ""; }; + F09BBBE517B7C8BC00900F01 /* ReaderThumbRender.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderThumbRender.m; sourceTree = ""; }; + F09BBBE617B7C8BC00900F01 /* ReaderThumbRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderThumbRequest.h; sourceTree = ""; }; + F09BBBE717B7C8BC00900F01 /* ReaderThumbRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderThumbRequest.m; sourceTree = ""; }; + F09BBBE817B7C8BC00900F01 /* ReaderThumbsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderThumbsView.h; sourceTree = ""; }; + F09BBBE917B7C8BC00900F01 /* ReaderThumbsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderThumbsView.m; sourceTree = ""; }; + F09BBBEA17B7C8BC00900F01 /* ReaderThumbView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderThumbView.h; sourceTree = ""; }; + F09BBBEB17B7C8BC00900F01 /* ReaderThumbView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderThumbView.m; sourceTree = ""; }; + F09BBBEC17B7C8BC00900F01 /* ReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderViewController.h; sourceTree = ""; }; + F09BBBED17B7C8BC00900F01 /* ReaderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderViewController.m; sourceTree = ""; }; + F09BBBEE17B7C8BC00900F01 /* ThumbsMainToolbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThumbsMainToolbar.h; sourceTree = ""; }; + F09BBBEF17B7C8BC00900F01 /* ThumbsMainToolbar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThumbsMainToolbar.m; sourceTree = ""; }; + F09BBBF017B7C8BC00900F01 /* ThumbsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThumbsViewController.h; sourceTree = ""; }; + F09BBBF117B7C8BC00900F01 /* ThumbsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThumbsViewController.m; sourceTree = ""; }; + F09BBBF217B7C8BC00900F01 /* UIXToolbarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIXToolbarView.h; sourceTree = ""; }; + F09BBBF317B7C8BC00900F01 /* UIXToolbarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIXToolbarView.m; sourceTree = ""; }; + F09BBC0817B7C8EA00900F01 /* ReaderAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderAppDelegate.h; sourceTree = ""; }; + F09BBC0917B7C8EA00900F01 /* ReaderAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderAppDelegate.m; sourceTree = ""; }; + F09BBC0A17B7C8EA00900F01 /* ReaderBookDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderBookDelegate.h; sourceTree = ""; }; + F09BBC0B17B7C8EA00900F01 /* ReaderBookDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderBookDelegate.m; sourceTree = ""; }; + F09BBC0C17B7C8EA00900F01 /* ReaderDemoController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderDemoController.h; sourceTree = ""; }; + F09BBC0D17B7C8EA00900F01 /* ReaderDemoController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderDemoController.m; sourceTree = ""; }; + F09BBC1A17B7D62A00900F01 /* NightReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NightReaderViewController.h; sourceTree = ""; }; + F09BBC1B17B7D62A00900F01 /* NightReaderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NightReaderViewController.m; sourceTree = ""; }; + F09BBC1C17B7D62B00900F01 /* NightReaderViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NightReaderViewController.xib; sourceTree = ""; }; + F09BBC2117B8D4EB00900F01 /* NightReaderViewController-ipad.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = "NightReaderViewController-ipad.xib"; sourceTree = ""; }; + F09BBC2417B8DA2900900F01 /* 84-lightbulb.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "84-lightbulb.png"; path = "Night Reader/84-lightbulb.png"; sourceTree = ""; }; + F09BBC2517B8DA2900900F01 /* 84-lightbulb@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "84-lightbulb@2x.png"; path = "Night Reader/84-lightbulb@2x.png"; sourceTree = ""; }; + F09BBC2617B8DA2900900F01 /* redslider2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = redslider2.png; path = "Night Reader/redslider2.png"; sourceTree = ""; }; + F09BBC2A17B8E34000900F01 /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-72.png"; sourceTree = ""; }; + F09BBC2B17B8E34000900F01 /* Icon-72@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-72@2x.png"; sourceTree = ""; }; + F09BBC2C17B8E34000900F01 /* Icon-Small-50.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-50.png"; sourceTree = ""; }; + F09BBC2D17B8E34000900F01 /* Icon-Small-50@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-50@2x.png"; sourceTree = ""; }; + F09BBC2E17B8E34000900F01 /* Icon-Small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small.png"; sourceTree = ""; }; + F09BBC2F17B8E34000900F01 /* Icon-Small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@2x.png"; sourceTree = ""; }; + F09BBC3017B8E34000900F01 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; }; + F09BBC3117B8E34100900F01 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon@2x.png"; sourceTree = ""; }; + F09BBC3217B8E34100900F01 /* iTunesArtwork */ = {isa = PBXFileReference; lastKnownFileType = file; path = iTunesArtwork; sourceTree = ""; }; + F09BBC3317B8E34100900F01 /* iTunesArtwork@2x */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTunesArtwork@2x"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F09BBB9017B7C6C300900F01 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F09BBBC117B7C6F100900F01 /* MessageUI.framework in Frameworks */, + F09BBBBF17B7C6E600900F01 /* ImageIO.framework in Frameworks */, + F09BBBBD17B7C6DC00900F01 /* QuartzCore.framework in Frameworks */, + F09BBB9717B7C6C400900F01 /* UIKit.framework in Frameworks */, + F09BBB9917B7C6C400900F01 /* Foundation.framework in Frameworks */, + F09BBB9B17B7C6C400900F01 /* CoreGraphics.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + F09BBB8A17B7C6C300900F01 = { + isa = PBXGroup; + children = ( + F09BBC3E17B8E6B700900F01 /* Artwork */, + F09BBB9C17B7C6C400900F01 /* Night Reader */, + F09BBB9517B7C6C300900F01 /* Frameworks */, + F09BBB9417B7C6C300900F01 /* Products */, + ); + sourceTree = ""; + }; + F09BBB9417B7C6C300900F01 /* Products */ = { + isa = PBXGroup; + children = ( + F09BBB9317B7C6C300900F01 /* Night Reader.app */, + ); + name = Products; + sourceTree = ""; + }; + F09BBB9517B7C6C300900F01 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F09BBBC017B7C6F100900F01 /* MessageUI.framework */, + F09BBBBE17B7C6E600900F01 /* ImageIO.framework */, + F09BBBBC17B7C6DC00900F01 /* QuartzCore.framework */, + F09BBB9617B7C6C300900F01 /* UIKit.framework */, + F09BBB9817B7C6C400900F01 /* Foundation.framework */, + F09BBB9A17B7C6C400900F01 /* CoreGraphics.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + F09BBB9C17B7C6C400900F01 /* Night Reader */ = { + isa = PBXGroup; + children = ( + F09BBBCB17B7C8A300900F01 /* Sources */, + F09BBBA517B7C6C400900F01 /* AppDelegate.h */, + F09BBBA617B7C6C400900F01 /* AppDelegate.m */, + F09BBC1A17B7D62A00900F01 /* NightReaderViewController.h */, + F09BBC1B17B7D62A00900F01 /* NightReaderViewController.m */, + F09BBC1C17B7D62B00900F01 /* NightReaderViewController.xib */, + F09BBC2117B8D4EB00900F01 /* NightReaderViewController-ipad.xib */, + F09BBB9D17B7C6C400900F01 /* Supporting Files */, + ); + path = "Night Reader"; + sourceTree = ""; + }; + F09BBB9D17B7C6C400900F01 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + F09BBB9E17B7C6C400900F01 /* Night Reader-Info.plist */, + F09BBB9F17B7C6C400900F01 /* InfoPlist.strings */, + F09BBBA217B7C6C400900F01 /* main.m */, + F09BBBA417B7C6C400900F01 /* Night Reader-Prefix.pch */, + F09BBBA817B7C6C400900F01 /* Default.png */, + F09BBBAA17B7C6C400900F01 /* Default@2x.png */, + F09BBBAC17B7C6C400900F01 /* Default-568h@2x.png */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + F09BBBCB17B7C8A300900F01 /* Sources */ = { + isa = PBXGroup; + children = ( + F09BBC0817B7C8EA00900F01 /* ReaderAppDelegate.h */, + F09BBC0917B7C8EA00900F01 /* ReaderAppDelegate.m */, + F09BBC0A17B7C8EA00900F01 /* ReaderBookDelegate.h */, + F09BBC0B17B7C8EA00900F01 /* ReaderBookDelegate.m */, + F09BBC0C17B7C8EA00900F01 /* ReaderDemoController.h */, + F09BBC0D17B7C8EA00900F01 /* ReaderDemoController.m */, + F09BBBCC17B7C8BC00900F01 /* CGPDFDocument.h */, + F09BBBCD17B7C8BC00900F01 /* CGPDFDocument.m */, + F09BBBCE17B7C8BC00900F01 /* ReaderConstants.h */, + F09BBBCF17B7C8BC00900F01 /* ReaderConstants.m */, + F09BBBD017B7C8BC00900F01 /* ReaderContentPage.h */, + F09BBBD117B7C8BC00900F01 /* ReaderContentPage.m */, + F09BBBD217B7C8BC00900F01 /* ReaderContentTile.h */, + F09BBBD317B7C8BC00900F01 /* ReaderContentTile.m */, + F09BBBD417B7C8BC00900F01 /* ReaderContentView.h */, + F09BBBD517B7C8BC00900F01 /* ReaderContentView.m */, + F09BBBD617B7C8BC00900F01 /* ReaderDocument.h */, + F09BBBD717B7C8BC00900F01 /* ReaderDocument.m */, + F09BBBD817B7C8BC00900F01 /* ReaderDocumentOutline.h */, + F09BBBD917B7C8BC00900F01 /* ReaderDocumentOutline.m */, + F09BBBDA17B7C8BC00900F01 /* ReaderMainPagebar.h */, + F09BBBDB17B7C8BC00900F01 /* ReaderMainPagebar.m */, + F09BBBDC17B7C8BC00900F01 /* ReaderMainToolbar.h */, + F09BBBDD17B7C8BC00900F01 /* ReaderMainToolbar.m */, + F09BBBDE17B7C8BC00900F01 /* ReaderThumbCache.h */, + F09BBBDF17B7C8BC00900F01 /* ReaderThumbCache.m */, + F09BBBE017B7C8BC00900F01 /* ReaderThumbFetch.h */, + F09BBBE117B7C8BC00900F01 /* ReaderThumbFetch.m */, + F09BBBE217B7C8BC00900F01 /* ReaderThumbQueue.h */, + F09BBBE317B7C8BC00900F01 /* ReaderThumbQueue.m */, + F09BBBE417B7C8BC00900F01 /* ReaderThumbRender.h */, + F09BBBE517B7C8BC00900F01 /* ReaderThumbRender.m */, + F09BBBE617B7C8BC00900F01 /* ReaderThumbRequest.h */, + F09BBBE717B7C8BC00900F01 /* ReaderThumbRequest.m */, + F09BBBE817B7C8BC00900F01 /* ReaderThumbsView.h */, + F09BBBE917B7C8BC00900F01 /* ReaderThumbsView.m */, + F09BBBEA17B7C8BC00900F01 /* ReaderThumbView.h */, + F09BBBEB17B7C8BC00900F01 /* ReaderThumbView.m */, + F09BBBEC17B7C8BC00900F01 /* ReaderViewController.h */, + F09BBBED17B7C8BC00900F01 /* ReaderViewController.m */, + F09BBBEE17B7C8BC00900F01 /* ThumbsMainToolbar.h */, + F09BBBEF17B7C8BC00900F01 /* ThumbsMainToolbar.m */, + F09BBBF017B7C8BC00900F01 /* ThumbsViewController.h */, + F09BBBF117B7C8BC00900F01 /* ThumbsViewController.m */, + F09BBBF217B7C8BC00900F01 /* UIXToolbarView.h */, + F09BBBF317B7C8BC00900F01 /* UIXToolbarView.m */, + ); + name = Sources; + sourceTree = ""; + }; + F09BBC3E17B8E6B700900F01 /* Artwork */ = { + isa = PBXGroup; + children = ( + F09BBC2417B8DA2900900F01 /* 84-lightbulb.png */, + F09BBC2517B8DA2900900F01 /* 84-lightbulb@2x.png */, + F09BBC2617B8DA2900900F01 /* redslider2.png */, + F09BBC2A17B8E34000900F01 /* Icon-72.png */, + F09BBC2B17B8E34000900F01 /* Icon-72@2x.png */, + F09BBC2C17B8E34000900F01 /* Icon-Small-50.png */, + F09BBC2D17B8E34000900F01 /* Icon-Small-50@2x.png */, + F09BBC2E17B8E34000900F01 /* Icon-Small.png */, + F09BBC2F17B8E34000900F01 /* Icon-Small@2x.png */, + F09BBC3017B8E34000900F01 /* Icon.png */, + F09BBC3117B8E34100900F01 /* Icon@2x.png */, + F09BBC3217B8E34100900F01 /* iTunesArtwork */, + F09BBC3317B8E34100900F01 /* iTunesArtwork@2x */, + ); + name = Artwork; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F09BBB9217B7C6C300900F01 /* Night Reader */ = { + isa = PBXNativeTarget; + buildConfigurationList = F09BBBB917B7C6C400900F01 /* Build configuration list for PBXNativeTarget "Night Reader" */; + buildPhases = ( + F09BBB8F17B7C6C300900F01 /* Sources */, + F09BBB9017B7C6C300900F01 /* Frameworks */, + F09BBB9117B7C6C300900F01 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Night Reader"; + productName = "Night Reader"; + productReference = F09BBB9317B7C6C300900F01 /* Night Reader.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F09BBB8B17B7C6C300900F01 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0460; + ORGANIZATIONNAME = "Norbert Schmidt"; + }; + buildConfigurationList = F09BBB8E17B7C6C300900F01 /* Build configuration list for PBXProject "Night Reader" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = F09BBB8A17B7C6C300900F01; + productRefGroup = F09BBB9417B7C6C300900F01 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F09BBB9217B7C6C300900F01 /* Night Reader */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + F09BBB9117B7C6C300900F01 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F09BBBA117B7C6C400900F01 /* InfoPlist.strings in Resources */, + F09BBBA917B7C6C400900F01 /* Default.png in Resources */, + F09BBBAB17B7C6C400900F01 /* Default@2x.png in Resources */, + F09BBBAD17B7C6C400900F01 /* Default-568h@2x.png in Resources */, + F09BBC1E17B7D62B00900F01 /* NightReaderViewController.xib in Resources */, + F09BBC2317B8D4EB00900F01 /* NightReaderViewController-ipad.xib in Resources */, + F09BBC2717B8DA2900900F01 /* 84-lightbulb.png in Resources */, + F09BBC2817B8DA2900900F01 /* 84-lightbulb@2x.png in Resources */, + F09BBC2917B8DA2900900F01 /* redslider2.png in Resources */, + F09BBC3417B8E34100900F01 /* Icon-72.png in Resources */, + F09BBC3517B8E34100900F01 /* Icon-72@2x.png in Resources */, + F09BBC3617B8E34100900F01 /* Icon-Small-50.png in Resources */, + F09BBC3717B8E34100900F01 /* Icon-Small-50@2x.png in Resources */, + F09BBC3817B8E34100900F01 /* Icon-Small.png in Resources */, + F09BBC3917B8E34100900F01 /* Icon-Small@2x.png in Resources */, + F09BBC3A17B8E34100900F01 /* Icon.png in Resources */, + F09BBC3B17B8E34100900F01 /* Icon@2x.png in Resources */, + F09BBC3C17B8E34100900F01 /* iTunesArtwork in Resources */, + F09BBC3D17B8E34100900F01 /* iTunesArtwork@2x in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F09BBB8F17B7C6C300900F01 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F09BBBA317B7C6C400900F01 /* main.m in Sources */, + F09BBBA717B7C6C400900F01 /* AppDelegate.m in Sources */, + F09BBBF417B7C8BC00900F01 /* CGPDFDocument.m in Sources */, + F09BBBF517B7C8BC00900F01 /* ReaderConstants.m in Sources */, + F09BBBF617B7C8BC00900F01 /* ReaderContentPage.m in Sources */, + F09BBBF717B7C8BC00900F01 /* ReaderContentTile.m in Sources */, + F09BBBF817B7C8BC00900F01 /* ReaderContentView.m in Sources */, + F09BBBF917B7C8BC00900F01 /* ReaderDocument.m in Sources */, + F09BBBFA17B7C8BC00900F01 /* ReaderDocumentOutline.m in Sources */, + F09BBBFB17B7C8BC00900F01 /* ReaderMainPagebar.m in Sources */, + F09BBBFC17B7C8BC00900F01 /* ReaderMainToolbar.m in Sources */, + F09BBBFD17B7C8BC00900F01 /* ReaderThumbCache.m in Sources */, + F09BBBFE17B7C8BC00900F01 /* ReaderThumbFetch.m in Sources */, + F09BBBFF17B7C8BC00900F01 /* ReaderThumbQueue.m in Sources */, + F09BBC0017B7C8BC00900F01 /* ReaderThumbRender.m in Sources */, + F09BBC0117B7C8BC00900F01 /* ReaderThumbRequest.m in Sources */, + F09BBC0217B7C8BC00900F01 /* ReaderThumbsView.m in Sources */, + F09BBC0317B7C8BC00900F01 /* ReaderThumbView.m in Sources */, + F09BBC0417B7C8BC00900F01 /* ReaderViewController.m in Sources */, + F09BBC0517B7C8BC00900F01 /* ThumbsMainToolbar.m in Sources */, + F09BBC0617B7C8BC00900F01 /* ThumbsViewController.m in Sources */, + F09BBC0717B7C8BC00900F01 /* UIXToolbarView.m in Sources */, + F09BBC0E17B7C8EA00900F01 /* ReaderAppDelegate.m in Sources */, + F09BBC0F17B7C8EA00900F01 /* ReaderBookDelegate.m in Sources */, + F09BBC1017B7C8EA00900F01 /* ReaderDemoController.m in Sources */, + F09BBC1D17B7D62B00900F01 /* NightReaderViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + F09BBB9F17B7C6C400900F01 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + F09BBBA017B7C6C400900F01 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + F09BBBB717B7C6C400900F01 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + 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_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F09BBBB817B7C6C400900F01 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + F09BBBBA17B7C6C400900F01 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Distribution: Stichting Klinisch Wetenschappelijk Onderzoek Slotervaartziekenhuis (94KAKB756Z)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Stichting Klinisch Wetenschappelijk Onderzoek Slotervaartziekenhuis (94KAKB756Z)"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "Night Reader/Night Reader-Prefix.pch"; + INFOPLIST_FILE = "Night Reader/Night Reader-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "A2DA4094-40CD-40A9-8FDA-5A589F41622C"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "A2DA4094-40CD-40A9-8FDA-5A589F41622C"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + F09BBBBB17B7C6C400900F01 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Distribution: Stichting Klinisch Wetenschappelijk Onderzoek Slotervaartziekenhuis (94KAKB756Z)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Stichting Klinisch Wetenschappelijk Onderzoek Slotervaartziekenhuis (94KAKB756Z)"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "Night Reader/Night Reader-Prefix.pch"; + INFOPLIST_FILE = "Night Reader/Night Reader-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "A2DA4094-40CD-40A9-8FDA-5A589F41622C"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "A2DA4094-40CD-40A9-8FDA-5A589F41622C"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F09BBB8E17B7C6C300900F01 /* Build configuration list for PBXProject "Night Reader" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F09BBBB717B7C6C400900F01 /* Debug */, + F09BBBB817B7C6C400900F01 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F09BBBB917B7C6C400900F01 /* Build configuration list for PBXNativeTarget "Night Reader" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F09BBBBA17B7C6C400900F01 /* Debug */, + F09BBBBB17B7C6C400900F01 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F09BBB8B17B7C6C300900F01 /* Project object */; +} diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..37bc8a3 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/xcuserdata/nop.xcuserdatad/UserInterfaceState.xcuserstate b/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/xcuserdata/nop.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..2218e29 Binary files /dev/null and b/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/xcuserdata/nop.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/xcuserdata/nop.xcuserdatad/WorkspaceSettings.xcsettings b/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/xcuserdata/nop.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..659c876 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/xcuserdata/nop.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,10 @@ + + + + + HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges + + SnapshotAutomaticallyBeforeSignificantChanges + + + diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/xcuserdata/norbertschmidt.xcuserdatad/UserInterfaceState.xcuserstate b/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/xcuserdata/norbertschmidt.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..3c4e4af Binary files /dev/null and b/Night Reader/Night Reader/Night Reader.xcodeproj/project.xcworkspace/xcuserdata/norbertschmidt.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist new file mode 100644 index 0000000..2fd6102 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist @@ -0,0 +1,20 @@ + + + + + + + diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..5b4ef0f --- /dev/null +++ b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcschemes/Night Reader.xcscheme b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcschemes/Night Reader.xcscheme new file mode 100644 index 0000000..5bfd779 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcschemes/Night Reader.xcscheme @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcschemes/xcschememanagement.plist b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..3616801 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/nop.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + Night Reader.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + F09BBB9217B7C6C300900F01 + + primary + + + + + diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/norbertschmidt.xcuserdatad/xcschemes/Night Reader.xcscheme b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/norbertschmidt.xcuserdatad/xcschemes/Night Reader.xcscheme new file mode 100644 index 0000000..b2c9d00 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/norbertschmidt.xcuserdatad/xcschemes/Night Reader.xcscheme @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/norbertschmidt.xcuserdatad/xcschemes/xcschememanagement.plist b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/norbertschmidt.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..3616801 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader.xcodeproj/xcuserdata/norbertschmidt.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + Night Reader.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + F09BBB9217B7C6C300900F01 + + primary + + + + + diff --git a/Night Reader/Night Reader/Night Reader/84-lightbulb.png b/Night Reader/Night Reader/Night Reader/84-lightbulb.png new file mode 100644 index 0000000..91d4ce1 Binary files /dev/null and b/Night Reader/Night Reader/Night Reader/84-lightbulb.png differ diff --git a/Night Reader/Night Reader/Night Reader/84-lightbulb@2x.png b/Night Reader/Night Reader/Night Reader/84-lightbulb@2x.png new file mode 100644 index 0000000..8632a73 Binary files /dev/null and b/Night Reader/Night Reader/Night Reader/84-lightbulb@2x.png differ diff --git a/Night Reader/Night Reader/Night Reader/AppDelegate.h b/Night Reader/Night Reader/Night Reader/AppDelegate.h new file mode 100644 index 0000000..ede74c6 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/AppDelegate.h @@ -0,0 +1,19 @@ +// +// AppDelegate.h +// Night Reader +// +// Created by Norbert Schmidt on 11-08-13. +// Copyright (c) 2013 Norbert Schmidt. All rights reserved. +// + +#import + +@class NightReaderViewController; + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@property (strong, nonatomic) NightReaderViewController *viewController; + +@end diff --git a/Night Reader/Night Reader/Night Reader/AppDelegate.m b/Night Reader/Night Reader/Night Reader/AppDelegate.m new file mode 100644 index 0000000..61a63b4 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/AppDelegate.m @@ -0,0 +1,56 @@ +// +// AppDelegate.m +// Night Reader +// +// Created by Norbert Schmidt on 11-08-13. +// Copyright (c) 2013 Norbert Schmidt. All rights reserved. +// + +#import "AppDelegate.h" + +#import "NightReaderViewController.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + // Override point for customization after application launch. + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + self.viewController = [[NightReaderViewController alloc] initWithNibName:@"NightReaderViewController" bundle:nil]; + } else { + self.viewController = [[NightReaderViewController alloc] initWithNibName:@"NightReaderViewController-ipad" bundle:nil]; + } + self.window.rootViewController = self.viewController; + [self.window makeKeyAndVisible]; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/CGPDFDocument.h b/Night Reader/Night Reader/Night Reader/CGPDFDocument.h new file mode 100644 index 0000000..54c02ff --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/CGPDFDocument.h @@ -0,0 +1,35 @@ +// +// CGPDFDocument.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import +#import + +// +// Custom CGPDFDocument[...] functions +// + +CGPDFDocumentRef CGPDFDocumentCreateX(CFURLRef theURL, NSString *password); + +BOOL CGPDFDocumentNeedsPassword(CFURLRef theURL, NSString *password); diff --git a/Night Reader/Night Reader/Night Reader/CGPDFDocument.m b/Night Reader/Night Reader/Night Reader/CGPDFDocument.m new file mode 100644 index 0000000..2176cae --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/CGPDFDocument.m @@ -0,0 +1,136 @@ +// +// CGPDFDocument.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "CGPDFDocument.h" + +// +// CGPDFDocumentRef CGPDFDocumentCreateX(CFURLRef, NSString *) function +// +// OF HIER DOEN NOP + +CGPDFDocumentRef CGPDFDocumentCreateX(CFURLRef theURL, NSString *password) +{ + CGPDFDocumentRef thePDFDocRef = NULL; + + if (theURL != NULL) // Check for non-NULL CFURLRef + { + thePDFDocRef = CGPDFDocumentCreateWithURL(theURL); + + if (thePDFDocRef != NULL) // Check for non-NULL CGPDFDocumentRef + { + if (CGPDFDocumentIsEncrypted(thePDFDocRef) == TRUE) // Encrypted + { + // Try a blank password first, per Apple's Quartz PDF example + + if (CGPDFDocumentUnlockWithPassword(thePDFDocRef, "") == FALSE) + { + // Nope, now let's try the provided password to unlock the PDF + + if ((password != nil) && ([password length] > 0)) // Not blank? + { + char text[128]; // char array buffer for the string conversion + + [password getCString:text maxLength:126 encoding:NSUTF8StringEncoding]; + + if (CGPDFDocumentUnlockWithPassword(thePDFDocRef, text) == FALSE) // Log failure + { + #ifdef DEBUG + NSLog(@"CGPDFDocumentCreateX: Unable to unlock [%@] with [%@]", theURL, password); + #endif + } + } + } + + if (CGPDFDocumentIsUnlocked(thePDFDocRef) == FALSE) // Cleanup unlock failure + { + CGPDFDocumentRelease(thePDFDocRef), thePDFDocRef = NULL; + } + } + } + } + else // Log an error diagnostic + { + #ifdef DEBUG + NSLog(@"CGPDFDocumentCreateX: theURL == NULL"); + #endif + } + + return thePDFDocRef; +} + +// +// BOOL CGPDFDocumentNeedsPassword(CFURLRef, NSString *) function +// + +BOOL CGPDFDocumentNeedsPassword(CFURLRef theURL, NSString *password) +{ + BOOL needPassword = NO; // Default flag + + if (theURL != NULL) // Check for non-NULL CFURLRef + { + CGPDFDocumentRef thePDFDocRef = CGPDFDocumentCreateWithURL(theURL); + + if (thePDFDocRef != NULL) // Check for non-NULL CGPDFDocumentRef + { + if (CGPDFDocumentIsEncrypted(thePDFDocRef) == TRUE) // Encrypted + { + // Try a blank password first, per Apple's Quartz PDF example + + if (CGPDFDocumentUnlockWithPassword(thePDFDocRef, "") == FALSE) + { + // Nope, now let's try the provided password to unlock the PDF + + if ((password != nil) && ([password length] > 0)) // Not blank? + { + char text[128]; // char array buffer for the string conversion + + [password getCString:text maxLength:126 encoding:NSUTF8StringEncoding]; + + if (CGPDFDocumentUnlockWithPassword(thePDFDocRef, text) == FALSE) + { + needPassword = YES; + } + } + else + { + needPassword = YES; + } + } + } + + CGPDFDocumentRelease(thePDFDocRef); // Cleanup CGPDFDocumentRef + } + } + else // Log an error diagnostic + { + #ifdef DEBUG + NSLog(@"CGPDFDocumentNeedsPassword: theURL == NULL"); + #endif + } + + return needPassword; +} + +// EOF diff --git a/Night Reader/Night Reader/Night Reader/Default-568h@2x.png b/Night Reader/Night Reader/Night Reader/Default-568h@2x.png new file mode 100644 index 0000000..0891b7a Binary files /dev/null and b/Night Reader/Night Reader/Night Reader/Default-568h@2x.png differ diff --git a/Night Reader/Night Reader/Night Reader/Default.png b/Night Reader/Night Reader/Night Reader/Default.png new file mode 100644 index 0000000..4c8ca6f Binary files /dev/null and b/Night Reader/Night Reader/Night Reader/Default.png differ diff --git a/Night Reader/Night Reader/Night Reader/Default@2x.png b/Night Reader/Night Reader/Night Reader/Default@2x.png new file mode 100644 index 0000000..35b84cf Binary files /dev/null and b/Night Reader/Night Reader/Night Reader/Default@2x.png differ diff --git a/Night Reader/Night Reader/Night Reader/Night Reader-Info.plist b/Night Reader/Night Reader/Night Reader/Night Reader-Info.plist new file mode 100644 index 0000000..2855fdc --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/Night Reader-Info.plist @@ -0,0 +1,64 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Night Reader + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconFiles + + Icon.png + Icon@2x.png + + UIPrerenderedIcon + + + + CFBundleIdentifier + nl.slz.30minutes + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIFileSharingEnabled + + UIPrerenderedIcon + + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Night Reader/Night Reader/Night Reader/Night Reader-Prefix.pch b/Night Reader/Night Reader/Night Reader/Night Reader-Prefix.pch new file mode 100644 index 0000000..ecf9ca3 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/Night Reader-Prefix.pch @@ -0,0 +1,14 @@ +// +// Prefix header for all source files of the 'Night Reader' target in the 'Night Reader' project +// + +#import + +#ifndef __IPHONE_4_0 +#warning "This project uses features only available in iOS SDK 4.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/Night Reader/Night Reader/Night Reader/NightReaderViewController-ipad.xib b/Night Reader/Night Reader/Night Reader/NightReaderViewController-ipad.xib new file mode 100644 index 0000000..30aebe2 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/NightReaderViewController-ipad.xib @@ -0,0 +1,257 @@ + + + + 1552 + 12E55 + 3084 + 1187.39 + 626.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 2083 + + + IBProxyObject + IBUIButton + IBUILabel + IBUITableView + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBIPadFramework + + + IBFirstResponder + IBIPadFramework + + + + 292 + + + + 274 + {{0, 49}, {768, 955}} + + + + _NS:9 + + 1 + MCAwIDAAA + + YES + IBIPadFramework + NO + 1 + NO + 1 + 0 + YES + 44 + 22 + 22 + + + + 292 + {{730, 13}, {18, 19}} + + + + _NS:9 + NO + IBIPadFramework + 0 + 0 + 4 + YES + + 3 + MQA + + + 1 + MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + + + 3 + MC41AA + + + 2 + 15 + + + Helvetica-Bold + 15 + 16 + + + + + 292 + {{273, 11}, {222, 21}} + + + + _NS:9 + NO + YES + 7 + NO + IBIPadFramework + Night Reader Documents + + 2 + MC42NTA5ODA0MTMgMCAwAA + + + 2 + MC42NTA5ODA0MTMgMCAwAA + + 0 + + 2 + 17 + + + Helvetica-Bold + 17 + 16 + + NO + + + {768, 1004} + + + + + 1 + MC4xNTUwNzQxMzkgMC4xNTUwNzQxMzkgMC4xNTUwNzQxMzkAA + + NO + IBIPadFramework + + + + + + + view + + + + 3 + + + + MaintableView + + + + 52 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 2 + + + + + + + + + + 47 + + + + + 48 + + + + + 49 + + + + + + + NightReaderViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 52 + + + + + NightReaderViewController + UIViewController + + MaintableView + UITableView + + + MaintableView + + MaintableView + UITableView + + + + IBProjectSource + ./Classes/NightReaderViewController.h + + + + + 0 + IBIPadFramework + YES + 3 + 2083 + + diff --git a/Night Reader/Night Reader/Night Reader/NightReaderViewController.h b/Night Reader/Night Reader/Night Reader/NightReaderViewController.h new file mode 100644 index 0000000..86c8390 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/NightReaderViewController.h @@ -0,0 +1,21 @@ +// +// ViewController.h +// FileSharingDemo +// +// Created by Kashif Jilani on 11/15/12. +// Copyright (c) 2012 Kashif Jilani. All rights reserved. +// + +#import +#import "ReaderViewController.h" + +@interface NightReaderViewController : UIViewController { + + IBOutlet UITableView *MaintableView; + NSArray *dataArray; + NSMutableArray *finalArray; +} + +@property (nonatomic, retain) UITableView *MaintableView; + +@end diff --git a/Night Reader/Night Reader/Night Reader/NightReaderViewController.m b/Night Reader/Night Reader/Night Reader/NightReaderViewController.m new file mode 100644 index 0000000..a5d72e0 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/NightReaderViewController.m @@ -0,0 +1,173 @@ +// +// ViewController.m +// FileSharingDemo +// +// Created by Kashif Jilani on 11/15/12. +// Copyright (c) 2012 Kashif Jilani. All rights reserved. +// + +#import "NightReaderViewController.h" + +@interface NightReaderViewController () + +@end + +@implementation NightReaderViewController +@synthesize MaintableView; + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. + + [MaintableView + setBackgroundColor:[UIColor blackColor]]; + + [MaintableView setDelegate:self]; + + UIColor *NightColor=[UIColor colorWithRed:70 green:0 blue:0 alpha:0.8]; + + + + [MaintableView setSeparatorColor:NightColor]; + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); + NSString *documentsPath = [paths objectAtIndex:0]; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + + NSArray *bundleDirectory = [fileManager contentsOfDirectoryAtPath:documentsPath error:nil]; + + NSPredicate *filter = [NSPredicate predicateWithFormat:@"self ENDSWITH '.pdf'"]; + dataArray = [bundleDirectory filteredArrayUsingPredicate:filter]; + finalArray = [dataArray copy]; + + + + NSString *valueString = [[finalArray objectAtIndex:0] stringByDeletingPathExtension]; + + NSLog(@"Finalarray count %d", finalArray.count); + NSLog (@"Valuestring %@", valueString); + + + + + +} + +#pragma mark - Table view data source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + // Return the number of sections. + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + int x=[finalArray count]; + // Return the number of rows in the section. + NSLog (@"x= %d", x); + return 2; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *CellIdentifier = @"Cell"; + UITableViewCell *cell = [MaintableView dequeueReusableCellWithIdentifier:CellIdentifier]; + if (cell == nil) + { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ; + } + // Configure the cell... + UIFont *myFont = [ UIFont fontWithName: @"System" size: 16.0 ]; + cell.textLabel.font = myFont; + // Set up the cell... + + UIColor *NightColor=[UIColor colorWithRed:70 green:0 blue:0 alpha:0.8]; + + UIColor *DarkNightColor=[UIColor colorWithRed:70 green:0 blue:0 alpha:0.3]; + + + + cell.textLabel.highlightedTextColor =DarkNightColor; + + cell.textLabel.textColor=NightColor; +// [[cell textLabel] setText:contentForThisRow]; + + NSString *testje= [[finalArray objectAtIndex:indexPath.row] stringByDeletingPathExtension]; + + NSLog (@"Testje %@", testje); + + cell.textLabel.text = testje; + + + + + + + + return cell; +} + + + +#pragma mark - Table view delegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + + + + + + + UITableViewCell *cell; + cell = [MaintableView cellForRowAtIndexPath:indexPath]; + + UIView *bgColorView = [[UIView alloc] init]; + + UIColor *DarkNightColor=[UIColor colorWithRed:70 green:0 blue:0 alpha:0.3]; + UIColor *NightColor=[UIColor colorWithRed:70 green:0 blue:0 alpha:0.8]; + [bgColorView setBackgroundColor:DarkNightColor]; + [cell setSelectedBackgroundView:bgColorView]; + + + cell.textLabel.textColor=NightColor; + + NSString *valueString = [[finalArray objectAtIndex:indexPath.row] stringByDeletingPathExtension]; + NSString *file = [[NSBundle mainBundle] pathForResource:valueString ofType:@"pdf"]; + + + NSLog (@"Valuestring %@", file); + + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + //Considering your pdf is stored in documents directory with name as "pdfFileName" + + NSString *pdfPath = [[paths objectAtIndex:0]stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.pdf",valueString]]; + + + ReaderDocument *document = [ReaderDocument withDocumentFilePath:pdfPath password:nil]; + + ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document]; + readerViewController.delegate = self; + + [self presentViewController:readerViewController animated:YES completion:nil]; + + +} + +- (void)dismissReaderViewController:(ReaderViewController *)viewController { + [self dismissViewControllerAnimated:YES completion:nil]; +} + + + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/NightReaderViewController.xib b/Night Reader/Night Reader/Night Reader/NightReaderViewController.xib new file mode 100644 index 0000000..2c4560f --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/NightReaderViewController.xib @@ -0,0 +1,263 @@ + + + + 1552 + 12E55 + 3084 + 1187.39 + 626.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 2083 + + + IBProxyObject + IBUIButton + IBUILabel + IBUITableView + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + + + 274 + {{0, 32}, {320, 428}} + + _NS:9 + + 1 + MCAwIDAAA + + YES + IBCocoaTouchFramework + NO + 1 + NO + 1 + 0 + YES + 44 + 22 + 22 + + + + 292 + {{49, 5}, {222, 21}} + + + _NS:9 + NO + YES + 7 + NO + IBCocoaTouchFramework + Night Reader Documents + + 2 + MC42NTA5ODA0MTMgMCAwAA + + + 2 + MC42NTA5ODA0MTMgMCAwAA + + 0 + + 2 + 17 + + + Helvetica-Bold + 17 + 16 + + NO + + + + 292 + {{287, 6}, {18, 19}} + + _NS:9 + NO + IBCocoaTouchFramework + 0 + 0 + 4 + YES + + 3 + MQA + + + 1 + MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + + + 3 + MC41AA + + + 2 + 15 + + + Helvetica-Bold + 15 + 16 + + + + {{0, 20}, {320, 460}} + + + + 1 + MC4xNTUwNzQxMzkgMC4xNTUwNzQxMzkgMC4xNTUwNzQxMzkAA + + NO + + + IBUIScreenMetrics + + YES + + + + + + {320, 480} + {480, 320} + + + IBCocoaTouchFramework + Retina 3.5 Full Screen + 0 + + IBCocoaTouchFramework + + + + + + + view + + + + 7 + + + + MaintableView + + + + 41 + + + + dataSource + + + + 22 + + + + delegate + + + + 23 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 6 + + + + + + + + + + 29 + + + + + 30 + + + + + 20 + + + + + + + NightReaderViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 41 + + + 0 + IBCocoaTouchFramework + YES + 3 + 2083 + + diff --git a/Night Reader/Night Reader/Night Reader/ReaderAppDelegate.h b/Night Reader/Night Reader/Night Reader/ReaderAppDelegate.h new file mode 100644 index 0000000..dec4d33 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderAppDelegate.h @@ -0,0 +1,30 @@ +// +// ReaderAppDelegate.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface ReaderAppDelegate : NSObject + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderAppDelegate.m b/Night Reader/Night Reader/Night Reader/ReaderAppDelegate.m new file mode 100644 index 0000000..27b7c55 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderAppDelegate.m @@ -0,0 +1,103 @@ +// +// ReaderAppDelegate.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderAppDelegate.h" +#import "ReaderDemoController.h" + +@implementation ReaderAppDelegate +{ + UIWindow *mainWindow; // Main App Window + + UINavigationController *navigationController; + + ReaderDemoController *readerDemoController; +} + +#pragma mark UIApplicationDelegate methods + +- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url +{ + return NO; +} + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + mainWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; // Main application window + + readerDemoController = [[ReaderDemoController alloc] initWithNibName:nil bundle:nil]; // Demo controller + + navigationController = [[UINavigationController alloc] initWithRootViewController:readerDemoController]; + + mainWindow.backgroundColor = [UIColor blackColor]; // Window background color + + navigationController.navigationBar.barStyle = UIBarStyleBlack; navigationController.navigationBar.translucent = YES; + + mainWindow.rootViewController = navigationController; // Set the root view controller + + [mainWindow makeKeyAndVisible]; + + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + // Sent when the application is about to move from active to inactive state. This can occur for certain types of + // temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application + // and it begins the transition to the background state. Use this method to pause ongoing tasks, disable timers, + // and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + // Use this method to release shared resources, save user data, invalidate timers, and store enough + // application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + // Called as part of transition from the background to the inactive state: here you can undo many + // of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + // Restart any tasks that were paused (or not yet started) while the application was inactive. + // If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + // Called when the application is about to terminate. + // See also applicationDidEnterBackground:. +} + +- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application +{ + // Free up as much memory as possible by purging cached data objects that can be recreated + // (or reloaded from disk) later. +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderBookDelegate.h b/Night Reader/Night Reader/Night Reader/ReaderBookDelegate.h new file mode 100644 index 0000000..6add8b2 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderBookDelegate.h @@ -0,0 +1,30 @@ +// +// ReaderBookDelegate.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface ReaderBookDelegate : NSObject + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderBookDelegate.m b/Night Reader/Night Reader/Night Reader/ReaderBookDelegate.m new file mode 100644 index 0000000..8e2e583 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderBookDelegate.m @@ -0,0 +1,121 @@ +// +// ReaderBookDelegate.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderBookDelegate.h" +#import "ReaderViewController.h" + +@interface ReaderBookDelegate () + +@end + +@implementation ReaderBookDelegate +{ + UIWindow *mainWindow; // Main App Window + + ReaderViewController *readerViewController; +} + +#pragma mark UIApplicationDelegate methods + +- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url +{ + return NO; +} + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + mainWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; // Main application window + + mainWindow.backgroundColor = [UIColor blackColor]; // Window background color + + NSString *phrase = nil; // Document password (for unlocking most encrypted PDF files) + + NSArray *pdfs = [[NSBundle mainBundle] pathsForResourcesOfType:@"pdf" inDirectory:nil]; + + NSString *filePath = [pdfs lastObject]; assert(filePath != nil); // Path to last PDF file + + ReaderDocument *document = [ReaderDocument withDocumentFilePath:filePath password:phrase]; + + if (document != nil) // Must have a valid ReaderDocument object in order to proceed + { + readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document]; + + readerViewController.delegate = self; // Set the ReaderViewController delegate to self + + mainWindow.rootViewController = readerViewController; // Set the root view controller + } + + [mainWindow makeKeyAndVisible]; + + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + // Sent when the application is about to move from active to inactive state. This can occur for certain types of + // temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application + // and it begins the transition to the background state. Use this method to pause ongoing tasks, disable timers, + // and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + // Use this method to release shared resources, save user data, invalidate timers, and store enough + // application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + // Called as part of transition from the background to the inactive state: here you can undo many + // of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + // Restart any tasks that were paused (or not yet started) while the application was inactive. + // If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + // Called when the application is about to terminate. + // See also applicationDidEnterBackground:. +} + +- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application +{ + // Free up as much memory as possible by purging cached data objects that can be recreated + // (or reloaded from disk) later. +} + +#pragma mark ReaderViewControllerDelegate methods + +- (void)dismissReaderViewController:(ReaderViewController *)viewController +{ + // Do nothing +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderConstants.h b/Night Reader/Night Reader/Night Reader/ReaderConstants.h new file mode 100644 index 0000000..db4f8f3 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderConstants.h @@ -0,0 +1,42 @@ +// +// ReaderConstants.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#if !__has_feature(objc_arc) + #error ARC (-fobjc-arc) is required to build this code. +#endif + +#import + +#define READER_BOOKMARKS FALSE +#define READER_ENABLE_MAIL FALSE +#define READER_ENABLE_PRINT FALSE +#define READER_ENABLE_THUMBS FALSE +#define READER_ENABLE_PREVIEW FALSE +#define READER_DISABLE_RETINA FALSE +#define READER_DISABLE_IDLE FALSE +#define READER_SHOW_SHADOWS FALSE +#define READER_STANDALONE FALSE + +extern NSString *const kReaderCopyrightNotice; diff --git a/Night Reader/Night Reader/Night Reader/ReaderConstants.m b/Night Reader/Night Reader/Night Reader/ReaderConstants.m new file mode 100644 index 0000000..fa5b66a --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderConstants.m @@ -0,0 +1,28 @@ +// +// ReaderConstants.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderConstants.h" + +NSString *const kReaderCopyrightNotice = @"Reader v2.x • Copyright © 2011-2013 Julius Oklamcak. All rights reserved."; diff --git a/Night Reader/Night Reader/Night Reader/ReaderContentPage.h b/Night Reader/Night Reader/Night Reader/ReaderContentPage.h new file mode 100644 index 0000000..c2c3234 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderContentPage.h @@ -0,0 +1,52 @@ +// +// ReaderContentPage.h +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface ReaderContentPage : UIView + +- (id)initWithURL:(NSURL *)fileURL page:(NSInteger)page password:(NSString *)phrase; + +- (id)processSingleTap:(UITapGestureRecognizer *)recognizer; + +@end + +#pragma mark - + +// +// ReaderDocumentLink class interface +// + +@interface ReaderDocumentLink : NSObject + +@property (nonatomic, assign, readonly) CGRect rect; + +@property (nonatomic, assign, readonly) CGPDFDictionaryRef dictionary; + ++ (id)newWithRect:(CGRect)linkRect dictionary:(CGPDFDictionaryRef)linkDictionary; + +- (id)initWithRect:(CGRect)linkRect dictionary:(CGPDFDictionaryRef)linkDictionary; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderContentPage.m b/Night Reader/Night Reader/Night Reader/ReaderContentPage.m new file mode 100644 index 0000000..2bfce32 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderContentPage.m @@ -0,0 +1,602 @@ +// +// ReaderContentPage.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderConstants.h" +#import "ReaderContentPage.h" +#import "ReaderContentTile.h" +#import "CGPDFDocument.h" + +@implementation ReaderContentPage +{ + NSMutableArray *_links; + + CGPDFDocumentRef _PDFDocRef; + + CGPDFPageRef _PDFPageRef; + + NSInteger _pageAngle; + + CGFloat _pageWidth; + CGFloat _pageHeight; + + CGFloat _pageOffsetX; + CGFloat _pageOffsetY; +} + +#pragma mark ReaderContentPage class methods + ++ (Class)layerClass +{ + return [ReaderContentTile class]; +} + +#pragma mark ReaderContentPage PDF link methods + +- (void)highlightPageLinks +{ + if (_links.count > 0) // Add highlight views over all links + { + UIColor *hilite = [UIColor colorWithRed:0.0f green:0.0f blue:1.0f alpha:0.15f]; + + for (ReaderDocumentLink *link in _links) // Enumerate the links array + { + UIView *highlight = [[UIView alloc] initWithFrame:link.rect]; + + highlight.autoresizesSubviews = NO; + highlight.userInteractionEnabled = NO; + highlight.contentMode = UIViewContentModeRedraw; + highlight.autoresizingMask = UIViewAutoresizingNone; + highlight.backgroundColor = hilite; // Color + + [self addSubview:highlight]; + } + } +} + +- (ReaderDocumentLink *)linkFromAnnotation:(CGPDFDictionaryRef)annotationDictionary +{ + ReaderDocumentLink *documentLink = nil; // Document link object + + CGPDFArrayRef annotationRectArray = NULL; // Annotation co-ordinates array + + if (CGPDFDictionaryGetArray(annotationDictionary, "Rect", &annotationRectArray)) + { + CGPDFReal ll_x = 0.0f; CGPDFReal ll_y = 0.0f; // PDFRect lower-left X and Y + CGPDFReal ur_x = 0.0f; CGPDFReal ur_y = 0.0f; // PDFRect upper-right X and Y + + CGPDFArrayGetNumber(annotationRectArray, 0, &ll_x); // Lower-left X co-ordinate + CGPDFArrayGetNumber(annotationRectArray, 1, &ll_y); // Lower-left Y co-ordinate + + CGPDFArrayGetNumber(annotationRectArray, 2, &ur_x); // Upper-right X co-ordinate + CGPDFArrayGetNumber(annotationRectArray, 3, &ur_y); // Upper-right Y co-ordinate + + if (ll_x > ur_x) { CGPDFReal t = ll_x; ll_x = ur_x; ur_x = t; } // Normalize Xs + if (ll_y > ur_y) { CGPDFReal t = ll_y; ll_y = ur_y; ur_y = t; } // Normalize Ys + + ll_x -= _pageOffsetX; ll_y -= _pageOffsetY; // Offset lower-left co-ordinate + ur_x -= _pageOffsetX; ur_y -= _pageOffsetY; // Offset upper-right co-ordinate + + switch (_pageAngle) // Page rotation angle (in degrees) + { + case 90: // 90 degree page rotation + { + CGPDFReal swap; + swap = ll_y; ll_y = ll_x; ll_x = swap; + swap = ur_y; ur_y = ur_x; ur_x = swap; + break; + } + + case 270: // 270 degree page rotation + { + CGPDFReal swap; + swap = ll_y; ll_y = ll_x; ll_x = swap; + swap = ur_y; ur_y = ur_x; ur_x = swap; + ll_x = ((0.0f - ll_x) + _pageWidth); + ur_x = ((0.0f - ur_x) + _pageWidth); + break; + } + + case 0: // 0 degree page rotation + { + ll_y = ((0.0f - ll_y) + _pageHeight); + ur_y = ((0.0f - ur_y) + _pageHeight); + break; + } + } + + NSInteger vr_x = ll_x; NSInteger vr_w = (ur_x - ll_x); // Integer X and width + NSInteger vr_y = ll_y; NSInteger vr_h = (ur_y - ll_y); // Integer Y and height + + CGRect viewRect = CGRectMake(vr_x, vr_y, vr_w, vr_h); // View CGRect from PDFRect + + documentLink = [ReaderDocumentLink newWithRect:viewRect dictionary:annotationDictionary]; + } + + return documentLink; +} + +- (void)buildAnnotationLinksList +{ + _links = [NSMutableArray new]; // Links list array + + CGPDFArrayRef pageAnnotations = NULL; // Page annotations array + + CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(_PDFPageRef); + + if (CGPDFDictionaryGetArray(pageDictionary, "Annots", &pageAnnotations) == true) + { + NSInteger count = CGPDFArrayGetCount(pageAnnotations); // Number of annotations + + for (NSInteger index = 0; index < count; index++) // Iterate through all annotations + { + CGPDFDictionaryRef annotationDictionary = NULL; // PDF annotation dictionary + + if (CGPDFArrayGetDictionary(pageAnnotations, index, &annotationDictionary) == true) + { + const char *annotationSubtype = NULL; // PDF annotation subtype string + + if (CGPDFDictionaryGetName(annotationDictionary, "Subtype", &annotationSubtype) == true) + { + if (strcmp(annotationSubtype, "Link") == 0) // Found annotation subtype of 'Link' + { + ReaderDocumentLink *documentLink = [self linkFromAnnotation:annotationDictionary]; + + if (documentLink != nil) [_links insertObject:documentLink atIndex:0]; // Add link + } + } + } + } + + //[self highlightPageLinks]; // Link support debugging + } +} + +- (CGPDFArrayRef)destinationWithName:(const char *)destinationName inDestsTree:(CGPDFDictionaryRef)node +{ + CGPDFArrayRef destinationArray = NULL; + + CGPDFArrayRef limitsArray = NULL; // Limits array + + if (CGPDFDictionaryGetArray(node, "Limits", &limitsArray) == true) + { + CGPDFStringRef lowerLimit = NULL; CGPDFStringRef upperLimit = NULL; + + if (CGPDFArrayGetString(limitsArray, 0, &lowerLimit) == true) // Lower limit + { + if (CGPDFArrayGetString(limitsArray, 1, &upperLimit) == true) // Upper limit + { + const char *ll = (const char *)CGPDFStringGetBytePtr(lowerLimit); // Lower string + const char *ul = (const char *)CGPDFStringGetBytePtr(upperLimit); // Upper string + + if ((strcmp(destinationName, ll) < 0) || (strcmp(destinationName, ul) > 0)) + { + return NULL; // Destination name is outside this node's limits + } + } + } + } + + CGPDFArrayRef namesArray = NULL; // Names array + + if (CGPDFDictionaryGetArray(node, "Names", &namesArray) == true) + { + NSInteger namesCount = CGPDFArrayGetCount(namesArray); + + for (NSInteger index = 0; index < namesCount; index += 2) + { + CGPDFStringRef destName; // Destination name string + + if (CGPDFArrayGetString(namesArray, index, &destName) == true) + { + const char *dn = (const char *)CGPDFStringGetBytePtr(destName); + + if (strcmp(dn, destinationName) == 0) // Found the destination name + { + if (CGPDFArrayGetArray(namesArray, (index + 1), &destinationArray) == false) + { + CGPDFDictionaryRef destinationDictionary = NULL; // Destination dictionary + + if (CGPDFArrayGetDictionary(namesArray, (index + 1), &destinationDictionary) == true) + { + CGPDFDictionaryGetArray(destinationDictionary, "D", &destinationArray); + } + } + + return destinationArray; // Return the destination array + } + } + } + } + + CGPDFArrayRef kidsArray = NULL; // Kids array + + if (CGPDFDictionaryGetArray(node, "Kids", &kidsArray) == true) + { + NSInteger kidsCount = CGPDFArrayGetCount(kidsArray); + + for (NSInteger index = 0; index < kidsCount; index++) + { + CGPDFDictionaryRef kidNode = NULL; // Kid node dictionary + + if (CGPDFArrayGetDictionary(kidsArray, index, &kidNode) == true) // Recurse into node + { + destinationArray = [self destinationWithName:destinationName inDestsTree:kidNode]; + + if (destinationArray != NULL) return destinationArray; // Return destination array + } + } + } + + return NULL; +} + +- (id)annotationLinkTarget:(CGPDFDictionaryRef)annotationDictionary +{ + id linkTarget = nil; // Link target object + + CGPDFStringRef destName = NULL; const char *destString = NULL; + + CGPDFDictionaryRef actionDictionary = NULL; CGPDFArrayRef destArray = NULL; + + if (CGPDFDictionaryGetDictionary(annotationDictionary, "A", &actionDictionary) == true) + { + const char *actionType = NULL; // Annotation action type string + + if (CGPDFDictionaryGetName(actionDictionary, "S", &actionType) == true) + { + if (strcmp(actionType, "GoTo") == 0) // GoTo action type + { + if (CGPDFDictionaryGetArray(actionDictionary, "D", &destArray) == false) + { + CGPDFDictionaryGetString(actionDictionary, "D", &destName); + } + } + else // Handle other link action type possibility + { + if (strcmp(actionType, "URI") == 0) // URI action type + { + CGPDFStringRef uriString = NULL; // Action's URI string + + if (CGPDFDictionaryGetString(actionDictionary, "URI", &uriString) == true) + { + const char *uri = (const char *)CGPDFStringGetBytePtr(uriString); // Destination URI string + + linkTarget = [NSURL URLWithString:[NSString stringWithCString:uri encoding:NSASCIIStringEncoding]]; + } + } + } + } + } + else // Handle other link target possibilities + { + if (CGPDFDictionaryGetArray(annotationDictionary, "Dest", &destArray) == false) + { + if (CGPDFDictionaryGetString(annotationDictionary, "Dest", &destName) == false) + { + CGPDFDictionaryGetName(annotationDictionary, "Dest", &destString); + } + } + } + + if (destName != NULL) // Handle a destination name + { + CGPDFDictionaryRef catalogDictionary = CGPDFDocumentGetCatalog(_PDFDocRef); + + CGPDFDictionaryRef namesDictionary = NULL; // Destination names in the document + + if (CGPDFDictionaryGetDictionary(catalogDictionary, "Names", &namesDictionary) == true) + { + CGPDFDictionaryRef destsDictionary = NULL; // Document destinations dictionary + + if (CGPDFDictionaryGetDictionary(namesDictionary, "Dests", &destsDictionary) == true) + { + const char *destinationName = (const char *)CGPDFStringGetBytePtr(destName); // Name + + destArray = [self destinationWithName:destinationName inDestsTree:destsDictionary]; + } + } + } + + if (destString != NULL) // Handle a destination string + { + CGPDFDictionaryRef catalogDictionary = CGPDFDocumentGetCatalog(_PDFDocRef); + + CGPDFDictionaryRef destsDictionary = NULL; // Document destinations dictionary + + if (CGPDFDictionaryGetDictionary(catalogDictionary, "Dests", &destsDictionary) == true) + { + CGPDFDictionaryRef targetDictionary = NULL; // Destination target dictionary + + if (CGPDFDictionaryGetDictionary(destsDictionary, destString, &targetDictionary) == true) + { + CGPDFDictionaryGetArray(targetDictionary, "D", &destArray); + } + } + } + + if (destArray != NULL) // Handle a destination array + { + NSInteger targetPageNumber = 0; // The target page number + + CGPDFDictionaryRef pageDictionaryFromDestArray = NULL; // Target reference + + if (CGPDFArrayGetDictionary(destArray, 0, &pageDictionaryFromDestArray) == true) + { + NSInteger pageCount = CGPDFDocumentGetNumberOfPages(_PDFDocRef); // Pages + + for (NSInteger pageNumber = 1; pageNumber <= pageCount; pageNumber++) + { + CGPDFPageRef pageRef = CGPDFDocumentGetPage(_PDFDocRef, pageNumber); + + CGPDFDictionaryRef pageDictionaryFromPage = CGPDFPageGetDictionary(pageRef); + + if (pageDictionaryFromPage == pageDictionaryFromDestArray) // Found it + { + targetPageNumber = pageNumber; break; + } + } + } + else // Try page number from array possibility + { + CGPDFInteger pageNumber = 0; // Page number in array + + if (CGPDFArrayGetInteger(destArray, 0, &pageNumber) == true) + { + targetPageNumber = (pageNumber + 1); // 1-based + } + } + + if (targetPageNumber > 0) // We have a target page number + { + linkTarget = [NSNumber numberWithInteger:targetPageNumber]; + } + } + + return linkTarget; +} + +- (id)processSingleTap:(UITapGestureRecognizer *)recognizer +{ + id result = nil; // Tap result object + + if (recognizer.state == UIGestureRecognizerStateRecognized) + { + if (_links.count > 0) // Process the single tap + { + CGPoint point = [recognizer locationInView:self]; + + for (ReaderDocumentLink *link in _links) // Enumerate links + { + if (CGRectContainsPoint(link.rect, point) == true) // Found it + { + result = [self annotationLinkTarget:link.dictionary]; break; + } + } + } + } + + return result; +} + +#pragma mark ReaderContentPage instance methods + +- (id)initWithFrame:(CGRect)frame +{ + id view = nil; // UIView + + if (CGRectIsEmpty(frame) == false) + { + if ((self = [super initWithFrame:frame])) + { + self.autoresizesSubviews = NO; + self.userInteractionEnabled = NO; + self.contentMode = UIViewContentModeRedraw; + self.autoresizingMask = UIViewAutoresizingNone; + self.backgroundColor = [UIColor blackColor]; + + view = self; // Return self + } + } + else // Handle invalid frame size + { + self = nil; + } + + return view; +} + +// VOLGENS MIJ STAAT HIER DE CODE DIE JE MOET HEBBEN //NOP + +- (id)initWithURL:(NSURL *)fileURL page:(NSInteger)page password:(NSString *)phrase +{ + CGRect viewRect = CGRectZero; // View rect + + if (fileURL != nil) // Check for non-nil file URL + { + _PDFDocRef = CGPDFDocumentCreateX((__bridge CFURLRef)fileURL, phrase); + + if (_PDFDocRef != NULL) // Check for non-NULL CGPDFDocumentRef + { + if (page < 1) page = 1; // Check the lower page bounds + + NSInteger pages = CGPDFDocumentGetNumberOfPages(_PDFDocRef); + + if (page > pages) page = pages; // Check the upper page bounds + + _PDFPageRef = CGPDFDocumentGetPage(_PDFDocRef, page); // Get page + + if (_PDFPageRef != NULL) // Check for non-NULL CGPDFPageRef + { + CGPDFPageRetain(_PDFPageRef); // Retain the PDF page + + CGRect cropBoxRect = CGPDFPageGetBoxRect(_PDFPageRef, kCGPDFCropBox); + CGRect mediaBoxRect = CGPDFPageGetBoxRect(_PDFPageRef, kCGPDFMediaBox); + CGRect effectiveRect = CGRectIntersection(cropBoxRect, mediaBoxRect); + + _pageAngle = CGPDFPageGetRotationAngle(_PDFPageRef); // Angle + + switch (_pageAngle) // Page rotation angle (in degrees) + { + default: // Default case + case 0: case 180: // 0 and 180 degrees + { + _pageWidth = effectiveRect.size.width; + _pageHeight = effectiveRect.size.height; + _pageOffsetX = effectiveRect.origin.x; + _pageOffsetY = effectiveRect.origin.y; + break; + } + + case 90: case 270: // 90 and 270 degrees + { + _pageWidth = effectiveRect.size.height; + _pageHeight = effectiveRect.size.width; + _pageOffsetX = effectiveRect.origin.y; + _pageOffsetY = effectiveRect.origin.x; + break; + } + } + + NSInteger page_w = _pageWidth; // Integer width + NSInteger page_h = _pageHeight; // Integer height + + if (page_w % 2) page_w--; if (page_h % 2) page_h--; // Even + + viewRect.size = CGSizeMake(page_w, page_h); // View size + } + else // Error out with a diagnostic + { + CGPDFDocumentRelease(_PDFDocRef), _PDFDocRef = NULL; + + NSAssert(NO, @"CGPDFPageRef == NULL"); + } + } + else // Error out with a diagnostic + { + NSAssert(NO, @"CGPDFDocumentRef == NULL"); + } + } + else // Error out with a diagnostic + { + NSAssert(NO, @"fileURL == nil"); + } + + id view = [self initWithFrame:viewRect]; // UIView setup + + if (view != nil) [self buildAnnotationLinksList]; // Links + + return view; +} + +- (void)removeFromSuperview +{ + self.layer.delegate = nil; + + //self.layer.contents = nil; + + [super removeFromSuperview]; +} + +- (void)dealloc +{ + CGPDFPageRelease(_PDFPageRef), _PDFPageRef = NULL; + + CGPDFDocumentRelease(_PDFDocRef), _PDFDocRef = NULL; +} + +#if (READER_DISABLE_RETINA == TRUE) // Option + +- (void)didMoveToWindow +{ + self.contentScaleFactor = 1.0f; // Override scale factor +} + +#endif // end of READER_DISABLE_RETINA Option + +#pragma mark CATiledLayer delegate methods + +- (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)context +{ + ReaderContentPage *readerContentPage = self; // Retain self + + CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // White + + CGContextFillRect(context, CGContextGetClipBoundingBox(context)); // Fill + + //NSLog(@"%s %@", __FUNCTION__, NSStringFromCGRect(CGContextGetClipBoundingBox(context))); + + CGContextTranslateCTM(context, 0.0f, self.bounds.size.height); CGContextScaleCTM(context, 1.0f, -1.0f); + + CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(_PDFPageRef, kCGPDFCropBox, self.bounds, 0, true)); + + //CGContextSetRenderingIntent(context, kCGRenderingIntentDefault); CGContextSetInterpolationQuality(context, kCGInterpolationDefault); + + CGContextDrawPDFPage(context, _PDFPageRef); // Render the PDF page into the context + + if (readerContentPage != nil) readerContentPage = nil; // Release self +} + +@end + +#pragma mark - + +// +// ReaderDocumentLink class implementation +// + +@implementation ReaderDocumentLink +{ + CGPDFDictionaryRef _dictionary; + + CGRect _rect; +} + +#pragma mark Properties + +@synthesize rect = _rect; +@synthesize dictionary = _dictionary; + +#pragma mark ReaderDocumentLink class methods + ++ (id)newWithRect:(CGRect)linkRect dictionary:(CGPDFDictionaryRef)linkDictionary +{ + return [[ReaderDocumentLink alloc] initWithRect:linkRect dictionary:linkDictionary]; +} + +#pragma mark ReaderDocumentLink instance methods + +- (id)initWithRect:(CGRect)linkRect dictionary:(CGPDFDictionaryRef)linkDictionary +{ + if ((self = [super init])) + { + _dictionary = linkDictionary; + + _rect = linkRect; + } + + return self; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderContentTile.h b/Night Reader/Night Reader/Night Reader/ReaderContentTile.h new file mode 100644 index 0000000..7662176 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderContentTile.h @@ -0,0 +1,31 @@ +// +// ReaderContentTile.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import +#import + +@interface ReaderContentTile : CATiledLayer + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderContentTile.m b/Night Reader/Night Reader/Night Reader/ReaderContentTile.m new file mode 100644 index 0000000..3aceedb --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderContentTile.m @@ -0,0 +1,72 @@ +// +// ReaderContentTile.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderContentTile.h" + +@implementation ReaderContentTile + +#pragma mark Constants + +#define LEVELS_OF_DETAIL 4 +#define LEVELS_OF_DETAIL_BIAS 3 + +#pragma mark ReaderContentTile class methods + ++ (CFTimeInterval)fadeDuration +{ + return 0.001; // iOS bug (flickering tiles) workaround +} + +#pragma mark ReaderContentTile instance methods + +- (id)init +{ + if ((self = [super init])) + { + self.levelsOfDetail = LEVELS_OF_DETAIL; // Zoom levels + + UIScreen *mainScreen = [UIScreen mainScreen]; // Main screen + + CGFloat screenScale = [mainScreen scale]; // Main screen scale + + self.levelsOfDetailBias = (screenScale > 1.0f) ? 1 : LEVELS_OF_DETAIL_BIAS; + + CGRect screenBounds = [mainScreen bounds]; // Main screen bounds + + CGFloat w_pixels = (screenBounds.size.width * screenScale); + + CGFloat h_pixels = (screenBounds.size.height * screenScale); + + CGFloat max = ((w_pixels < h_pixels) ? h_pixels : w_pixels); + + CGFloat sizeOfTiles = ((max < 512.0f) ? 512.0f : 1024.0f); + + self.tileSize = CGSizeMake(sizeOfTiles, sizeOfTiles); + } + + return self; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderContentView.h b/Night Reader/Night Reader/Night Reader/ReaderContentView.h new file mode 100644 index 0000000..878f2b7 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderContentView.h @@ -0,0 +1,66 @@ +// +// ReaderContentView.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "ReaderThumbView.h" + +@class ReaderContentView; +@class ReaderContentPage; +@class ReaderContentThumb; + +@protocol ReaderContentViewDelegate + +@required // Delegate protocols + +- (void)contentView:(ReaderContentView *)contentView touchesBegan:(NSSet *)touches; + +@end + +@interface ReaderContentView : UIScrollView + +@property (nonatomic, unsafe_unretained, readwrite) id message; + +- (id)initWithFrame:(CGRect)frame fileURL:(NSURL *)fileURL page:(NSUInteger)page password:(NSString *)phrase; + +- (void)showPageThumb:(NSURL *)fileURL page:(NSInteger)page password:(NSString *)phrase guid:(NSString *)guid; + +- (id)processSingleTap:(UITapGestureRecognizer *)recognizer; + +- (void)zoomIncrement; +- (void)zoomDecrement; +- (void)zoomReset; + +@end + +#pragma mark - + +// +// ReaderContentThumb class interface +// + +@interface ReaderContentThumb : ReaderThumbView + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderContentView.m b/Night Reader/Night Reader/Night Reader/ReaderContentView.m new file mode 100644 index 0000000..d97ef1f --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderContentView.m @@ -0,0 +1,332 @@ +// +// ReaderContentView.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderConstants.h" +#import "ReaderContentView.h" +#import "ReaderContentPage.h" +#import "ReaderThumbCache.h" + +#import + +@implementation ReaderContentView +{ + ReaderContentPage *theContentView; + + ReaderContentThumb *theThumbView; + + UIView *theContainerView; + + CGFloat zoomAmount; +} + +#pragma mark Constants + +#define ZOOM_LEVELS 4 + +#if (READER_SHOW_SHADOWS == TRUE) // Option + #define CONTENT_INSET 4.0f +#else + #define CONTENT_INSET 2.0f +#endif // end of READER_SHOW_SHADOWS Option + +#define PAGE_THUMB_LARGE 240 +#define PAGE_THUMB_SMALL 144 + +static void *ReaderContentViewContext = &ReaderContentViewContext; + +#pragma mark Properties + +@synthesize message; + +#pragma mark ReaderContentView functions + +static inline CGFloat ZoomScaleThatFits(CGSize target, CGSize source) +{ + CGFloat w_scale = (target.width / source.width); + + CGFloat h_scale = (target.height / source.height); + + return ((w_scale < h_scale) ? w_scale : h_scale); +} + +#pragma mark ReaderContentView instance methods + +- (void)updateMinimumMaximumZoom +{ + CGRect targetRect = CGRectInset(self.bounds, CONTENT_INSET, CONTENT_INSET); + + CGFloat zoomScale = ZoomScaleThatFits(targetRect.size, theContentView.bounds.size); + + self.minimumZoomScale = zoomScale; // Set the minimum and maximum zoom scales + + self.maximumZoomScale = (zoomScale * ZOOM_LEVELS); // Max number of zoom levels + + zoomAmount = ((self.maximumZoomScale - self.minimumZoomScale) / ZOOM_LEVELS); +} + +- (id)initWithFrame:(CGRect)frame fileURL:(NSURL *)fileURL page:(NSUInteger)page password:(NSString *)phrase +{ + if ((self = [super initWithFrame:frame])) + { + self.scrollsToTop = NO; + self.delaysContentTouches = NO; + self.showsVerticalScrollIndicator = NO; + self.showsHorizontalScrollIndicator = NO; + self.contentMode = UIViewContentModeRedraw; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.backgroundColor = [UIColor clearColor]; + self.userInteractionEnabled = YES; + self.autoresizesSubviews = NO; + self.bouncesZoom = YES; + self.delegate = self; + + theContentView = [[ReaderContentPage alloc] initWithURL:fileURL page:page password:phrase]; + + if (theContentView != nil) // Must have a valid and initialized content view + { + theContainerView = [[UIView alloc] initWithFrame:theContentView.bounds]; + + theContainerView.autoresizesSubviews = NO; + theContainerView.userInteractionEnabled = NO; + theContainerView.contentMode = UIViewContentModeRedraw; + theContainerView.autoresizingMask = UIViewAutoresizingNone; + theContainerView.backgroundColor = [UIColor blackColor]; + +#if (READER_SHOW_SHADOWS == TRUE) // Option + + theContainerView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f); + theContainerView.layer.shadowRadius = 4.0f; theContainerView.layer.shadowOpacity = 1.0f; + theContainerView.layer.shadowPath = [UIBezierPath bezierPathWithRect:theContainerView.bounds].CGPath; + +#endif // end of READER_SHOW_SHADOWS Option + + self.contentSize = theContentView.bounds.size; // Content size same as view size + self.contentOffset = CGPointMake((0.0f - CONTENT_INSET), (0.0f - CONTENT_INSET)); // Offset + self.contentInset = UIEdgeInsetsMake(CONTENT_INSET, CONTENT_INSET, CONTENT_INSET, CONTENT_INSET); + +#if (READER_ENABLE_PREVIEW == TRUE) // Option + + theThumbView = [[ReaderContentThumb alloc] initWithFrame:theContentView.bounds]; // Page thumb view + + [theContainerView addSubview:theThumbView]; // Add the thumb view to the container view + +#endif // end of READER_ENABLE_PREVIEW Option + + [theContainerView addSubview:theContentView]; // Add the content view to the container view + + [self addSubview:theContainerView]; // Add the container view to the scroll view + + [self updateMinimumMaximumZoom]; // Update the minimum and maximum zoom scales + + self.zoomScale = self.minimumZoomScale; // Set zoom to fit page content + } + + [self addObserver:self forKeyPath:@"frame" options:0 context:ReaderContentViewContext]; + + self.tag = page; // Tag the view with the page number + } + + return self; +} + +- (void)dealloc +{ +// [self removeObserver:self forKeyPath:@"frame" context:ReaderContentViewContext]; + + [self removeObserver:self forKeyPath:@"frame"]; // Maintain iOS 4.x compatability +} + +- (void)showPageThumb:(NSURL *)fileURL page:(NSInteger)page password:(NSString *)phrase guid:(NSString *)guid +{ +#if (READER_ENABLE_PREVIEW == TRUE) // Option + + BOOL large = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad); // Page thumb size + + CGSize size = (large ? CGSizeMake(PAGE_THUMB_LARGE, PAGE_THUMB_LARGE) : CGSizeMake(PAGE_THUMB_SMALL, PAGE_THUMB_SMALL)); + + ReaderThumbRequest *request = [ReaderThumbRequest newForView:theThumbView fileURL:fileURL password:phrase guid:guid page:page size:size]; + + UIImage *image = [[ReaderThumbCache sharedInstance] thumbRequest:request priority:YES]; // Request the page thumb + + if ([image isKindOfClass:[UIImage class]]) [theThumbView showImage:image]; // Show image from cache + +#endif // end of READER_ENABLE_PREVIEW Option +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (context == ReaderContentViewContext) // Our context + { + if ((object == self) && [keyPath isEqualToString:@"frame"]) + { + CGFloat oldMinimumZoomScale = self.minimumZoomScale; + + [self updateMinimumMaximumZoom]; // Update zoom scale limits + + if (self.zoomScale == oldMinimumZoomScale) // Old minimum + { + self.zoomScale = self.minimumZoomScale; + } + else // Check against minimum zoom scale + { + if (self.zoomScale < self.minimumZoomScale) + { + self.zoomScale = self.minimumZoomScale; + } + else // Check against maximum zoom scale + { + if (self.zoomScale > self.maximumZoomScale) + { + self.zoomScale = self.maximumZoomScale; + } + } + } + } + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGSize boundsSize = self.bounds.size; + CGRect viewFrame = theContainerView.frame; + + if (viewFrame.size.width < boundsSize.width) + viewFrame.origin.x = (((boundsSize.width - viewFrame.size.width) / 2.0f) + self.contentOffset.x); + else + viewFrame.origin.x = 0.0f; + + if (viewFrame.size.height < boundsSize.height) + viewFrame.origin.y = (((boundsSize.height - viewFrame.size.height) / 2.0f) + self.contentOffset.y); + else + viewFrame.origin.y = 0.0f; + + theContainerView.frame = viewFrame; +} + +- (id)processSingleTap:(UITapGestureRecognizer *)recognizer +{ + return [theContentView processSingleTap:recognizer]; +} + +- (void)zoomIncrement +{ + CGFloat zoomScale = self.zoomScale; + + if (zoomScale < self.maximumZoomScale) + { + zoomScale += zoomAmount; // += value + + if (zoomScale > self.maximumZoomScale) + { + zoomScale = self.maximumZoomScale; + } + + [self setZoomScale:zoomScale animated:YES]; + } +} + +- (void)zoomDecrement +{ + CGFloat zoomScale = self.zoomScale; + + if (zoomScale > self.minimumZoomScale) + { + zoomScale -= zoomAmount; // -= value + + if (zoomScale < self.minimumZoomScale) + { + zoomScale = self.minimumZoomScale; + } + + [self setZoomScale:zoomScale animated:YES]; + } +} + +- (void)zoomReset +{ + if (self.zoomScale > self.minimumZoomScale) + { + self.zoomScale = self.minimumZoomScale; + } +} + +#pragma mark UIScrollViewDelegate methods + +- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView +{ + return theContainerView; +} + +#pragma mark UIResponder instance methods + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; // Message superclass + + [message contentView:self touchesBegan:touches]; // Message delegate +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesCancelled:touches withEvent:event]; // Message superclass +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesEnded:touches withEvent:event]; // Message superclass +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesMoved:touches withEvent:event]; // Message superclass +} + +@end + +#pragma mark - + +// +// ReaderContentThumb class implementation +// + +@implementation ReaderContentThumb + +#pragma mark ReaderContentThumb instance methods + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) // Superclass init + { + imageView.contentMode = UIViewContentModeScaleAspectFill; + + imageView.clipsToBounds = YES; // Needed for aspect fill + } + + return self; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderDemoController.h b/Night Reader/Night Reader/Night Reader/ReaderDemoController.h new file mode 100644 index 0000000..ea96dca --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderDemoController.h @@ -0,0 +1,30 @@ +// +// ReaderDemoController.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface ReaderDemoController : UIViewController + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderDemoController.m b/Night Reader/Night Reader/Night Reader/ReaderDemoController.m new file mode 100644 index 0000000..b0fb74b --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderDemoController.m @@ -0,0 +1,217 @@ +// +// ReaderDemoController.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderDemoController.h" +#import "ReaderViewController.h" + +@interface ReaderDemoController () + +@end + +@implementation ReaderDemoController + +#pragma mark Constants + +#define DEMO_VIEW_CONTROLLER_PUSH FALSE + +#pragma mark UIViewController methods + +/* +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) + { + // Custom initialization + } + + return self; +} +*/ + +/* +- (void)loadView +{ + // Implement loadView to create a view hierarchy programmatically, without using a nib. +} +*/ + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor clearColor]; // Transparent + + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; + + NSString *name = [infoDictionary objectForKey:@"CFBundleName"]; + + NSString *version = [infoDictionary objectForKey:@"CFBundleVersion"]; + + self.title = [NSString stringWithFormat:@"%@ v%@", name, version]; + + CGSize viewSize = self.view.bounds.size; + + CGRect labelRect = CGRectMake(0.0f, 0.0f, 80.0f, 32.0f); + + UILabel *tapLabel = [[UILabel alloc] initWithFrame:labelRect]; + + tapLabel.text = @"Tap"; + tapLabel.textColor = [UIColor blackColor]; + tapLabel.textAlignment = NSTextAlignmentCenter; + tapLabel.backgroundColor = [UIColor clearColor]; + tapLabel.font = [UIFont systemFontOfSize:24.0f]; + tapLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; + tapLabel.autoresizingMask |= UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + tapLabel.center = CGPointMake(viewSize.width / 2.0f, viewSize.height / 2.0f); + + [self.view addSubview:tapLabel]; + + UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; + //singleTap.numberOfTouchesRequired = 1; singleTap.numberOfTapsRequired = 1; //singleTap.delegate = self; + [self.view addGestureRecognizer:singleTap]; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + +#if (DEMO_VIEW_CONTROLLER_PUSH == TRUE) + + [self.navigationController setNavigationBarHidden:NO animated:animated]; + +#endif // DEMO_VIEW_CONTROLLER_PUSH +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; + +#if (DEMO_VIEW_CONTROLLER_PUSH == TRUE) + + [self.navigationController setNavigationBarHidden:YES animated:animated]; + +#endif // DEMO_VIEW_CONTROLLER_PUSH +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; +} + +- (void)viewDidUnload +{ +#ifdef DEBUG + NSLog(@"%s", __FUNCTION__); +#endif + + [super viewDidUnload]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) // See README + return UIInterfaceOrientationIsPortrait(interfaceOrientation); + else + return YES; +} + +/* +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +{ +} + +- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration +{ +} + +- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation +{ + //if (fromInterfaceOrientation == self.interfaceOrientation) return; +} +*/ + +- (void)didReceiveMemoryWarning +{ +#ifdef DEBUG + NSLog(@"%s", __FUNCTION__); +#endif + + [super didReceiveMemoryWarning]; +} + +#pragma mark UIGestureRecognizer methods + +- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer +{ + NSString *phrase = nil; // Document password (for unlocking most encrypted PDF files) + + NSArray *pdfs = [[NSBundle mainBundle] pathsForResourcesOfType:@"pdf" inDirectory:nil]; + + NSString *filePath = [pdfs lastObject]; assert(filePath != nil); // Path to last PDF file + + ReaderDocument *document = [ReaderDocument withDocumentFilePath:filePath password:phrase]; + + if (document != nil) // Must have a valid ReaderDocument object in order to proceed with things + { + ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document]; + + readerViewController.delegate = self; // Set the ReaderViewController delegate to self + +#if (DEMO_VIEW_CONTROLLER_PUSH == TRUE) + + [self.navigationController pushViewController:readerViewController animated:YES]; + +#else // present in a modal view controller + + readerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + readerViewController.modalPresentationStyle = UIModalPresentationFullScreen; + + [self presentViewController:readerViewController animated:YES completion:nil ]; + +#endif // DEMO_VIEW_CONTROLLER_PUSH + } +} + +#pragma mark ReaderViewControllerDelegate methods + +- (void)dismissReaderViewController:(ReaderViewController *)viewController +{ +#if (DEMO_VIEW_CONTROLLER_PUSH == TRUE) + + [self.navigationController popViewControllerAnimated:YES]; + +#else // dismiss the modal view controller + + [self dismissViewControllerAnimated:YES completion:nil]; + +#endif // DEMO_VIEW_CONTROLLER_PUSH +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderDocument.h b/Night Reader/Night Reader/Night Reader/ReaderDocument.h new file mode 100644 index 0000000..98768de --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderDocument.h @@ -0,0 +1,51 @@ +// +// ReaderDocument.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface ReaderDocument : NSObject + +@property (nonatomic, strong, readonly) NSString *guid; +@property (nonatomic, strong, readonly) NSDate *fileDate; +@property (nonatomic, strong, readwrite) NSDate *lastOpen; +@property (nonatomic, strong, readonly) NSNumber *fileSize; +@property (nonatomic, strong, readonly) NSNumber *pageCount; +@property (nonatomic, strong, readwrite) NSNumber *pageNumber; +@property (nonatomic, strong, readonly) NSMutableIndexSet *bookmarks; +@property (nonatomic, strong, readonly) NSString *fileName; +@property (nonatomic, strong, readonly) NSString *password; +@property (nonatomic, strong, readonly) NSURL *fileURL; + ++ (ReaderDocument *)withDocumentFilePath:(NSString *)filename password:(NSString *)phrase; + ++ (ReaderDocument *)unarchiveFromFileName:(NSString *)filename password:(NSString *)phrase; + +- (id)initWithFilePath:(NSString *)fullFilePath password:(NSString *)phrase; + +- (void)saveReaderDocument; + +- (void)updateProperties; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderDocument.m b/Night Reader/Night Reader/Night Reader/ReaderDocument.m new file mode 100644 index 0000000..4a1f841 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderDocument.m @@ -0,0 +1,358 @@ +// +// ReaderDocument.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderDocument.h" +#import "CGPDFDocument.h" +#import + +@implementation ReaderDocument +{ + NSString *_guid; + + NSDate *_fileDate; + + NSDate *_lastOpen; + + NSNumber *_fileSize; + + NSNumber *_pageCount; + + NSNumber *_pageNumber; + + NSMutableIndexSet *_bookmarks; + + NSString *_fileName; + + NSString *_password; + + NSURL *_fileURL; +} + +#pragma mark Properties + +@synthesize guid = _guid; +@synthesize fileDate = _fileDate; +@synthesize fileSize = _fileSize; +@synthesize pageCount = _pageCount; +@synthesize pageNumber = _pageNumber; +@synthesize bookmarks = _bookmarks; +@synthesize lastOpen = _lastOpen; +@synthesize password = _password; +@dynamic fileName, fileURL; + +#pragma mark ReaderDocument class methods + ++ (NSString *)GUID +{ + CFUUIDRef theUUID = CFUUIDCreate(NULL); + + CFStringRef theString = CFUUIDCreateString(NULL, theUUID); + + NSString *unique = [NSString stringWithString:(__bridge id)theString]; + + CFRelease(theString); CFRelease(theUUID); // Cleanup CF objects + + return unique; +} + ++ (NSString *)documentsPath +{ + NSArray *documentsPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + + return [documentsPaths objectAtIndex:0]; // Path to the application's "~/Documents" directory +} + ++ (NSString *)applicationPath +{ + NSArray *documentsPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + + return [[documentsPaths objectAtIndex:0] stringByDeletingLastPathComponent]; // Strip "Documents" component +} + ++ (NSString *)applicationSupportPath +{ + NSFileManager *fileManager = [NSFileManager new]; // File manager instance + + NSURL *pathURL = [fileManager URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL]; + + return [pathURL path]; // Path to the application's "~/Library/Application Support" directory +} + ++ (NSString *)relativeFilePath:(NSString *)fullFilePath +{ + assert(fullFilePath != nil); // Ensure that the full file path is not nil + + NSString *applicationPath = [ReaderDocument applicationPath]; // Get the application path + + NSRange range = [fullFilePath rangeOfString:applicationPath]; // Look for the application path + + assert(range.location != NSNotFound); // Ensure that the application path is in the full file path + + return [fullFilePath stringByReplacingCharactersInRange:range withString:@""]; // Strip it out +} + ++ (NSString *)archiveFilePath:(NSString *)filename +{ + assert(filename != nil); // Ensure that the archive file name is not nil + + //NSString *archivePath = [ReaderDocument documentsPath]; // Application's "~/Documents" path + + NSString *archivePath = [ReaderDocument applicationSupportPath]; // Application's "~/Library/Application Support" path + + NSString *archiveName = [[filename stringByDeletingPathExtension] stringByAppendingPathExtension:@"plist"]; + + return [archivePath stringByAppendingPathComponent:archiveName]; // "{archivePath}/'filename'.plist" +} + ++ (ReaderDocument *)unarchiveFromFileName:(NSString *)filename password:(NSString *)phrase +{ + ReaderDocument *document = nil; // ReaderDocument object + + NSString *withName = [filename lastPathComponent]; // File name only + + NSString *archiveFilePath = [ReaderDocument archiveFilePath:withName]; + + @try // Unarchive an archived ReaderDocument object from its property list + { + document = [NSKeyedUnarchiver unarchiveObjectWithFile:archiveFilePath]; + + if ((document != nil) && (phrase != nil)) // Set the document password + { + [document setValue:[phrase copy] forKey:@"password"]; + } + } + @catch (NSException *exception) // Exception handling (just in case O_o) + { + #ifdef DEBUG + NSLog(@"%s Caught %@: %@", __FUNCTION__, [exception name], [exception reason]); + #endif + } + + return document; +} + ++ (ReaderDocument *)withDocumentFilePath:(NSString *)filePath password:(NSString *)phrase +{ + ReaderDocument *document = nil; // ReaderDocument object + + document = [ReaderDocument unarchiveFromFileName:filePath password:phrase]; + + if (document == nil) // Unarchive failed so we create a new ReaderDocument object + { + document = [[ReaderDocument alloc] initWithFilePath:filePath password:phrase]; + } + + return document; +} + ++ (BOOL)isPDF:(NSString *)filePath +{ + BOOL state = NO; + + if (filePath != nil) // Must have a file path + { + const char *path = [filePath fileSystemRepresentation]; + + int fd = open(path, O_RDONLY); // Open the file + + if (fd > 0) // We have a valid file descriptor + { + const char sig[1024]; // File signature buffer + + ssize_t len = read(fd, (void *)&sig, sizeof(sig)); + + state = (strnstr(sig, "%PDF", len) != NULL); + + close(fd); // Close the file + } + } + + return state; +} + +#pragma mark ReaderDocument instance methods + +- (id)initWithFilePath:(NSString *)fullFilePath password:(NSString *)phrase +{ + id object = nil; // ReaderDocument object + + if ([ReaderDocument isPDF:fullFilePath] == YES) // File must exist + { + if ((self = [super init])) // Initialize superclass object first + { + _guid = [ReaderDocument GUID]; // Create a document GUID + + _password = [phrase copy]; // Keep copy of any document password + + _bookmarks = [NSMutableIndexSet new]; // Bookmarked pages index set + + _pageNumber = [NSNumber numberWithInteger:1]; // Start on page 1 + + _fileName = [ReaderDocument relativeFilePath:fullFilePath]; // File name + + CFURLRef docURLRef = (__bridge CFURLRef)[self fileURL]; // CFURLRef from NSURL + + CGPDFDocumentRef thePDFDocRef = CGPDFDocumentCreateX(docURLRef, _password); + + if (thePDFDocRef != NULL) // Get the number of pages in the document + { + NSInteger pageCount = CGPDFDocumentGetNumberOfPages(thePDFDocRef); + + _pageCount = [NSNumber numberWithInteger:pageCount]; + + CGPDFDocumentRelease(thePDFDocRef); // Cleanup + } + else // Cupertino, we have a problem with the document + { + NSAssert(NO, @"CGPDFDocumentRef == NULL"); + } + + NSFileManager *fileManager = [NSFileManager new]; // File manager instance + + _lastOpen = [NSDate dateWithTimeIntervalSinceReferenceDate:0.0]; // Last opened + + NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:fullFilePath error:NULL]; + + _fileDate = [fileAttributes objectForKey:NSFileModificationDate]; // File date + + _fileSize = [fileAttributes objectForKey:NSFileSize]; // File size (bytes) + + [self saveReaderDocument]; // Save the ReaderDocument object + + object = self; // Return initialized ReaderDocument object + } + } + + return object; +} + +- (NSString *)fileName +{ + return [_fileName lastPathComponent]; +} + +- (NSURL *)fileURL +{ + if (_fileURL == nil) // Create and keep the file URL the first time it is requested + { + NSString *fullFilePath = [[ReaderDocument applicationPath] stringByAppendingPathComponent:_fileName]; + + _fileURL = [[NSURL alloc] initFileURLWithPath:fullFilePath isDirectory:NO]; // File URL from full file path + } + + return _fileURL; +} + +- (BOOL)archiveWithFileName:(NSString *)filename +{ + NSString *archiveFilePath = [ReaderDocument archiveFilePath:filename]; + + return [NSKeyedArchiver archiveRootObject:self toFile:archiveFilePath]; +} + +- (void)saveReaderDocument +{ + [self archiveWithFileName:[self fileName]]; +} + +- (void)updateProperties +{ + CFURLRef docURLRef = (__bridge CFURLRef)self.fileURL; // File URL + + CGPDFDocumentRef thePDFDocRef = CGPDFDocumentCreateWithURL(docURLRef); + + if (thePDFDocRef != NULL) // Get the number of pages in the document + { + NSInteger pageCount = CGPDFDocumentGetNumberOfPages(thePDFDocRef); + + _pageCount = [NSNumber numberWithInteger:pageCount]; + + CGPDFDocumentRelease(thePDFDocRef); // Cleanup + } + + NSString *fullFilePath = [self.fileURL path]; // Full file path + + NSFileManager *fileManager = [NSFileManager new]; // File manager instance + + NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:fullFilePath error:NULL]; + + _fileDate = [fileAttributes objectForKey:NSFileModificationDate]; // File date + + _fileSize = [fileAttributes objectForKey:NSFileSize]; // File size +} + +#pragma mark NSCoding protocol methods + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + [encoder encodeObject:_guid forKey:@"FileGUID"]; + + [encoder encodeObject:_fileName forKey:@"FileName"]; + + [encoder encodeObject:_fileDate forKey:@"FileDate"]; + + [encoder encodeObject:_pageCount forKey:@"PageCount"]; + + [encoder encodeObject:_pageNumber forKey:@"PageNumber"]; + + [encoder encodeObject:_bookmarks forKey:@"Bookmarks"]; + + [encoder encodeObject:_fileSize forKey:@"FileSize"]; + + [encoder encodeObject:_lastOpen forKey:@"LastOpen"]; +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + if ((self = [super init])) // Superclass init + { + _guid = [decoder decodeObjectForKey:@"FileGUID"]; + + _fileName = [decoder decodeObjectForKey:@"FileName"]; + + _fileDate = [decoder decodeObjectForKey:@"FileDate"]; + + _pageCount = [decoder decodeObjectForKey:@"PageCount"]; + + _pageNumber = [decoder decodeObjectForKey:@"PageNumber"]; + + _bookmarks = [decoder decodeObjectForKey:@"Bookmarks"]; + + _fileSize = [decoder decodeObjectForKey:@"FileSize"]; + + _lastOpen = [decoder decodeObjectForKey:@"LastOpen"]; + + if (_guid == nil) _guid = [ReaderDocument GUID]; + + if (_bookmarks != nil) + _bookmarks = [_bookmarks mutableCopy]; + else + _bookmarks = [NSMutableIndexSet new]; + } + + return self; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderDocumentOutline.h b/Night Reader/Night Reader/Night Reader/ReaderDocumentOutline.h new file mode 100644 index 0000000..7a4a7ae --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderDocumentOutline.h @@ -0,0 +1,47 @@ +// +// ReaderDocumentOutline.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2012-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface ReaderDocumentOutline : NSObject + ++ (NSArray *)outlineFromFileURL:(NSURL *)fileURL password:(NSString *)phrase; + ++ (void)logDocumentOutlineArray:(NSArray *)array; + +@end + +@interface DocumentOutlineEntry : NSObject + ++ (id)newWithTitle:(NSString *)title target:(id)target level:(NSInteger)level; + +- (id)initWithTitle:(NSString *)title target:(id)target level:(NSInteger)level; + +@property (nonatomic, assign, readonly) NSInteger level; +@property (nonatomic, strong, readwrite) NSMutableArray *children; +@property (nonatomic, strong, readonly) NSString *title; +@property (nonatomic, strong, readonly) id target; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderDocumentOutline.m b/Night Reader/Night Reader/Night Reader/ReaderDocumentOutline.m new file mode 100644 index 0000000..149eb65 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderDocumentOutline.m @@ -0,0 +1,467 @@ +// +// ReaderDocumentOutline.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2012-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderDocumentOutline.h" +#import "CGPDFDocument.h" + +@implementation ReaderDocumentOutline + +#pragma mark Build option flags + +#define HIERARCHICAL_OUTLINE TRUE + +#pragma mark ReaderDocumentOutline functions + +void logDictionaryEntry(const char *key, CGPDFObjectRef object, void *info) +{ + //CGPDFDictionaryApplyFunction(dictionary, logDictionaryEntry, NULL); + + NSString *kind = nil; // CGPDFObject type + + CGPDFObjectType type = CGPDFObjectGetType(object); + + switch (type) // CGPDFObjectTypes + { + case kCGPDFObjectTypeNull: + kind = @"CGPDFObjectTypeNull"; + NSLog(@"%s %@", key, kind); + break; + + case kCGPDFObjectTypeBoolean: + kind = @"CGPDFObjectTypeBoolean"; + NSLog(@"%s %@", key, kind); + break; + + case kCGPDFObjectTypeInteger: + kind = @"CGPDFObjectTypeInteger"; + NSLog(@"%s %@", key, kind); + break; + + case kCGPDFObjectTypeReal: + kind = @"CGPDFObjectTypeReal"; + NSLog(@"%s %@", key, kind); + break; + + case kCGPDFObjectTypeName: + { + kind = @"CGPDFObjectTypeName"; const char *pdfName = NULL; + + if (CGPDFObjectGetValue(object, kCGPDFObjectTypeName, &pdfName)) + { + if (pdfName != NULL) NSLog(@"%s %@ %s", key, kind, pdfName); + } + break; + } + + case kCGPDFObjectTypeString: + { + kind = @"CGPDFObjectTypeString"; CGPDFStringRef pdfString = NULL; + + if (CGPDFObjectGetValue(object, kCGPDFObjectTypeString, &pdfString)) + { + const unsigned char *string = CGPDFStringGetBytePtr(pdfString); + + if (string != NULL) NSLog(@"%s %@ %s", key, kind, string); + } + break; + } + + case kCGPDFObjectTypeArray: + kind = @"CGPDFObjectTypeArray"; + NSLog(@"%s %@", key, kind); + break; + + case kCGPDFObjectTypeDictionary: + kind = @"CGPDFObjectTypeDictionary"; + NSLog(@"%s %@", key, kind); + break; + + case kCGPDFObjectTypeStream: + kind = @"CGPDFObjectTypeStream"; + NSLog(@"%s %@", key, kind); + break; + } +} + +#pragma mark ReaderDocumentOutline class methods + ++ (void)logDocumentOutlineArray:(NSArray *)array +{ + for (DocumentOutlineEntry *item in array) // Enumerate array entries + { + NSInteger indent = (item.level * 2); // Indent amount for NSLog output + + NSLog(@"%@%@", [@"" stringByPaddingToLength:indent withString:@" " startingAtIndex:0], item); + + [self logDocumentOutlineArray:item.children]; // Log any child entries + } +} + ++ (CGPDFArrayRef)destinationWithName:(const char *)destinationName inDestsTree:(CGPDFDictionaryRef)node +{ + CGPDFArrayRef destinationArray = NULL; + + CGPDFArrayRef limitsArray = NULL; // Limits array + + if (CGPDFDictionaryGetArray(node, "Limits", &limitsArray) == true) + { + CGPDFStringRef lowerLimit = NULL; CGPDFStringRef upperLimit = NULL; + + if (CGPDFArrayGetString(limitsArray, 0, &lowerLimit) == true) // Lower limit + { + if (CGPDFArrayGetString(limitsArray, 1, &upperLimit) == true) // Upper limit + { + const char *ll = (const char *)CGPDFStringGetBytePtr(lowerLimit); // Lower string + const char *ul = (const char *)CGPDFStringGetBytePtr(upperLimit); // Upper string + + if ((strcmp(destinationName, ll) < 0) || (strcmp(destinationName, ul) > 0)) + { + return NULL; // Destination name is outside this node's limits + } + } + } + } + + CGPDFArrayRef namesArray = NULL; // Names array + + if (CGPDFDictionaryGetArray(node, "Names", &namesArray) == true) + { + NSInteger namesCount = CGPDFArrayGetCount(namesArray); + + for (NSInteger index = 0; index < namesCount; index += 2) + { + CGPDFStringRef destName; // Destination name string + + if (CGPDFArrayGetString(namesArray, index, &destName) == true) + { + const char *dn = (const char *)CGPDFStringGetBytePtr(destName); + + if (strcmp(dn, destinationName) == 0) // Found the destination name + { + if (CGPDFArrayGetArray(namesArray, (index + 1), &destinationArray) == false) + { + CGPDFDictionaryRef destinationDictionary = NULL; // Destination dictionary + + if (CGPDFArrayGetDictionary(namesArray, (index + 1), &destinationDictionary) == true) + { + CGPDFDictionaryGetArray(destinationDictionary, "D", &destinationArray); + } + } + + return destinationArray; // Return the destination array + } + } + } + } + + CGPDFArrayRef kidsArray = NULL; // Kids array + + if (CGPDFDictionaryGetArray(node, "Kids", &kidsArray) == true) + { + NSInteger kidsCount = CGPDFArrayGetCount(kidsArray); + + for (NSInteger index = 0; index < kidsCount; index++) + { + CGPDFDictionaryRef kidNode = NULL; // Kid node dictionary + + if (CGPDFArrayGetDictionary(kidsArray, index, &kidNode) == true) // Recurse into node + { + destinationArray = [self destinationWithName:destinationName inDestsTree:kidNode]; + + if (destinationArray != NULL) return destinationArray; // Return destination array + } + } + } + + return NULL; +} + ++ (id)outlineEntryTarget:(CGPDFDictionaryRef)outlineDictionary document:(CGPDFDocumentRef)document +{ + id entryTarget = nil; // Entry target object + + CGPDFStringRef destName = NULL; const char *destString = NULL; + + CGPDFDictionaryRef actionDictionary = NULL; CGPDFArrayRef destArray = NULL; + + if (CGPDFDictionaryGetDictionary(outlineDictionary, "A", &actionDictionary) == true) + { + const char *actionType = NULL; // Outline entry action type string + + if (CGPDFDictionaryGetName(actionDictionary, "S", &actionType) == true) + { + if (strcmp(actionType, "GoTo") == 0) // GoTo action type + { + if (CGPDFDictionaryGetArray(actionDictionary, "D", &destArray) == false) + { + CGPDFDictionaryGetString(actionDictionary, "D", &destName); + } + } + else // Handle other entry action type possibility + { + if (strcmp(actionType, "URI") == 0) // URI action type + { + CGPDFStringRef uriString = NULL; // Action's URI string + + if (CGPDFDictionaryGetString(actionDictionary, "URI", &uriString) == true) + { + const char *uri = (const char *)CGPDFStringGetBytePtr(uriString); // Destination URI string + + entryTarget = [NSURL URLWithString:[NSString stringWithCString:uri encoding:NSASCIIStringEncoding]]; + } + } + } + } + } + else // Handle other entry target possibilities + { + if (CGPDFDictionaryGetArray(outlineDictionary, "Dest", &destArray) == false) + { + if (CGPDFDictionaryGetString(outlineDictionary, "Dest", &destName) == false) + { + CGPDFDictionaryGetName(outlineDictionary, "Dest", &destString); + } + } + } + + if (destName != NULL) // Handle a destination name + { + CGPDFDictionaryRef catalogDictionary = CGPDFDocumentGetCatalog(document); + + CGPDFDictionaryRef namesDictionary = NULL; // Destination names in the document + + if (CGPDFDictionaryGetDictionary(catalogDictionary, "Names", &namesDictionary) == true) + { + CGPDFDictionaryRef destsDictionary = NULL; // Document destinations dictionary + + if (CGPDFDictionaryGetDictionary(namesDictionary, "Dests", &destsDictionary) == true) + { + const char *destinationName = (const char *)CGPDFStringGetBytePtr(destName); // Name + + destArray = [self destinationWithName:destinationName inDestsTree:destsDictionary]; + } + } + } + + if (destString != NULL) // Handle a destination string + { + CGPDFDictionaryRef catalogDictionary = CGPDFDocumentGetCatalog(document); + + CGPDFDictionaryRef destsDictionary = NULL; // Document destinations dictionary + + if (CGPDFDictionaryGetDictionary(catalogDictionary, "Dests", &destsDictionary) == true) + { + CGPDFDictionaryRef targetDictionary = NULL; // Destination target dictionary + + if (CGPDFDictionaryGetDictionary(destsDictionary, destString, &targetDictionary) == true) + { + CGPDFDictionaryGetArray(targetDictionary, "D", &destArray); + } + } + } + + if (destArray != NULL) // Handle a destination array + { + NSInteger targetPageNumber = 0; // The target page number + + CGPDFDictionaryRef pageDictionaryFromDestArray = NULL; // Target reference + + if (CGPDFArrayGetDictionary(destArray, 0, &pageDictionaryFromDestArray) == true) + { + NSInteger pageCount = CGPDFDocumentGetNumberOfPages(document); // Pages + + for (NSInteger pageNumber = 1; pageNumber <= pageCount; pageNumber++) + { + CGPDFPageRef pageRef = CGPDFDocumentGetPage(document, pageNumber); + + CGPDFDictionaryRef pageDictionaryFromPage = CGPDFPageGetDictionary(pageRef); + + if (pageDictionaryFromPage == pageDictionaryFromDestArray) // Found it + { + targetPageNumber = pageNumber; break; + } + } + } + else // Try page number from array possibility + { + CGPDFInteger pageNumber = 0; // Page number in array + + if (CGPDFArrayGetInteger(destArray, 0, &pageNumber) == true) + { + targetPageNumber = (pageNumber + 1); // 1-based + } + } + + if (targetPageNumber > 0) // We have a target page number + { + entryTarget = [NSNumber numberWithInteger:targetPageNumber]; + } + } + + return entryTarget; +} + ++ (void)outlineItems:(CGPDFDictionaryRef)outlineDictionary document:(CGPDFDocumentRef)document array:(NSMutableArray *)array level:(NSInteger)level +{ + do // Loop through current level outline entries + { + DocumentOutlineEntry *outlineEntry = nil; // An entry + + CGPDFStringRef string = NULL; // Outline entry title string + + if (CGPDFDictionaryGetString(outlineDictionary, "Title", &string) == true) + { + CFStringRef title = NULL; // Actual outline title (CFObject) string + + if ((title = CGPDFStringCopyTextString(string)) != NULL) // Copy of CFObject string + { + NSString *titleString = (__bridge NSString *)title; // CFString to NSString toll-free bridge cast + + id entryTarget = [self outlineEntryTarget:outlineDictionary document:document]; // Get target object + + NSString *trimmed = [titleString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + outlineEntry = [DocumentOutlineEntry newWithTitle:trimmed target:entryTarget level:level]; // New entry + + [array addObject:outlineEntry]; CFRelease(title); // Add new entry and cleanup + } + } + + if (outlineEntry != nil) // Must have a current outline entry + { + CGPDFDictionaryRef childItem = NULL; // First child outline item + + if (CGPDFDictionaryGetDictionary(outlineDictionary, "First", &childItem) == true) + { +#if (HIERARCHICAL_OUTLINE == TRUE) // Option + NSMutableArray *childArray = [NSMutableArray array]; outlineEntry.children = childArray; + + [self outlineItems:childItem document:document array:childArray level:(level + 1)]; +#else // Flat + [self outlineItems:childItem document:document array:array level:(level + 1)]; + +#endif // end of HIERARCHICAL_OUTLINE Option + } + } + + } while (CGPDFDictionaryGetDictionary(outlineDictionary, "Next", &outlineDictionary) == true); +} + ++ (NSArray *)outlineFromFileURL:(NSURL *)fileURL password:(NSString *)phrase +{ + NSMutableArray *outlineArray = nil; // Mutable outline array + + if ((fileURL != nil) && [fileURL isFileURL]) // Check for valid file URL + { + CGPDFDocumentRef document = CGPDFDocumentCreateX((__bridge CFURLRef)fileURL, phrase); + + if (document != NULL) // Check for non-NULL CGPDFDocumentRef + { + CGPDFDictionaryRef outlines = NULL; // Document's outlines + + CGPDFDictionaryRef catalog = CGPDFDocumentGetCatalog(document); + + if (CGPDFDictionaryGetDictionary(catalog, "Outlines", &outlines) == true) + { + CGPDFDictionaryRef firstItem = NULL; // First outline item entry + + if (CGPDFDictionaryGetDictionary(outlines, "First", &firstItem) == true) + { + outlineArray = [NSMutableArray array]; // Top level outline entries array + + [self outlineItems:firstItem document:document array:outlineArray level:0]; + } + } + + CGPDFDocumentRelease(document); // Cleanup + } + } + + //[self logDocumentOutlineArray:outlineArray]; // Log it + + return [outlineArray copy]; // NSArray +} + +@end + +#pragma mark - + +// +// DocumentOutlineEntry class implementation +// + +@interface DocumentOutlineEntry () + +@property (nonatomic, assign, readwrite) NSInteger level; +@property (nonatomic, strong, readwrite) NSString *title; +@property (nonatomic, strong, readwrite) id target; + +@end + +@implementation DocumentOutlineEntry +{ + NSInteger _level; + + NSMutableArray *_children; + + NSString *_title; + + id _target; +} + +#pragma mark Properties + +@synthesize level = _level; +@synthesize children = _children; +@synthesize target = _target; +@synthesize title = _title; + +#pragma mark DocumentOutlineEntry class methods + ++ (id)newWithTitle:(NSString *)title target:(id)target level:(NSInteger)level +{ + return [[DocumentOutlineEntry alloc] initWithTitle:title target:target level:level]; +} + +#pragma mark DocumentOutlineEntry instance methods + +- (id)initWithTitle:(NSString *)title target:(id)target level:(NSInteger)level +{ + if ((self = [super init])) + { + self.title = title; self.target = target; self.level = level; + } + + return self; +} + +- (NSString *)description +{ + NSString *format = @"%@ Title = '%@', Target = '%@', Level = (%i)"; + + return [NSString stringWithFormat:format, [super description], _title, _target, _level]; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderMainPagebar.h b/Night Reader/Night Reader/Night Reader/ReaderMainPagebar.h new file mode 100644 index 0000000..741c9cf --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderMainPagebar.h @@ -0,0 +1,88 @@ +// +// ReaderMainPagebar.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "ReaderThumbView.h" + +@class ReaderMainPagebar; +@class ReaderTrackControl; +@class ReaderPagebarThumb; +@class ReaderDocument; + +@protocol ReaderMainPagebarDelegate + +@required // Delegate protocols + +- (void)pagebar:(ReaderMainPagebar *)pagebar gotoPage:(NSInteger)page; + +@end + +@interface ReaderMainPagebar : UIView + +@property (nonatomic, unsafe_unretained, readwrite) id delegate; + +- (id)initWithFrame:(CGRect)frame document:(ReaderDocument *)object; + +- (void)updatePagebar; + +- (void)hidePagebar; +- (void)showPagebar; + +@end + +#pragma mark - + +// +// ReaderTrackControl class interface +// + +@interface ReaderTrackControl : UIControl + +@property (nonatomic, assign, readonly) CGFloat value; + +@end + +#pragma mark - + +// +// ReaderPagebarThumb class interface +// + +@interface ReaderPagebarThumb : ReaderThumbView + +- (id)initWithFrame:(CGRect)frame small:(BOOL)small; + +@end + +#pragma mark - + +// +// ReaderPagebarShadow class interface +// + +@interface ReaderPagebarShadow : UIView + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderMainPagebar.m b/Night Reader/Night Reader/Night Reader/ReaderMainPagebar.m new file mode 100644 index 0000000..70f2b7a --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderMainPagebar.m @@ -0,0 +1,625 @@ +// +// ReaderMainPagebar.m +// Reader v2.6.2 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderMainPagebar.h" +#import "ReaderThumbCache.h" +#import "ReaderDocument.h" + +#import + +@implementation ReaderMainPagebar +{ + ReaderDocument *document; + + ReaderTrackControl *trackControl; + + NSMutableDictionary *miniThumbViews; + + ReaderPagebarThumb *pageThumbView; + + UILabel *pageNumberLabel; + + UIView *pageNumberView; + + NSTimer *enableTimer; + NSTimer *trackTimer; +} + +#pragma mark Constants + +#define THUMB_SMALL_GAP 2 +#define THUMB_SMALL_WIDTH 22 +#define THUMB_SMALL_HEIGHT 28 + +#define THUMB_LARGE_WIDTH 32 +#define THUMB_LARGE_HEIGHT 42 + +#define PAGE_NUMBER_WIDTH 96.0f +#define PAGE_NUMBER_HEIGHT 30.0f +#define PAGE_NUMBER_SPACE 20.0f + +#pragma mark Properties + +@synthesize delegate; + +#pragma mark ReaderMainPagebar class methods + ++ (Class)layerClass +{ + return [CAGradientLayer class]; +} + +#pragma mark ReaderMainPagebar instance methods + +- (id)initWithFrame:(CGRect)frame +{ + return [self initWithFrame:frame document:nil]; +} + +- (void)updatePageThumbView:(NSInteger)page +{ + NSInteger pages = [document.pageCount integerValue]; + + if (pages > 1) // Only update frame if more than one page + { + CGFloat controlWidth = trackControl.bounds.size.width; + + CGFloat useableWidth = (controlWidth - THUMB_LARGE_WIDTH); + + CGFloat stride = (useableWidth / (pages - 1)); // Page stride + + NSInteger X = (stride * (page - 1)); CGFloat pageThumbX = X; + + CGRect pageThumbRect = pageThumbView.frame; // Current frame + + if (pageThumbX != pageThumbRect.origin.x) // Only if different + { + pageThumbRect.origin.x = pageThumbX; // The new X position + + pageThumbView.frame = pageThumbRect; // Update the frame + } + } + + if (page != pageThumbView.tag) // Only if page number changed + { + pageThumbView.tag = page; [pageThumbView reuse]; // Reuse the thumb view + + CGSize size = CGSizeMake(THUMB_LARGE_WIDTH, THUMB_LARGE_HEIGHT); // Maximum thumb size + + NSURL *fileURL = document.fileURL; NSString *guid = document.guid; NSString *phrase = document.password; + + ReaderThumbRequest *request = [ReaderThumbRequest newForView:pageThumbView fileURL:fileURL password:phrase guid:guid page:page size:size]; + + UIImage *image = [[ReaderThumbCache sharedInstance] thumbRequest:request priority:YES]; // Request the thumb + + UIImage *thumb = ([image isKindOfClass:[UIImage class]] ? image : nil); [pageThumbView showImage:thumb]; + } +} + +- (void)updatePageNumberText:(NSInteger)page +{ + if (page != pageNumberLabel.tag) // Only if page number changed + { + NSInteger pages = [document.pageCount integerValue]; // Total pages + + NSString *format = NSLocalizedString(@"%d of %d", @"format"); // Format + + NSString *number = [NSString stringWithFormat:format, page, pages]; // Text + + pageNumberLabel.text = number; // Update the page number label text + + pageNumberLabel.tag = page; // Update the last page number tag + } +} + +- (id)initWithFrame:(CGRect)frame document:(ReaderDocument *)object +{ + assert(object != nil); // Must have a valid ReaderDocument + + + + UIColor *DarkNightColor=[UIColor colorWithRed:70 green:0 blue:0 alpha:0.3]; + + + + + + + + if ((self = [super initWithFrame:frame])) + { + self.autoresizesSubviews = YES; + self.userInteractionEnabled = YES; + self.contentMode = UIViewContentModeRedraw; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + self.backgroundColor = [UIColor blackColor]; + + CAGradientLayer *layer = (CAGradientLayer *)self.layer; + UIColor *liteColor = DarkNightColor; + UIColor *darkColor = DarkNightColor; + layer.colors = [NSArray arrayWithObjects:(id)liteColor.CGColor, (id)darkColor.CGColor, nil]; + + // CGRect shadowRect = self.bounds; shadowRect.size.height = 4.0f; shadowRect.origin.y -= shadowRect.size.height; + + // ReaderPagebarShadow *shadowView = [[ReaderPagebarShadow alloc] initWithFrame:shadowRect]; + + // [self addSubview:shadowView]; // Add the shadow to the view + + CGFloat numberY = (0.0f - (PAGE_NUMBER_HEIGHT + PAGE_NUMBER_SPACE)); + CGFloat numberX = ((self.bounds.size.width - PAGE_NUMBER_WIDTH) / 2.0f); + CGRect numberRect = CGRectMake(numberX, numberY, PAGE_NUMBER_WIDTH, PAGE_NUMBER_HEIGHT); + + pageNumberView = [[UIView alloc] initWithFrame:numberRect]; // Page numbers view + + pageNumberView.autoresizesSubviews = NO; + pageNumberView.userInteractionEnabled = NO; + pageNumberView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + pageNumberView.backgroundColor = [UIColor blackColor]; + + pageNumberView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f); + pageNumberView.layer.shadowColor = [UIColor colorWithWhite:0.0f alpha:0.6f].CGColor; + pageNumberView.layer.shadowPath = [UIBezierPath bezierPathWithRect:pageNumberView.bounds].CGPath; + pageNumberView.layer.shadowRadius = 2.0f; pageNumberView.layer.shadowOpacity = 1.0f; + + CGRect textRect = CGRectInset(pageNumberView.bounds, 4.0f, 2.0f); // Inset the text a bit + + pageNumberLabel = [[UILabel alloc] initWithFrame:textRect]; // Page numbers label + + pageNumberLabel.autoresizesSubviews = NO; + pageNumberLabel.autoresizingMask = UIViewAutoresizingNone; + pageNumberLabel.textAlignment = NSTextAlignmentCenter; + pageNumberLabel.backgroundColor = [UIColor clearColor]; + pageNumberLabel.textColor = [UIColor whiteColor]; + pageNumberLabel.font = [UIFont systemFontOfSize:16.0f]; + pageNumberLabel.shadowOffset = CGSizeMake(0.0f, 1.0f); + pageNumberLabel.shadowColor = [UIColor blackColor]; + pageNumberLabel.adjustsFontSizeToFitWidth = YES; + pageNumberLabel.MinimumScaleFactor = 12.0f; + + [pageNumberView addSubview:pageNumberLabel]; // Add label view + + [self addSubview:pageNumberView]; // Add page numbers display view + + trackControl = [[ReaderTrackControl alloc] initWithFrame:self.bounds]; // Track control view + + [trackControl addTarget:self action:@selector(trackViewTouchDown:) forControlEvents:UIControlEventTouchDown]; + [trackControl addTarget:self action:@selector(trackViewValueChanged:) forControlEvents:UIControlEventValueChanged]; + [trackControl addTarget:self action:@selector(trackViewTouchUp:) forControlEvents:UIControlEventTouchUpOutside]; + [trackControl addTarget:self action:@selector(trackViewTouchUp:) forControlEvents:UIControlEventTouchUpInside]; + + [self addSubview:trackControl]; // Add the track control and thumbs view + + document = object; // Retain the document object for our use + + [self updatePageNumberText:[document.pageNumber integerValue]]; + + miniThumbViews = [NSMutableDictionary new]; // Small thumbs + } + + return self; +} + +- (void)removeFromSuperview +{ + [trackTimer invalidate]; [enableTimer invalidate]; + + [super removeFromSuperview]; +} + +- (void)layoutSubviews +{ + CGRect controlRect = CGRectInset(self.bounds, 4.0f, 0.0f); + + CGFloat thumbWidth = (THUMB_SMALL_WIDTH + THUMB_SMALL_GAP); + + NSInteger thumbs = (controlRect.size.width / thumbWidth); + + NSInteger pages = [document.pageCount integerValue]; // Pages + + if (thumbs > pages) thumbs = pages; // No more than total pages + + CGFloat controlWidth = ((thumbs * thumbWidth) - THUMB_SMALL_GAP); + + controlRect.size.width = controlWidth; // Update control width + + CGFloat widthDelta = (self.bounds.size.width - controlWidth); + + NSInteger X = (widthDelta / 2.0f); controlRect.origin.x = X; + + trackControl.frame = controlRect; // Update track control frame + + if (pageThumbView == nil) // Create the page thumb view when needed + { + CGFloat heightDelta = (controlRect.size.height - THUMB_LARGE_HEIGHT); + + NSInteger thumbY = (heightDelta / 2.0f); NSInteger thumbX = 0; // Thumb X, Y + + CGRect thumbRect = CGRectMake(thumbX, thumbY, THUMB_LARGE_WIDTH, THUMB_LARGE_HEIGHT); + + pageThumbView = [[ReaderPagebarThumb alloc] initWithFrame:thumbRect]; // Create the thumb view + + pageThumbView.layer.zPosition = 1.0f; // Z position so that it sits on top of the small thumbs + + [trackControl addSubview:pageThumbView]; // Add as the first subview of the track control + } + + [self updatePageThumbView:[document.pageNumber integerValue]]; // Update page thumb view + + NSInteger strideThumbs = (thumbs - 1); if (strideThumbs < 1) strideThumbs = 1; + + CGFloat stride = ((CGFloat)pages / (CGFloat)strideThumbs); // Page stride + + CGFloat heightDelta = (controlRect.size.height - THUMB_SMALL_HEIGHT); + + NSInteger thumbY = (heightDelta / 2.0f); NSInteger thumbX = 0; // Initial X, Y + + CGRect thumbRect = CGRectMake(thumbX, thumbY, THUMB_SMALL_WIDTH, THUMB_SMALL_HEIGHT); + + NSMutableDictionary *thumbsToHide = [miniThumbViews mutableCopy]; + + for (NSInteger thumb = 0; thumb < thumbs; thumb++) // Iterate through needed thumbs + { + NSInteger page = ((stride * thumb) + 1); if (page > pages) page = pages; // Page + + NSNumber *key = [NSNumber numberWithInteger:page]; // Page number key for thumb view + + ReaderPagebarThumb *smallThumbView = [miniThumbViews objectForKey:key]; // Thumb view + + if (smallThumbView == nil) // We need to create a new small thumb view for the page number + { + CGSize size = CGSizeMake(THUMB_SMALL_WIDTH, THUMB_SMALL_HEIGHT); // Maximum thumb size + + NSURL *fileURL = document.fileURL; NSString *guid = document.guid; NSString *phrase = document.password; + + smallThumbView = [[ReaderPagebarThumb alloc] initWithFrame:thumbRect small:YES]; // Create a small thumb view + + ReaderThumbRequest *thumbRequest = [ReaderThumbRequest newForView:smallThumbView fileURL:fileURL password:phrase guid:guid page:page size:size]; + + UIImage *image = [[ReaderThumbCache sharedInstance] thumbRequest:thumbRequest priority:NO]; // Request the thumb + + if ([image isKindOfClass:[UIImage class]]) [smallThumbView showImage:image]; // Use thumb image from cache + + [trackControl addSubview:smallThumbView]; [miniThumbViews setObject:smallThumbView forKey:key]; + } + else // Resue existing small thumb view for the page number + { + smallThumbView.hidden = NO; [thumbsToHide removeObjectForKey:key]; + + if (CGRectEqualToRect(smallThumbView.frame, thumbRect) == false) + { + smallThumbView.frame = thumbRect; // Update thumb frame + } + } + + thumbRect.origin.x += thumbWidth; // Next thumb X position + } + + [thumbsToHide enumerateKeysAndObjectsUsingBlock: // Hide unused thumbs + ^(id key, id object, BOOL *stop) + { + ReaderPagebarThumb *thumb = object; thumb.hidden = YES; + } + ]; +} + +- (void)updatePagebarViews +{ + NSInteger page = [document.pageNumber integerValue]; // # + + [self updatePageNumberText:page]; // Update page number text + + [self updatePageThumbView:page]; // Update page thumb view +} + +- (void)updatePagebar +{ + if (self.hidden == NO) // Only if visible + { + [self updatePagebarViews]; // Update views + } +} + +- (void)hidePagebar +{ + if (self.hidden == NO) // Only if visible + { + [UIView animateWithDuration:0.25 delay:0.0 + options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction + animations:^(void) + { + self.alpha = 0.0f; + } + completion:^(BOOL finished) + { + self.hidden = YES; + } + ]; + } +} + +- (void)showPagebar +{ + if (self.hidden == YES) // Only if hidden + { + [self updatePagebarViews]; // Update views first + + [UIView animateWithDuration:0.25 delay:0.0 + options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction + animations:^(void) + { + self.hidden = NO; + self.alpha = 1.0f; + } + completion:NULL + ]; + } +} + +#pragma mark ReaderTrackControl action methods + +- (void)trackTimerFired:(NSTimer *)timer +{ + [trackTimer invalidate]; trackTimer = nil; // Cleanup timer + + if (trackControl.tag != [document.pageNumber integerValue]) // Only if different + { + [delegate pagebar:self gotoPage:trackControl.tag]; // Go to document page + } +} + +- (void)enableTimerFired:(NSTimer *)timer +{ + [enableTimer invalidate]; enableTimer = nil; // Cleanup timer + + trackControl.userInteractionEnabled = YES; // Enable track control interaction +} + +- (void)restartTrackTimer +{ + if (trackTimer != nil) { [trackTimer invalidate]; trackTimer = nil; } // Invalidate and release previous timer + + trackTimer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(trackTimerFired:) userInfo:nil repeats:NO]; +} + +- (void)startEnableTimer +{ + if (enableTimer != nil) { [enableTimer invalidate]; enableTimer = nil; } // Invalidate and release previous timer + + enableTimer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(enableTimerFired:) userInfo:nil repeats:NO]; +} + +- (NSInteger)trackViewPageNumber:(ReaderTrackControl *)trackView +{ + CGFloat controlWidth = trackView.bounds.size.width; // View width + + CGFloat stride = (controlWidth / [document.pageCount integerValue]); + + NSInteger page = (trackView.value / stride); // Integer page number + + return (page + 1); // + 1 +} + +- (void)trackViewTouchDown:(ReaderTrackControl *)trackView +{ + NSInteger page = [self trackViewPageNumber:trackView]; // Page + + if (page != [document.pageNumber integerValue]) // Only if different + { + [self updatePageNumberText:page]; // Update page number text + + [self updatePageThumbView:page]; // Update page thumb view + + [self restartTrackTimer]; // Start the track timer + } + + trackView.tag = page; // Start page tracking +} + +- (void)trackViewValueChanged:(ReaderTrackControl *)trackView +{ + NSInteger page = [self trackViewPageNumber:trackView]; // Page + + if (page != trackView.tag) // Only if the page number has changed + { + [self updatePageNumberText:page]; // Update page number text + + [self updatePageThumbView:page]; // Update page thumb view + + trackView.tag = page; // Update the page tracking tag + + [self restartTrackTimer]; // Restart the track timer + } +} + +- (void)trackViewTouchUp:(ReaderTrackControl *)trackView +{ + [trackTimer invalidate]; trackTimer = nil; // Cleanup + + if (trackView.tag != [document.pageNumber integerValue]) // Only if different + { + trackView.userInteractionEnabled = NO; // Disable track control interaction + + [delegate pagebar:self gotoPage:trackView.tag]; // Go to document page + + [self startEnableTimer]; // Start track control enable timer + } + + trackView.tag = 0; // Reset page tracking +} + +@end + +#pragma mark - + +// +// ReaderTrackControl class implementation +// + +@implementation ReaderTrackControl +{ + CGFloat _value; +} + +#pragma mark Properties + +@synthesize value = _value; + +#pragma mark ReaderTrackControl instance methods + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) + { + self.autoresizesSubviews = NO; + self.userInteractionEnabled = YES; + self.contentMode = UIViewContentModeRedraw; + self.autoresizingMask = UIViewAutoresizingNone; + self.backgroundColor = [UIColor clearColor]; + self.exclusiveTouch = YES; + } + + return self; +} + +- (CGFloat)limitValue:(CGFloat)valueX +{ + CGFloat minX = self.bounds.origin.x; // 0.0f; + CGFloat maxX = (self.bounds.size.width - 1.0f); + + if (valueX < minX) valueX = minX; // Minimum X + if (valueX > maxX) valueX = maxX; // Maximum X + + return valueX; +} + +#pragma mark UIControl subclass methods + +- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event +{ + CGPoint point = [touch locationInView:self]; // Touch point + + _value = [self limitValue:point.x]; // Limit control value + + return YES; +} + +- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event +{ + if (self.touchInside == YES) // Only if inside the control + { + CGPoint point = [touch locationInView:touch.view]; // Touch point + + CGFloat x = [self limitValue:point.x]; // Potential new control value + + if (x != _value) // Only if the new value has changed since the last time + { + _value = x; [self sendActionsForControlEvents:UIControlEventValueChanged]; + } + } + + return YES; +} + +- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event +{ + CGPoint point = [touch locationInView:self]; // Touch point + + _value = [self limitValue:point.x]; // Limit control value +} + +@end + +#pragma mark - + +// +// ReaderPagebarThumb class implementation +// + +@implementation ReaderPagebarThumb + +#pragma mark ReaderPagebarThumb instance methods + +- (id)initWithFrame:(CGRect)frame +{ + return [self initWithFrame:frame small:NO]; +} + +- (id)initWithFrame:(CGRect)frame small:(BOOL)small +{ + if ((self = [super initWithFrame:frame])) // Superclass init + { + CGFloat value = (small ? 0.6f : 0.7f); // Size based alpha value + + UIColor *background = [UIColor colorWithWhite:0.8f alpha:value]; + + self.backgroundColor = background; imageView.backgroundColor = background; + + imageView.layer.borderColor = [UIColor colorWithWhite:0.4f alpha:0.6f].CGColor; + + imageView.layer.borderWidth = 1.0f; // Give the thumb image view a border + } + + return self; +} + +@end + +#pragma mark - + +// +// ReaderPagebarShadow class implementation +// + +@implementation ReaderPagebarShadow + +#pragma mark ReaderPagebarShadow class methods + ++ (Class)layerClass +{ + return [CAGradientLayer class]; +} + +#pragma mark ReaderPagebarShadow instance methods + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) + { + self.autoresizesSubviews = NO; + self.userInteractionEnabled = NO; + self.contentMode = UIViewContentModeRedraw; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth; + self.backgroundColor = [UIColor clearColor]; + + CAGradientLayer *layer = (CAGradientLayer *)self.layer; + UIColor *blackColor = [UIColor colorWithWhite:0.42f alpha:1.0f]; + UIColor *clearColor = [UIColor colorWithWhite:0.42f alpha:0.0f]; + layer.colors = [NSArray arrayWithObjects:(id)clearColor.CGColor, (id)blackColor.CGColor, nil]; + } + + return self; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderMainToolbar.h b/Night Reader/Night Reader/Night Reader/ReaderMainToolbar.h new file mode 100644 index 0000000..9ca51a6 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderMainToolbar.h @@ -0,0 +1,56 @@ +// +// ReaderMainToolbar.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "UIXToolbarView.h" + +@class ReaderMainToolbar; +@class ReaderDocument; + +@protocol ReaderMainToolbarDelegate + +@required // Delegate protocols + +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar doneButton:(UIButton *)button; +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar thumbsButton:(UIButton *)button; +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar printButton:(UIButton *)button; +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar emailButton:(UIButton *)button; +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar markButton:(UIButton *)button; + +@end + +@interface ReaderMainToolbar : UIXToolbarView + +@property (nonatomic, unsafe_unretained, readwrite) id delegate; + +- (id)initWithFrame:(CGRect)frame document:(ReaderDocument *)object; + +- (void)setBookmarkState:(BOOL)state; + +- (void)hideToolbar; +- (void)showToolbar; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderMainToolbar.m b/Night Reader/Night Reader/Night Reader/ReaderMainToolbar.m new file mode 100644 index 0000000..3a5b43c --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderMainToolbar.m @@ -0,0 +1,334 @@ +// +// ReaderMainToolbar.m +// Reader v2.6.2 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderConstants.h" +#import "ReaderMainToolbar.h" +#import "ReaderDocument.h" + +#import + +@implementation ReaderMainToolbar +{ + UIButton *markButton; + + UIImage *markImageN; + UIImage *markImageY; +} + +#pragma mark Constants + +#define BUTTON_X 8.0f +#define BUTTON_Y 8.0f +#define BUTTON_SPACE 8.0f +#define BUTTON_HEIGHT 30.0f + +#define DONE_BUTTON_WIDTH 56.0f +#define THUMBS_BUTTON_WIDTH 40.0f +#define PRINT_BUTTON_WIDTH 40.0f +#define EMAIL_BUTTON_WIDTH 40.0f +#define MARK_BUTTON_WIDTH 40.0f + +#define TITLE_HEIGHT 28.0f + +#pragma mark Properties + +@synthesize delegate; + +#pragma mark ReaderMainToolbar instance methods + +- (id)initWithFrame:(CGRect)frame +{ + return [self initWithFrame:frame document:nil]; +} + +- (id)initWithFrame:(CGRect)frame document:(ReaderDocument *)object +{ + assert(object != nil); // Must have a valid ReaderDocument + + if ((self = [super initWithFrame:frame])) + { + CGFloat viewWidth = self.bounds.size.width; + + UIImage *imageH = [UIImage imageNamed:@"Reader-Button-H"]; + UIImage *imageN = [UIImage imageNamed:@"Reader-Button-N"]; + + UIImage *buttonH = [imageH stretchableImageWithLeftCapWidth:5 topCapHeight:0]; + UIImage *buttonN = [imageN stretchableImageWithLeftCapWidth:5 topCapHeight:0]; + + CGFloat titleX = BUTTON_X; CGFloat titleWidth = (viewWidth - (titleX + titleX)); + + CGFloat leftButtonX = BUTTON_X; // Left button start X position + +#if (READER_STANDALONE == FALSE) // Option + + UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeCustom]; + UIColor *NightColor=[UIColor colorWithRed:70 green:0 blue:0 alpha:0.8]; + + // UIColor *DarkNightColor=[UIColor colorWithRed:70 green:0 blue:0 alpha:0.3]; + + doneButton.frame = CGRectMake(leftButtonX, BUTTON_Y, DONE_BUTTON_WIDTH, BUTTON_HEIGHT); + [doneButton setTitle:NSLocalizedString(@"Done", @"button") forState:UIControlStateNormal]; + [doneButton setTitleColor:NightColor forState:UIControlStateNormal]; + [doneButton setTitleColor:NightColor forState:UIControlStateHighlighted]; + [doneButton addTarget:self action:@selector(doneButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; + [doneButton setBackgroundImage:buttonH forState:UIControlStateHighlighted]; + [doneButton setBackgroundImage:buttonN forState:UIControlStateNormal]; + doneButton.titleLabel.font = [UIFont systemFontOfSize:14.0f]; + doneButton.autoresizingMask = UIViewAutoresizingNone; + doneButton.exclusiveTouch = YES; + + [self addSubview:doneButton]; leftButtonX += (DONE_BUTTON_WIDTH + BUTTON_SPACE); + + titleX += (DONE_BUTTON_WIDTH + BUTTON_SPACE); titleWidth -= (DONE_BUTTON_WIDTH + BUTTON_SPACE); + +#endif // end of READER_STANDALONE Option + +#if (READER_ENABLE_THUMBS == TRUE) // Option + + UIButton *thumbsButton = [UIButton buttonWithType:UIButtonTypeCustom]; + + thumbsButton.frame = CGRectMake(leftButtonX, BUTTON_Y, THUMBS_BUTTON_WIDTH, BUTTON_HEIGHT); + [thumbsButton setImage:[UIImage imageNamed:@"Reader-Thumbs"] forState:UIControlStateNormal]; + [thumbsButton addTarget:self action:@selector(thumbsButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; + [thumbsButton setBackgroundImage:buttonH forState:UIControlStateHighlighted]; + [thumbsButton setBackgroundImage:buttonN forState:UIControlStateNormal]; + thumbsButton.autoresizingMask = UIViewAutoresizingNone; + thumbsButton.exclusiveTouch = YES; + + [self addSubview:thumbsButton]; //leftButtonX += (THUMBS_BUTTON_WIDTH + BUTTON_SPACE); + + titleX += (THUMBS_BUTTON_WIDTH + BUTTON_SPACE); titleWidth -= (THUMBS_BUTTON_WIDTH + BUTTON_SPACE); + +#endif // end of READER_ENABLE_THUMBS Option + +#if (READER_BOOKMARKS == TRUE || READER_ENABLE_MAIL == TRUE || READER_ENABLE_PRINT == TRUE) + + CGFloat rightButtonX = viewWidth; // Right button start X position + +#endif // end of READER_BOOKMARKS || READER_ENABLE_MAIL || READER_ENABLE_PRINT Options + +#if (READER_BOOKMARKS == TRUE) // Option + + rightButtonX -= (MARK_BUTTON_WIDTH + BUTTON_SPACE); + + UIButton *flagButton = [UIButton buttonWithType:UIButtonTypeCustom]; + + flagButton.frame = CGRectMake(rightButtonX, BUTTON_Y, MARK_BUTTON_WIDTH, BUTTON_HEIGHT); + //[flagButton setImage:[UIImage imageNamed:@"Reader-Mark-N"] forState:UIControlStateNormal]; + [flagButton addTarget:self action:@selector(markButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; + [flagButton setBackgroundImage:buttonH forState:UIControlStateHighlighted]; + [flagButton setBackgroundImage:buttonN forState:UIControlStateNormal]; + flagButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + flagButton.exclusiveTouch = YES; + + [self addSubview:flagButton]; titleWidth -= (MARK_BUTTON_WIDTH + BUTTON_SPACE); + + markButton = flagButton; markButton.enabled = NO; markButton.tag = NSIntegerMin; + + markImageN = [UIImage imageNamed:@"Reader-Mark-N"]; // N image + markImageY = [UIImage imageNamed:@"Reader-Mark-Y"]; // Y image + +#endif // end of READER_BOOKMARKS Option + +#if (READER_ENABLE_MAIL == TRUE) // Option + + if ([MFMailComposeViewController canSendMail] == YES) // Can email + { + unsigned long long fileSize = [object.fileSize unsignedLongLongValue]; + + if (fileSize < (unsigned long long)15728640) // Check attachment size limit (15MB) + { + rightButtonX -= (EMAIL_BUTTON_WIDTH + BUTTON_SPACE); + + UIButton *emailButton = [UIButton buttonWithType:UIButtonTypeCustom]; + + emailButton.frame = CGRectMake(rightButtonX, BUTTON_Y, EMAIL_BUTTON_WIDTH, BUTTON_HEIGHT); + [emailButton setImage:[UIImage imageNamed:@"Reader-Email"] forState:UIControlStateNormal]; + [emailButton addTarget:self action:@selector(emailButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; + [emailButton setBackgroundImage:buttonH forState:UIControlStateHighlighted]; + [emailButton setBackgroundImage:buttonN forState:UIControlStateNormal]; + emailButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + emailButton.exclusiveTouch = YES; + + [self addSubview:emailButton]; titleWidth -= (EMAIL_BUTTON_WIDTH + BUTTON_SPACE); + } + } + +#endif // end of READER_ENABLE_MAIL Option + +#if (READER_ENABLE_PRINT == TRUE) // Option + + if (object.password == nil) // We can only print documents without passwords + { + Class printInteractionController = NSClassFromString(@"UIPrintInteractionController"); + + if ((printInteractionController != nil) && [printInteractionController isPrintingAvailable]) + { + rightButtonX -= (PRINT_BUTTON_WIDTH + BUTTON_SPACE); + + UIButton *printButton = [UIButton buttonWithType:UIButtonTypeCustom]; + + printButton.frame = CGRectMake(rightButtonX, BUTTON_Y, PRINT_BUTTON_WIDTH, BUTTON_HEIGHT); + [printButton setImage:[UIImage imageNamed:@"Reader-Print"] forState:UIControlStateNormal]; + [printButton addTarget:self action:@selector(printButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; + [printButton setBackgroundImage:buttonH forState:UIControlStateHighlighted]; + [printButton setBackgroundImage:buttonN forState:UIControlStateNormal]; + printButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + printButton.exclusiveTouch = YES; + + [self addSubview:printButton]; titleWidth -= (PRINT_BUTTON_WIDTH + BUTTON_SPACE); + } + } + +#endif // end of READER_ENABLE_PRINT Option + + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) + { + CGRect titleRect = CGRectMake(titleX, BUTTON_Y, titleWidth, TITLE_HEIGHT); + + UILabel *titleLabel = [[UILabel alloc] initWithFrame:titleRect]; + + titleLabel.textAlignment = NSTextAlignmentCenter; + titleLabel.font = [UIFont systemFontOfSize:19.0f]; + titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; + titleLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; + titleLabel.textColor = [UIColor colorWithWhite:0.0f alpha:1.0f]; + titleLabel.shadowColor = [UIColor colorWithWhite:0.65f alpha:1.0f]; + titleLabel.backgroundColor = [UIColor clearColor]; + titleLabel.shadowOffset = CGSizeMake(0.0f, 1.0f); + titleLabel.adjustsFontSizeToFitWidth = YES; + titleLabel.MinimumScaleFactor = 14.0f; + titleLabel.text = [object.fileName stringByDeletingPathExtension]; + + [self addSubview:titleLabel]; + } + } + + return self; +} + +- (void)setBookmarkState:(BOOL)state +{ +#if (READER_BOOKMARKS == TRUE) // Option + + if (state != markButton.tag) // Only if different state + { + if (self.hidden == NO) // Only if toolbar is visible + { + UIImage *image = (state ? markImageY : markImageN); + + [markButton setImage:image forState:UIControlStateNormal]; + } + + markButton.tag = state; // Update bookmarked state tag + } + + if (markButton.enabled == NO) markButton.enabled = YES; + +#endif // end of READER_BOOKMARKS Option +} + +- (void)updateBookmarkImage +{ +#if (READER_BOOKMARKS == TRUE) // Option + + if (markButton.tag != NSIntegerMin) // Valid tag + { + BOOL state = markButton.tag; // Bookmarked state + + UIImage *image = (state ? markImageY : markImageN); + + [markButton setImage:image forState:UIControlStateNormal]; + } + + if (markButton.enabled == NO) markButton.enabled = YES; + +#endif // end of READER_BOOKMARKS Option +} + +- (void)hideToolbar +{ + if (self.hidden == NO) + { + [UIView animateWithDuration:0.25 delay:0.0 + options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction + animations:^(void) + { + self.alpha = 0.0f; + } + completion:^(BOOL finished) + { + self.hidden = YES; + } + ]; + } +} + +- (void)showToolbar +{ + if (self.hidden == YES) + { + [self updateBookmarkImage]; // First + + [UIView animateWithDuration:0.25 delay:0.0 + options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction + animations:^(void) + { + self.hidden = NO; + self.alpha = 1.0f; + } + completion:NULL + ]; + } +} + +#pragma mark UIButton action methods + +- (void)doneButtonTapped:(UIButton *)button +{ + [delegate tappedInToolbar:self doneButton:button]; +} + +- (void)thumbsButtonTapped:(UIButton *)button +{ + [delegate tappedInToolbar:self thumbsButton:button]; +} + +- (void)printButtonTapped:(UIButton *)button +{ + [delegate tappedInToolbar:self printButton:button]; +} + +- (void)emailButtonTapped:(UIButton *)button +{ + [delegate tappedInToolbar:self emailButton:button]; +} + +- (void)markButtonTapped:(UIButton *)button +{ + [delegate tappedInToolbar:self markButton:button]; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbCache.h b/Night Reader/Night Reader/Night Reader/ReaderThumbCache.h new file mode 100644 index 0000000..e06b498 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbCache.h @@ -0,0 +1,54 @@ +// +// ReaderThumbCache.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "ReaderThumbRequest.h" + +@interface ReaderThumbCache : NSObject + ++ (ReaderThumbCache *)sharedInstance; + ++ (void)touchThumbCacheWithGUID:(NSString *)guid; + ++ (void)createThumbCacheWithGUID:(NSString *)guid; + ++ (void)removeThumbCacheWithGUID:(NSString *)guid; + ++ (void)purgeThumbCachesOlderThan:(NSTimeInterval)age; + ++ (NSString *)thumbCachePathForGUID:(NSString *)guid; + +- (id)thumbRequest:(ReaderThumbRequest *)request priority:(BOOL)priority; + +- (void)setObject:(UIImage *)image forKey:(NSString *)key; + +- (void)removeObjectForKey:(NSString *)key; + +- (void)removeNullForKey:(NSString *)key; + +- (void)removeAllObjects; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbCache.m b/Night Reader/Night Reader/Night Reader/ReaderThumbCache.m new file mode 100644 index 0000000..0917365 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbCache.m @@ -0,0 +1,228 @@ +// +// ReaderThumbCache.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderThumbCache.h" +#import "ReaderThumbQueue.h" +#import "ReaderThumbFetch.h" +#import "ReaderThumbView.h" + +@implementation ReaderThumbCache +{ + NSCache *thumbCache; +} + +#pragma mark Constants + +#define CACHE_SIZE 2097152 + +#pragma mark ReaderThumbCache class methods + ++ (ReaderThumbCache *)sharedInstance +{ + static dispatch_once_t predicate = 0; + + static ReaderThumbCache *object = nil; // Object + + dispatch_once(&predicate, ^{ object = [self new]; }); + + return object; // ReaderThumbCache singleton +} + ++ (NSString *)appCachesPath +{ + static dispatch_once_t predicate = 0; + + static NSString *theCachesPath = nil; // Application caches path string + + dispatch_once(&predicate, // Save a copy of the application caches path the first time it is needed + ^{ + NSArray *cachesPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + + theCachesPath = [[cachesPaths objectAtIndex:0] copy]; // Keep a copy for later abusage + }); + + return theCachesPath; +} + ++ (NSString *)thumbCachePathForGUID:(NSString *)guid +{ + NSString *cachesPath = [ReaderThumbCache appCachesPath]; // Caches path + + return [cachesPath stringByAppendingPathComponent:guid]; // Append GUID +} + ++ (void)createThumbCacheWithGUID:(NSString *)guid +{ + NSFileManager *fileManager = [NSFileManager new]; // File manager instance + + NSString *cachePath = [ReaderThumbCache thumbCachePathForGUID:guid]; // Thumb cache path + + [fileManager createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:NULL]; +} + ++ (void)removeThumbCacheWithGUID:(NSString *)guid +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + ^{ + NSFileManager *fileManager = [NSFileManager new]; // File manager instance + + NSString *cachePath = [ReaderThumbCache thumbCachePathForGUID:guid]; // Thumb cache path + + [fileManager removeItemAtPath:cachePath error:NULL]; // Remove thumb cache directory + }); +} + ++ (void)touchThumbCacheWithGUID:(NSString *)guid +{ + NSFileManager *fileManager = [NSFileManager new]; // File manager instance + + NSString *cachePath = [ReaderThumbCache thumbCachePathForGUID:guid]; // Thumb cache path + + NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate]; + + [fileManager setAttributes:attributes ofItemAtPath:cachePath error:NULL]; // New modification date +} + ++ (void)purgeThumbCachesOlderThan:(NSTimeInterval)age +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + ^{ + NSDate *now = [NSDate date]; // Right about now time + + NSString *cachesPath = [ReaderThumbCache appCachesPath]; // Caches path + + NSFileManager *fileManager = [NSFileManager new]; // File manager instance + + NSArray *cachesList = [fileManager contentsOfDirectoryAtPath:cachesPath error:NULL]; + + if (cachesList != nil) // Process caches directory contents + { + for (NSString *cacheName in cachesList) // Enumerate directory contents + { + if (cacheName.length == 36) // This is a very hacky cache ident kludge + { + NSString *cachePath = [cachesPath stringByAppendingPathComponent:cacheName]; + + NSDictionary *attributes = [fileManager attributesOfItemAtPath:cachePath error:NULL]; + + NSDate *cacheDate = [attributes objectForKey:NSFileModificationDate]; // Cache date + + NSTimeInterval seconds = [now timeIntervalSinceDate:cacheDate]; // Cache age + + if (seconds > age) // Older than so remove the thumb cache + { + [fileManager removeItemAtPath:cachePath error:NULL]; + + #ifdef DEBUG + NSLog(@"%s purged %@", __FUNCTION__, cacheName); + #endif + } + } + } + } + }); +} + +#pragma mark ReaderThumbCache instance methods + +- (id)init +{ + if ((self = [super init])) // Initialize + { + thumbCache = [NSCache new]; // Cache + + [thumbCache setName:@"ReaderThumbCache"]; + + [thumbCache setTotalCostLimit:CACHE_SIZE]; + } + + return self; +} + +- (id)thumbRequest:(ReaderThumbRequest *)request priority:(BOOL)priority +{ + @synchronized(thumbCache) // Mutex lock + { + id object = [thumbCache objectForKey:request.cacheKey]; + + if (object == nil) // Thumb object does not yet exist in the cache + { + object = [NSNull null]; // Return an NSNull thumb placeholder object + + [thumbCache setObject:object forKey:request.cacheKey cost:2]; // Cache the placeholder object + + ReaderThumbFetch *thumbFetch = [[ReaderThumbFetch alloc] initWithRequest:request]; // Create a thumb fetch operation + + [thumbFetch setQueuePriority:(priority ? NSOperationQueuePriorityNormal : NSOperationQueuePriorityLow)]; // Queue priority + + request.thumbView.operation = thumbFetch; [thumbFetch setThreadPriority:(priority ? 0.55 : 0.35)]; // Thread priority + + [[ReaderThumbQueue sharedInstance] addLoadOperation:thumbFetch]; // Queue the operation + } + + return object; // NSNull or UIImage + } +} + +- (void)setObject:(UIImage *)image forKey:(NSString *)key +{ + @synchronized(thumbCache) // Mutex lock + { + NSUInteger bytes = (image.size.width * image.size.height * 4.0f); + + [thumbCache setObject:image forKey:key cost:bytes]; // Cache image + } +} + +- (void)removeObjectForKey:(NSString *)key +{ + @synchronized(thumbCache) // Mutex lock + { + [thumbCache removeObjectForKey:key]; + } +} + +- (void)removeNullForKey:(NSString *)key +{ + @synchronized(thumbCache) // Mutex lock + { + id object = [thumbCache objectForKey:key]; + + if ([object isMemberOfClass:[NSNull class]]) + { + [thumbCache removeObjectForKey:key]; + } + } +} + +- (void)removeAllObjects +{ + @synchronized(thumbCache) // Mutex lock + { + [thumbCache removeAllObjects]; + } +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbFetch.h b/Night Reader/Night Reader/Night Reader/ReaderThumbFetch.h new file mode 100644 index 0000000..c585bf2 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbFetch.h @@ -0,0 +1,36 @@ +// +// ReaderThumbFetch.h +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "ReaderThumbQueue.h" + +@class ReaderThumbRequest; + +@interface ReaderThumbFetch : ReaderThumbOperation + +- (id)initWithRequest:(ReaderThumbRequest *)options; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbFetch.m b/Night Reader/Night Reader/Night Reader/ReaderThumbFetch.m new file mode 100644 index 0000000..df74e16 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbFetch.m @@ -0,0 +1,128 @@ +// +// ReaderThumbFetch.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderThumbFetch.h" +#import "ReaderThumbRender.h" +#import "ReaderThumbCache.h" +#import "ReaderThumbView.h" + +#import + +@implementation ReaderThumbFetch +{ + ReaderThumbRequest *request; +} + +#pragma mark ReaderThumbFetch instance methods + +- (id)initWithRequest:(ReaderThumbRequest *)options +{ + if ((self = [super initWithGUID:options.guid])) + { + request = options; + } + + return self; +} + +- (void)cancel +{ + [super cancel]; // Cancel the operation + + request.thumbView.operation = nil; // Break retain loop + + request.thumbView = nil; // Release target thumb view on cancel + + [[ReaderThumbCache sharedInstance] removeNullForKey:request.cacheKey]; +} + +- (NSURL *)thumbFileURL +{ + NSString *cachePath = [ReaderThumbCache thumbCachePathForGUID:request.guid]; // Thumb cache path + + NSString *fileName = [NSString stringWithFormat:@"%@.png", request.thumbName]; // Thumb file name + + return [NSURL fileURLWithPath:[cachePath stringByAppendingPathComponent:fileName]]; // File URL +} + +- (void)main +{ + CGImageRef imageRef = NULL; NSURL *thumbURL = [self thumbFileURL]; + + CGImageSourceRef loadRef = CGImageSourceCreateWithURL((__bridge CFURLRef)thumbURL, NULL); + + if (loadRef != NULL) // Load the existing thumb image + { + imageRef = CGImageSourceCreateImageAtIndex(loadRef, 0, NULL); // Load it + + CFRelease(loadRef); // Release CGImageSource reference + } + else // Existing thumb image not found - so create and queue up a thumb render operation on the work queue + { + ReaderThumbRender *thumbRender = [[ReaderThumbRender alloc] initWithRequest:request]; // Create a thumb render operation + + [thumbRender setQueuePriority:self.queuePriority]; [thumbRender setThreadPriority:(self.threadPriority - 0.1)]; // Priority + + if (self.isCancelled == NO) // We're not cancelled - so update things and add the render operation to the work queue + { + request.thumbView.operation = thumbRender; // Update the thumb view operation property to the new operation + + [[ReaderThumbQueue sharedInstance] addWorkOperation:thumbRender]; return; // Queue the operation + } + } + + if (imageRef != NULL) // Create a UIImage from a CGImage and show it + { + UIImage *image = [UIImage imageWithCGImage:imageRef scale:request.scale orientation:UIImageOrientationUp]; + + CGImageRelease(imageRef); // Release the CGImage reference from the above thumb load code + + UIGraphicsBeginImageContextWithOptions(image.size, YES, request.scale); // Graphics context + + [image drawAtPoint:CGPointZero]; // Decode and draw the image on this background thread + + UIImage *decoded = UIGraphicsGetImageFromCurrentImageContext(); // Newly decoded image + + UIGraphicsEndImageContext(); // Cleanup after the bitmap-based graphics drawing context + + [[ReaderThumbCache sharedInstance] setObject:decoded forKey:request.cacheKey]; // Cache it + + if (self.isCancelled == NO) // Show the image in the target thumb view on the main thread + { + ReaderThumbView *thumbView = request.thumbView; // Target thumb view for image show + + NSUInteger targetTag = request.targetTag; // Target reference tag for image show + + dispatch_async(dispatch_get_main_queue(), // Queue image show on main thread + ^{ + if (thumbView.targetTag == targetTag) [thumbView showImage:decoded]; + }); + } + } + + request.thumbView.operation = nil; // Break retain loop +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbQueue.h b/Night Reader/Night Reader/Night Reader/ReaderThumbQueue.h new file mode 100644 index 0000000..fe6c50f --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbQueue.h @@ -0,0 +1,54 @@ +// +// ReaderThumbQueue.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface ReaderThumbQueue : NSObject + ++ (ReaderThumbQueue *)sharedInstance; + +- (void)addLoadOperation:(NSOperation *)operation; + +- (void)addWorkOperation:(NSOperation *)operation; + +- (void)cancelOperationsWithGUID:(NSString *)guid; + +- (void)cancelAllOperations; + +@end + +#pragma mark - + +// +// ReaderThumbOperation class interface +// + +@interface ReaderThumbOperation : NSOperation + +@property (nonatomic, strong, readonly) NSString *guid; + +- (id)initWithGUID:(NSString *)guid; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbQueue.m b/Night Reader/Night Reader/Night Reader/ReaderThumbQueue.m new file mode 100644 index 0000000..59896e3 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbQueue.m @@ -0,0 +1,141 @@ +// +// ReaderThumbQueue.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderThumbQueue.h" + +@implementation ReaderThumbQueue +{ + NSOperationQueue *loadQueue; + + NSOperationQueue *workQueue; +} + +#pragma mark ReaderThumbQueue class methods + ++ (ReaderThumbQueue *)sharedInstance +{ + static dispatch_once_t predicate = 0; + + static ReaderThumbQueue *object = nil; // Object + + dispatch_once(&predicate, ^{ object = [self new]; }); + + return object; // ReaderThumbQueue singleton +} + +#pragma mark ReaderThumbQueue instance methods + +- (id)init +{ + if ((self = [super init])) // Initialize + { + loadQueue = [NSOperationQueue new]; + + [loadQueue setName:@"ReaderThumbLoadQueue"]; + + [loadQueue setMaxConcurrentOperationCount:1]; + + workQueue = [NSOperationQueue new]; + + [workQueue setName:@"ReaderThumbWorkQueue"]; + + [workQueue setMaxConcurrentOperationCount:1]; + } + + return self; +} + +- (void)addLoadOperation:(NSOperation *)operation +{ + if ([operation isKindOfClass:[ReaderThumbOperation class]]) + { + [loadQueue addOperation:operation]; // Add to load queue + } +} + +- (void)addWorkOperation:(NSOperation *)operation +{ + if ([operation isKindOfClass:[ReaderThumbOperation class]]) + { + [workQueue addOperation:operation]; // Add to work queue + } +} + +- (void)cancelOperationsWithGUID:(NSString *)guid +{ + [loadQueue setSuspended:YES]; [workQueue setSuspended:YES]; + + for (ReaderThumbOperation *operation in loadQueue.operations) + { + if ([operation isKindOfClass:[ReaderThumbOperation class]]) + { + if ([operation.guid isEqualToString:guid]) [operation cancel]; + } + } + + for (ReaderThumbOperation *operation in workQueue.operations) + { + if ([operation isKindOfClass:[ReaderThumbOperation class]]) + { + if ([operation.guid isEqualToString:guid]) [operation cancel]; + } + } + + [workQueue setSuspended:NO]; [loadQueue setSuspended:NO]; +} + +- (void)cancelAllOperations +{ + [loadQueue cancelAllOperations]; [workQueue cancelAllOperations]; +} + +@end + +#pragma mark - + +// +// ReaderThumbOperation class implementation +// + +@implementation ReaderThumbOperation +{ + NSString *_guid; +} + +@synthesize guid = _guid; + +#pragma mark ReaderThumbOperation instance methods + +- (id)initWithGUID:(NSString *)guid +{ + if ((self = [super init])) + { + _guid = guid; + } + + return self; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbRender.h b/Night Reader/Night Reader/Night Reader/ReaderThumbRender.h new file mode 100644 index 0000000..2717509 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbRender.h @@ -0,0 +1,36 @@ +// +// ReaderThumbRender.h +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "ReaderThumbQueue.h" + +@class ReaderThumbRequest; + +@interface ReaderThumbRender : ReaderThumbOperation + +- (id)initWithRequest:(ReaderThumbRequest *)options; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbRender.m b/Night Reader/Night Reader/Night Reader/ReaderThumbRender.m new file mode 100644 index 0000000..7c0cdf2 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbRender.m @@ -0,0 +1,204 @@ +// +// ReaderThumbRender.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderThumbRender.h" +#import "ReaderThumbCache.h" +#import "ReaderThumbView.h" +#import "CGPDFDocument.h" + +#import + +@implementation ReaderThumbRender +{ + ReaderThumbRequest *request; +} + +#pragma mark ReaderThumbRender instance methods + +- (id)initWithRequest:(ReaderThumbRequest *)options +{ + if ((self = [super initWithGUID:options.guid])) + { + request = options; + } + + return self; +} + +- (void)cancel +{ + [super cancel]; // Cancel the operation + + request.thumbView.operation = nil; // Break retain loop + + request.thumbView = nil; // Release target thumb view on cancel + + [[ReaderThumbCache sharedInstance] removeNullForKey:request.cacheKey]; +} + +- (NSURL *)thumbFileURL +{ + NSFileManager *fileManager = [NSFileManager new]; // File manager instance + + NSString *cachePath = [ReaderThumbCache thumbCachePathForGUID:request.guid]; // Thumb cache path + + [fileManager createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:NULL]; + + NSString *fileName = [NSString stringWithFormat:@"%@.png", request.thumbName]; // Thumb file name + + return [NSURL fileURLWithPath:[cachePath stringByAppendingPathComponent:fileName]]; // File URL +} + +- (void)main +{ + NSInteger page = request.thumbPage; NSString *password = request.password; + + CGImageRef imageRef = NULL; CFURLRef fileURL = (__bridge CFURLRef)request.fileURL; + + CGPDFDocumentRef thePDFDocRef = CGPDFDocumentCreateX(fileURL, password); + + if (thePDFDocRef != NULL) // Check for non-NULL CGPDFDocumentRef + { + CGPDFPageRef thePDFPageRef = CGPDFDocumentGetPage(thePDFDocRef, page); + + if (thePDFPageRef != NULL) // Check for non-NULL CGPDFPageRef + { + CGFloat thumb_w = request.thumbSize.width; // Maximum thumb width + CGFloat thumb_h = request.thumbSize.height; // Maximum thumb height + + CGRect cropBoxRect = CGPDFPageGetBoxRect(thePDFPageRef, kCGPDFCropBox); + CGRect mediaBoxRect = CGPDFPageGetBoxRect(thePDFPageRef, kCGPDFMediaBox); + CGRect effectiveRect = CGRectIntersection(cropBoxRect, mediaBoxRect); + + NSInteger pageRotate = CGPDFPageGetRotationAngle(thePDFPageRef); // Angle + + CGFloat page_w = 0.0f; CGFloat page_h = 0.0f; // Rotated page size + + switch (pageRotate) // Page rotation (in degrees) + { + default: // Default case + case 0: case 180: // 0 and 180 degrees + { + page_w = effectiveRect.size.width; + page_h = effectiveRect.size.height; + break; + } + + case 90: case 270: // 90 and 270 degrees + { + page_h = effectiveRect.size.width; + page_w = effectiveRect.size.height; + break; + } + } + + CGFloat scale_w = (thumb_w / page_w); // Width scale + CGFloat scale_h = (thumb_h / page_h); // Height scale + + CGFloat scale = 0.0f; // Page to target thumb size scale + + if (page_h > page_w) + scale = ((thumb_h > thumb_w) ? scale_w : scale_h); // Portrait + else + scale = ((thumb_h < thumb_w) ? scale_h : scale_w); // Landscape + + NSInteger target_w = (page_w * scale); // Integer target thumb width + NSInteger target_h = (page_h * scale); // Integer target thumb height + + if (target_w % 2) target_w--; if (target_h % 2) target_h--; // Even + + target_w *= request.scale; target_h *= request.scale; // Screen scale + + CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); // RGB color space + + CGBitmapInfo bmi = (kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst); + + CGContextRef context = CGBitmapContextCreate(NULL, target_w, target_h, 8, 0, rgb, bmi); + + if (context != NULL) // Must have a valid custom CGBitmap context to draw into + { + CGRect thumbRect = CGRectMake(0.0f, 0.0f, target_w, target_h); // Target thumb rect + + CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); CGContextFillRect(context, thumbRect); // White fill + + CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(thePDFPageRef, kCGPDFCropBox, thumbRect, 0, true)); // Fit rect + + //CGContextSetRenderingIntent(context, kCGRenderingIntentDefault); CGContextSetInterpolationQuality(context, kCGInterpolationDefault); + + CGContextDrawPDFPage(context, thePDFPageRef); // Render the PDF page into the custom CGBitmap context + + imageRef = CGBitmapContextCreateImage(context); // Create CGImage from custom CGBitmap context + + CGContextRelease(context); // Release custom CGBitmap context reference + } + + CGColorSpaceRelease(rgb); // Release device RGB color space reference + } + + CGPDFDocumentRelease(thePDFDocRef); // Release CGPDFDocumentRef reference + } + + if (imageRef != NULL) // Create UIImage from CGImage and show it, then save thumb as PNG + { + UIImage *image = [UIImage imageWithCGImage:imageRef scale:request.scale orientation:UIImageOrientationUp]; + + [[ReaderThumbCache sharedInstance] setObject:image forKey:request.cacheKey]; // Update cache + + if (self.isCancelled == NO) // Show the image in the target thumb view on the main thread + { + ReaderThumbView *thumbView = request.thumbView; // Target thumb view for image show + + NSUInteger targetTag = request.targetTag; // Target reference tag for image show + + dispatch_async(dispatch_get_main_queue(), // Queue image show on main thread + ^{ + if (thumbView.targetTag == targetTag) [thumbView showImage:image]; + }); + } + + CFURLRef thumbURL = (__bridge CFURLRef)[self thumbFileURL]; // Thumb cache path with PNG file name URL + + CGImageDestinationRef thumbRef = CGImageDestinationCreateWithURL(thumbURL, (CFStringRef)@"public.png", 1, NULL); + + if (thumbRef != NULL) // Write the thumb image file out to the thumb cache directory + { + CGImageDestinationAddImage(thumbRef, imageRef, NULL); // Add the image + + CGImageDestinationFinalize(thumbRef); // Finalize the image file + + CFRelease(thumbRef); // Release CGImageDestination reference + } + + CGImageRelease(imageRef); // Release CGImage reference + } + else // No image - so remove the placeholder object from the cache + { + [[ReaderThumbCache sharedInstance] removeNullForKey:request.cacheKey]; + } + + request.thumbView.operation = nil; // Break retain loop +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbRequest.h b/Night Reader/Night Reader/Night Reader/ReaderThumbRequest.h new file mode 100644 index 0000000..80a8aef --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbRequest.h @@ -0,0 +1,47 @@ +// +// ReaderThumbRequest.h +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@class ReaderThumbView; + +@interface ReaderThumbRequest : NSObject + +@property (nonatomic, strong, readonly) NSURL *fileURL; +@property (nonatomic, strong, readonly) NSString *guid; +@property (nonatomic, strong, readonly) NSString *password; +@property (nonatomic, strong, readonly) NSString *cacheKey; +@property (nonatomic, strong, readonly) NSString *thumbName; +@property (nonatomic, strong, readwrite) ReaderThumbView *thumbView; +@property (nonatomic, assign, readonly) NSUInteger targetTag; +@property (nonatomic, assign, readonly) NSInteger thumbPage; +@property (nonatomic, assign, readonly) CGSize thumbSize; +@property (nonatomic, assign, readonly) CGFloat scale; + ++ (id)newForView:(ReaderThumbView *)view fileURL:(NSURL *)url password:(NSString *)phrase guid:(NSString *)guid page:(NSInteger)page size:(CGSize)size; + +- (id)initWithView:(ReaderThumbView *)view fileURL:(NSURL *)url password:(NSString *)phrase guid:(NSString *)guid page:(NSInteger)page size:(CGSize)size; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbRequest.m b/Night Reader/Night Reader/Night Reader/ReaderThumbRequest.m new file mode 100644 index 0000000..4a4a3ef --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbRequest.m @@ -0,0 +1,96 @@ +// +// ReaderThumbRequest.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderThumbRequest.h" +#import "ReaderThumbView.h" + +@implementation ReaderThumbRequest +{ + NSURL *_fileURL; + + NSString *_guid; + + NSString *_password; + + NSString *_cacheKey; + + NSString *_thumbName; + + ReaderThumbView *_thumbView; + + NSUInteger _targetTag; + + NSInteger _thumbPage; + + CGSize _thumbSize; + + CGFloat _scale; +} + +#pragma mark Properties + +@synthesize guid = _guid; +@synthesize fileURL = _fileURL; +@synthesize password = _password; +@synthesize thumbView = _thumbView; +@synthesize thumbPage = _thumbPage; +@synthesize thumbSize = _thumbSize; +@synthesize thumbName = _thumbName; +@synthesize targetTag = _targetTag; +@synthesize cacheKey = _cacheKey; +@synthesize scale = _scale; + +#pragma mark ReaderThumbRequest class methods + ++ (id)newForView:(ReaderThumbView *)view fileURL:(NSURL *)url password:(NSString *)phrase guid:(NSString *)guid page:(NSInteger)page size:(CGSize)size +{ + return [[ReaderThumbRequest alloc] initWithView:view fileURL:url password:phrase guid:guid page:page size:size]; +} + +#pragma mark ReaderThumbRequest instance methods + +- (id)initWithView:(ReaderThumbView *)view fileURL:(NSURL *)url password:(NSString *)phrase guid:(NSString *)guid page:(NSInteger)page size:(CGSize)size +{ + if ((self = [super init])) // Initialize object + { + NSInteger w = size.width; NSInteger h = size.height; + + _thumbView = view; _thumbPage = page; _thumbSize = size; + + _fileURL = [url copy]; _password = [phrase copy]; _guid = [guid copy]; + + _thumbName = [[NSString alloc] initWithFormat:@"%07d-%04dx%04d", page, w, h]; + + _cacheKey = [[NSString alloc] initWithFormat:@"%@+%@", _thumbName, _guid]; + + _targetTag = [_cacheKey hash]; _thumbView.targetTag = _targetTag; + + _scale = [[UIScreen mainScreen] scale]; // Thumb screen scale + } + + return self; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbView.h b/Night Reader/Night Reader/Night Reader/ReaderThumbView.h new file mode 100644 index 0000000..92c177d --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbView.h @@ -0,0 +1,45 @@ +// +// ReaderThumbView.h +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface ReaderThumbView : UIView +{ +@protected // Instance variables + + UIImageView *imageView; +} + +@property (atomic, strong, readwrite) NSOperation *operation; + +@property (nonatomic, assign, readwrite) NSUInteger targetTag; + +- (void)showImage:(UIImage *)image; + +- (void)showTouched:(BOOL)touched; + +- (void)reuse; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbView.m b/Night Reader/Night Reader/Night Reader/ReaderThumbView.m new file mode 100644 index 0000000..6a7b9db --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbView.m @@ -0,0 +1,93 @@ +// +// ReaderThumbView.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderThumbView.h" + +@implementation ReaderThumbView +{ + NSOperation *_operation; + + NSUInteger _targetTag; +} + +#pragma mark Properties + +@synthesize operation = _operation; +@synthesize targetTag = _targetTag; + +#pragma mark ReaderThumbView instance methods + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) + { + self.autoresizesSubviews = NO; + self.userInteractionEnabled = NO; + self.contentMode = UIViewContentModeRedraw; + self.autoresizingMask = UIViewAutoresizingNone; + self.backgroundColor = [UIColor blackColor]; + + imageView = [[UIImageView alloc] initWithFrame:self.bounds]; + + imageView.autoresizesSubviews = NO; + imageView.userInteractionEnabled = NO; + imageView.autoresizingMask = UIViewAutoresizingNone; + imageView.contentMode = UIViewContentModeScaleAspectFit; + + [self addSubview:imageView]; + } + + return self; +} + +- (void)showImage:(UIImage *)image +{ + imageView.image = image; // Show image +} + +- (void)showTouched:(BOOL)touched +{ + // Implemented by ReaderThumbView subclass +} + +- (void)removeFromSuperview +{ + _targetTag = 0; // Clear target tag + + [self.operation cancel]; // Cancel operation + + [super removeFromSuperview]; // Remove view +} + +- (void)reuse +{ + _targetTag = 0; // Clear target tag + + [self.operation cancel]; // Cancel operation + + imageView.image = nil; // Release image +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbsView.h b/Night Reader/Night Reader/Night Reader/ReaderThumbsView.h new file mode 100644 index 0000000..eb75007 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbsView.h @@ -0,0 +1,68 @@ +// +// ReaderThumbsView.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "ReaderThumbView.h" + +@class ReaderThumbsView; + +@protocol ReaderThumbsViewDelegate + +@required // Delegate protocols + +- (NSUInteger)numberOfThumbsInThumbsView:(ReaderThumbsView *)thumbsView; + +- (id)thumbsView:(ReaderThumbsView *)thumbsView thumbCellWithFrame:(CGRect)frame; + +- (void)thumbsView:(ReaderThumbsView *)thumbsView updateThumbCell:(id)thumbCell forIndex:(NSInteger)index; + +- (void)thumbsView:(ReaderThumbsView *)thumbsView didSelectThumbWithIndex:(NSInteger)index; + +@optional // Delegate protocols + +- (void)thumbsView:(ReaderThumbsView *)thumbsView refreshThumbCell:(id)thumbCell forIndex:(NSInteger)index; + +- (void)thumbsView:(ReaderThumbsView *)thumbsView didPressThumbWithIndex:(NSInteger)index; + +@end + +@interface ReaderThumbsView : UIScrollView + +@property (nonatomic, unsafe_unretained, readwrite) id delegate; + +- (void)setThumbSize:(CGSize)thumbSize; + +- (void)reloadThumbsCenterOnIndex:(NSInteger)index; + +- (void)reloadThumbsContentOffset:(CGPoint)newContentOffset; + +- (void)refreshThumbWithIndex:(NSInteger)index; + +- (void)refreshVisibleThumbs; + +- (CGPoint)insetContentOffset; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderThumbsView.m b/Night Reader/Night Reader/Night Reader/ReaderThumbsView.m new file mode 100644 index 0000000..950d4ed --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderThumbsView.m @@ -0,0 +1,545 @@ +// +// ReaderThumbsView.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderThumbsView.h" + +@interface ReaderThumbsView () + +@end + +@implementation ReaderThumbsView +{ + CGPoint lastContentOffset; + + ReaderThumbView *touchedCell; + + NSMutableArray *thumbCellsQueue; + + NSMutableArray *thumbCellsVisible; + + NSInteger _thumbsX, _thumbsY, _thumbX; + + CGSize _thumbSize, _lastViewSize; + + NSUInteger _thumbCount; + + BOOL canUpdate; +} + +#pragma mark Properties + +@synthesize delegate; + +#pragma mark ReaderThumbsView instance methods + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) + { + self.scrollsToTop = NO; + self.autoresizesSubviews = NO; + self.delaysContentTouches = NO; + self.alwaysBounceVertical = YES; + self.contentMode = UIViewContentModeRedraw; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.backgroundColor = [UIColor yellowColor]; + + [super setDelegate:self]; // Set the superclass UIScrollView delegate + + thumbCellsQueue = [NSMutableArray new]; thumbCellsVisible = [NSMutableArray new]; // Cell management arrays + + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; + //tapGesture.numberOfTouchesRequired = 1; tapGesture.numberOfTapsRequired = 1; tapGesture.delegate = self; + [self addGestureRecognizer:tapGesture]; + + UILongPressGestureRecognizer *pressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePressGesture:)]; + pressGesture.minimumPressDuration = 0.8; //pressGesture.numberOfTouchesRequired = 1; pressGesture.delegate = self; + [self addGestureRecognizer:pressGesture]; + + lastContentOffset = CGPointMake(CGFLOAT_MIN, CGFLOAT_MIN); + } + + return self; +} + +- (void)requeueThumbCell:(ReaderThumbView *)tvCell +{ + [thumbCellsQueue addObject:tvCell]; + + [thumbCellsVisible removeObject:tvCell]; + + tvCell.tag = NSIntegerMin; tvCell.hidden = YES; + + [tvCell reuse]; // Reuse the cell +} + +- (void)requeueAllThumbCells +{ + if (thumbCellsVisible.count > 0) + { + NSArray *visible = [thumbCellsVisible copy]; + + for (ReaderThumbView *tvCell in visible) + { + [self requeueThumbCell:tvCell]; + } + } +} + +- (ReaderThumbView *)dequeueThumbCellWithFrame:(CGRect)frame +{ + ReaderThumbView *theCell = nil; + + if (thumbCellsQueue.count > 0) // Reuse existing cell + { + theCell = [thumbCellsQueue objectAtIndex:0]; + + [thumbCellsQueue removeObjectAtIndex:0]; // Dequeue it + + theCell.frame = frame; // Position the reused cell + } + else // Allocate a brand new thumb cell subclass for our use + { + theCell = [delegate thumbsView:self thumbCellWithFrame:frame]; + + //assert([theCell isKindOfClass:[ReaderThumbView class]]); + + theCell.tag = NSIntegerMin; theCell.hidden = YES; + + [self insertSubview:theCell atIndex:0]; // Add + } + + [thumbCellsVisible addObject:theCell]; + + return theCell; +} + +- (NSMutableIndexSet *)visibleIndexSetForContentOffset +{ + CGFloat minY = self.contentOffset.y; // Content offset + CGFloat maxY = (minY + self.bounds.size.height - 1.0f); + + NSInteger startRow = (minY / _thumbSize.height); // Start row + NSInteger finalRow = (maxY / _thumbSize.height); // Final row + + NSInteger startIndex = (startRow * _thumbsX); // Start index + NSInteger finalIndex = (finalRow * _thumbsX); // Final index + + finalIndex += (_thumbsX - 1); // Last index value in last row + + NSInteger maximumIndex = (_thumbCount - 1); // Maximum index value + + if (finalIndex > maximumIndex) finalIndex = maximumIndex; // Limit it + + NSRange indexRange = NSMakeRange(startIndex, (finalIndex - startIndex + 1)); + + return [NSMutableIndexSet indexSetWithIndexesInRange:indexRange]; +} + +- (ReaderThumbView *)thumbCellContainingPoint:(CGPoint)point +{ + ReaderThumbView *theCell = nil; + + for (ReaderThumbView *tvCell in thumbCellsVisible) + { + if (CGRectContainsPoint(tvCell.frame, point) == true) + { + theCell = tvCell; break; // Found it + } + } + + return theCell; +} + +- (CGRect)thumbCellFrameForIndex:(NSInteger)index +{ + CGRect thumbRect; thumbRect.size = _thumbSize; + + NSInteger thumbY = ((index / _thumbsX) * _thumbSize.height); // X, Y + + NSInteger thumbX = (((index % _thumbsX) * _thumbSize.width) + _thumbX); + + thumbRect.origin.x = thumbX; thumbRect.origin.y = thumbY; + + return thumbRect; +} + +- (void)updateContentSize:(NSUInteger)thumbCount +{ + canUpdate = NO; // Disable updates + + if (thumbCount > 0) // Have some thumbs + { + CGFloat bw = self.bounds.size.width; + + _thumbsX = (bw / _thumbSize.width); + + if (_thumbsX < 1) _thumbsX = 1; + + _thumbsY = (thumbCount / _thumbsX); + + if ((_thumbsX * _thumbsY) < thumbCount) _thumbsY++; + + CGFloat tw = (_thumbsX * _thumbSize.width); + CGFloat th = (_thumbsY * _thumbSize.height); + + if (tw < bw) + _thumbX = ((bw - tw) / 2.0f); + else + _thumbX = 0; // Reset + + if (tw < bw) tw = bw; // Limit + + [self setContentSize:CGSizeMake(tw, th)]; + } + else // Zero (0) thumbs + { + [self setContentSize:CGSizeZero]; + } + + canUpdate = YES; // Enable updates +} + +- (void)layoutSubviews +{ + if (CGSizeEqualToSize(_lastViewSize, CGSizeZero) == true) + { + _lastViewSize = self.bounds.size; // Initial view size + } + else + if (CGSizeEqualToSize(_lastViewSize, self.bounds.size) == false) + { + _lastViewSize = self.bounds.size; // Track the view size + + [self updateContentSize:_thumbCount]; // Update the content size + + NSMutableArray *requeueCells = [NSMutableArray array]; // Requeue cell list + + NSMutableIndexSet *visibleIndexSet = [self visibleIndexSetForContentOffset]; + + for (ReaderThumbView *tvCell in thumbCellsVisible) // Enumerate visible cells + { + NSInteger index = tvCell.tag; // Get the cell's index value + + if ([visibleIndexSet containsIndex:index] == YES) // Visible cell + { + tvCell.frame = [self thumbCellFrameForIndex:index]; // Frame + + [visibleIndexSet removeIndex:index]; // Remove from set + } + else // Add it to the list of cells to requeue + { + [requeueCells addObject:tvCell]; + } + } + + for (ReaderThumbView *tvCell in requeueCells) // Enumerate requeue cells + { + [self requeueThumbCell:tvCell]; // Requeue the thumb cell + } + + [visibleIndexSet enumerateIndexesUsingBlock: // Enumerate visible indexes + ^(NSUInteger index, BOOL *stop) + { + CGRect thumbRect = [self thumbCellFrameForIndex:index]; // Frame + + ReaderThumbView *tvCell = [self dequeueThumbCellWithFrame:thumbRect]; + + [delegate thumbsView:self updateThumbCell:tvCell forIndex:index]; + + tvCell.tag = index; tvCell.hidden = NO; // Tag and show it + } + ]; + } +} + +- (void)setThumbSize:(CGSize)thumbSize +{ + if (CGSizeEqualToSize(_thumbSize, CGSizeZero) == true) + { + if (CGSizeEqualToSize(thumbSize, CGSizeZero) == false) + { + _thumbSize = thumbSize; // Set the maximum thumb size + } + } +} + +- (void)reloadThumbsCenterOnIndex:(NSInteger)index +{ + assert(delegate != nil); // Check delegate + + assert(CGSizeEqualToSize(_thumbSize, CGSizeZero) == false); + + if (self.decelerating == YES) // Stop scroll view movement + { + [self setContentOffset:self.contentOffset animated:NO]; + } + + CGPoint newContentOffset = CGPointZero; // At top + + lastContentOffset = CGPointMake(CGFLOAT_MIN, CGFLOAT_MIN); + + [self requeueAllThumbCells]; // Start off fresh + + _thumbCount = 0; // Reset the thumb count to zero + + NSUInteger thumbCount = [delegate numberOfThumbsInThumbsView:self]; + + [self updateContentSize:thumbCount]; _thumbCount = thumbCount; + + if (thumbCount > 0) // Have some thumbs + { + NSInteger boundsHeight = self.bounds.size.height; + + NSInteger maxY = (self.contentSize.height - boundsHeight); + + NSInteger minY = 0; maxY--; if (maxY < minY) maxY = minY; // Limits + + if (index < 0) index = 0; else if (index > thumbCount) index = (thumbCount - 1); + + NSInteger thumbY = ((index / _thumbsX) * _thumbSize.height); // Thumb Y + + NSInteger offsetY = (thumbY - (boundsHeight / 2) + (_thumbSize.height / 2)); + + if (offsetY < minY) offsetY = minY; else if (offsetY > maxY) offsetY = maxY; + + newContentOffset.y = offsetY; // Calculated content offset Y position + } + + newContentOffset.y -= self.contentInset.top; // Content inset adjust + + if (CGPointEqualToPoint(self.contentOffset, newContentOffset) == false) + [self setContentOffset:newContentOffset animated:NO]; + else + [self scrollViewDidScroll:self]; + + [self flashScrollIndicators]; +} + +- (void)reloadThumbsContentOffset:(CGPoint)newContentOffset +{ + assert(delegate != nil); // Check delegate + + assert(CGSizeEqualToSize(_thumbSize, CGSizeZero) == false); + + if (self.decelerating == YES) // Stop scroll view movement + { + [self setContentOffset:self.contentOffset animated:NO]; + } + + lastContentOffset = CGPointMake(CGFLOAT_MIN, CGFLOAT_MIN); + + [self requeueAllThumbCells]; // Start off fresh + + _thumbCount = 0; // Reset the thumb count to zero + + NSUInteger thumbCount = [delegate numberOfThumbsInThumbsView:self]; + + [self updateContentSize:thumbCount]; _thumbCount = thumbCount; + + if (thumbCount > 0) // Have some thumbs + { + NSInteger boundsHeight = self.bounds.size.height; + + NSInteger maxY = (self.contentSize.height - boundsHeight); + + NSInteger minY = 0; maxY--; if (maxY < minY) maxY = minY; // Limits + + NSInteger offsetY = newContentOffset.y; // Requested content offset Y + + if (offsetY < minY) offsetY = minY; else if (offsetY > maxY) offsetY = maxY; + + newContentOffset.y = offsetY; newContentOffset.x = 0.0f; // Validated + } + else // Zero (0) thumbs + { + newContentOffset = CGPointZero; + } + + newContentOffset.y -= self.contentInset.top; // Content inset adjust + + if (CGPointEqualToPoint(self.contentOffset, newContentOffset) == false) + [self setContentOffset:newContentOffset animated:NO]; + else + [self scrollViewDidScroll:self]; + + [self flashScrollIndicators]; +} + +- (void)refreshThumbWithIndex:(NSInteger)index +{ + for (ReaderThumbView *tvCell in thumbCellsVisible) // Enumerate visible cells + { + if (tvCell.tag == index) // Found a visible thumb cell with the index value + { + if ([delegate respondsToSelector:@selector(thumbsView:refreshThumbCell:forIndex:)]) + { + [delegate thumbsView:self refreshThumbCell:tvCell forIndex:index]; // Refresh + } + + break; + } + } +} + +- (void)refreshVisibleThumbs +{ + for (ReaderThumbView *tvCell in thumbCellsVisible) // Enumerate visible cells + { + if ([delegate respondsToSelector:@selector(thumbsView:refreshThumbCell:forIndex:)]) + { + [delegate thumbsView:self refreshThumbCell:tvCell forIndex:tvCell.tag]; // Refresh + } + } +} + +- (CGPoint)insetContentOffset +{ + CGPoint insetContentOffset = self.contentOffset; // Offset + + insetContentOffset.y += self.contentInset.top; // Inset adjust + + return insetContentOffset; // Adjusted content offset +} + +#pragma mark UIGestureRecognizer action methods + +- (void)handleTapGesture:(UITapGestureRecognizer *)recognizer +{ + if (recognizer.state == UIGestureRecognizerStateRecognized) // Handle the tap + { + CGPoint point = [recognizer locationInView:recognizer.view]; // Tap location + + ReaderThumbView *tvCell = [self thumbCellContainingPoint:point]; // Look for cell + + if (tvCell != nil) [delegate thumbsView:self didSelectThumbWithIndex:tvCell.tag]; + } +} + +- (void)handlePressGesture:(UILongPressGestureRecognizer *)recognizer +{ + if (recognizer.state == UIGestureRecognizerStateBegan) // Handle the press + { + if ([delegate respondsToSelector:@selector(thumbsView:didPressThumbWithIndex:)]) + { + CGPoint point = [recognizer locationInView:recognizer.view]; // Press location + + ReaderThumbView *tvCell = [self thumbCellContainingPoint:point]; // Look for cell + + if (tvCell != nil) [delegate thumbsView:self didPressThumbWithIndex:tvCell.tag]; + } + } +} + +#pragma mark UIScrollViewDelegate methods + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + if ((canUpdate == YES) && (_thumbCount > 0)) // Check flag and thumb count + { + if (CGPointEqualToPoint(scrollView.contentOffset, lastContentOffset) == false) + { + lastContentOffset = scrollView.contentOffset; // Work around a 'feature' + + CGRect visibleBounds = self.bounds; // Visible bounds in the scroll view + + NSMutableArray *requeueCells = [NSMutableArray array]; // Requeue cell list + + NSMutableIndexSet *visibleCellSet = [NSMutableIndexSet indexSet]; // Visible set + + for (ReaderThumbView *tvCell in thumbCellsVisible) // Enumerate visible cells + { + if (CGRectIntersectsRect(tvCell.frame, visibleBounds) == true) + [visibleCellSet addIndex:tvCell.tag]; + else + [requeueCells addObject:tvCell]; + } + + for (ReaderThumbView *tvCell in requeueCells) // Enumerate requeue cells + { + [self requeueThumbCell:tvCell]; // Requeue the thumb cell + } + + NSMutableIndexSet *visibleIndexSet = [self visibleIndexSetForContentOffset]; + + [visibleIndexSet enumerateIndexesUsingBlock: // Enumerate visible indexes + ^(NSUInteger index, BOOL *stop) + { + if ([visibleCellSet containsIndex:index] == NO) // Index not visible + { + CGRect thumbRect = [self thumbCellFrameForIndex:index]; // Frame + + ReaderThumbView *tvCell = [self dequeueThumbCellWithFrame:thumbRect]; + + [delegate thumbsView:self updateThumbCell:tvCell forIndex:index]; + + tvCell.tag = index; tvCell.hidden = NO; // Tag and show it + } + } + ]; + } + } +} + +#pragma mark UIResponder instance methods + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; // Message superclass + + if (touchedCell != nil) { [touchedCell showTouched:NO]; touchedCell = nil; } + + if (touches.count == 1) // Show selection on single touch + { + UITouch *touch = [touches anyObject]; // Get touch from set + + CGPoint point = [touch locationInView:touch.view]; // Touch location + + ReaderThumbView *tvCell = [self thumbCellContainingPoint:point]; // Look for cell + + if (tvCell != nil) { touchedCell = tvCell; [touchedCell showTouched:YES]; } + } +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesCancelled:touches withEvent:event]; // Message superclass + + if (touchedCell != nil) { [touchedCell showTouched:NO]; touchedCell = nil; } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesEnded:touches withEvent:event]; // Message superclass + + if (touchedCell != nil) { [touchedCell showTouched:NO]; touchedCell = nil; } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesMoved:touches withEvent:event]; // Message superclass +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderViewController.h b/Night Reader/Night Reader/Night Reader/ReaderViewController.h new file mode 100644 index 0000000..456352e --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderViewController.h @@ -0,0 +1,46 @@ +// +// ReaderViewController.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "ReaderDocument.h" + +@class ReaderViewController; + +@protocol ReaderViewControllerDelegate + +@optional // Delegate protocols + +- (void)dismissReaderViewController:(ReaderViewController *)viewController; + +@end + +@interface ReaderViewController : UIViewController + +@property (nonatomic, unsafe_unretained, readwrite) id delegate; + +- (id)initWithReaderDocument:(ReaderDocument *)object; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ReaderViewController.m b/Night Reader/Night Reader/Night Reader/ReaderViewController.m new file mode 100644 index 0000000..cc232d4 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ReaderViewController.m @@ -0,0 +1,916 @@ +// +// ReaderViewController.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-07-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderConstants.h" +#import "ReaderViewController.h" +#import "ThumbsViewController.h" +#import "ReaderMainToolbar.h" +#import "ReaderMainPagebar.h" +#import "ReaderContentView.h" +#import "ReaderThumbCache.h" +#import "ReaderThumbQueue.h" + +#import + +@interface ReaderViewController () +@end + +@implementation ReaderViewController +{ + ReaderDocument *document; + + UIScrollView *theScrollView; + + ReaderMainToolbar *mainToolbar; + + ReaderMainPagebar *mainPagebar; + + NSMutableDictionary *contentViews; + + UIPrintInteractionController *printInteraction; + + NSInteger currentPage; + + CGSize lastAppearSize; + + NSDate *lastHideTime; + + BOOL isVisible; +} + +#pragma mark Constants + +#define PAGING_VIEWS 3 + +#define TOOLBAR_HEIGHT 44.0f +#define PAGEBAR_HEIGHT 48.0f + +#define TAP_AREA_SIZE 48.0f + +#pragma mark Properties + +@synthesize delegate; + +#pragma mark Support methods + +- (void)updateScrollViewContentSize +{ + NSInteger count = [document.pageCount integerValue]; + + if (count > PAGING_VIEWS) count = PAGING_VIEWS; // Limit + + CGFloat contentHeight = theScrollView.bounds.size.height; + + CGFloat contentWidth = (theScrollView.bounds.size.width * count); + + theScrollView.contentSize = CGSizeMake(contentWidth, contentHeight); +} + +- (void)updateScrollViewContentViews +{ + [self updateScrollViewContentSize]; // Update the content size + + NSMutableIndexSet *pageSet = [NSMutableIndexSet indexSet]; // Page set + + [contentViews enumerateKeysAndObjectsUsingBlock: // Enumerate content views + ^(id key, id object, BOOL *stop) + { + ReaderContentView *contentView = object; [pageSet addIndex:contentView.tag]; + } + ]; + + __block CGRect viewRect = CGRectZero; viewRect.size = theScrollView.bounds.size; + + __block CGPoint contentOffset = CGPointZero; NSInteger page = [document.pageNumber integerValue]; + + [pageSet enumerateIndexesUsingBlock: // Enumerate page number set + ^(NSUInteger number, BOOL *stop) + { + NSNumber *key = [NSNumber numberWithInteger:number]; // # key + + ReaderContentView *contentView = [contentViews objectForKey:key]; + + contentView.frame = viewRect; if (page == number) contentOffset = viewRect.origin; + + viewRect.origin.x += viewRect.size.width; // Next view frame position + } + ]; + + if (CGPointEqualToPoint(theScrollView.contentOffset, contentOffset) == false) + { + theScrollView.contentOffset = contentOffset; // Update content offset + } +} + +- (void)updateToolbarBookmarkIcon +{ + NSInteger page = [document.pageNumber integerValue]; + + BOOL bookmarked = [document.bookmarks containsIndex:page]; + + [mainToolbar setBookmarkState:bookmarked]; // Update +} + +- (void)showDocumentPage:(NSInteger)page +{ + if (page != currentPage) // Only if different + { + NSInteger minValue; NSInteger maxValue; + NSInteger maxPage = [document.pageCount integerValue]; + NSInteger minPage = 1; + + if ((page < minPage) || (page > maxPage)) return; + + if (maxPage <= PAGING_VIEWS) // Few pages + { + minValue = minPage; + maxValue = maxPage; + } + else // Handle more pages + { + minValue = (page - 1); + maxValue = (page + 1); + + if (minValue < minPage) + {minValue++; maxValue++;} + else + if (maxValue > maxPage) + {minValue--; maxValue--;} + } + + NSMutableIndexSet *newPageSet = [NSMutableIndexSet new]; + + NSMutableDictionary *unusedViews = [contentViews mutableCopy]; + + CGRect viewRect = CGRectZero; viewRect.size = theScrollView.bounds.size; + + for (NSInteger number = minValue; number <= maxValue; number++) + { + NSNumber *key = [NSNumber numberWithInteger:number]; // # key + + ReaderContentView *contentView = [contentViews objectForKey:key]; + + if (contentView == nil) // Create a brand new document content view + { + NSURL *fileURL = document.fileURL; NSString *phrase = document.password; // Document properties + + contentView = [[ReaderContentView alloc] initWithFrame:viewRect fileURL:fileURL page:number password:phrase]; + + [theScrollView addSubview:contentView]; [contentViews setObject:contentView forKey:key]; + + contentView.message = self; [newPageSet addIndex:number]; + } + else // Reposition the existing content view + { + contentView.frame = viewRect; [contentView zoomReset]; + + [unusedViews removeObjectForKey:key]; + } + + viewRect.origin.x += viewRect.size.width; + } + + [unusedViews enumerateKeysAndObjectsUsingBlock: // Remove unused views + ^(id key, id object, BOOL *stop) + { + [contentViews removeObjectForKey:key]; + + ReaderContentView *contentView = object; + + [contentView removeFromSuperview]; + } + ]; + + unusedViews = nil; // Release unused views + + CGFloat viewWidthX1 = viewRect.size.width; + CGFloat viewWidthX2 = (viewWidthX1 * 2.0f); + + CGPoint contentOffset = CGPointZero; + + if (maxPage >= PAGING_VIEWS) + { + if (page == maxPage) + contentOffset.x = viewWidthX2; + else + if (page != minPage) + contentOffset.x = viewWidthX1; + } + else + if (page == (PAGING_VIEWS - 1)) + contentOffset.x = viewWidthX1; + + if (CGPointEqualToPoint(theScrollView.contentOffset, contentOffset) == false) + { + theScrollView.contentOffset = contentOffset; // Update content offset + } + + if ([document.pageNumber integerValue] != page) // Only if different + { + document.pageNumber = [NSNumber numberWithInteger:page]; // Update page number + } + + NSURL *fileURL = document.fileURL; NSString *phrase = document.password; NSString *guid = document.guid; + + if ([newPageSet containsIndex:page] == YES) // Preview visible page first + { + NSNumber *key = [NSNumber numberWithInteger:page]; // # key + + ReaderContentView *targetView = [contentViews objectForKey:key]; + + [targetView showPageThumb:fileURL page:page password:phrase guid:guid]; + + [newPageSet removeIndex:page]; // Remove visible page from set + } + + [newPageSet enumerateIndexesWithOptions:NSEnumerationReverse usingBlock: // Show previews + ^(NSUInteger number, BOOL *stop) + { + NSNumber *key = [NSNumber numberWithInteger:number]; // # key + + ReaderContentView *targetView = [contentViews objectForKey:key]; + + [targetView showPageThumb:fileURL page:number password:phrase guid:guid]; + } + ]; + + newPageSet = nil; // Release new page set + + [mainPagebar updatePagebar]; // Update the pagebar display + + [self updateToolbarBookmarkIcon]; // Update bookmark + + currentPage = page; // Track current page number + } +} + +- (void)showDocument:(id)object +{ + [self updateScrollViewContentSize]; // Set content size + + [self showDocumentPage:[document.pageNumber integerValue]]; + + document.lastOpen = [NSDate date]; // Update last opened date + + isVisible = YES; // iOS present modal bodge +} + +#pragma mark UIViewController methods + +- (id)initWithReaderDocument:(ReaderDocument *)object +{ + id reader = nil; // ReaderViewController object + + if ((object != nil) && ([object isKindOfClass:[ReaderDocument class]])) + { + if ((self = [super initWithNibName:nil bundle:nil])) // Designated initializer + { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver:self selector:@selector(applicationWill:) name:UIApplicationWillTerminateNotification object:nil]; + + [notificationCenter addObserver:self selector:@selector(applicationWill:) name:UIApplicationWillResignActiveNotification object:nil]; + + [object updateProperties]; document = object; // Retain the supplied ReaderDocument object for our use + + [ReaderThumbCache touchThumbCacheWithGUID:object.guid]; // Touch the document thumb cache directory + + reader = self; // Return an initialized ReaderViewController object + } + } + + return reader; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + assert(document != nil); // Must have a valid ReaderDocument + + self.view.backgroundColor = [UIColor blackColor]; + + CGRect viewRect = self.view.bounds; // View controller's view bounds + + theScrollView = [[UIScrollView alloc] initWithFrame:viewRect]; // All + + theScrollView.scrollsToTop = NO; + theScrollView.pagingEnabled = YES; + theScrollView.delaysContentTouches = NO; + theScrollView.showsVerticalScrollIndicator = NO; + theScrollView.showsHorizontalScrollIndicator = NO; + theScrollView.contentMode = UIViewContentModeRedraw; + theScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + theScrollView.backgroundColor = [UIColor blackColor]; + theScrollView.userInteractionEnabled = YES; + theScrollView.autoresizesSubviews = NO; + theScrollView.delegate = self; + + [self.view addSubview:theScrollView]; + + CGRect toolbarRect = viewRect; + toolbarRect.size.height = TOOLBAR_HEIGHT; + + mainToolbar = [[ReaderMainToolbar alloc] initWithFrame:toolbarRect document:document]; // At top + + mainToolbar.delegate = self; + + [self.view addSubview:mainToolbar]; + + CGRect pagebarRect = viewRect; + pagebarRect.size.height = PAGEBAR_HEIGHT; + pagebarRect.origin.y = (viewRect.size.height - PAGEBAR_HEIGHT); + + mainPagebar = [[ReaderMainPagebar alloc] initWithFrame:pagebarRect document:document]; // At bottom + + mainPagebar.delegate = self; + + [self.view addSubview:mainPagebar]; + + UITapGestureRecognizer *singleTapOne = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; + singleTapOne.numberOfTouchesRequired = 1; singleTapOne.numberOfTapsRequired = 1; singleTapOne.delegate = self; + [self.view addGestureRecognizer:singleTapOne]; + + UITapGestureRecognizer *doubleTapOne = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)]; + doubleTapOne.numberOfTouchesRequired = 1; doubleTapOne.numberOfTapsRequired = 2; doubleTapOne.delegate = self; + [self.view addGestureRecognizer:doubleTapOne]; + + UITapGestureRecognizer *doubleTapTwo = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)]; + doubleTapTwo.numberOfTouchesRequired = 2; doubleTapTwo.numberOfTapsRequired = 2; doubleTapTwo.delegate = self; + [self.view addGestureRecognizer:doubleTapTwo]; + + [singleTapOne requireGestureRecognizerToFail:doubleTapOne]; // Single tap requires double tap to fail + + contentViews = [NSMutableDictionary new]; lastHideTime = [NSDate date]; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + if (CGSizeEqualToSize(lastAppearSize, CGSizeZero) == false) + { + if (CGSizeEqualToSize(lastAppearSize, self.view.bounds.size) == false) + { + [self updateScrollViewContentViews]; // Update content views + } + + lastAppearSize = CGSizeZero; // Reset view size tracking + } +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + if (CGSizeEqualToSize(theScrollView.contentSize, CGSizeZero)) // First time + { + [self performSelector:@selector(showDocument:) withObject:nil afterDelay:0.02]; + } + +#if (READER_DISABLE_IDLE == TRUE) // Option + + [UIApplication sharedApplication].idleTimerDisabled = YES; + +#endif // end of READER_DISABLE_IDLE Option +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; + + lastAppearSize = self.view.bounds.size; // Track view size + +#if (READER_DISABLE_IDLE == TRUE) // Option + + [UIApplication sharedApplication].idleTimerDisabled = NO; + +#endif // end of READER_DISABLE_IDLE Option +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; +} + +- (void)viewDidUnload +{ +#ifdef DEBUG + NSLog(@"%s", __FUNCTION__); +#endif + + mainToolbar = nil; mainPagebar = nil; + + theScrollView = nil; contentViews = nil; lastHideTime = nil; + + lastAppearSize = CGSizeZero; currentPage = 0; + + [super viewDidUnload]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +{ + if (isVisible == NO) return; // iOS present modal bodge + + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) + { + if (printInteraction != nil) [printInteraction dismissAnimated:NO]; + } +} + +- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration +{ + if (isVisible == NO) return; // iOS present modal bodge + + [self updateScrollViewContentViews]; // Update content views + + lastAppearSize = CGSizeZero; // Reset view size tracking +} + +/* +- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation +{ + //if (isVisible == NO) return; // iOS present modal bodge + + //if (fromInterfaceOrientation == self.interfaceOrientation) return; +} +*/ + +- (void)didReceiveMemoryWarning +{ +#ifdef DEBUG + NSLog(@"%s", __FUNCTION__); +#endif + + [super didReceiveMemoryWarning]; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark UIScrollViewDelegate methods + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + __block NSInteger page = 0; + + CGFloat contentOffsetX = scrollView.contentOffset.x; + + [contentViews enumerateKeysAndObjectsUsingBlock: // Enumerate content views + ^(id key, id object, BOOL *stop) + { + ReaderContentView *contentView = object; + + if (contentView.frame.origin.x == contentOffsetX) + { + page = contentView.tag; *stop = YES; + } + } + ]; + + if (page != 0) [self showDocumentPage:page]; // Show the page +} + +- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView +{ + [self showDocumentPage:theScrollView.tag]; // Show page + + theScrollView.tag = 0; // Clear page number tag +} + +#pragma mark UIGestureRecognizerDelegate methods + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)recognizer shouldReceiveTouch:(UITouch *)touch +{ + if ([touch.view isKindOfClass:[UIScrollView class]]) return YES; + + return NO; +} + +#pragma mark UIGestureRecognizer action methods + +- (void)decrementPageNumber +{ + if (theScrollView.tag == 0) // Scroll view did end + { + NSInteger page = [document.pageNumber integerValue]; + NSInteger maxPage = [document.pageCount integerValue]; + NSInteger minPage = 1; // Minimum + + if ((maxPage > minPage) && (page != minPage)) + { + CGPoint contentOffset = theScrollView.contentOffset; + + contentOffset.x -= theScrollView.bounds.size.width; // -= 1 + + [theScrollView setContentOffset:contentOffset animated:YES]; + + theScrollView.tag = (page - 1); // Decrement page number + } + } +} + +- (void)incrementPageNumber +{ + if (theScrollView.tag == 0) // Scroll view did end + { + NSInteger page = [document.pageNumber integerValue]; + NSInteger maxPage = [document.pageCount integerValue]; + NSInteger minPage = 1; // Minimum + + if ((maxPage > minPage) && (page != maxPage)) + { + CGPoint contentOffset = theScrollView.contentOffset; + + contentOffset.x += theScrollView.bounds.size.width; // += 1 + + [theScrollView setContentOffset:contentOffset animated:YES]; + + theScrollView.tag = (page + 1); // Increment page number + } + } +} + +- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer +{ + if (recognizer.state == UIGestureRecognizerStateRecognized) + { + CGRect viewRect = recognizer.view.bounds; // View bounds + + CGPoint point = [recognizer locationInView:recognizer.view]; + + CGRect areaRect = CGRectInset(viewRect, TAP_AREA_SIZE, 0.0f); // Area + + if (CGRectContainsPoint(areaRect, point)) // Single tap is inside the area + { + NSInteger page = [document.pageNumber integerValue]; // Current page # + + NSNumber *key = [NSNumber numberWithInteger:page]; // Page number key + + ReaderContentView *targetView = [contentViews objectForKey:key]; + + id target = [targetView processSingleTap:recognizer]; // Target + + if (target != nil) // Handle the returned target object + { + if ([target isKindOfClass:[NSURL class]]) // Open a URL + { + NSURL *url = (NSURL *)target; // Cast to a NSURL object + + if (url.scheme == nil) // Handle a missing URL scheme + { + NSString *www = url.absoluteString; // Get URL string + + if ([www hasPrefix:@"www"] == YES) // Check for 'www' prefix + { + NSString *http = [NSString stringWithFormat:@"http://%@", www]; + + url = [NSURL URLWithString:http]; // Proper http-based URL + } + } + + if ([[UIApplication sharedApplication] openURL:url] == NO) + { + #ifdef DEBUG + NSLog(@"%s '%@'", __FUNCTION__, url); // Bad or unknown URL + #endif + } + } + else // Not a URL, so check for other possible object type + { + if ([target isKindOfClass:[NSNumber class]]) // Goto page + { + NSInteger value = [target integerValue]; // Number + + [self showDocumentPage:value]; // Show the page + } + } + } + else // Nothing active tapped in the target content view + { + if ([lastHideTime timeIntervalSinceNow] < -0.75) // Delay since hide + { + if ((mainToolbar.hidden == YES) || (mainPagebar.hidden == YES)) + { + [mainToolbar showToolbar]; [mainPagebar showPagebar]; // Show + } + } + } + + return; + } + + CGRect nextPageRect = viewRect; + nextPageRect.size.width = TAP_AREA_SIZE; + nextPageRect.origin.x = (viewRect.size.width - TAP_AREA_SIZE); + + if (CGRectContainsPoint(nextPageRect, point)) // page++ area + { + [self incrementPageNumber]; return; + } + + CGRect prevPageRect = viewRect; + prevPageRect.size.width = TAP_AREA_SIZE; + + if (CGRectContainsPoint(prevPageRect, point)) // page-- area + { + [self decrementPageNumber]; return; + } + } +} + +- (void)handleDoubleTap:(UITapGestureRecognizer *)recognizer +{ + if (recognizer.state == UIGestureRecognizerStateRecognized) + { + CGRect viewRect = recognizer.view.bounds; // View bounds + + CGPoint point = [recognizer locationInView:recognizer.view]; + + CGRect zoomArea = CGRectInset(viewRect, TAP_AREA_SIZE, TAP_AREA_SIZE); + + if (CGRectContainsPoint(zoomArea, point)) // Double tap is in the zoom area + { + NSInteger page = [document.pageNumber integerValue]; // Current page # + + NSNumber *key = [NSNumber numberWithInteger:page]; // Page number key + + ReaderContentView *targetView = [contentViews objectForKey:key]; + + switch (recognizer.numberOfTouchesRequired) // Touches count + { + case 1: // One finger double tap: zoom ++ + { + [targetView zoomIncrement]; break; + } + + case 2: // Two finger double tap: zoom -- + { + [targetView zoomDecrement]; break; + } + } + + return; + } + + CGRect nextPageRect = viewRect; + nextPageRect.size.width = TAP_AREA_SIZE; + nextPageRect.origin.x = (viewRect.size.width - TAP_AREA_SIZE); + + if (CGRectContainsPoint(nextPageRect, point)) // page++ area + { + [self incrementPageNumber]; return; + } + + CGRect prevPageRect = viewRect; + prevPageRect.size.width = TAP_AREA_SIZE; + + if (CGRectContainsPoint(prevPageRect, point)) // page-- area + { + [self decrementPageNumber]; return; + } + } +} + +#pragma mark ReaderContentViewDelegate methods + +- (void)contentView:(ReaderContentView *)contentView touchesBegan:(NSSet *)touches +{ + if ((mainToolbar.hidden == NO) || (mainPagebar.hidden == NO)) + { + if (touches.count == 1) // Single touches only + { + UITouch *touch = [touches anyObject]; // Touch info + + CGPoint point = [touch locationInView:self.view]; // Touch location + + CGRect areaRect = CGRectInset(self.view.bounds, TAP_AREA_SIZE, TAP_AREA_SIZE); + + if (CGRectContainsPoint(areaRect, point) == false) return; + } + + [mainToolbar hideToolbar]; [mainPagebar hidePagebar]; // Hide + + lastHideTime = [NSDate date]; + } +} + +#pragma mark ReaderMainToolbarDelegate methods + +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar doneButton:(UIButton *)button +{ +#if (READER_STANDALONE == FALSE) // Option + + [document saveReaderDocument]; // Save any ReaderDocument object changes + + [[ReaderThumbQueue sharedInstance] cancelOperationsWithGUID:document.guid]; + + [[ReaderThumbCache sharedInstance] removeAllObjects]; // Empty the thumb cache + + if (printInteraction != nil) [printInteraction dismissAnimated:NO]; // Dismiss + + if ([delegate respondsToSelector:@selector(dismissReaderViewController:)] == YES) + { + [delegate dismissReaderViewController:self]; // Dismiss the ReaderViewController + } + else // We have a "Delegate must respond to -dismissReaderViewController: error" + { + NSAssert(NO, @"Delegate must respond to -dismissReaderViewController:"); + } + +#endif // end of READER_STANDALONE Option +} + +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar thumbsButton:(UIButton *)button +{ + if (printInteraction != nil) [printInteraction dismissAnimated:NO]; // Dismiss + + ThumbsViewController *thumbsViewController = [[ThumbsViewController alloc] initWithReaderDocument:document]; + + thumbsViewController.delegate = self; thumbsViewController.title = self.title; + + thumbsViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + thumbsViewController.modalPresentationStyle = UIModalPresentationFullScreen; + + [self presentViewController:thumbsViewController animated:NO completion:nil]; +} + +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar printButton:(UIButton *)button +{ +#if (READER_ENABLE_PRINT == TRUE) // Option + + Class printInteractionController = NSClassFromString(@"UIPrintInteractionController"); + + if ((printInteractionController != nil) && [printInteractionController isPrintingAvailable]) + { + NSURL *fileURL = document.fileURL; // Document file URL + + printInteraction = [printInteractionController sharedPrintController]; + + if ([printInteractionController canPrintURL:fileURL] == YES) // Check first + { + UIPrintInfo *printInfo = [NSClassFromString(@"UIPrintInfo") printInfo]; + + printInfo.duplex = UIPrintInfoDuplexLongEdge; + printInfo.outputType = UIPrintInfoOutputGeneral; + printInfo.jobName = document.fileName; + + printInteraction.printInfo = printInfo; + printInteraction.printingItem = fileURL; + printInteraction.showsPageRange = YES; + + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) + { + [printInteraction presentFromRect:button.bounds inView:button animated:YES completionHandler: + ^(UIPrintInteractionController *pic, BOOL completed, NSError *error) + { + #ifdef DEBUG + if ((completed == NO) && (error != nil)) NSLog(@"%s %@", __FUNCTION__, error); + #endif + } + ]; + } + else // Presume UIUserInterfaceIdiomPhone + { + [printInteraction presentAnimated:YES completionHandler: + ^(UIPrintInteractionController *pic, BOOL completed, NSError *error) + { + #ifdef DEBUG + if ((completed == NO) && (error != nil)) NSLog(@"%s %@", __FUNCTION__, error); + #endif + } + ]; + } + } + } + +#endif // end of READER_ENABLE_PRINT Option +} + +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar emailButton:(UIButton *)button +{ +#if (READER_ENABLE_MAIL == TRUE) // Option + + if ([MFMailComposeViewController canSendMail] == NO) return; + + if (printInteraction != nil) [printInteraction dismissAnimated:YES]; + + unsigned long long fileSize = [document.fileSize unsignedLongLongValue]; + + if (fileSize < (unsigned long long)15728640) // Check attachment size limit (15MB) + { + NSURL *fileURL = document.fileURL; NSString *fileName = document.fileName; // Document + + NSData *attachment = [NSData dataWithContentsOfURL:fileURL options:(NSDataReadingMapped|NSDataReadingUncached) error:nil]; + + if (attachment != nil) // Ensure that we have valid document file attachment data + { + MFMailComposeViewController *mailComposer = [MFMailComposeViewController new]; + + [mailComposer addAttachmentData:attachment mimeType:@"application/pdf" fileName:fileName]; + + [mailComposer setSubject:fileName]; // Use the document file name for the subject + + mailComposer.modalTransitionStyle = UIModalTransitionStyleCoverVertical; + mailComposer.modalPresentationStyle = UIModalPresentationFormSheet; + + mailComposer.mailComposeDelegate = self; // Set the delegate + + [self presentViewController:mailComposer animated:YES completion:nil]; + } + } + +#endif // end of READER_ENABLE_MAIL Option +} + +- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar markButton:(UIButton *)button +{ + if (printInteraction != nil) [printInteraction dismissAnimated:YES]; + + NSInteger page = [document.pageNumber integerValue]; + + if ([document.bookmarks containsIndex:page]) // Remove bookmark + { + [mainToolbar setBookmarkState:NO]; [document.bookmarks removeIndex:page]; + } + else // Add the bookmarked page index to the bookmarks set + { + [mainToolbar setBookmarkState:YES]; [document.bookmarks addIndex:page]; + } +} + +#pragma mark MFMailComposeViewControllerDelegate methods + +- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error +{ + #ifdef DEBUG + if ((result == MFMailComposeResultFailed) && (error != NULL)) NSLog(@"%@", error); + #endif + + [self dismissViewControllerAnimated:YES completion:nil]; // Dismiss +} + +#pragma mark ThumbsViewControllerDelegate methods + +- (void)dismissThumbsViewController:(ThumbsViewController *)viewController +{ + [self updateToolbarBookmarkIcon]; // Update bookmark icon + + [self dismissViewControllerAnimated:NO completion:nil]; // Dismiss +} + +- (void)thumbsViewController:(ThumbsViewController *)viewController gotoPage:(NSInteger)page +{ + [self showDocumentPage:page]; // Show the page +} + +#pragma mark ReaderMainPagebarDelegate methods + +- (void)pagebar:(ReaderMainPagebar *)pagebar gotoPage:(NSInteger)page +{ + [self showDocumentPage:page]; // Show the page +} + +#pragma mark UIApplication notification methods + +- (void)applicationWill:(NSNotification *)notification +{ + [document saveReaderDocument]; // Save any ReaderDocument object changes + + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) + { + if (printInteraction != nil) [printInteraction dismissAnimated:NO]; + } +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ThumbsMainToolbar.h b/Night Reader/Night Reader/Night Reader/ThumbsMainToolbar.h new file mode 100644 index 0000000..6d48f17 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ThumbsMainToolbar.h @@ -0,0 +1,47 @@ +// +// ThumbsMainToolbar.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "UIXToolbarView.h" + +@class ThumbsMainToolbar; + +@protocol ThumbsMainToolbarDelegate + +@required // Delegate protocols + +- (void)tappedInToolbar:(ThumbsMainToolbar *)toolbar doneButton:(UIButton *)button; +- (void)tappedInToolbar:(ThumbsMainToolbar *)toolbar showControl:(UISegmentedControl *)control; + +@end + +@interface ThumbsMainToolbar : UIXToolbarView + +@property (nonatomic, unsafe_unretained, readwrite) id delegate; + +- (id)initWithFrame:(CGRect)frame title:(NSString *)title; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ThumbsMainToolbar.m b/Night Reader/Night Reader/Night Reader/ThumbsMainToolbar.m new file mode 100644 index 0000000..9f8755b --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ThumbsMainToolbar.m @@ -0,0 +1,149 @@ +// +// ThumbsMainToolbar.m +// Reader v2.6.2 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderConstants.h" +#import "ThumbsMainToolbar.h" + +@implementation ThumbsMainToolbar + +#pragma mark Constants + +#define BUTTON_X 8.0f +#define BUTTON_Y 8.0f +#define BUTTON_SPACE 8.0f +#define BUTTON_HEIGHT 30.0f + +#define DONE_BUTTON_WIDTH 56.0f +#define SHOW_CONTROL_WIDTH 78.0f + +#define TITLE_HEIGHT 28.0f + +#pragma mark Properties + +@synthesize delegate; + +#pragma mark ThumbsMainToolbar instance methods + +- (id)initWithFrame:(CGRect)frame +{ + return [self initWithFrame:frame title:nil]; +} + +- (id)initWithFrame:(CGRect)frame title:(NSString *)title +{ + if ((self = [super initWithFrame:frame])) + { + CGFloat viewWidth = self.bounds.size.width; + + UIImage *imageH = [UIImage imageNamed:@"Reader-Button-H"]; + UIImage *imageN = [UIImage imageNamed:@"Reader-Button-N"]; + + UIImage *buttonH = [imageH stretchableImageWithLeftCapWidth:5 topCapHeight:0]; + UIImage *buttonN = [imageN stretchableImageWithLeftCapWidth:5 topCapHeight:0]; + + CGFloat titleX = BUTTON_X; CGFloat titleWidth = (viewWidth - (titleX + titleX)); + + UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeCustom]; + + doneButton.frame = CGRectMake(BUTTON_X, BUTTON_Y, DONE_BUTTON_WIDTH, BUTTON_HEIGHT); + [doneButton setTitle:NSLocalizedString(@"Done", @"button") forState:UIControlStateNormal]; + [doneButton setTitleColor:[UIColor colorWithWhite:0.0f alpha:1.0f] forState:UIControlStateNormal]; + [doneButton setTitleColor:[UIColor colorWithWhite:1.0f alpha:1.0f] forState:UIControlStateHighlighted]; + [doneButton addTarget:self action:@selector(doneButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; + [doneButton setBackgroundImage:buttonH forState:UIControlStateHighlighted]; + [doneButton setBackgroundImage:buttonN forState:UIControlStateNormal]; + doneButton.titleLabel.font = [UIFont systemFontOfSize:14.0f]; + doneButton.autoresizingMask = UIViewAutoresizingNone; + doneButton.exclusiveTouch = YES; + + [self addSubview:doneButton]; + + titleX += (DONE_BUTTON_WIDTH + BUTTON_SPACE); titleWidth -= (DONE_BUTTON_WIDTH + BUTTON_SPACE); + +#if (READER_BOOKMARKS == TRUE) // Option + + CGFloat showControlX = (viewWidth - (SHOW_CONTROL_WIDTH + BUTTON_SPACE)); + + UIImage *thumbsImage = [UIImage imageNamed:@"Reader-Thumbs"]; + UIImage *bookmarkImage = [UIImage imageNamed:@"Reader-Mark-Y"]; + NSArray *buttonItems = [NSArray arrayWithObjects:thumbsImage, bookmarkImage, nil]; + + UISegmentedControl *showControl = [[UISegmentedControl alloc] initWithItems:buttonItems]; + + showControl.frame = CGRectMake(showControlX, BUTTON_Y, SHOW_CONTROL_WIDTH, BUTTON_HEIGHT); + showControl.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + // showControl.segmentedControlStyle = UISegmentedControlStyleBar; + showControl.tintColor = [UIColor blackColor]; + showControl.selectedSegmentIndex = 0; // Default segment index + showControl.exclusiveTouch = YES; + + [showControl addTarget:self action:@selector(showControlTapped:) forControlEvents:UIControlEventValueChanged]; + + [self addSubview:showControl]; + + titleWidth -= (SHOW_CONTROL_WIDTH + BUTTON_SPACE); + +#endif // end of READER_BOOKMARKS Option + + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) + { + CGRect titleRect = CGRectMake(titleX, BUTTON_Y, titleWidth, TITLE_HEIGHT); + + UILabel *titleLabel = [[UILabel alloc] initWithFrame:titleRect]; + + titleLabel.textAlignment = NSTextAlignmentCenter; + titleLabel.font = [UIFont systemFontOfSize:19.0f]; + titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; + titleLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; + titleLabel.textColor = [UIColor redColor]; + titleLabel.shadowColor = [UIColor colorWithWhite:0.65f alpha:1.0f]; + titleLabel.backgroundColor = [UIColor blackColor]; + titleLabel.shadowOffset = CGSizeMake(0.0f, 1.0f); + titleLabel.adjustsFontSizeToFitWidth = YES; + titleLabel.MinimumScaleFactor = 14.0f; + titleLabel.text = title; + + [self addSubview:titleLabel]; + } + } + + return self; +} + +#pragma mark UISegmentedControl action methods + +- (void)showControlTapped:(UISegmentedControl *)control +{ + [delegate tappedInToolbar:self showControl:control]; +} + +#pragma mark UIButton action methods + +- (void)doneButtonTapped:(UIButton *)button +{ + [delegate tappedInToolbar:self doneButton:button]; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/ThumbsViewController.h b/Night Reader/Night Reader/Night Reader/ThumbsViewController.h new file mode 100644 index 0000000..dc1bcaa --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ThumbsViewController.h @@ -0,0 +1,66 @@ +// +// ThumbsViewController.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#import "ThumbsMainToolbar.h" +#import "ReaderThumbsView.h" + +@class ReaderDocument; +@class ThumbsViewController; + +@protocol ThumbsViewControllerDelegate + +@required // Delegate protocols + +- (void)thumbsViewController:(ThumbsViewController *)viewController gotoPage:(NSInteger)page; + +- (void)dismissThumbsViewController:(ThumbsViewController *)viewController; + +@end + +@interface ThumbsViewController : UIViewController + +@property (nonatomic, unsafe_unretained, readwrite) id delegate; + +- (id)initWithReaderDocument:(ReaderDocument *)object; + +@end + +#pragma mark - + +// +// ThumbsPageThumb class interface +// + +@interface ThumbsPageThumb : ReaderThumbView + +- (CGSize)maximumContentSize; + +- (void)showText:(NSString *)text; + +- (void)showBookmark:(BOOL)show; + +@end diff --git a/Night Reader/Night Reader/Night Reader/ThumbsViewController.m b/Night Reader/Night Reader/Night Reader/ThumbsViewController.m new file mode 100644 index 0000000..cb18b99 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/ThumbsViewController.m @@ -0,0 +1,480 @@ +// +// ThumbsViewController.m +// Reader v2.6.1 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ReaderConstants.h" +#import "ThumbsViewController.h" +#import "ReaderThumbRequest.h" +#import "ReaderThumbCache.h" +#import "ReaderDocument.h" + +#import + +@interface ThumbsViewController () + +@end + +@implementation ThumbsViewController +{ + ReaderDocument *document; + + ThumbsMainToolbar *mainToolbar; + + ReaderThumbsView *theThumbsView; + + NSMutableArray *bookmarked; + + CGPoint thumbsOffset; + CGPoint markedOffset; + + BOOL updateBookmarked; + BOOL showBookmarked; +} + +#pragma mark Constants + +#define TOOLBAR_HEIGHT 44.0f + +#define PAGE_THUMB_SMALL 160 +#define PAGE_THUMB_LARGE 256 + +#pragma mark Properties + +@synthesize delegate; + +#pragma mark UIViewController methods + +- (id)initWithReaderDocument:(ReaderDocument *)object +{ + id thumbs = nil; // ThumbsViewController object + + if ((object != nil) && ([object isKindOfClass:[ReaderDocument class]])) + { + if ((self = [super initWithNibName:nil bundle:nil])) // Designated initializer + { + updateBookmarked = YES; bookmarked = [NSMutableArray new]; // Bookmarked pages + + document = object; // Retain the ReaderDocument object for our use + + thumbs = self; // Return an initialized ThumbsViewController object + } + } + + return thumbs; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + assert(delegate != nil); assert(document != nil); + + self.view.backgroundColor = [UIColor blackColor]; + + CGRect viewRect = self.view.bounds; // View controller's view bounds + + NSString *toolbarTitle = [document.fileName stringByDeletingPathExtension]; + + CGRect toolbarRect = viewRect; toolbarRect.size.height = TOOLBAR_HEIGHT; + + mainToolbar = [[ThumbsMainToolbar alloc] initWithFrame:toolbarRect title:toolbarTitle]; // At top + + mainToolbar.delegate = self; + + [self.view addSubview:mainToolbar]; + + CGRect thumbsRect = viewRect; UIEdgeInsets insets = UIEdgeInsetsZero; + + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) + { + thumbsRect.origin.y += TOOLBAR_HEIGHT; thumbsRect.size.height -= TOOLBAR_HEIGHT; + } + else // Set UIScrollView insets for non-UIUserInterfaceIdiomPad case + { + insets.top = TOOLBAR_HEIGHT; + } + + theThumbsView = [[ReaderThumbsView alloc] initWithFrame:thumbsRect]; // Rest + + theThumbsView.contentInset = insets; theThumbsView.scrollIndicatorInsets = insets; + + theThumbsView.delegate = self; + + [self.view insertSubview:theThumbsView belowSubview:mainToolbar]; + + BOOL large = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad); + + NSInteger thumbSize = (large ? PAGE_THUMB_LARGE : PAGE_THUMB_SMALL); // Thumb dimensions + + [theThumbsView setThumbSize:CGSizeMake(thumbSize, thumbSize)]; // Thumb size based on device +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + [theThumbsView reloadThumbsCenterOnIndex:([document.pageNumber integerValue] - 1)]; // Page +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; +} + +- (void)viewDidUnload +{ +#ifdef DEBUG + NSLog(@"%s", __FUNCTION__); +#endif + + mainToolbar = nil; theThumbsView = nil; + + [super viewDidUnload]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return YES; +} + +/* +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +{ +} + +- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration +{ +} + +- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation +{ + //if (fromInterfaceOrientation == self.interfaceOrientation) return; +} +*/ + +- (void)didReceiveMemoryWarning +{ +#ifdef DEBUG + NSLog(@"%s", __FUNCTION__); +#endif + + [super didReceiveMemoryWarning]; +} + +#pragma mark ThumbsMainToolbarDelegate methods + +- (void)tappedInToolbar:(ThumbsMainToolbar *)toolbar showControl:(UISegmentedControl *)control +{ + switch (control.selectedSegmentIndex) + { + case 0: // Show all page thumbs + { + showBookmarked = NO; // Show all thumbs + + markedOffset = [theThumbsView insetContentOffset]; + + [theThumbsView reloadThumbsContentOffset:thumbsOffset]; + + break; // We're done + } + + case 1: // Show bookmarked thumbs + { + showBookmarked = YES; // Only bookmarked + + thumbsOffset = [theThumbsView insetContentOffset]; + + if (updateBookmarked == YES) // Update bookmarked list + { + [bookmarked removeAllObjects]; // Empty the list first + + [document.bookmarks enumerateIndexesUsingBlock: // Enumerate + ^(NSUInteger page, BOOL *stop) + { + [bookmarked addObject:[NSNumber numberWithInteger:page]]; + } + ]; + + markedOffset = CGPointZero; updateBookmarked = NO; // Reset + } + + [theThumbsView reloadThumbsContentOffset:markedOffset]; + + break; // We're done + } + } +} + +- (void)tappedInToolbar:(ThumbsMainToolbar *)toolbar doneButton:(UIButton *)button +{ + [delegate dismissThumbsViewController:self]; // Dismiss thumbs display +} + +#pragma mark UIThumbsViewDelegate methods + +- (NSUInteger)numberOfThumbsInThumbsView:(ReaderThumbsView *)thumbsView +{ + return (showBookmarked ? bookmarked.count : [document.pageCount integerValue]); +} + +- (id)thumbsView:(ReaderThumbsView *)thumbsView thumbCellWithFrame:(CGRect)frame +{ + return [[ThumbsPageThumb alloc] initWithFrame:frame]; +} + +- (void)thumbsView:(ReaderThumbsView *)thumbsView updateThumbCell:(ThumbsPageThumb *)thumbCell forIndex:(NSInteger)index +{ + CGSize size = [thumbCell maximumContentSize]; // Get the cell's maximum content size + + NSInteger page = (showBookmarked ? [[bookmarked objectAtIndex:index] integerValue] : (index + 1)); + + [thumbCell showText:[NSString stringWithFormat:@"%d", page]]; // Page number place holder + + [thumbCell showBookmark:[document.bookmarks containsIndex:page]]; // Show bookmarked status + + NSURL *fileURL = document.fileURL; NSString *guid = document.guid; NSString *phrase = document.password; // Document info + + ReaderThumbRequest *thumbRequest = [ReaderThumbRequest newForView:thumbCell fileURL:fileURL password:phrase guid:guid page:page size:size]; + + UIImage *image = [[ReaderThumbCache sharedInstance] thumbRequest:thumbRequest priority:YES]; // Request the thumbnail + + if ([image isKindOfClass:[UIImage class]]) [thumbCell showImage:image]; // Show image from cache +} + +- (void)thumbsView:(ReaderThumbsView *)thumbsView refreshThumbCell:(ThumbsPageThumb *)thumbCell forIndex:(NSInteger)index +{ + NSInteger page = (showBookmarked ? [[bookmarked objectAtIndex:index] integerValue] : (index + 1)); + + [thumbCell showBookmark:[document.bookmarks containsIndex:page]]; // Show bookmarked status +} + +- (void)thumbsView:(ReaderThumbsView *)thumbsView didSelectThumbWithIndex:(NSInteger)index +{ + NSInteger page = (showBookmarked ? [[bookmarked objectAtIndex:index] integerValue] : (index + 1)); + + [delegate thumbsViewController:self gotoPage:page]; // Show the selected page + + [delegate dismissThumbsViewController:self]; // Dismiss thumbs display +} + +- (void)thumbsView:(ReaderThumbsView *)thumbsView didPressThumbWithIndex:(NSInteger)index +{ + NSInteger page = (showBookmarked ? [[bookmarked objectAtIndex:index] integerValue] : (index + 1)); + + if ([document.bookmarks containsIndex:page]) [document.bookmarks removeIndex:page]; else [document.bookmarks addIndex:page]; + + updateBookmarked = YES; [thumbsView refreshThumbWithIndex:index]; // Refresh page thumb +} + +@end + +#pragma mark - + +// +// ThumbsPageThumb class implementation +// + +@implementation ThumbsPageThumb +{ + UIView *backView; + + UIView *tintView; + + UILabel *textLabel; + + UIImageView *bookMark; + + CGSize maximumSize; + + CGRect defaultRect; +} + +#pragma mark Constants + +#define CONTENT_INSET 8.0f + +#pragma mark ThumbsPageThumb instance methods + +- (CGRect)markRectInImageView +{ + CGRect iconRect = bookMark.frame; iconRect.origin.y = (-2.0f); + + iconRect.origin.x = (imageView.bounds.size.width - bookMark.image.size.width - 8.0f); + + return iconRect; // Frame position rect inside of image view +} + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) + { + imageView.contentMode = UIViewContentModeCenter; + + defaultRect = CGRectInset(self.bounds, CONTENT_INSET, CONTENT_INSET); + + maximumSize = defaultRect.size; // Maximum thumb content size + + CGFloat newWidth = ((defaultRect.size.width / 4.0f) * 3.0f); + + CGFloat offsetX = ((defaultRect.size.width - newWidth) / 2.0f); + + defaultRect.size.width = newWidth; defaultRect.origin.x += offsetX; + + imageView.frame = defaultRect; // Update the image view frame + + CGFloat fontSize = (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) ? 19.0f : 16.0f); + + textLabel = [[UILabel alloc] initWithFrame:defaultRect]; + + textLabel.autoresizesSubviews = NO; + textLabel.userInteractionEnabled = NO; + textLabel.contentMode = UIViewContentModeRedraw; + textLabel.autoresizingMask = UIViewAutoresizingNone; + textLabel.textAlignment = NSTextAlignmentCenter; + textLabel.font = [UIFont systemFontOfSize:fontSize]; + textLabel.textColor = [UIColor redColor]; + textLabel.backgroundColor = [UIColor blackColor]; + + [self insertSubview:textLabel belowSubview:imageView]; + + backView = [[UIView alloc] initWithFrame:defaultRect]; + + backView.autoresizesSubviews = NO; + backView.userInteractionEnabled = NO; + backView.contentMode = UIViewContentModeRedraw; + backView.autoresizingMask = UIViewAutoresizingNone; + backView.backgroundColor = [UIColor blackColor]; + +#if (READER_SHOW_SHADOWS == TRUE) // Option + + backView.layer.shadowOffset = CGSizeMake(0.0f, 1.0f); + backView.layer.shadowRadius = 3.0f; backView.layer.shadowOpacity = 1.0f; + backView.layer.shadowPath = [UIBezierPath bezierPathWithRect:backView.bounds].CGPath; + +#endif // end of READER_SHOW_SHADOWS Option + + [self insertSubview:backView belowSubview:textLabel]; +/* + tintView = [[UIView alloc] initWithFrame:imageView.bounds]; + + tintView.hidden = YES; + tintView.autoresizesSubviews = NO; + tintView.userInteractionEnabled = NO; + tintView.contentMode = UIViewContentModeRedraw; + tintView.autoresizingMask = UIViewAutoresizingNone; + tintView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.25f]; + + [imageView addSubview:tintView]; +*/ + UIImage *image = [UIImage imageNamed:@"Reader-Mark-Y"]; + + bookMark = [[UIImageView alloc] initWithImage:image]; + + bookMark.hidden = YES; + bookMark.autoresizesSubviews = NO; + bookMark.userInteractionEnabled = NO; + bookMark.contentMode = UIViewContentModeCenter; + bookMark.autoresizingMask = UIViewAutoresizingNone; + bookMark.frame = [self markRectInImageView]; + + [imageView addSubview:bookMark]; + } + + return self; +} + +- (CGSize)maximumContentSize +{ + return maximumSize; +} + +- (void)showImage:(UIImage *)image +{ + NSInteger x = (self.bounds.size.width / 2.0f); + NSInteger y = (self.bounds.size.height / 2.0f); + + CGPoint location = CGPointMake(x, y); // Center point + + CGRect viewRect = CGRectZero; viewRect.size = image.size; + + textLabel.bounds = viewRect; textLabel.center = location; // Position + + imageView.bounds = viewRect; imageView.center = location; imageView.image = image; + + bookMark.frame = [self markRectInImageView]; // Position bookmark image + + tintView.frame = imageView.bounds; backView.bounds = viewRect; backView.center = location; + +#if (READER_SHOW_SHADOWS == TRUE) // Option + + backView.layer.shadowPath = [UIBezierPath bezierPathWithRect:backView.bounds].CGPath; + +#endif // end of READER_SHOW_SHADOWS Option +} + +- (void)reuse +{ + [super reuse]; // Reuse thumb view + + textLabel.text = nil; textLabel.frame = defaultRect; + + imageView.image = nil; imageView.frame = defaultRect; + + bookMark.hidden = YES; bookMark.frame = [self markRectInImageView]; + + tintView.hidden = YES; tintView.frame = imageView.bounds; backView.frame = defaultRect; + +#if (READER_SHOW_SHADOWS == TRUE) // Option + + backView.layer.shadowPath = [UIBezierPath bezierPathWithRect:backView.bounds].CGPath; + +#endif // end of READER_SHOW_SHADOWS Option +} + +- (void)showBookmark:(BOOL)show +{ + bookMark.hidden = (show ? NO : YES); +} + +- (void)showTouched:(BOOL)touched +{ + tintView.hidden = (touched ? NO : YES); +} + +- (void)showText:(NSString *)text +{ + textLabel.text = text; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/UIXToolbarView.h b/Night Reader/Night Reader/Night Reader/UIXToolbarView.h new file mode 100644 index 0000000..724a3b8 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/UIXToolbarView.h @@ -0,0 +1,40 @@ +// +// UIXToolbarView.h +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface UIXToolbarView : UIView + +@end + +#pragma mark - + +// +// UIXToolbarShadow class interface +// + +@interface UIXToolbarShadow : UIView + +@end diff --git a/Night Reader/Night Reader/Night Reader/UIXToolbarView.m b/Night Reader/Night Reader/Night Reader/UIXToolbarView.m new file mode 100644 index 0000000..7f77a9b --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/UIXToolbarView.m @@ -0,0 +1,105 @@ +// +// UIXToolbarView.m +// Reader v2.6.0 +// +// Created by Julius Oklamcak on 2011-09-01. +// Copyright © 2011-2013 Julius Oklamcak. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to +// do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#import "UIXToolbarView.h" + +#import + +@implementation UIXToolbarView + +#pragma mark Constants + +#define SHADOW_HEIGHT 4.0f + +#pragma mark UIXToolbarView class methods + ++ (Class)layerClass +{ + return [CAGradientLayer class]; +} + +#pragma mark UIXToolbarView instance methods + +- (id)initWithFrame:(CGRect)frame +{ + +// UIColor *NightColor=[UIColor colorWithRed:70 green:0 blue:0 alpha:0.8]; + + UIColor *DarkNightColor=[UIColor blackColor]; + + if ((self = [super initWithFrame:frame])) + { + self.autoresizesSubviews = YES; + self.userInteractionEnabled = YES; + self.contentMode = UIViewContentModeRedraw; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth; + self.backgroundColor = DarkNightColor; + + } + + return self; +} + +@end + +#pragma mark - + +// +// UIXToolbarShadow class implementation +// + +@implementation UIXToolbarShadow + +#pragma mark UIXToolbarShadow class methods + ++ (Class)layerClass +{ + return [CAGradientLayer class]; +} + +#pragma mark UIXToolbarShadow instance methods + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) + { + self.autoresizesSubviews = NO; + self.userInteractionEnabled = NO; + self.contentMode = UIViewContentModeRedraw; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth; + self.backgroundColor = [UIColor blackColor]; + +/* CAGradientLayer *layer = (CAGradientLayer *)self.layer; + UIColor *blackColor = [UIColor colorWithWhite:0.24f alpha:1.0f]; + UIColor *clearColor = [UIColor colorWithWhite:0.24f alpha:0.0f]; + layer.colors = [NSArray arrayWithObjects:(id)blackColor.CGColor, (id)clearColor.CGColor, nil]; + + */ + } + + return self; +} + +@end diff --git a/Night Reader/Night Reader/Night Reader/en.lproj/InfoPlist.strings b/Night Reader/Night Reader/Night Reader/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Night Reader/Night Reader/Night Reader/en.lproj/MTViewController_iPhone.xib b/Night Reader/Night Reader/Night Reader/en.lproj/MTViewController_iPhone.xib new file mode 100644 index 0000000..2912fa3 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/en.lproj/MTViewController_iPhone.xib @@ -0,0 +1,262 @@ + + + + 1552 + 12E55 + 3084 + 1187.39 + 626.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 2083 + + + IBNSLayoutConstraint + IBProxyObject + IBUIButton + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + + + 292 + {{124, 252}, {73, 44}} + + + _NS:9 + NO + IBCocoaTouchFramework + 0 + 0 + 1 + Button + + 3 + MQA + + + 1 + MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + + + 3 + MC41AA + + + 2 + 15 + + + Helvetica-Bold + 15 + 16 + + + + {{0, 20}, {320, 548}} + + + + 3 + MC43NQA + + 2 + + + NO + + + IBUIScreenMetrics + + YES + + + + + + {320, 568} + {568, 320} + + + IBCocoaTouchFramework + Retina 4 Full Screen + 2 + + IBCocoaTouchFramework + + + + + + + view + + + + 7 + + + + didclickPDF: + + + 7 + + 11 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 6 + + + + + 3 + 0 + + 3 + 1 + + 252 + + 1000 + + 3 + 9 + 3 + + + + 9 + 0 + + 9 + 1 + + 0.0 + + 1000 + + 5 + 22 + 2 + + + + + + + 8 + + + + + 9 + + + + + 10 + + + + + + + MTViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 11 + + + + + MTViewController + UIViewController + + didclickPDF: + id + + + didclickPDF: + + didclickPDF: + id + + + + IBProjectSource + ./Classes/MTViewController.h + + + + NSLayoutConstraint + NSObject + + IBProjectSource + ./Classes/NSLayoutConstraint.h + + + + + 0 + IBCocoaTouchFramework + YES + 3 + YES + 2083 + + diff --git a/Night Reader/Night Reader/Night Reader/main.m b/Night Reader/Night Reader/Night Reader/main.m new file mode 100644 index 0000000..a6763e3 --- /dev/null +++ b/Night Reader/Night Reader/Night Reader/main.m @@ -0,0 +1,18 @@ +// +// main.m +// Night Reader +// +// Created by Norbert Schmidt on 11-08-13. +// Copyright (c) 2013 Norbert Schmidt. All rights reserved. +// + +#import + +#import "AppDelegate.h" + +int main(int argc, char *argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/Night Reader/Night Reader/Night Reader/redslider2.png b/Night Reader/Night Reader/Night Reader/redslider2.png new file mode 100644 index 0000000..7ceb8b6 Binary files /dev/null and b/Night Reader/Night Reader/Night Reader/redslider2.png differ diff --git a/Night Reader/Night Reader/iTunesArtwork b/Night Reader/Night Reader/iTunesArtwork new file mode 100644 index 0000000..28881a2 Binary files /dev/null and b/Night Reader/Night Reader/iTunesArtwork differ diff --git a/Night Reader/Night Reader/iTunesArtwork@2x b/Night Reader/Night Reader/iTunesArtwork@2x new file mode 100644 index 0000000..1b477aa Binary files /dev/null and b/Night Reader/Night Reader/iTunesArtwork@2x differ