Is there a way to programmatically scroll to a PDF page within a UIWebView?
Asked Answered
M

5

9

It is possible to use JavaScript to set the pixel y-offset of a UIWebView, e.g.:

[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"scrollTo(0, %d)", offset]];

So is there a way to get:

  1. The pixel height of an individual page of a PDF within the web view?
  2. The size of the gap between pages?

Is this information available from the UIWebView or can it be calculated through alternative means?

I'm thinking that if I have the number of pages (via CGPDFDocumentGetNumberOfPages), the pixel height gap between pages, or the pixel height of an individual page, I can calculate the offset to use with the JavaScript call. Then I just wire up a UIButton or UISlider to move between pages.


EDIT I

I have a solution, but it uses UIWebDocumentView, a private subview of UIWebView.

I create a view controller called PDFViewerViewController, which is a subclass of WebViewerViewController, which itself is a view controller that contains a UIToolbar, a UIWebView, and conforms to the UIWebViewDelegate protocol.

My PDFViewerViewController calculates some information about the enclosing web view and the PDF data, after the web view delegate method -webViewDidFinishLoad: gets called.

This information is used to calculate an approximate per-page offset that gets fed to the web view via JavaScript.

PDFViewerViewController.h

#import <UIKit/UIKit.h>
#import "WebViewerViewController.h"

@interface UIWebDocumentView : NSObject {}
@end

@interface PDFViewerViewController : WebViewerViewController {
    NSUInteger offset;
    NSUInteger currentPage;
    NSUInteger documentPages;
    CGFloat documentHeight;
    CGFloat pageHeight;
}

@property (assign) NSUInteger offset;
@property (assign) NSUInteger currentPage;
@property (assign) NSUInteger documentPages;
@property (assign) CGFloat documentHeight;
@property (assign) CGFloat pageHeight;

@end

PDFViewerViewController.m

#import "PDFViewerViewController.h"

@implementation PDFViewerViewController

@synthesize offset;
@synthesize currentPage;
@synthesize documentPages;
@synthesize documentHeight;
@synthesize pageHeight;

- (void) viewDidLoad {
    [super viewDidLoad];

    UIBarButtonItem *_leftArrow = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ArrowLeft.png"] style:UIBarButtonItemStylePlain target:self action:@selector(leftArrow:)];
    UIBarButtonItem *_flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
    UIBarButtonItem *_rightArrow = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ArrowRight.png"] style:UIBarButtonItemStylePlain target:self action:@selector(rightArrow:)];
    [[super.viewerToolbarView toolbar] setItems:[NSArray arrayWithObjects:_leftArrow, _flexibleSpace, _rightArrow, nil]];
    [_leftArrow release];
    [_flexibleSpace release];
    [_rightArrow release];

    self.currentPage = 0;
}

- (void) webViewDidFinishLoad:(UIWebView *)_webView {
    for (UIView *_subview in [[[_webView subviews] objectAtIndex:0] subviews]) {
        if ([_subview isKindOfClass:[UIWebDocumentView class]]) {
            self.documentHeight = _subview.bounds.size.height;
        }
    }

    CGPDFDocumentRef pdfDocument = CGPDFDocumentCreateWithURL((CFURLRef)baseURL);
    self.documentPages = CGPDFDocumentGetNumberOfPages(pdfDocument);
    CGPDFDocumentRelease(pdfDocument);

    self.pageHeight = (self.documentHeight + (10 * self.documentPages)) / self.documentPages;
    self.currentPage = 1;
    self.offset = 0;
}

- (void) leftArrow:(id)_param {
    if (self.currentPage == 1) 
        return;
    self.offset -= (NSUInteger)self.pageHeight;
    self.currentPage--;
    [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"scrollTo(0, %d)", (self.offset * 2)]];
}

- (void) rightArrow:(id)_param {
    if (self.currentPage == self.documentPages) 
        return;
    self.offset += (NSUInteger)self.pageHeight;
    self.currentPage++;
    [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"scrollTo(0, %d)", (self.offset * 2)]];
}

Some observations

  1. The offset calculation isn't page-perfect. If the PDF document isn't 8.5 x 11 (e.g. A4) then the offset error gets worse more quickly.

  2. The self.currentPage property doesn't get updated when scrolling through the web view by way of touch-drag. One might drag a few pages, and then touching the left or right arrow on the toolbar will cause the offset to unexpectedly move to a previous page.

  3. This solution uses UIWebDocumentView, which is private and may likely cause app rejection.

I think I'll file a feature enhancement request with Apple.

Has anyone built a non-UIWebView-based PDF viewer (with source code)?

Mcginty answered 18/12, 2009 at 12:6 Comment(7)
If that doesn't work, did you try putting the PDF in an iframe (standard frames discouraged by iPhone docs but iframes may be okay) and calling a JavaScript in the parent HTML document?Disincline
I don't think the number of pages is available from the web view. Your suggestion seems okay. Try setting scalesPagesToFit=YES then page height should be browser view/frame height.Disincline
I suppose you're scuppered when the user changes the zoom level! Tricky. Did you try inspecting the DOM model when the PDF is embedded in an iframe - though this might start to move towards non-public API use perhaps?Disincline
If I log the frame and bounds of the web view in -webViewDidFinishLoad:, I get values that do not reflect the actual height of the content. (For example, for a four-page PDF, I should get something much more than 456 pixels for a PDF displayed in portrait orientation.)Mcginty
ah yes that is the other fascinating thing Apple have added. The coordinates in the JavaScript reflect coordinates as if the iPhone screen is a desktop monitor size and not a iPhone size. They have done this to maximize compatibility with desktops I suppose. I couldn't find a way to get iPhone screen coordinates in JavaScript without the extra conceptual-mash of it being made to appear like the coordinate system of a desktop. I think you need to be friends with someone at Apple to get permission to use private APIs or something.Disincline
Just thought I'd add I found this area of the iPhone SDK a right old pain, if that's any help.Disincline
In response to your added PDFViewerViewController.m listing: I wonder if you could schedule a background task that iterates pages with CGPDFDocumentGetPage and CGPDFPageGetBoxRect(page,kCGPDFMediaBox), translates (and adds any kludging factors) from "default user space units" into whatever UIWebView seems to use, show a spinner, and leave doc positioned at start until correct offset found I suppose, but doesn't this mean loading the PDF into memory twice over, and relying on UIWebView not changing its behaviour? What if its a big PDF... Anyway good luck with the feature request.Disincline
H
4

So this post has been open for quite a while now but since it's the first entry on a Google search for "scroll pdf uiwebview" I thought I'd provide my take at this for future readers.

You can indeed scroll to a particular page in a PDF document using nothing but pure Java Script. I've posted sample code on our blog (http://apptech.next-munich.com/2011/02/pdf-in-uiwebview.html) but here's a short summary of what you need to do:

  1. Use the window.outerHeight DOM property to find the total height of the rendered PDF document.
  2. Use the window.innerHeight DOM property to find the height of the browser window in browser coordinates.
  3. Jump to the page using the window.scrollTo(x, y) function.

There is one catch, though, that you cannot just device the outerHeight by the number of pages and jump to what you get. Instead, you will have to multiply by the innerHeight/webView.bounds.size.height ratio.

Heraldry answered 11/2, 2011 at 9:22 Comment(2)
As a follow-up to my answer: this technique no longer works starting with iOS 5.Heraldry
This also assumed that the pdf consisted of pages of the same orientation.Weslee
D
1

I think that you'll have to load and display the PDF yourself using the PDF APIs rather than UIWebView. Here is an possible idea of how to simulate snapping scrolling to rectangular grid boundaries which may help you make the experience more like a UIWebView for your users.

iPhone sdk Cocoa Touch - Pass touches down from parent UIView to child UIScrollview

Disincline answered 18/12, 2009 at 13:14 Comment(0)
L
1

I have seen your edit in question. Has anyone built a non-UIWebView-based PDF viewer (with source code)?

See my question.

Reading PDF files as string through iPhone application

It has functionality to display each pdf page in an image view. user can easily change pages. But the missing points are as follows.

  • user can not make any selection - as it is not text but an image
  • user can't find any text.
  • user can not zoom ( oh yes - you can put image view in scroll view for zooming functionality )
Lannie answered 4/3, 2010 at 9:46 Comment(0)
A
1

The uiwebview commonly used to load pdf's has it's own scrollview object that you can make calls to. try something like this:

    [webview.scrollView setContentOffset:CGPointMake(0, webview.scrollView.contentOffset.y + 100) animated:YES];    

This will scroll the view for the pdf down 100 pixels. As for getting the pagenumber of the pdf, I can only imagine that javascript is what you need to take a look at. Or, If you simply know the number of pixels on each page of the pdf, then increment or decrement as necessary. :)

Annmaria answered 14/9, 2012 at 0:56 Comment(0)
W
1

I have faced a similar problem, in which I had to scroll to particular page in PDF (which is displayed using UIWebView) on selection of a cell corresponding to that page in tableView (thumbnail tableview).

So I wrote a piece of code which allows you to scroll to particular page in PDF.

I am not using any javascript code. I am just keeping track of the zoom scale applied to the original PDF page each time and calculating the updated offset required to scroll to each page. So, this works even if the PDF is in zoomed state.

You can find the code here.

Wehrmacht answered 24/9, 2013 at 5:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.