display specific pdf page in the UIWebview ios - ios

I am currently working on a project and I have ios need to display a pdf file.
However i want choose the page to display.
For example see page 10 of 37 in a UIWebView.
I have not found a way to cleanly separate the pages of a pfd.
thank you for your help.

Use UIWebView's delegate method to do this:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//Check if file still loading
if(!webView.isLoading)
{
//now traverse to specific page
[self performSelector:#selector(traverseInWebViewWithPage) withObject:nil afterDelay:0.1];
}
}
Now add below method to traverse to your page. Note need valid PDF file path and provide your valid specific page no you want traverse in PDF file.
-(void)traverseInWebViewWithPage
{
//Get total pages in PDF File ----------- PDF File name here ---------------
NSString *strPDFFilePath = [[NSBundle mainBundle] pathForResource:#"yourPDFFileNameHere" ofType:#"pdf"];
NSInteger totalPDFPages = [self getTotalPDFPages:strPDFFilePath];
//Get total PDF pages height in webView
CGFloat totalPDFHeight = yourWebViewPDF.scrollView.contentSize.height;
NSLog ( #"total pdf height: %f", totalPDFHeight);
//Calculate page height of single PDF page in webView
NSInteger horizontalPaddingBetweenPages = 10*(totalPDFPages+1);
CGFloat pageHeight = (totalPDFHeight-horizontalPaddingBetweenPages)/(CGFloat)totalPDFPages;
NSLog ( #"pdf page height: %f", pageHeight);
//scroll to specific page --------------- here your page number -----------
NSInteger specificPageNo = 2;
if(specificPageNo <= totalPDFPages)
{
//calculate offset point in webView
CGPoint offsetPoint = CGPointMake(0, (10*(specificPageNo-1))+(pageHeight*(specificPageNo-1)));
//set offset in webView
[yourWebViewPDF.scrollView setContentOffset:offsetPoint];
}
}
For calculation of total PDF pages
-(NSInteger)getTotalPDFPages:(NSString *)strPDFFilePath
{
NSURL *pdfUrl = [NSURL fileURLWithPath:strPDFFilePath];
CGPDFDocumentRef document = CGPDFDocumentCreateWithURL((CFURLRef)pdfUrl);
size_t pageCount = CGPDFDocumentGetNumberOfPages(document);
return pageCount;
}
Enjoy coding .....

You can use setContentOffset property of webview to show that page,
[[webView scrollView] setContentOffset:CGPointMake(0,10*pageheight) animated:YES];
where pageheight=your page height, 10 is your page no,

Related

UIWebView "document.height" evaluation returns wrong height on older iphones (specifically 4s and 5)

The problem is that, i have a tableview with several cells, and have a webView in one of these cells. By disabling the scroll on the webview, i should have a static web content view scrolling in the tableView, not by itself, which all works great.
In the webViewDidFinishLoad method i call
[[webView stringByEvaluatingJavaScriptFromString: #"document.height"] floatValue];
then set this value in heightForRowAtIndexPath by reloading the table. It all works great on iPhone 6 and up, but on 4s and 5, the bottom of my web content is clipped off. The returned height difference is completely random, with 724 pixel height being unclipped and fine, on other content with nearly the same returned pixel height cut off by more than 30% percent of the original.
I tried several approaches that i found here, but none of them worked.
Tried to get the max value from evaluating the offsetheight, scrollheight and other properties, but also without success.
Also tried running jquery commands on my webview, but again, without success so far.
If anyone has any experience with a problem like this, please help.
The full code snippet, where "articleViewHeight" is a constraint for the mentioned webView:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return [self getCellHeight:indexPath.row];
}
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self getCellHeight:indexPath.row];
}
-(CGFloat)getCellHeight:(NSInteger)row{
switch (row) {
case 0:
return self.view.frame.size.height * 0.1f;
break;
case 1:
return self.view.frame.size.height * 0.375f;
break;
case 2:
return UITableViewAutomaticDimension;//self.view.frame.size.height * 0.15f;
break;
case 3:
return self.articleViewHeight;
break;
case 4:
return self.view.frame.size.height * 0.2f;
break;
default:
return UITableViewAutomaticDimension;
break;
}
}
- (void)webViewDidFinishLoad:(UIWebView * _Nonnull)webView{
CGFloat height = [self getWebViewPageHeight];
self.articleViewHeight = height;
[self.tableView reloadData];
[self removeLoader];
}
- (CGFloat) getWebViewPageHeight {
CGFloat height1 = [[self.articleView stringByEvaluatingJavaScriptFromString: #"document.height"] floatValue];
CGFloat height2 = [[self.articleView stringByEvaluatingJavaScriptFromString: #"document.body.scrollHeight"] floatValue];
return MAX(height1, height2);
}
Update: the htmlstring loading code as requested
-(void)doneLoading{
NSString *pathToFile =[[NSBundle mainBundle] pathForResource:#"news" ofType:#"css"];
NSString *fileString = [NSString stringWithContentsOfFile:pathToFile encoding:NSUTF8StringEncoding error:nil];
NSString* headerString = #"<!DOCTYPE html><html><head title='test'><style type=\"text/css\" media=\"all\">%#</style></head><body><div id='content'>%#</div></body></html>";
NSString * theString = [NSString stringWithFormat:headerString,fileString,self.htmlString];
[self.articleView loadHTMLString:theString baseURL:nil];
}
This solution works only for local html string which you can freely modify (well, you could download webpage separately into a string and inject this but then you get a load of other problems.
I've had similar problem and found following method to solve them. First of all, add this script in the <head> tag of your html.
<script type="text/javascript">
window.onload = function(){
window.location.href = "height://"+document.getElementById("content").offsetHeight;
}
</script>
This is needed because when - (void)webViewDidFinishLoad:(UIWebView *)webView; is called the content still may not have correct height. Also, put all your html content into one div with id from the script ("content" int this example).
... html header
<body>
<div id="content">
... your content
</div>
</body>
Then modify your - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType; method.
-(BOOL)webView:(UIWebView*)pWebView shouldStartLoadWithRequest:(NSURLRequest*)pRequest navigationType:(UIWebViewNavigationType)pNavigationType{
NSURL* url = pRequest.URL;
if(pNavigationType==UIWebViewNavigationTypeLinkClicked){
return ![[UIApplication sharedApplication] openURL:url];
// or do whatever you do with normal links
} else if([url.scheme isEqualToString:#"height"]){
CGFloat contentHeight = [url.host floatValue];
// do your thing with height
}
}
And viola! This made it for me. Any height error was so marginal that adding small (2px) additional bottom padding for content in CSS completely shown my content.
EDIT:
I just noticed important thing in my code. The webview have .scaleToPage flag set to NO and even more important: initial height of webview. It's set to 6k. If I change it to 0, sure enought, the content is cut after loading.
So to get correct height, the initial height of wevbiew during loading must be big enough to store whole html content without scrolling.
Since you have your webview in a cell, this won't help you. So maybe put another, hidden and big webview on the bottom of drawing stack, with same width as the one in the cell and load your conent there first, get height and then load the one in a cell.
I have had a requirement to have web view in scrollview.
The way I handled, tested without any hack. Actually this delegate method is called multiple times in a single request. Your webViewDidFinishLoad must be like below:
There is a delegate - (void)webViewDidFinishLoad:(UIWebView *)webView
I calculate web view height:-
- (void)webViewDidFinishLoad:(UIWebView *)webView{
if (webView.isLoading == NO)// as it is called multiple times so wait untill last call
{
NSDictionary* userInfo = #{#"webView": webView};
[[NSNotificationCenter defaultCenter] postNotificationName:WEB_VIEW_FINISH_LOAD object:nil userInfo:userInfo];//sending to compute content size for any view which is using this class
}
[self hideActivityOverlayAnimation:NO];
}
This is how i have computed web view height:
-(void)webViewFinishedLoad:(NSNotification*)notification{
if ([notification.name isEqualToString:WEB_VIEW_FINISH_LOAD])
{
NSDictionary* userInfo = notification.userInfo;
UIWebView* webView = (UIWebView*)userInfo[#"webView"];
[webView.scrollView setScrollEnabled:NO];// it was my requirement, dont have to do any thing with height calculation
CGFloat **height** = [[webView stringByEvaluatingJavaScriptFromString:#"document.height"] floatValue];//every time it returnes different
}
}
You can use this height to resize your cell height.
The main part which puzzle me was above. My set Up method of web view:
self.webView = [[UIWebView alloc] initWithFrame:webViewFrame];
[self.webView setDelegate:self];
self.webView.scalesPageToFit = YES;
self.webView.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:self.webView];
Hope it will help you !!!

Is there an equivalent of "renderInContext" for UIPrintPageRenderer

You can call renderInContext on a layer. Is there something like that for UIPrintPageRenderer? I basically want to create a UIImage out of the first page of a PDF document of a UIPrintPageRenderer. I have the rest of the code except for the actual rendering in context part.
Edit: Am I misunderstanding some basic underlying concept here? If so, please feel free to give me a quick lesson.
Getting most of my information from Vel Genov in this post, here is what you should do:
The example code below adds a Category to UIPrintPageRenderer to create the actual PDF data.
#interface UIPrintPageRenderer (PDF)
- (NSData*) createPDF;
#end
#implementation UIPrintPageRenderer (PDF)
- (NSData*) createPDF
{
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData( pdfData, self.paperRect, nil );
[self prepareForDrawingPages: NSMakeRange(0, self.numberOfPages)];
CGRect bounds = UIGraphicsGetPDFContextBounds();
for ( int i = 0 ; i < self.numberOfPages ; i++ )
{
UIGraphicsBeginPDFPage();
[self drawPageAtIndex: i inRect: bounds];
}
UIGraphicsEndPDFContext();
return pdfData;
}
#end
Then, this goes in the webViewDidFinishLoad()
- (void)webViewDidFinishLoad:(UIWebView *)webViewIn {
NSLog(#"web view did finish loading");
// webViewDidFinishLoad() could get called multiple times before
// the page is 100% loaded. That's why we check if the page is still loading
if (webViewIn.isLoading)
return;
UIPrintPageRenderer *render = [[UIPrintPageRenderer alloc] init];
[render addPrintFormatter:webViewIn.viewPrintFormatter startingAtPageAtIndex:0];
// Padding is desirable, but optional
float padding = 10.0f;
// Define the printableRect and paperRect
// If the printableRect defines the printable area of the page
CGRect paperRect = CGRectMake(0, 0, PDFSize.width, PDFSize.height);
CGRect printableRect = CGRectMake(padding, padding, PDFSize.width-(padding * 2), PDFSize.height-(padding * 2));
[render setValue:[NSValue valueWithCGRect:paperRect] forKey:#"paperRect"];
[render setValue:[NSValue valueWithCGRect:printableRect] forKey:#"printableRect"];
// Call the printToPDF helper method that will do the actual PDF creation using values set above
NSData *pdfData = [render createPDF];
// Save the PDF to a file, if creating one is successful
if (pdfData) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *pdfPath = [path stringByAppendingPathComponent:[NSString stringWithFormat:#"Purchase Order.pdf"]];
[pdfData writeToFile:pdfPath atomically:YES];
}
else
{
NSLog(#"error creating PDF");
}
}
PDFSize is defined as a constant, set to a standard A4 page size. It can be edited to meet your needs.
#define PDFSize CGSizeMake(595.2,841.8)
Here is what Val says about the code:
When webViewDidFinishLoad() gets called, the view might not be 100% loaded. A check is necessary, to see if the view is still loading. This is important, as it might be the source of your problem. If it's not, then we are good to go. There is a very important note here. Some web pages are loaded dynamically (defined in the page itself). Take youtube.com for example. The page displays almost immediately, with a "loading" screen. This will trick our web view, and it's "isLoading" property will be set to "false", while the web page is still loading content dynamically. This is a pretty rare case though, and in the general case this solution will work well. If you need to generate a PDF file from such a dynamic loading web page, you might need to move the actual generation to a different spot. Even with a dynamic loading web page, you will end up with a PDF showing the loading screen, and not an empty PDF file.
Another key aspect is setting the printableRect and pageRect. Note that those are set separately. If the printableRect is smaller than the paperRect, you will end up with some padding around the content - see code for example. Here is a link to Apple's API doc with some short descriptions for both.

iOS UIWebView shown just part of content in UIScrollView

I would like to have UIImage and UIWebView in UIScrollView so when I scroll first it would disappear image and then I would scroll with just webView. So I set it in storyboard and I added these methods to my code:
- (void)setScrollViewContentSize
{
float sizeOfContent = 0;
UIView *lLast = [_scrollView.subviews objectAtIndex:1];
NSInteger wd = lLast.frame.origin.y;
NSInteger ht = lLast.frame.size.height;
sizeOfContent = wd+ht;
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width, sizeOfContent);
}
- (void)webViewDidFinishLoad:(UIWebView *)webView{
[webView sizeToFit];
[webView setFrame:CGRectMake(webView.frame.origin.x, webView.frame.origin.y, webView.frame.size.width, webView.frame.size.height)];
[self setScrollViewContentSize];
}
It's working just with one problem. When I scroll for a few lines I get new content of webView but after that I don't see anything. I think it has something to do with cache and that I am not scrolling in webView but with scrollView and so webView doesn't know that it should display that content. Anyone could help? Thanks

Ios pdf zooming at specific coordinate

i created a Ios app for reading pdf documents following Apple pdf zooming
https://developer.apple.com/library/ios/samplecode/zoomingpdfviewer/Introduction/Intro.html
but , i don't know how to zoom pdf pages at specific coordinate automatically ,
i mean when my did load method the pdf page zoomed at specific coordinate(x, y) such as : (10, 20)
some body help me ? please !
thank !
From Apple's Sample Code of Zooming PDF Viewer
UIScrollView has the API to do that
[yourScrollView zoomToRect:CGRectMake(X, Y, Width, Height) animated:YES];
Basically if you want to see the animation on your changes then you can put your code in View did Appear method.
#import "ZoomingPDFViewerViewController.h"
#import "PDFScrollView.h"
#import <QuartzCore/QuartzCore.h>
#implementation ZoomingPDFViewerViewController
- (void)viewDidLoad
{
[super viewDidLoad];
/*
Open the PDF document, extract the first page, and pass the page to the PDF scroll view.
*/
NSURL *pdfURL = [[NSBundle mainBundle] URLForResource:#"TestPage" withExtension:#"pdf"];
CGPDFDocumentRef PDFDocument = CGPDFDocumentCreateWithURL((__bridge CFURLRef)pdfURL);
CGPDFPageRef PDFPage = CGPDFDocumentGetPage(PDFDocument, 1);
[(PDFScrollView *)self.view setPDFPage:PDFPage];
CGPDFDocumentRelease(PDFDocument);
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// 1. Get the Scroll View
UIScrollView *scrollView = (UIScrollView*)self.view;
// 2. Zoom to specified rect
[scrollView zoomToRect:CGRectMake(X, Y, Width, Height) animated:YES];
}

Text to speech and navigation

I'm using the Acapela TTS on iOS.
I have a HTML document with all the text (and markup) that I want read out.
The text is split in paragraphs and I want to be able to start the TTS at each header.
At the moment I put each paragraph in it's own UIWebView (for handling the internal markup) and add these to a UIScrollView for navigating all the elements. Each UIWebView is overloaded with a button, so when it's pressed, the connected text is read aloud.
My problem now, is that I have to place the UIWebView and Buttons using a frame (meaning a static y-position), but this entails that I have the exact height, which is my current problem.
I have tried using the solutions mentioned in this question: UIWebView height, but it still doesn't look good - either the webviews are too high or short, and I need it to be perfect, so it looks like a consistent webpage.
Does anybody know of a different way of doing this type of TTS without splitting it up in separate webviews that I have overlooked?
This is my code that place the UIWebViews:
float yOffset = 0;
UIWebView *previous = nil;
//UIWebView delegate
-(void)webViewDidFinishLoad:(UIWebView *)webView{
NSString *height = [webView stringByEvaluatingJavaScriptFromString:#"document.body.scrollHeight;"];
if(previous!=nil){
yOffset += previous.frame.size.height;
}
CGFloat h = [height floatValue];
CGRect frame = CGRectMake(0, yOffset, scrollView.frame.size.width, h);
[webView setFrame:frame];
[webView setOpaque:NO];
[webView setBackgroundColor:[UIColor whiteColor]];
previous = webView;
[scrollView addSubview:webView];
yOffset += previous.frame.size.height;
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, yOffset)];
}
Just an idea: you could turn the complete paragraphs into clickable links that point to some identifier of the paragraph. Then you can override the clicking event with UIWebViewDelegate and get the paragraph text with javascript. I mean something like this:
HTML:
<p id="paragraph1">First paragraph</p>
<p id="paragraph2">Second paragraph</p>
Implement shouldStartLoadWithRequest of UIWebViewDelegate:
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
NSString* link = [request.URL lastPathComponent]; // not sure
// use rangeOfString to find all
if (inType == UIWebViewNavigationTypeLinkClicked && [link compare:#"paragraph1"] == NSOrderedSame)
{
// check this
NSString* javaScript =
[NSString stringWithFormat:#"document.getElementById('%#').nodeValue", link];
NSString paragraphToRead =
[webView stringByEvaluatingJavaScriptFromString:javaScript];
// read out paragraphToRead
return NO;
}
}

Resources