Is there an equivalent of "renderInContext" for UIPrintPageRenderer - ios

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.

Related

Objective-C Full UIView to PDF

I have this code here that takes my UIView in puts it into a PDF. My issue I am having is that my view is a detail view controller and is scrollable and the PDF only gets what inside the detail view controller at that time and not a full view, if I move around in the detail view, it will capture whatever part I am on, not the full thing. Is what I am trying to do possible?
- (IBAction)Share:(id)sender {
NSMutableData *pdfData = [NSMutableData data];
// Points the pdf converter to the mutable data object and to the UIView to be converted
UIGraphicsBeginPDFContextToData(pdfData, spreadSheet.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[spreadSheet.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:#"test.pdf"];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
UIActivityViewController * activityController = [[UIActivityViewController alloc] initWithActivityItems:#[pdfData] applicationActivities:nil];
UIPopoverController *popup = [[UIPopoverController alloc] initWithContentViewController:activityController];
[popup presentPopoverFromRect:CGRectMake(self.view.frame.size.width - 36, 60, 0, 0)inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
UPDATE
I have been really struggling with this, I can't get the full view into a PDF, I have also been thinking of going to route of taking my NSMutableArray and my NSArray which is the data used in my UIView and try to covert that into a PDF but that sounds very time consuming to make format it nicely, unless someone knows of way to do that. I guess I am confused on which route I should take.
From this
My issue I am having is that my view is a detail view controller and
is scrollable and the PDF only gets what inside the detail view
controller at that time and not a full view.
Looks like the problem is your content in view is scrollable and you pdf you creating is only capture the visible area.
Here is your problem
UIGraphicsBeginPDFContextToData(pdfData, spreadSheet.bounds, nil);
You need to fix it you need to give it the scrollable view (UITableView,UICollectionView or Scrollview) content size bounds here is how you do it.
-(NSData*)pdfDataFromTableViewContent
{
NSMutableData *pdfData = [NSMutableData data];
//The real size, not only the visible part use your scrollable view ((UITableView,UICollectionView or Scrollview))
CGSize contentSize = self.tableView.contentSize;
CGRect tableViewRealFrame = self.tableView.frame;
//Size tableView to show the whole content
self.tableView.frame = CGRectMake(0, 0, contentSize.width, contentSize.height);
//Generate PDF (one page)
UIGraphicsBeginPDFContextToData(pdfData,self.tableView.frame, nil);
UIGraphicsBeginPDFPage();
[self.tableView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIGraphicsEndPDFContext();
//Resize frame
self.tableView.frame = tableViewRealFrame;
return pdfData;
}
The above code will generates one page.
For more pages use following code
//MultiPage
CGRect mediaBox = self.tableView.frame;
CGSize pageSize = CGSizeMake(self.view.frame.size.width, 800);
UIGraphicsBeginPDFContextToData(pdfData, CGRectZero, nil);
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
NSInteger currentPage = 0;
BOOL done = NO;
do {
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0.0, pageSize.width, pageSize.height), nil);
CGContextTranslateCTM(pdfContext, 0, -(pageSize.height*currentPage));
[self.view.layer renderInContext:pdfContext];
if ((pageSize.height*(currentPage+1)) > mediaBox.size.height) done = YES;
else currentPage++;
} while (!done);
UIGraphicsEndPDFContext();
You can also look into this working project ScrollViewToPDF.
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.
It captures all visible as well as invisible part of scrollView
Rendering something into a context only renders what is currently in the view hierarchy. That means that if you're using a UITableView or UICollectionView, not every cell that represents your data is in the hierarchy at any given time. If it were me I would try temporarily setting the view to have a massive frame so that everything would be in the hierarchy. If that didn't work I'd be writing a custom view that on request could layout everything for all the data and then reset to a more efficient layout after the rendering was complete.
Try following code to convert UIView to PDF.
Note that the following method creates just a bitmap of the view; it does not create actual typography.
-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
// 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, aView.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView.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:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
}
Also make sure you import: QuartzCore/QuartzCore.h

Rotate all pages in a PDF graphics context

I'm creating a PDF file containige multiple pages:
NSMutableData *pdfData = [NSMutableData new];
CGRect rect = CGRectMake(0, 0, 300, 100);
UIGraphicsBeginPDFContextToData(pdfData, rect, nil);
for (NSInteger page = 0; page < 10; page++)
{
UIGraphicsBeginPDFPage();
// DRAW PAGE
}
UIGraphicsEndPDFContext();
The PDF will be printed, but always in along the longer page side. So at the end and before sending it to a printer, I want to rotate each page by 90°. How can this be done?
Instead of UIGraphicsBeginPDFPage, you can use UIGraphicsBeginPDFPageWithInfo(bounds, dict);
In the dictionary, set a key "Rotate" and value (NSNumber) 90.
EG:
NSDictionary* dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:90] forKey:#"Rotate"];
UIGraphicsBeginPDFPageWithInfo(bounds, dict);
You can do by taking one by one page in UIImage format and rotate image to 90 degree and then convert image bunch to PDF.

Create PDF from UIScrollView Objective C

this question has been posted before (Creating PDF from UIScrollView in iphone app) and the code I am using is from here.
Here is the code
-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
// 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, aView.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView.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];
documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
}
Setup: I have a UIScrollView, inside is a UIView. I want to save the entire UIView (950,500px) and it's in a space (the UIScrollView size) of (520,500). The PDF being generated is only the size of UIScrollView (520,500)
I read the answer but he apparently changed the code but it doesn't work for me. I've been trying to fix this all day.
I'm a beginner so please indicate anything I should add to my question that I missed. Thank you.
PS - this is an iPad app.
The context should have the size of the scrollview's content size, not the bounds.
Then you need to temporarily resize the scrollview to its content size, render it in the PDF context, and restore the size of the scrollview to its original size.
UIGraphicsBeginPDFContextToData(pdfData, (CGRect){0,0, scrollView.contentSize}, nil);
CGRect origSize = scrollView.frame;
CGRect newSize = origSize;
newSize.size = scrollView.contentSize;
[scrollView setFrame:newSize];
[scrollView.layer renderInContext:pdfContext];
[scrollView setFrame:origSize];
Check ScrollViewToPDF example and you will understand what you need to do.
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
If anyone was wondering, I found how to fix this.
Since my UIView is IN a UIScrollView, and I use another class for the UIView, when I call the method and chose the parameter aView, I just put in self.
So in my UIView class, when I want the PDF to be generated I type in
[self createPDFfromUIView:self saveToDocumentsWithFileName:UIViewPDF];

display specific pdf page in the UIWebview 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,

How can I generate a PDF with "real" text content on iOS?

I want to generate a good-looking PDF in my iOS 6 app.
I've tried:
UIView render in context
Using CoreText
Using NSString drawInRect
Using UILabel drawRect
Here is a code example:
-(CGContextRef) createPDFContext:(CGRect)inMediaBox path:(NSString *) path
{
CGContextRef myOutContext = NULL;
NSURL * url;
url = [NSURL fileURLWithPath:path];
if (url != NULL) {
myOutContext = CGPDFContextCreateWithURL ((__bridge CFURLRef) url,
&inMediaBox,
NULL);
}
return myOutContext;
}
-(void)savePdf:(NSString *)outputPath
{
if (!pageViews.count)
return;
UIView * first = [pageViews objectAtIndex:0];
CGContextRef pdfContext = [self createPDFContext:CGRectMake(0, 0, first.frame.size.width, first.frame.size.height) path:outputPath];
for(UIView * v in pageViews)
{
CGContextBeginPage (pdfContext,nil);
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformMakeTranslation(0, (int)(v.frame.size.height));
transform = CGAffineTransformScale(transform, 1, -1);
CGContextConcatCTM(pdfContext, transform);
CGContextSetFillColorWithColor(pdfContext, [UIColor whiteColor].CGColor);
CGContextFillRect(pdfContext, v.frame);
[v.layer renderInContext:pdfContext];
CGContextEndPage (pdfContext);
}
CGContextRelease (pdfContext);
}
The UIViews that are rendered only contain a UIImageView + a bunch of UILabels (some with and some without borders).
I also tried a suggestion found on stackoverflow: subclassing UILabel and doing this:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
BOOL isPDF = !CGRectIsEmpty(UIGraphicsGetPDFContextBounds());
if (!layer.shouldRasterize && isPDF)
[self drawRect:self.bounds]; // draw unrasterized
else
[super drawLayer:layer inContext:ctx];
}
But that didn't change anything either.
No matter what I do, when opening the PDF in Preview the text parts are selectable as a block, but not character per character, and zooming the pdf shows it is actually a bitmap image.
Any suggestions?
This Tutorial From Raywenderlich Saved my Day.Hope it will work for you too.
http://www.raywenderlich.com/6818/how-to-create-a-pdf-with-quartz-2d-in-ios-5-tutorial-part-2
My experience when I did this last year was that Apple didn't provide any library to do it. I ended up importing an open source C library (libHaru). Then I added a function for outputting to it in each class in my view hierarchy. Any view with subviews would call render on its subviews. My UILabels, UITextFields, UIImageViews, UISwitches etc would output their content either as text or graphics accordingly I also rendered background colors for some views.
It wasn't very daunting, but libHaru gave me some problems with fonts so iirc I ended up just using the default font and font size.
It works good with UILabels except that you have to work around a bug:
Rendering a UIView into a PDF as vectors on an iPad - Sometimes renders as bitmap, sometimes as vectors

Resources