diff options
author | Paul Gardiner <paul.gardiner@artifex.com> | 2013-09-24 16:58:27 +0100 |
---|---|---|
committer | Paul Gardiner <paul.gardiner@artifex.com> | 2013-09-24 16:58:27 +0100 |
commit | d4a69b3f24c3697cb0e77b7685468423c0949a71 (patch) | |
tree | 6677063014ee56fcdb45e8307805c87405d4b3b0 /platform/ios/Classes | |
parent | aa8bf97eb104986fae3245bc675bd50dded01804 (diff) | |
download | mupdf-d4a69b3f24c3697cb0e77b7685468423c0949a71.tar.xz |
iOS: add reflow mode
Diffstat (limited to 'platform/ios/Classes')
-rw-r--r-- | platform/ios/Classes/MuDocumentController.h | 5 | ||||
-rw-r--r-- | platform/ios/Classes/MuDocumentController.m | 112 | ||||
-rw-r--r-- | platform/ios/Classes/MuPageView.h | 46 | ||||
-rw-r--r-- | platform/ios/Classes/MuPageViewNormal.h | 41 | ||||
-rw-r--r-- | platform/ios/Classes/MuPageViewNormal.m (renamed from platform/ios/Classes/MuPageView.m) | 18 | ||||
-rw-r--r-- | platform/ios/Classes/MuPageViewReflow.h | 20 | ||||
-rw-r--r-- | platform/ios/Classes/MuPageViewReflow.m | 136 |
7 files changed, 298 insertions, 80 deletions
diff --git a/platform/ios/Classes/MuDocumentController.h b/platform/ios/Classes/MuDocumentController.h index b15d629a..3ec981f8 100644 --- a/platform/ios/Classes/MuDocumentController.h +++ b/platform/ios/Classes/MuDocumentController.h @@ -16,17 +16,19 @@ #import "MuOutlineController.h" #import "MuDocRef.h" -@interface MuDocumentController : UIViewController <UIScrollViewDelegate, UISearchBarDelegate> +@interface MuDocumentController : UIViewController <UIScrollViewDelegate, UIGestureRecognizerDelegate, UISearchBarDelegate> { fz_document *doc; MuDocRef *docRef; NSString *key; + BOOL reflowMode; MuOutlineController *outline; UIScrollView *canvas; UILabel *indicator; UISlider *slider; UISearchBar *searchBar; UIBarButtonItem *nextButton, *prevButton, *cancelButton, *searchButton, *outlineButton, *linkButton; + UIBarButtonItem *reflowButton; UIBarButtonItem *sliderWrapper; int searchPage; int cancelSearch; @@ -35,6 +37,7 @@ int height; int current; // currently visible page int scroll_animating; // stop view updates during scrolling animations + float scale; // scale applied to views (only used in reflow mode) } - (id) initWithFilename: (NSString*)nsfilename document: (MuDocRef *)aDoc; - (void) createPageView: (int)number; diff --git a/platform/ios/Classes/MuDocumentController.m b/platform/ios/Classes/MuDocumentController.m index 0b4f7d2e..29a9aadc 100644 --- a/platform/ios/Classes/MuDocumentController.m +++ b/platform/ios/Classes/MuDocumentController.m @@ -6,14 +6,17 @@ // #include "common.h" -#import "MuPageView.h" +#import "MuPageViewNormal.h" +#import "MuPageViewReflow.h" #import "MuDocumentController.h" #define GAP 20 #define INDICATOR_Y -44-24 #define SLIDER_W (width - GAP - 24) #define SEARCH_W (width - GAP - 170) +#define MIN_SCALE (1.0) +#define MAX_SCALE (5.0) static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_outline *outline, int level) { @@ -68,6 +71,11 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out return self; } +- (UIBarButtonItem *) resourceBasedButton:(NSString *)resource withAction:(SEL)selector +{ + return [[UIBarButtonItem alloc] initWithImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:resource ofType:@"png"]] style:UIBarButtonItemStylePlain target:self action:selector]; +} + - (void) loadView { [[NSUserDefaults standardUserDefaults] setObject: key forKey: @"OpenDocumentKey"]; @@ -87,7 +95,18 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out [canvas setShowsVerticalScrollIndicator: NO]; [canvas setDelegate: self]; - [canvas addGestureRecognizer: [[[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(onTap:)] autorelease]]; + UITapGestureRecognizer *tapRecog = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(onTap:)]; + tapRecog.delegate = self; + [canvas addGestureRecognizer: tapRecog]; + [tapRecog release]; + // In reflow mode, we need to track pinch gestures on the canvas and pass + // the scale changes to the subviews. + UIPinchGestureRecognizer *pinchRecog = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onPinch:)]; + pinchRecog.delegate = self; + [canvas addGestureRecognizer:pinchRecog]; + [pinchRecog release]; + + scale = 1.0; scroll_animating = NO; @@ -115,26 +134,14 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out // Set up the buttons on the navigation and search bar if (outline) { - outlineButton = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem: UIBarButtonSystemItemBookmarks - target:self action:@selector(onShowOutline:)]; + outlineButton = [self resourceBasedButton:@"ic_list" withAction:@selector(onShowOutline:)]; } - linkButton = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem: UIBarButtonSystemItemAction - target:self action:@selector(onToggleLinks:)]; - cancelButton = [[UIBarButtonItem alloc] - initWithTitle: @"Cancel" style: UIBarButtonItemStyleBordered - target:self action:@selector(onCancelSearch:)]; - searchButton = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem: UIBarButtonSystemItemSearch - target:self action:@selector(onShowSearch:)]; - prevButton = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem: UIBarButtonSystemItemRewind - target:self action:@selector(onSearchPrev:)]; - nextButton = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem: UIBarButtonSystemItemFastForward - target:self action:@selector(onSearchNext:)]; - + linkButton = [self resourceBasedButton:@"ic_link" withAction:@selector(onToggleLinks:)]; + cancelButton = [self resourceBasedButton:@"ic_cancel" withAction:@selector(onCancelSearch:)]; + searchButton = [self resourceBasedButton:@"ic_magnifying_glass" withAction:@selector(onShowSearch:)]; + prevButton = [self resourceBasedButton:@"ic_arrow_left" withAction:@selector(onSearchPrev:)]; + nextButton = [self resourceBasedButton:@"ic_arrow_right" withAction:@selector(onSearchNext:)]; + reflowButton = [self resourceBasedButton:@"ic_reflow" withAction:@selector(onToggleReflow:)]; searchBar = [[UISearchBar alloc] initWithFrame: CGRectMake(0,0,50,32)]; [searchBar setPlaceholder: @"Search"]; [searchBar setDelegate: self]; @@ -145,7 +152,7 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out [nextButton setEnabled: NO]; [[self navigationItem] setRightBarButtonItems: - [NSArray arrayWithObjects: searchButton, linkButton, outlineButton, nil]]; + [NSArray arrayWithObjects: searchButton, outlineButton, reflowButton, linkButton, nil]]; // TODO: add activityindicator to search bar @@ -159,6 +166,7 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out [indicator release]; indicator = nil; [slider release]; slider = nil; [sliderWrapper release]; sliderWrapper = nil; + [reflowButton release]; reflowButton = nil; [searchBar release]; searchBar = nil; [outlineButton release]; outlineButton = nil; [searchButton release]; searchButton = nil; @@ -204,13 +212,13 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out [canvas setContentSize: CGSizeMake(fz_count_pages(doc) * max_width, height)]; [canvas setContentOffset: CGPointMake(current * width, 0)]; - for (MuPageView *view in [canvas subviews]) { + for (UIView<MuPageView> *view in [canvas subviews]) { if ([view number] == current) { [view setFrame: CGRectMake([view number] * width, 0, width-GAP, height)]; [view willRotate]; } } - for (MuPageView *view in [canvas subviews]) { + for (UIView<MuPageView> *view in [canvas subviews]) { if ([view number] != current) { [view setFrame: CGRectMake([view number] * width, 0, width-GAP, height)]; [view willRotate]; @@ -279,7 +287,7 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out - (void) onToggleLinks: (id)sender { showLinks = !showLinks; - for (MuPageView *view in [canvas subviews]) + for (UIView<MuPageView> *view in [canvas subviews]) { if (showLinks) [view showLinks]; @@ -288,12 +296,19 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out } } +- (void) onToggleReflow: (id)sender +{ + reflowMode = !reflowMode; + [[canvas subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [self scrollViewDidScroll:canvas]; +} + - (void) onShowSearch: (id)sender { - [[self navigationItem] setTitleView: searchBar]; [[self navigationItem] setRightBarButtonItems: [NSArray arrayWithObjects: nextButton, prevButton, nil]]; [[self navigationItem] setLeftBarButtonItem: cancelButton]; + [[self navigationItem] setTitleView: searchBar]; [searchBar becomeFirstResponder]; } @@ -311,7 +326,7 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out - (void) resetSearch { searchPage = -1; - for (MuPageView *view in [canvas subviews]) + for (UIView<MuPageView> *view in [canvas subviews]) [view clearSearchResults]; } @@ -320,7 +335,7 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out printf("search found match on page %d\n", number); searchPage = number; [self gotoPage: number animated: NO]; - for (MuPageView *view in [canvas subviews]) + for (UIView<MuPageView> *view in [canvas subviews]) if ([view number] == number) [view showSearchResults: count]; else @@ -430,6 +445,13 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out [self gotoPage: number animated: NO]; } +- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + // For reflow mode, we load UIWebViews into the canvas. Returning YES + // here prevents them stealing our tap and pinch events. + return YES; +} + - (void) onTap: (UITapGestureRecognizer*)sender { CGPoint p = [sender locationInView: canvas]; @@ -450,6 +472,24 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out } } +- (void) onPinch:(UIPinchGestureRecognizer*)sender +{ + if (sender.state == UIGestureRecognizerStateBegan) + sender.scale = scale; + + if (sender.scale < MIN_SCALE) + sender.scale = MIN_SCALE; + + if (sender.scale > MAX_SCALE) + sender.scale = MAX_SCALE; + + if (sender.state == UIGestureRecognizerStateEnded) + scale = sender.scale; + + for (UIView<MuPageView> *view in [canvas subviews]) + [view setScale:sender.scale]; +} + - (void) scrollViewWillBeginDragging: (UIScrollView *)scrollView { [self hideNavigationBar]; @@ -474,13 +514,13 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out // swap the distant page views out NSMutableSet *invisiblePages = [[NSMutableSet alloc] init]; - for (MuPageView *view in [canvas subviews]) { + for (UIView<MuPageView> *view in [canvas subviews]) { if ([view number] != current) [view resetZoomAnimated: YES]; if ([view number] < current - 2 || [view number] > current + 2) [invisiblePages addObject: view]; } - for (MuPageView *view in invisiblePages) + for (UIView<MuPageView> *view in invisiblePages) [view removeFromSuperview]; [invisiblePages release]; // don't bother recycling them... @@ -498,11 +538,15 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out if (number < 0 || number >= fz_count_pages(doc)) return; int found = 0; - for (MuPageView *view in [canvas subviews]) + for (UIView<MuPageView> *view in [canvas subviews]) if ([view number] == number) found = 1; if (!found) { - MuPageView *view = [[MuPageView alloc] initWithFrame: CGRectMake(number * width, 0, width-GAP, height) document: docRef page: number]; + UIView<MuPageView> *view + = reflowMode + ? [[MuPageViewReflow alloc] initWithFrame:CGRectMake(number * width, 0, width-GAP, height) document:docRef page:number] + : [[MuPageViewNormal alloc] initWithFrame:CGRectMake(number * width, 0, width-GAP, height) document:docRef page:number]; + [view setScale:scale]; [canvas addSubview: view]; if (showLinks) [view showLinks]; @@ -533,7 +577,7 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out [UIView setAnimationDelegate: self]; [UIView setAnimationDidStopSelector: @selector(onGotoPageFinished)]; - for (MuPageView *view in [canvas subviews]) + for (UIView<MuPageView> *view in [canvas subviews]) [view resetZoomAnimated: NO]; [canvas setContentOffset: CGPointMake(number * width, 0)]; @@ -542,7 +586,7 @@ static void flattenOutline(NSMutableArray *titles, NSMutableArray *pages, fz_out [UIView commitAnimations]; } else { - for (MuPageView *view in [canvas subviews]) + for (UIView<MuPageView> *view in [canvas subviews]) [view resetZoomAnimated: NO]; [canvas setContentOffset: CGPointMake(number * width, 0)]; } diff --git a/platform/ios/Classes/MuPageView.h b/platform/ios/Classes/MuPageView.h index 58d1c8a7..963a4aa0 100644 --- a/platform/ios/Classes/MuPageView.h +++ b/platform/ios/Classes/MuPageView.h @@ -7,41 +7,13 @@ #import <UIKit/UIKit.h> -#undef ABS -#undef MIN -#undef MAX - -#include "mupdf/fitz.h" - -#import "MuDocRef.h" -#include "MuHitView.h" - -@interface MuPageView : UIScrollView <UIScrollViewDelegate> -{ - MuDocRef *docRef; - fz_document *doc; - fz_page *page; - int number; - UIActivityIndicatorView *loadingView; - UIImageView *imageView; - UIImageView *tileView; - MuHitView *hitView; - MuHitView *linkView; - CGSize pageSize; - CGRect tileFrame; - float tileScale; - BOOL cancel; -} -- (id) initWithFrame: (CGRect)frame document: (MuDocRef*)aDoc page: (int)aNumber; -- (void) displayImage: (UIImage*)image; -- (void) resizeImage; -- (void) loadPage; -- (void) loadTile; -- (void) willRotate; -- (void) resetZoomAnimated: (BOOL)animated; -- (void) showSearchResults: (int)count; -- (void) clearSearchResults; -- (void) showLinks; -- (void) hideLinks; -- (int) number; +@protocol MuPageView +-(int) number; +-(void) willRotate; +-(void) showLinks; +-(void) hideLinks; +-(void) showSearchResults: (int)count; +-(void) clearSearchResults; +-(void) resetZoomAnimated: (BOOL)animated; +-(void) setScale:(float)scale; @end diff --git a/platform/ios/Classes/MuPageViewNormal.h b/platform/ios/Classes/MuPageViewNormal.h new file mode 100644 index 00000000..1bd626fd --- /dev/null +++ b/platform/ios/Classes/MuPageViewNormal.h @@ -0,0 +1,41 @@ +// +// MuPageView.h +// MuPDF +// +// Copyright (c) 2013 Artifex Software, Inc. All rights reserved. +// + +#import <UIKit/UIKit.h> + +#undef ABS +#undef MIN +#undef MAX + +#include "mupdf/fitz.h" + +#import "MuHitView.h" +#import "MuPageView.h" +#import "MuDocRef.h" + +@interface MuPageViewNormal : UIScrollView <UIScrollViewDelegate,MuPageView> +{ + MuDocRef *docRef; + fz_document *doc; + fz_page *page; + int number; + UIActivityIndicatorView *loadingView; + UIImageView *imageView; + UIImageView *tileView; + MuHitView *hitView; + MuHitView *linkView; + CGSize pageSize; + CGRect tileFrame; + float tileScale; + BOOL cancel; +} +- (id) initWithFrame: (CGRect)frame document: (MuDocRef *)aDoc page: (int)aNumber; +- (void) displayImage: (UIImage*)image; +- (void) resizeImage; +- (void) loadPage; +- (void) loadTile; +@end diff --git a/platform/ios/Classes/MuPageView.m b/platform/ios/Classes/MuPageViewNormal.m index 6fa17f54..8cec8f02 100644 --- a/platform/ios/Classes/MuPageView.m +++ b/platform/ios/Classes/MuPageViewNormal.m @@ -19,12 +19,12 @@ static CGSize measurePage(fz_document *doc, fz_page *page) static void releasePixmap(void *info, const void *data, size_t size) { - if (queue) - dispatch_async(queue, ^{ - fz_drop_pixmap(ctx, info); - }); - else - fz_drop_pixmap(ctx, info); + if (queue) + dispatch_async(queue, ^{ + fz_drop_pixmap(ctx, info); + }); + else + fz_drop_pixmap(ctx, info); } static UIImage *newImageWithPixmap(fz_pixmap *pix) @@ -109,9 +109,9 @@ static UIImage *renderTile(fz_document *doc, fz_page *page, CGSize screenSize, C return newImageWithPixmap(pix); } -#import "MuPageView.h" +#import "MuPageViewNormal.h" -@implementation MuPageView +@implementation MuPageViewNormal - (id) initWithFrame: (CGRect)frame document: (MuDocRef *)aDoc page: (int)aNumber { @@ -461,4 +461,6 @@ static UIImage *renderTile(fz_document *doc, fz_page *page, CGSize screenSize, C [hitView setFrame: [imageView frame]]; } +- (void) setScale:(float)scale {} + @end diff --git a/platform/ios/Classes/MuPageViewReflow.h b/platform/ios/Classes/MuPageViewReflow.h new file mode 100644 index 00000000..28638bc8 --- /dev/null +++ b/platform/ios/Classes/MuPageViewReflow.h @@ -0,0 +1,20 @@ +// +// MuPageViewReflow.h +// MuPDF +// +// Copyright (c) 2013 Artifex Software, Inc. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "MuDocRef.h" +#import "MuPageView.h" + +@interface MuPageViewReflow : UIWebView <UIWebViewDelegate,UIGestureRecognizerDelegate,MuPageView> +{ + int number; + float scale; +} + +-(id) initWithFrame:(CGRect)frame document:(MuDocRef *)aDoc page:(int)aNumber; + +@end diff --git a/platform/ios/Classes/MuPageViewReflow.m b/platform/ios/Classes/MuPageViewReflow.m new file mode 100644 index 00000000..541731fb --- /dev/null +++ b/platform/ios/Classes/MuPageViewReflow.m @@ -0,0 +1,136 @@ +// +// MuPageViewReflow.m +// MuPDF +// +// Copyright (c) 2013 Artifex Software, Inc. All rights reserved. +// + +#include "common.h" +#import "MuPageViewReflow.h" + +NSString *textAsHtml(fz_document *doc, int pageNum) +{ + NSString *str = nil; + fz_page *page = NULL; + fz_text_sheet *sheet = NULL; + fz_text_page *text = NULL; + fz_device *dev = NULL; + fz_matrix ctm; + fz_buffer *buf = NULL; + fz_output *out = NULL; + + fz_var(page); + fz_var(sheet); + fz_var(text); + fz_var(dev); + fz_var(buf); + fz_var(out); + + fz_try(ctx) + { + int b, l, s, c; + + ctm = fz_identity; + sheet = fz_new_text_sheet(ctx); + text = fz_new_text_page(ctx); + dev = fz_new_text_device(ctx, sheet, text); + page = fz_load_page(doc, pageNum); + fz_run_page(doc, page, dev, &ctm, NULL); + fz_free_device(dev); + dev = NULL; + + fz_analyze_text(ctx, sheet, text); + + buf = fz_new_buffer(ctx, 256); + out = fz_new_output_with_buffer(ctx, buf); + fz_printf(out, "<html>\n"); + fz_printf(out, "<style>\n"); + fz_printf(out, "body{margin:0;}\n"); + fz_printf(out, "div.page{background-color:white;}\n"); + fz_printf(out, "div.block{margin:0pt;padding:0pt;}\n"); + fz_printf(out, "div.metaline{display:table;width:100%%}\n"); + fz_printf(out, "div.line{display:table-row;}\n"); + fz_printf(out, "div.cell{display:table-cell;padding-left:0.25em;padding-right:0.25em}\n"); + //fz_printf(out, "p{margin:0;padding:0;}\n"); + fz_printf(out, "</style>\n"); + fz_printf(out, "<body style=\"margin:0\"><div style=\"padding:10px\" id=\"content\">"); + fz_print_text_page_html(ctx, out, text); + fz_printf(out, "</div></body>\n"); + fz_printf(out, "<style>\n"); + fz_print_text_sheet(ctx, out, sheet); + fz_printf(out, "</style>\n</html>\n"); + fz_close_output(out); + out = NULL; + + str = [[[NSString alloc] initWithBytes:buf->data length:buf->len encoding:NSUTF8StringEncoding] autorelease]; + } + fz_always(ctx) + { + fz_free_text_page(ctx, text); + fz_free_text_sheet(ctx, sheet); + fz_free_device(dev); + fz_close_output(out); + fz_drop_buffer(ctx, buf); + fz_free_page(doc, page); + } + fz_catch(ctx) + { + str = nil; + } + + return str; +} + +@implementation MuPageViewReflow + +- (id)initWithFrame:(CGRect)frame document:(MuDocRef *)aDoc page:(int)aNumber +{ + self = [super initWithFrame:frame]; + if (self) { + number = aNumber; + scale = 1.0; + self.scalesPageToFit = NO; + [self setDelegate:self]; + dispatch_async(queue, ^{ + __block NSString *cont = [textAsHtml(aDoc->doc, aNumber) retain]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self loadHTMLString:cont baseURL:nil]; + }); + }); + } + return self; +} + +-(void) webViewDidFinishLoad:(UIWebView *)webView +{ + [self stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"document.getElementById('content').style.zoom=\"%f\"", scale]]; +} + +-(void) dealloc +{ + [self setDelegate:nil]; + [super dealloc]; +} + +-(int) number +{ + return number; +} + +-(void) willRotate {} +-(void) showLinks {} +-(void) hideLinks {} +-(void) showSearchResults: (int)count {} +-(void) clearSearchResults {} +-(void) resetZoomAnimated: (BOOL)animated +{ + [self.scrollView setContentOffset:CGPointZero animated:NO]; +} + +-(void) setScale:(float)aFloat +{ + scale = aFloat; + [self stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"document.getElementById('content').style.zoom=\"%f\"", scale]]; +} + +@end |