PDF Reader Source Code for iOS is now Available !!!

Hi customers and visitors !

This is PDF Reader source code for iOS.
Though pdf reader is not a part of our product(SkyEpub), SkyEpub has the function to read pdf files in iOS.

LGPL license
Here is the source code under LGPL license, so this source is copy left, license free and ok to use commercial purpose.
But in case of using this, please link this site and provide the original writer.

Thank you always.

PdfViewController.h
//
// PdfViewController.h
// CoreTest
//
// Created by SkyTree on 12. 2. 9..
// Copyright (c) 2012 Skytree Corporation. All rights reserved.
//

#import <UIKit/UIKit.h>

@class SearchResult;
@class PDFViewController;

@interface PDFPageInformation :NSObject{
NSInteger pageIndex;
NSInteger numberOfPages;
double pagePosition;
}
@property NSInteger pageIndex;
@property NSInteger numberOfPages;
@property double pagePosition;
@end

@protocol PDFViewControllerDelegate <NSObject>
@optional
-(void)pdfViewController:(PDFViewController*)pvc didDetectTapAtPositionInView:(CGPoint)positionInView positionInPage:(CGPoint)positionInPage;
-(void)pdfViewController:(PDFViewController*)pvc pageMoved:(PDFPageInformation*)pdfPageInformation;
-(void)pdfViewController:(PDFViewController*)pvc didSearchKey:(SearchResult*)searchResult;
-(void)pdfViewController:(PDFViewController*)pvc didFinishSearchAll:(SearchResult*)searchResult;
@end
@interface PDFViewController :UIViewController {
NSString *filePath; // full path for pdf file to be opened
NSString* version;
id <PDFViewControllerDelegate> delegate;
int transitionType;
}
@property (nonatomic,retain) NSString* version;
@property (nonatomic,retain) NSString* filePath;
@property (nonatomic,retain) id <PDFViewControllerDelegate> delegate;
@property int transitionType;

-(id)initWithStartPageIndex:(int)pageIndex;
-(void)gotoPageByPageIndex:(int)pageIndex;
-(void)gotoPrevPage;
-(void)gotoNextPage;
-(UIImage*)getPageImage:(int)pageIndex;
-(UIImage*)getThumbImage:(int)pageIndex;
-(void)searchKey:(NSString*)key;
-(int)getNumberOfPages;
-(void)debug0;
-(void)debug1;
-(void)debug2;
@end

PDFViewController.m


//
// PDFViewController.m
// CoreTest
//
// Created by SkyTree on 12. 2. 9..
// Copyright (c) 2012 Skytree Corporation. All rights reserved.
//

#import "PDFViewController.h"
#import <QuartzCore/QuartzCore.h>
#import "PDFSearcher.h"
#import "ReflowableViewController.h"

#define UP 0
#define DOWN 1
#define PageTransitionNone 0
#define PageTransitionSlide 1
#define PageTransitionCurl 2

#define offsetTop 0
#define offsetCenter 1
#define offsetBottom 2

#define DEBUGMODE 0

void XLog(NSString * formatString, ...);

@implementation PDFPageInformation
@synthesize pageIndex,numberOfPages,pagePosition;
@end

@interface PDFViewController() <UIWebViewDelegate,UIGestureRecognizerDelegate,PDFViewControllerDelegate> {
UIWebView *webView;
UIView *contentView;
int currentPageIndex;
int oldPageIndex;
int pastIndex;
int numberOfPages;
double pageHeight;
double documentHeight;
CGPDFDocumentRef pdfDocument;
UIScrollView *innerScrollView;
int lastOffset;
int direction;
BOOL isLocked;
BOOL btFlag;
double defaultHeight;
int currentPageOffsetMode;
int startPageIndex;
}

@property (strong, nonatomic) UIWebView *webView;
@property (retain, nonatomic) UIView *contentView;
@property (strong, nonatomic) UIScrollView *innerScrollView;
@property CGPDFDocumentRef pdfDocument;
@property double documentHeight;
@property int numberOfPages,currentPageIndex,oldPageIndex;
@property double pageHeight;
@property int startPageIndex;

-(int)getNumberOfPages;
-(void)gotoPage:(int)index;
-(void)makeXIB;
-(void)recalcFrames;
-(void)recalcLayout;
-(void)customizeWebView;
-(void)scrollToOffsetY:(float)offsetY;
-(void)recalcPageInformation;
-(BOOL)hasString:(NSString*)key inPage:(int)pageIndex;
-(void)writeImage:(UIImage *)image withFilename:(NSString* )fileName;
-(void)stickToTopOfNextPage;
-(void)stickToTopOfPrevPage;
-(void)stickToTopOfThisPage;
-(void)stickToBottomOfThisPage;
-(void)stickToBottomOfPrevPage;
-(void)gotoPageWithoutRecalc:(int)index;
-(BOOL)isLineAbove;
-(BOOL)isLineBelow;
-(BOOL)isUpperSide;
-(void)toLeftSwipeDetected:(UIGestureRecognizer *)sender;
-(void)toRightSwipeDetected:(UIGestureRecognizer *)sender;
-(void)animatePage:(bool)toLeft;
-(CGRect)getPageRect:(int)index;
-(void)displayAdjacentPages;
-(CGPoint)getOffsetByPageIndex:(int)pageIndex;
-(void)gotoStartPage;

@end
@implementation PDFViewController
@synthesize webView,contentView;
@synthesize documentHeight;
@synthesize pdfDocument,innerScrollView;
@synthesize pageHeight,numberOfPages,currentPageIndex,oldPageIndex;
@synthesize version,filePath;
@synthesize delegate;
@synthesize startPageIndex;
@synthesize transitionType;

-(id)init {
self = [super init];
if (self) {
// Custom initialization
self.startPageIndex = -100;
}
return self;
}

-(id)initWithStartPageIndex:(int)pageIndex {
self = [super init];
if (self) {
// Custom initialization
self.startPageIndex = pageIndex;
}
return self;
}

-(BOOL)isAbove5 {
@autoreleasepool {
// A system version of 3.1 or greater is required to use CADisplayLink. The NSTimer
// class is used as fallback when it isn't available.
NSString *reqSysVer = @"5.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) {
return YES;
}else {
return NO;
}
}
}

-(void)debug0 {
// [self gotoPrevPage];
// for (int i=0; i<self.numberOfPages; i++) {
// CGRect ri = [self getPageRect:i];
// XLog(@"pageIndex:i width:%f height:%f",ri.size.width*innerScrollView.zoomScale,ri.size.height*innerScrollView.zoomScale);
// }
[self gotoPage:10];
}

-(void)debug1 {
[self gotoNextPage];

// XLog(@"%f",[self getOuterHeightFromWebView:self.webView]);
}

-(void)debug2 {
[self displayAdjacentPages];
}

-(BOOL)isOriginalZoomScale {
if (innerScrollView.contentSize.width == self.contentView.bounds.size.width) return YES;
else return NO;
}

-(BOOL)isPortrait {
if (self.view.frame.size.height >self.view.frame.size.width) return YES;
else return NO;
}

-(CGRect)getPageRect:(int)index {
CGRect result = CGRectZero;

CGPDFPageRef page = CGPDFDocumentGetPage(pdfDocument,index+1); // assuming all the pages are the same size!

CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(page);

CGPDFArrayRef pageBoxArray;
if(!CGPDFDictionaryGetArray(pageDictionary, "MediaBox", &pageBoxArray)) {
return result; // we've got something wrong here!!!
}

int pageBoxArrayCount = CGPDFArrayGetCount( pageBoxArray );
CGPDFReal pageCoords[4];
for( int k = 0; k < pageBoxArrayCount; ++k )
{
CGPDFObjectRef pageRectObj;
if(!CGPDFArrayGetObject(pageBoxArray, k, &pageRectObj))
{
return result;
}

CGPDFReal pageCoord;
if(!CGPDFObjectGetValue(pageRectObj, kCGPDFObjectTypeReal, &pageCoord)) {
return result;
}

pageCoords[k] = pageCoord;
}

// XLog(@"PDF coordinates -- bottom left x %f ",pageCoords[0]); // should be 0
// XLog(@"PDF coordinates -- bottom left y %f ",pageCoords[1]); // should be 0
// XLog(@"PDF coordinates -- top right x %f ",pageCoords[2]);
// XLog(@"PDF coordinates -- top right y %f ",pageCoords[3]);
// XLog(@"-- i.e. PDF page is %f wide and %f high",pageCoords[2],pageCoords[3]);

// **** now to convert a point on the page from PDF coordinates to iOS coordinates.

double PDFHeight, PDFWidth;
PDFWidth = pageCoords[2];
PDFHeight = pageCoords[3];

// the size of your iOS view or image into which you have rendered your PDF page
// in this example full screen iPad in portrait orientation
double iOSWidth = 768.0;
double iOSHeight = 1024.0;

// the PDF co-ordinate values you want to convert
double PDFxval = 89; // or whatever
double PDFyval = 520; // or whatever

// the iOS coordinate values
int iOSxval, iOSyval;

iOSxval = (int) PDFxval * (iOSWidth/PDFWidth);
iOSyval = (int) (PDFHeight - PDFyval) * (iOSHeight/PDFHeight);

// XLog(@"PDF: %f %f",PDFxval,PDFyval);
// XLog(@"iOS: %i %i",iOSxval,iOSyval);

result = CGRectMake(0,0,pageCoords[2],pageCoords[3]);
return result;
}

-(double)getDefaultHeight {
float ah=0;
for (int i=0; i<self.numberOfPages; i++) {
CGRect ri = [self getPageRect:i];
ah += ri.size.height;
XLog(@"pageIndex:%d width:%f height:%f",i,ri.size.width*innerScrollView.zoomScale,ri.size.height*innerScrollView.zoomScale);
}
return ah;
}

-(double)getScaleFactor {
double factor = documentHeight/defaultHeight;
return factor;
}

-(void)recalcDefault {
defaultHeight = [self getDefaultHeight];
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}

-(int)getTransitionType {
return self.transitionType;
}

-(IBAction)tapPressed:(UIGestureRecognizer *)sender {
CGPoint point = [sender locationInView:self.webView];
CGPoint pointInView = [sender locationInView:self.view];
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(pdfViewController:didDetectTapAtPositionInView:positionInPage:)]) {
[self.delegate pdfViewController:self didDetectTapAtPositionInView:pointInView positionInPage:point];
}
if (pointInView.x <= (self.view.bounds.size.width)/5) {
[self toLeftSwipeDetected:sender];
}
if (pointInView.x >= (self.view.bounds.size.width)*.8) {
[self toRightSwipeDetected:sender]; }
}

-(IBAction)doubleTapPressed:(id)sender {
// innerScrollView.zoomScale = 1.0f;
}

-(IBAction)toLeftSwipeDetected:(UIGestureRecognizer *)sender {
if (currentPageIndex==0) return;
[self animatePage:YES];
[self gotoPrevPage];
}

-(IBAction)toRightSwipeDetected:(UIGestureRecognizer *)sender {
if (currentPageIndex>=(numberOfPages-1)) return;
[self animatePage:NO];
[self gotoNextPage];
}
-(void)makeGestures {
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapPressed:)];
tapRecognizer.delegate = self;
[webView addGestureRecognizer:tapRecognizer];

UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapPressed:)];
doubleTapRecognizer.delegate = self;
doubleTapRecognizer.numberOfTapsRequired = 2;
[webView addGestureRecognizer:doubleTapRecognizer];

UISwipeGestureRecognizer *toLeftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(toLeftSwipeDetected:)];
toLeftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
toLeftSwipeRecognizer.delaysTouchesBegan = YES;
[self.webView addGestureRecognizer:toLeftSwipeRecognizer];

UISwipeGestureRecognizer *toRightSwipeRecognizer =[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(toRightSwipeDetected:)];
toRightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
toRightSwipeRecognizer.delaysTouchesBegan = YES;
[self.webView addGestureRecognizer:toRightSwipeRecognizer];
}

-(void)makeXIB {
self.contentView = [[UIView alloc]initWithFrame:self.view.bounds];
self.contentView.contentMode = UIViewContentModeScaleToFill;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
self.contentView.backgroundColor = [UIColor grayColor];
[self.view addSubview:self.contentView];

self.webView = [[UIWebView alloc]initWithFrame:self.view.bounds];
self.webView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
self.webView.alpha = 1;
[self.webView setScalesPageToFit:YES];
// self.webView.contentMode = UIViewContentModeCenter;
self.webView.contentMode = UIViewContentModeScaleToFill;
self.webView.opaque = NO;
self.webView.delegate = self;
self.webView.userInteractionEnabled = YES;
self.webView.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.webView];
[self customizeWebView];

[self.view addSubview:self.contentView];
[self makeGestures];
}

-(void)animatePage:(bool)toLeft {
@autoreleasepool {
[UIView beginAnimations:@"page transition" context:nil];
[UIView setAnimationDuration:0.5];
transitionType = [self getTransitionType];

if (transitionType==PageTransitionSlide) {
NSString *transition = kCATransitionPush; //[transitions objectAtIndex:transitionsIndex];
NSString *moveDirection;
if (!toLeft) {
moveDirection = kCATransitionFromRight;
}else {
moveDirection = kCATransitionFromLeft;
}
//----------------------------------------------------------------------------
CATransition *animation = [CATransition animation];
[animation setDelegate:self];
[animation setDuration:0.3f];
[animation setType:transition];
[animation setSubtype:moveDirection];
//----------------------------------------------------------------------------
[[self.contentView layer] addAnimation:animation forKey:@"transitionViewAnimation"]; }
else {
[UIView setAnimationTransition:toLeft ? UIViewAnimationTransitionNone : UIViewAnimationTransitionNone forView:self.contentView cache:NO];
}
[UIView commitAnimations];
}
}
-(void)loadPDF:(NSString*)pdfPath {
NSURLCache *sharedCache = [NSURLCache sharedURLCache];
[sharedCache removeAllCachedResponses];
NSURL *targetURL = [[NSURL alloc]initFileURLWithPath:pdfPath];
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
pdfDocument = CGPDFDocumentCreateWithURL((__bridge_retained CFURLRef)targetURL);
numberOfPages = CGPDFDocumentGetNumberOfPages(pdfDocument);
[self.webView loadRequest:request];
}

- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.
NSURLCache *sharedCache = [NSURLCache sharedURLCache];
[sharedCache removeAllCachedResponses];
}

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}

-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
[self recalcPageInformation];
XLog(@"zoomScale %f",innerScrollView.zoomScale);
}

#pragma mark - View lifecycle

/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
}
*/

-(double)getOuterHeightFromWebView:(UIWebView *)view {
[view stringByEvaluatingJavaScriptFromString:@"function getOuterHeightl() { alert('outerHeight:'+window.outerHeight); return window.outerHeight;}"];
NSString*ret = [view stringByEvaluatingJavaScriptFromString:@"window.outerHeight"];
return [ret doubleValue];
}
-(void)searchKey:(NSString*)key {
int numberOfSearched=0;
for (int i=0; i<self.numberOfPages; i++) {
BOOL res = [self hasString:key inPage:i];
if (res) {
numberOfSearched++;
SearchResult* sr = [[SearchResult alloc]init];
sr.pageIndex = i;
sr.numberOfSearched = numberOfSearched;
if (self.delegate!=nil && [self.delegate respondsToSelector:@selector(pdfViewController:didSearchKey:)]) {
[delegate pdfViewController:self didSearchKey:sr];
}
}
}
SearchResult* sr = [[SearchResult alloc]init];
sr.numberOfSearched = numberOfSearched;
if (self.delegate!=nil && [self.delegate respondsToSelector:@selector(pdfViewController:didFinishSearchAll:)]) {
[delegate pdfViewController:self didFinishSearchAll:sr];
}
}

-(void)scrollToOffsetY:(float)offsetY {
// [self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.scrollTo(0.0, %f)", offsetY]];
// self.innerScrollView.contentOffset = CGPointMake(0, offsetY); // 이건 동작한다.
// if (offsetY<0) offsetY = 0;
[self.innerScrollView setContentOffset:CGPointMake(0, offsetY) animated:NO];

}

-(void)scrollToOffset:(CGPoint)offset {
if (offset.y<0) offset.y = 0;
[self.innerScrollView setContentOffset:offset animated:NO];
}

-(void)displayAdjacentPages {
XLog(@"======================================================");
XLog(@"pageInformation for index :%d",currentPageIndex);

for (int i=0; i<[innerScrollView.subviews count]; i++) {
UIView *view = [innerScrollView.subviews objectAtIndex:i];
XLog(@"%d tag:%d %f %f %f %f",i,view.tag,view.frame.origin.x,view.frame.origin.y,view.frame.size.width,view.frame.size.height);
}

UIView *firstView = [innerScrollView.subviews objectAtIndex:0];

for (int i=0; i<[firstView.subviews count]; i++) {
UIView *view = [firstView.subviews objectAtIndex:i];
XLog(@"%d tag:%d %f %f %f %f",i,view.tag,view.frame.origin.x,view.frame.origin.y,view.frame.size.width,view.frame.size.height);
}
}

-(CGPoint)getOffsetByPageIndex:(int)pageIndex offsetMode:(int)offsetMode {
int index;
double offsetX,offsetY;
CGPoint result = CGPointZero;
double zoomScale = innerScrollView.zoomScale;
double pageSpace;

if ([self isAbove5]) {
[self scrollToOffsetY:self.pageHeight*pageIndex]; // 근접위치로 이동
UIView *firstView = [innerScrollView.subviews objectAtIndex:0];

if ([firstView.subviews count]<2) return CGPointMake(-1,-1);

UIView *nextView = [firstView.subviews objectAtIndex:0];
UIView *preView =[firstView.subviews objectAtIndex:1];

pageSpace = fabs(nextView.frame.origin.y-(preView.frame.origin.y+preView.frame.size.height));
pageSpace = pageSpace-1;
if (pageSpace>50) pageSpace = 8;

for (int i=0; i<[firstView.subviews count]; i++) {
UIView *pageView = [firstView.subviews objectAtIndex:i];
pageView.hidden = NO;
index = (pageView.tag-1000000);
if (index==pageIndex) {
double deltaX,deltaY;
if (offsetMode==offsetTop) {
deltaX = (self.contentView.bounds.size.width-(pageView.frame.size.width)*zoomScale)/2;
deltaY = pageSpace*zoomScale;
}else if (offsetMode==offsetCenter) {
deltaX = (self.contentView.bounds.size.width-(pageView.frame.size.width)*zoomScale)/2;
deltaY = (self.contentView.bounds.size.height-(pageView.frame.size.height)*zoomScale)/2;
}else if (offsetMode==offsetBottom) {
deltaX = (self.contentView.bounds.size.width-(pageView.frame.size.width)*zoomScale)/2;
deltaY = (self.contentView.bounds.size.height-(pageView.frame.size.height)*zoomScale)-pageSpace*zoomScale;
}

offsetX = (pageView.frame.origin.x)*zoomScale-deltaX;
offsetY = (pageView.frame.origin.y)*zoomScale-deltaY;

// XLog(@"ch %f cw %f deltaX %f deltaY %f offsetX %f offsetY %f",self.contentView.bounds.size.width,self.contentView.bounds.size.height,deltaX,deltaY,offsetX,offsetY);

result.x = offsetX;
result.y = offsetY;

return result;
}
}
}else {
double deltaX,deltaY;
double pageSpace = 8.0;
if (offsetMode==offsetTop) {
deltaX = (self.contentView.bounds.size.width-(self.contentView.bounds.size.width)*zoomScale)/2;
deltaY = 0;
}else if (offsetMode==offsetCenter) {
deltaX = (self.contentView.bounds.size.width-(self.contentView.bounds.size.width)*zoomScale)/2;
deltaY = (self.contentView.bounds.size.height-pageHeight)/2;
}else {
deltaX = (self.contentView.bounds.size.width-(self.contentView.bounds.size.width)*zoomScale)/2;
deltaY = (self.contentView.bounds.size.height-pageHeight)-pageSpace*zoomScale;
}
result.x = -deltaX;
result.y = pageHeight*pageIndex -deltaY;
}
return result;
}
-(int)getPageIndexByOffset:(double)offset {
UIView *firstView = [innerScrollView.subviews objectAtIndex:0];

double zoomScale = innerScrollView.zoomScale;
double minDelta = 100000000000;
double signDelta;
double pageCenter;
int minIndex = -1;
int targetIndex;

if ([self isAbove5]) {
for (int i=0; i<[firstView.subviews count]; i++) {
UIView *pageView = [firstView.subviews objectAtIndex:i];
pageCenter = (pageView.frame.origin.y+(pageView.frame.size.height)/2)*zoomScale;
double delta = pageCenter-(offset+contentView.bounds.size.height/2);
if (fabs(delta)<=minDelta) {
minDelta = fabs(delta);
signDelta = delta;
minIndex = i;
}
}
UIView *targetView;
if (minIndex!=-1) {
targetView = [firstView.subviews objectAtIndex:minIndex];
targetIndex = targetView.tag - 1000000;
}else targetIndex = 0;
currentPageOffsetMode = offsetCenter;
if (signDelta<0) {
double lineDelta = fabs(minDelta-((targetView.frame.size.height/2)*zoomScale));
if (lineDelta<contentView.bounds.size.height/2) {
currentPageOffsetMode = offsetBottom;
}
}else if (signDelta>0) {
double lineDelta = fabs(minDelta-((targetView.frame.size.height/2)*zoomScale));
if (lineDelta<(contentView.bounds.size.height/2)) {
currentPageOffsetMode = offsetTop;
}
}
}else {
int ti = (offset/documentHeight)*numberOfPages;
for (int pi = ti-1;pi<=ti+1; pi++) {
double py,pageCenter;
py = (pageHeight)*pi;
pageCenter = py+(pageHeight/2);
double delta = pageCenter-(offset+contentView.bounds.size.height/2);
if (fabs(delta)<=minDelta) {
minDelta = fabs(delta);
signDelta = delta;
minIndex = pi;
}
}
targetIndex = minIndex;
currentPageOffsetMode = offsetCenter;
if (signDelta<0) {
double lineDelta = fabs(minDelta-((pageHeight/2)*zoomScale));
if (lineDelta<contentView.bounds.size.height/2) {
currentPageOffsetMode = offsetBottom;
}
}else if (signDelta>0) {
double lineDelta = fabs(minDelta-((pageHeight/2)*zoomScale));
if (lineDelta<(contentView.bounds.size.height/2)) {
currentPageOffsetMode = offsetTop;
}
}
}
return targetIndex;
}

-(CGPoint)getTopOffsetByPageIndex:(int)pageIndex {
return [self getOffsetByPageIndex:pageIndex offsetMode:offsetTop];
}

-(CGPoint)getCenterOffsetByPageIndex:(int)pageIndex {
return [self getOffsetByPageIndex:pageIndex offsetMode:offsetCenter];
}

-(CGPoint)getBottomOffsetByPageIndex:(int)pageIndex {
return [self getOffsetByPageIndex:pageIndex offsetMode:offsetBottom];
}

-(void)gotoPage:(int)index {
if (index<0) return;
currentPageIndex = index;
CGPoint res = [self getCenterOffsetByPageIndex:index];
[self scrollToOffset:res];
[self performSelector:@selector(recalcPageInformation) withObject:nil afterDelay:.3];
}

-(void)gotoNextPage {
currentPageIndex++;
if (currentPageIndex>=numberOfPages) {
currentPageIndex=numberOfPages-1;
return;
}
[self gotoPage:currentPageIndex];
}
-(void)gotoPrevPage {
currentPageIndex--;
if (currentPageIndex<0) {
currentPageIndex = 0;
return;
}
[self gotoPage:currentPageIndex];
}
-(void)gotoPageByPageIndex:(int)pageIndex {
[self gotoPage:pageIndex];
}
-(int)getNumberOfPages {
return numberOfPages;
}

-(void)monitor {
if (direction==UP) {
XLog(@"UP");
}else {
XLog(@"Down");
}

if (currentPageOffsetMode==offsetTop) {
XLog(@"offsetTop");
}else if (currentPageOffsetMode==offsetCenter) {
XLog(@"offsetCenter");
}else {
XLog(@"offsetBottom");
}
}

-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
UIView *viewForZoom=nil;
if ([self isAbove5]) {
viewForZoom = [innerScrollView.subviews objectAtIndex:0];
}else {
viewForZoom = [innerScrollView.subviews objectAtIndex:10];
}
return viewForZoom;
}

-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale{
[self recalcPageInformation];
}

-(void)scrollViewDidZoom:(UIScrollView *)scrollView{
[self recalcPageInformation];
XLog(@"zoomScale %f",scrollView.zoomScale);
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (isLocked) return;
[self recalcPageInformation];
if (lastOffset > scrollView.contentOffset.y) {
direction = UP;
}else if (lastOffset < scrollView.contentOffset.y) {
direction = DOWN;
}
lastOffset = scrollView.contentOffset.y;
}

-(void) scrollViewDidEndDragging:(UIScrollView*)scrollView willDecelerate:(BOOL)willDecelerate {
if (currentPageOffsetMode==offsetTop) {
[self scrollToOffset:[self getTopOffsetByPageIndex:currentPageIndex]];
}else if (currentPageOffsetMode == offsetBottom) {
[self scrollToOffset:[self getBottomOffsetByPageIndex:currentPageIndex]];
}
}

-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
[self monitor];
if (direction==UP) { 
if (currentPageOffsetMode==offsetBottom) {
[self scrollToOffset:[self getTopOffsetByPageIndex:currentPageIndex+1]];
}else if (currentPageOffsetMode==offsetTop) {
[self scrollToOffset:[self getBottomOffsetByPageIndex:currentPageIndex]];
}else if (currentPageOffsetMode==offsetCenter) {
[self scrollToOffset:[self getTopOffsetByPageIndex:currentPageIndex]];
}
return;
}
if (direction==DOWN) { 
if (currentPageOffsetMode==offsetTop) {
[self scrollToOffset:[self getBottomOffsetByPageIndex:currentPageIndex-1]];
}else if (currentPageOffsetMode==offsetCenter) {
[self scrollToOffset:[self getBottomOffsetByPageIndex:currentPageIndex]];
}else if (currentPageOffsetMode==offsetBottom) {
[self scrollToOffset:[self getTopOffsetByPageIndex:currentPageIndex+1]];
}
return;
}
}

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
}

-(void)customizeWebView {
for (id subview in webView.subviews) {
if ([[subview class] isSubclassOfClass: [UIScrollView class]]) {
innerScrollView = ((UIScrollView *)subview);
}
}
self.innerScrollView.delegate = self;
}

-(BOOL)hasString:(NSString*)key inPage:(int)pageIndex{
@autoreleasepool {
CGPDFPageRef pageRef = CGPDFDocumentGetPage(pdfDocument, pageIndex+1);
PDFSearcher *searcher = [[PDFSearcher alloc]init];
return [searcher page:pageRef containsString:key];
}
}

-(void)processPageChanged {
@autoreleasepool {
PDFPageInformation* pageInformation = [[PDFPageInformation alloc]init];
pageInformation.pageIndex = self.currentPageIndex;
pageInformation.numberOfPages = self.numberOfPages;
pageInformation.pagePosition = ((double)currentPageIndex/(double)numberOfPages);
[self.delegate pdfViewController:self pageMoved:pageInformation];
}
}

-(void)recalcPageInformation {
if (numberOfPages==0) return;
self.documentHeight = self.innerScrollView.contentSize.height;
self.pageHeight = ((double)self.documentHeight/(double)(self.numberOfPages));
CGPoint offset = [self.innerScrollView.layer.presentationLayer bounds].origin;
float contentOffsetY = offset.y;
self.currentPageIndex = [self getPageIndexByOffset:contentOffsetY];
if (self.currentPageIndex!=self.oldPageIndex) {
if (currentPageIndex==0 && (oldPageIndex!=1 )) return;
[self processPageChanged];
}
oldPageIndex = currentPageIndex;
}

-(void)writeImage:(UIImage *)image withFilename:(NSString* )filename {
@autoreleasepool{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) ;
NSString *imagePath = [paths objectAtIndex:0] ;
NSString *filepath = [NSString stringWithFormat:@"%@/%@", imagePath, filename] ;
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(image)];
[imageData writeToFile:filepath atomically:YES];
}
}

-(UIImage *)getPageImage:(int)pageIndex isThumb:(BOOL)isThumb {
CGFloat width = 60.0;
CGPDFPageRef page = CGPDFDocumentGetPage(pdfDocument, pageIndex+1);
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
float pdfScale;
if (isThumb) pdfScale = width/pageRect.size.width;
else pdfScale = 1.0;
pageRect.size = CGSizeMake(pageRect.size.width*pdfScale, pageRect.size.height*pdfScale);
pageRect.origin = CGPointZero;

UIGraphicsBeginImageContext(pageRect.size);

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,pageRect);

CGContextSaveGState(context);

// ***********
// Next 3 lines makes the rotations so that the page look in the right direction
// ***********
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page, kCGPDFMediaBox, pageRect, 0, true));

CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);

UIImage *thm = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();
return thm;
}

-(UIImage*)getPageImage:(int)pageIndex {
return [self getPageImage:pageIndex isThumb:NO];
}

-(UIImage*)getThumbImage:(int)pageIndex {
return [self getPageImage:pageIndex isThumb:YES];
}

-(BOOL)isReadyFor5 {
UIView *firstView = [innerScrollView.subviews objectAtIndex:0];
if ([firstView.subviews count]>0) return YES;
else return NO;
}

-(void)gotoStartPage {
if ([self isAbove5]) {
while (![self isReadyFor5]) {
[NSThread sleepForTimeInterval:.1];
}
}

[self recalcPageInformation];

if (startPageIndex!=-100) {
[self gotoPage:startPageIndex];
self.currentPageIndex = startPageIndex;
oldPageIndex = startPageIndex;
[self processPageChanged];
}
}

-(void)webViewDidFinishLoad:(UIWebView *)webView {
if ([self isAbove5]) [self performSelectorInBackground:@selector(gotoStartPage) withObject:nil];
else [self performSelector:@selector(gotoStartPage) withObject:nil afterDelay:0];
}

-(void)unload {
NSURLCache *sharedCache = [NSURLCache sharedURLCache];
[sharedCache removeAllCachedResponses];
return;
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
[self makeXIB];
// [self performSelector:@selector(loadPDF:) withObject:self.filePath afterDelay:.2];
[self loadPDF:self.filePath];
self.transitionType = PageTransitionSlide;
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.webView = nil;
self.contentView = nil;
}

-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:(BOOL)animated]; // Call the super class implementation.
self.webView = nil;
self.contentView = nil;
}

-(void)viewWillAppear:(BOOL)animated {

}

@end