Am trying to integrate the signature in a pdf file by fetching the signature from document directory and could place it on the pdf file on fixed position say (50,50). But when trying to integrate the signature by user's tapping position, it is not placed at appropriate position.
I screenshot of the signature added at multiple position of the pdf file is shown below.
Here I tapped at each and every position of the pdf file, but signature is added only at the centre of the pdf file,
CGPoint tapLocation = [gesture locationInView: self.view];
NSLog(#"tapped location is %# \n",NSStringFromCGPoint(tapLocation));
NSInteger x,y;
x=tapLocation.x;
y=tapLocation.y;
CGRect imageRect = CGRectMake(x,568-y, image.size.width, image.size.height);
Adding signature at tapped location is not possible here, but i tried translateCTM and ScaleCTM that also yields the same result. What else to be done, for getting the signature at appropriate tapping position.
UPDATED QUESTION
webView= [[UIWebView alloc] initWithFrame:CGRectMake(0, 60, 320, 568)];
- (void) singleTap:(UITapGestureRecognizer*)gesture
{
// handle event
NSLog(#"single tap is handled");
CGPoint tapLocation = [gesture locationInView:webView];
NSLog(#"tapped location is %# \n",NSStringFromCGPoint(tapLocation));
NSInteger x,y;
x=tapLocation.x;
y=tapLocation.y;
NSLog(#"location:x %d\n",x);
NSLog(#"location:y %d\n",y);
CGPoint pointInView1 = [webView convertPoint:tapLocation toView:self.window];//change of +80 in y
NSLog(#"pointinview is %# \n",NSStringFromCGPoint(pointInView1));
xy = pointInView1.x;
yz = pointInView1.y;
if (entered==1)
{
// CFStringRef path;
CFURLRef url;
url = (CFURLRef)CFBridgingRetain([NSURL fileURLWithPath:documentDirectoryPath]);
CGPDFDocumentRef myDocument;
myDocument = CGPDFDocumentCreateWithURL((CFURLRef)url);
// Create PDF context
CGContextRef pdfContext = CGPDFContextCreateWithURL(url, NULL, NULL);
int totalPages = (int)CGPDFDocumentGetNumberOfPages(myDocument);
NSLog(#"no. of pages in pdf is %d \n",totalPages);
//alter each page of a pdf
for (int currentPage = 1; currentPage <= totalPages; currentPage++)
{
CGPDFContextBeginPage(pdfContext, NULL);
UIGraphicsPushContext(pdfContext);
CGContextDrawPDFPage(UIGraphicsGetCurrentContext(), CGPDFDocumentGetPage(myDocument, currentPage)); //"page" is current pdf page to be signed
if (page == currentPage)
{
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Image.png"];// "image.png" is the saved user's signature in document directory
image = [[UIImage alloc] initWithContentsOfFile:filePath];
// Translate the origin of the coordinate system at the
// bottom left corner of the page rectangle.
// CGContextTranslateCTM(pdfContext, 0,1);
// Reverse the Y axis to grow from bottom to top.
// CGContextScaleCTM(pdfContext, 1, 1);
CGRect imageRect = CGRectMake(x,568-y, image.size.width, image.size.height);
CGContextDrawImage(UIGraphicsGetCurrentContext(), imageRect, image.CGImage);
}
// Clean up
UIGraphicsPopContext();
CGPDFContextEndPage(pdfContext);
}
CGPDFContextClose(pdfContext);
// _sign_label.text = #"";
}
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
[webView loadRequest:request];
[self.view addSubview:webView];
}
Related
This question already has answers here:
Creating PDF file from UIWebView
(6 answers)
Closed 5 years ago.
In my iOS application, I want to create a PDF from UIWebView/UIView (including subviews). In my app, I will first load the original incoming PDF in UIWebView, and then add an image as a subview on UIWebView. I want to create a PDF from the UIWebview with this image (subview) with original clarity and no data loss.
PS : Image in rendered PDF should be in the same place as in the UIWebView.
I am able to create a PDF from UIWebView, but it lacks the PDF clarity and creates a border issue.
Can anyone please provide a clear solution for PDF rendering from UIWebView (including subviews)?
EDITED CONTENT:
Above is the screenshot of UIWebView. Signature(test) is the image in the subview. I want to render this as a PDF with clarity and without any data loss.
In the below answers, UIPrintPageRenderer renders the PDF from UIWebView, but it ignores the subviews above UIWebView. This is the major issue with this option.
Another answer using the createPDFfromUIView method lacks the original clarity:
-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename;
A border issue also occurs with this method.
I have also tried to write on the PDF directly, without taking a screenshot, using the below code from this reference.
- (void) drawCustomPDFContent
{
// Put your drawing calls here
// Draw a red box
[[UIColor redColor] set];
UIRectFill(CGRectMake(20, 20, 100, 100));
// Example of drawing your view into PDF, note that this will be a rasterized bitmap, including the text.
// To get smoother text you'll need to use the NSString draw methods
CGContextRef ctx = UIGraphicsGetCurrentContext();
[view.layer renderInContext:ctx];
}
- (void) createCustomPDF
{
NSURL* pdfURL = ... /* URL to pdf file */;
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
const size_t numberOfPages = CGPDFDocumentGetNumberOfPages(pdf);
NSMutableData* data = [NSMutableData data];
UIGraphicsBeginPDFContextToData(data, CGRectZero, nil);
for(size_t page = 1; page <= numberOfPages; page++)
{
// Get the current page and page frame
CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdf, page);
const CGRect pageFrame = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
UIGraphicsBeginPDFPageWithInfo(pageFrame, nil);
// Draw the page (flipped)
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -pageFrame.size.height);
CGContextDrawPDFPage(ctx, pdfPage);
CGContextRestoreGState(ctx);
if(page == 1)
{
[self drawCustomPDFContent];
}
}
UIGraphicsEndPDFContext();
CGPDFDocumentRelease(pdf);
pdf = nil;
// Do something with data here
[data writeToFile:... atomically:YES];
}
It does the job. However, (x,y) coordinates in UIWebView differ from the original PDF coordinates, so I can't map the exact coordinates to draw on the PDF to render.
Hopefully, this clears up my issue. Please suggest a way to resolve my issue. If it is likely impossible, please suggest the iOS PDF kit/SDK that meets my requirement.
Use UIPrintPageRenderer from UIWebView Follow below steps :
Add Category of UIPrintPageRenderer for getting PDF Data
#interface UIPrintPageRenderer (PDF)
- (NSData*) printToPDF;
#end
#implementation UIPrintPageRenderer (PDF)
- (NSData*) printToPDF
{
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
Add these define for A4 size
#define kPaperSizeA4 CGSizeMake(595.2,841.8)
Now in UIWebView's webViewDidFinishLoad delegate use UIPrintPageRenderer property of UIWebView.
- (void)webViewDidFinishLoad:(UIWebView *)awebView
{
if (awebView.isLoading)
return;
UIPrintPageRenderer *render = [[UIPrintPageRenderer alloc] init];
[render addPrintFormatter:awebView.viewPrintFormatter startingAtPageAtIndex:0];
//increase these values according to your requirement
float topPadding = 10.0f;
float bottomPadding = 10.0f;
float leftPadding = 10.0f;
float rightPadding = 10.0f;
CGRect printableRect = CGRectMake(leftPadding,
topPadding,
kPaperSizeA4.width-leftPadding-rightPadding,
kPaperSizeA4.height-topPadding-bottomPadding);
CGRect paperRect = CGRectMake(0, 0, kPaperSizeA4.width, kPaperSizeA4.height);
[render setValue:[NSValue valueWithCGRect:paperRect] forKey:#"paperRect"];
[render setValue:[NSValue valueWithCGRect:printableRect] forKey:#"printableRect"];
NSData *pdfData = [render printToPDF];
if (pdfData) {
[pdfData writeToFile:[NSString stringWithFormat:#"%#/tmp.pdf",NSTemporaryDirectory()] atomically: YES];
}
else
{
NSLog(#"PDF couldnot be created");
}
}
-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
// Creates a mutable data object for updating with binary data, like a byte array
UIWebView *webView = (UIWebView*)aView;
NSString *heightStr = [webView stringByEvaluatingJavaScriptFromString:#"document.body.scrollHeight;"];
int height = [heightStr intValue];
// CGRect screenRect = [[UIScreen mainScreen] bounds];
// CGFloat screenHeight = (self.contentWebView.hidden)?screenRect.size.width:screenRect.size.height;
CGFloat screenHeight = webView.bounds.size.height;
int pages = ceil(height / screenHeight);
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, webView.bounds, nil);
CGRect frame = [webView frame];
for (int i = 0; i < pages; i++) {
// Check to screenHeight if page draws more than the height of the UIWebView
if ((i+1) * screenHeight > height) {
CGRect f = [webView frame];
f.size.height -= (((i+1) * screenHeight) - height);
[webView setFrame: f];
}
UIGraphicsBeginPDFPage();
CGContextRef currentContext = UIGraphicsGetCurrentContext();
// CGContextTranslateCTM(currentContext, 72, 72); // Translate for 1" margins
[[[webView subviews] lastObject] setContentOffset:CGPointMake(0, screenHeight * i) animated:NO];
[webView.layer renderInContext:currentContext];
}
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
[webView setFrame:frame];
}
My app takes in the contents of a UIWebView and generates a PDF of the web page. This works fine on smaller pages but when it reaches about 10 pages it crashes "Due to Memory Pressure". Also, this is an ARC app.
The predominant answer I have seen is to use UIGraphicsBeginPDFContextToFile instead of UIGraphicsBeginPDFContextToData and after changing to use File I still get a Memory Pressure crash. I don't understand why it's not clearing the pages from memory. I also added the #autoreleasepool { ... } in the loop as recommended in another question. Any ideas on what I am doing wrong here?
Here's the PDF creation code:
UIGraphicsBeginPDFContextToFile(dataFile, CGRectZero, nil);
for (int i = 0; i < pages; i++) {
#autoreleasepool {
NSLog(#"Creating Page %i", i);
// Check to see if page draws more than the height of the UIWebView
if ((i+1) * 720 > height) {
CGRect f = [_appWebView frame];
f.size.height -= (((i+1) * 720.0) - height);
[_appWebView setFrame: f];
}
UIGraphicsBeginPDFPage();
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(currentContext, 36, 36); // Translate for 0.5" margins
[[[_appWebView subviews] lastObject] setContentOffset:CGPointMake(0, 720 * i) animated:NO];
[_appWebView.layer renderInContext:currentContext];
}
}
UIGraphicsEndPDFContext();
Here's the full method if it helps any:
-(void) generatePDF {
startingFrame = _appWebView.frame;
// Memory warning seems to happen on almost every PDF, clear cache here to be proactive.
[[NSURLCache sharedURLCache] removeAllCachedResponses];
UIWebView *webView = [[UIWebView alloc] initWithFrame: CGRectMake(0, 0, 6.5 * 72, 9 * 72)];
[webView setDelegate: self];
// Adjust to letter size paper size in portrait mode
CGRect frame = _appWebView.frame;
frame.size.height = 10*72; // 11" - 1" Margins = 720px (72px / inch)
frame.size.width = 7.5*72; // 8.5 - 1" Margins = 612px (72px / inch)
_appWebView.frame = frame;
[_appWebView stringByEvaluatingJavaScriptFromString:#"window.scroll(0, 0);"];
// Get the height of our webView
NSString *heightStr = [_appWebView stringByEvaluatingJavaScriptFromString:#"document.body.scrollHeight;"];
int height = [heightStr intValue];
// Get the number of pages needed to print. 10 * 72 = 720
int pages = ceil(height / 720.0);
// File
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dataFile = [documentsDirectory stringByAppendingPathComponent:#"Configuration.pdf"];
NSLog(#"File: %#", dataFile);
UIGraphicsBeginPDFContextToFile(dataFile, CGRectZero, nil);
for (int i = 0; i < pages; i++) {
#autoreleasepool {
NSLog(#"Creating Page %i", i);
// Check to see if page draws more than the height of the UIWebView
if ((i+1) * 720 > height) {
CGRect f = [_appWebView frame];
f.size.height -= (((i+1) * 720.0) - height);
[_appWebView setFrame: f];
}
UIGraphicsBeginPDFPage();
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(currentContext, 36, 36); // Translate for 0.5" margins
[[[_appWebView subviews] lastObject] setContentOffset:CGPointMake(0, 720 * i) animated:NO];
[_appWebView.layer renderInContext:currentContext];
}
}
UIGraphicsEndPDFContext();
// Adjust to original size
_appWebView.frame = startingFrame;
}
I don't know of a reason you are getting the memory error, but I had a similar issue with searching of PDF's. Basically the OS was loading the entire PDF into memory, then searching, then removing the pdf even thought I was going page by page. My solution was to only do one page at a time, and that resolved the memory issue for me.
My code looks like this:
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.MaxConcurrentOperationCount = 1;
for(int i = 1; i <= totalPages; i++)
{
// a block of operation
[operationQueue addOperationWithBlock: ^ {
}];
}
I've tried to create PDF from HTML (webview). I did it. But, the problem i'm facing badly is i'm getting 1 vertical & 1 horizontal line on each page. As i attached in this link.
Click for image
Zoom it if u can't see the lines.
I've tried this code
// Creates a mutable data object for updating with binary data, like a byte array
NSMutableData *pdfData = [NSMutableData data];
// Points the pdf converter to the mutable data object and to the UIView to be converted
UIGraphicsBeginPDFContextToData(pdfData, webView.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[webView.layer renderInContext:pdfContext];
// remove PDF rendering context
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:#"tmp.pdf"];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
// Creates a mutable data object for updating with binary data, like a byte array
NSString *heightStr = [webView stringByEvaluatingJavaScriptFromString:#"document.body.scrollHeight;"];
int height = [heightStr intValue];
// CGRect screenRect = [[UIScreen mainScreen] bounds];
// CGFloat screenHeight = (self.contentWebView.hidden)?screenRect.size.width:screenRect.size.height;
CGFloat screenHeight = webView.bounds.size.height;
int pages = ceil(height / screenHeight);
UIGraphicsBeginPDFContextToData(pdfData, webView.bounds, nil);
CGRect frame = [webView frame];
for (int i = 0; i < pages; i++) {
// Check to screenHeight if page draws more than the height of the UIWebView
if ((i+1) * screenHeight > height) {
CGRect f = [webView frame];
f.size.height -= (((i+1) * screenHeight) - height);
[webView setFrame: f];
}
UIGraphicsBeginPDFPage();
CGContextRef currentContext = UIGraphicsGetCurrentContext();
//CGContextTranslateCTM(currentContext, 72, 72); // Translate for 1" margins
[[[webView subviews] lastObject] setContentOffset:CGPointMake(0, screenHeight * i) animated:NO];
[webView.layer renderInContext:currentContext];
}
UIGraphicsEndPDFContext();
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:self.PDFpath atomically:YES];
[webView setFrame:frame];
Any one have idea about this ?
Okay, i got it. It wasn't lines the code i was using above makes 4 parts of each page from UIWebView and print them to PDF as image. So it was the gap between those parts.
I'm trying to save all of the contents of a UIScrollView to a .pdf. I found some tutorials for saving the current view, and they all rely on creating a CGContextRef using UIGraphicsGetCurrentContext(). Right now, I capture my view with just
CGContextRef context = UIGraphicsGetCurrentContext();
[myView.layer renderInContext:context];
And that works fine for a plain UIView. However, when I try to pass a UIScrollView as myView, it passes the visible part of the UIScrollView fine, but anything offscreen is just white space. Is there a way I can expand context somehow to get all of the content in my UIScrollView, and not just what is currently visible? I suspect that I can't use UIGraphicsGetCurrentContext() for a UIScrollView, but I don't know what to use instead, and the Apple docs on this aren't really very helpful.
If you have a subview taking the whole content size of the scrollView with the scrolling content you can do it like this:
CGContextRef context = UIGraphicsGetCurrentContext();
UIView *contentView = [scrollView subviews][0];
[contentView.layer renderInContext:context];
If there are multiple views in the scrollView you can do it like this:
UIView *contentView = [[UIView alloc] initWithFrame:CGRectMake(
scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.contentSize.width,
scrollView.contentSize.height)];
for(UIView *view in [scrollView subviews]){
[contentView addSubview: view];
}
CGContextRef context = UIGraphicsGetCurrentContext();
[contentView.layer renderInContext:context];
Then you need to get the views back into the scrollView. Probably there is a way to copy them but I am not sure how. Anyhow here is what should work:
for(UIView *view in [contentView subviews]){
[view removeFromSuperView];
[scrollView addSubview:view];
}
You can set frame of UIScrollView equal to content size of it and content offset to 0 before calling renderInContext. After that revert frame to original.
myView.frame = CGRectMake(myView.frame.origin.x,myView.frame.origin.y,myView.contentSize.with,myView.contentSize.height);
This code i used to make pdf from uiscrollview and it does help but it will not be as good as if we draw on pdf -- please have look -- Pdf from UIScrollView
- (void) createPDF
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *directroyPath = nil;
directroyPath = [documentsDirectory stringByAppendingPathComponent:#"temp"];
NSString *filePath = [directroyPath stringByAppendingPathComponent:#"test.pdf"];
// check for the "PDF" directory
NSError *error;
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
} else {
[[NSFileManager defaultManager] createDirectoryAtPath:directroyPath
withIntermediateDirectories:NO
attributes:nil
error:&error];
}
CGContextRef pdfContext = [self createPDFContext:_scrollView2.bounds path:(CFStringRef)filePath];
NSLog(#"PDF Context created");
/*
Here limit of i is no of pages in your uiscrollview or
you can use hit and trial here because there is a
length of page is going to be equal to
the visible size of uiscrollView in your application
*/
for (int i = 0 ; i< 2 ; i++)
{
// page 1
CGContextBeginPage (pdfContext,nil);
//turn PDF upsidedown
CGAffineTransform transform = CGAffineTransformIdentity;
//here 365 is equal to the height of myScrollView.frame.size.height
transform = CGAffineTransformMakeTranslation(0, (i+1) * 365);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(pdfContext, transform);
//Draw view into PDF
[_scrollView2.layer renderInContext:pdfContext];
CGContextEndPage (pdfContext);
[_scrollView2 setContentOffset:CGPointMake(0, (i+1) * 365) animated:NO];
}
CGContextRelease (pdfContext);
}
- (CGContextRef) createPDFContext:(CGRect)inMediaBox path:(CFStringRef) path
{
CGContextRef myOutContext = NULL;
CFURLRef url;
url = CFURLCreateWithFileSystemPath (NULL, path,
kCFURLPOSIXPathStyle,
false);
if (url != NULL) {
myOutContext = CGPDFContextCreateWithURL (url,
&inMediaBox,
NULL);
CFRelease(url);
}
return myOutContext;
}
Check ScrollViewToPDF example and understand it.
It uses same scrollview's layer renderInContext but here PDF is created according to your requirement such as one page PDF or multiple page PDF
Note : It captures all visible as well as invisible part of scrollView
I just want to know if it's possible to know the size of a pdf after loading it in a UIWebView ?
When we use a code like this :
[self.siteWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.test.fr/test.pdf"]]];
Yes. It is absolutely possible to get height of pdf (1 page height or anything) in uiwebview. I have done before. Below code show how to get 1 page height in uiwebview.
-(void)getPDFInfo
{
NSURL* pdfFileUrl = self.fileLocation;
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfFileUrl);
CGPDFPageRef page;
// CGRect aRect = CGRectMake(0, 0, 100, 200); // thumbnail size
NSUInteger totalNum = CGPDFDocumentGetNumberOfPages(pdf);
numberOfPage= totalNum;
for(int i = 0; i < totalNum; i++ ) {
CGPDFPageRef myPageRef=CGPDFDocumentGetPage(pdf, i+1);
//aRect=CGPDFPageGetBoxRect(myPageRef, kCGPDFCropBox);
CGRect cropBox = CGPDFPageGetBoxRect(myPageRef, kCGPDFCropBox);
pdfWidth= cropBox.size.width;
pdfHeight=cropBox.size.height;
NSLog(#"cropbox size is %#",NSStringFromCGRect(cropBox));
int pageRotation = CGPDFPageGetRotationAngle(myPageRef);
CGSize pageVisibleSize = CGSizeMake(cropBox.size.width, cropBox.size.height);
if ((pageRotation == 90) || (pageRotation == 270) ||(pageRotation == -90)) {
pageVisibleSize = CGSizeMake(cropBox.size.height, cropBox.size.width);
}
}
}
After you call this function, call this line to get your pdf file size in uiwebview.
documentHeightInWebview = pdfHeight * (documentWidthInWebview/pdfWidth);
where documentWidthInWebview is your real uiwebview width which will show your pdf file.
You can use javascript to get information. put it in webview did load. For example, the following will get the height of the page.
-(void) webViewDidFinishLoad:(UIWebView *)webView {
//get the height of the chunk, and store it.
CGFloat height = [[webView stringByEvaluatingJavaScriptFromString:#"document.height;"] floatValue];