Rotate all pages in a PDF graphics context - ios

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.

Related

Drawn image over PDF is upside-down

I am trying to draw an image over an existing PDF (displayed on the screen). The final goal would be to sign the pdf anywhere.
My sample code is here on Github
The code (basic) works like this :
The user clicks where he wants to draw the image
The PDF editing occurs and the final PDF appears on screen
However the problem is that my image appears upside down. While working with PDF, I learned that you have to flip context to render, it may be related.
- (void)editPDFWithName:(NSString*)pdfName
{
// grab reference to bundled PDF
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)[[NSBundle mainBundle] URLForResource:pdfName withExtension:#"pdf"]);
const size_t numberOfPages = CGPDFDocumentGetNumberOfPages(pdf);
// Begin PDF context
NSMutableData* data = [NSMutableData data];
UIGraphicsBeginPDFContextToData(data, CGRectZero, nil);
// loop over PDF pages to render it
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);
// Y axis is negative in order to render PDF (mandatory)
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -pageFrame.size.height);
CGContextDrawPDFPage(ctx, pdfPage);
CGContextRestoreGState(ctx);
if(page == [self getCurrentPage])
{
// 0.75f factor to convert pixel/points
CGContextDrawImage(ctx, CGRectMake(self.xTouchCoord*0.75f, self.yTouchCoord*0.75f, self.logo.size.width, self.logo.size.height), self.logo.CGImage);
}
}
UIGraphicsEndPDFContext();
CGPDFDocumentRelease(pdf);
pdf = nil;
// Save PDF
[self savePDF:data];
}
I tried multiple ways to flip image as suggested on this post
But the image is still flipped.
One thing I noticed is if I use the method drawAtPoint method on the image, it appears not flipped but does not appear at the specified position, like there is an offset (see other post here)
Can someone please suggest any idea ? I'm betting on the Save and Restore context methods but cannot get it working.
Well I finally found the solution :
use drawInRectinstead of CGContextDrawImage
for better precision, don't forget to subtract half of the width and height to the coordinates to adjust the center :
// 0.75f factor to convert pixel/points
[self.logo drawInRect:CGRectMake(self.xTouchCoord*0.75f - self.logo.size.width/2, self.yTouchCoord*0.75f - self.logo.size.width/2, self.logo.size.width, self.logo.size.height)];

Is it possible to create a pdf with different page sizes in iOS - Objective C?

I've done multiple times a pdf with all the pages at same size. Is it really possible to do a mixed sizes PDF on iOS? (like for example fixed width and multiple heights pages)
I'm finding a lot of discordant answers around, and very few data on platform-specific capabilities.
It has been as easy as just adding every page with a different mediabox.
// Start PDF file
NSMutableData *pdfFile = [[NSMutableData alloc] init];
CGDataConsumerRef pdfConsumer = CGDataConsumerCreateWithCFData((CFMutableDataRef)pdfFile);
CGRect mediaBox = CGRectMake(0, 0, pageSize.width, pageSize.height);
CGContextRef pdfContext = CGPDFContextCreate(pdfConsumer, &mediaBox, NULL);
CGContextBeginPage(pdfContext, &mediaBox);
CGContextDrawImage(pdfContext, imageBox, [image CGImage]);
CGContextEndPage(pdfContext);
// Change mediabox
CGContextBeginPage(pdfContext, &mediaBox);
... etc....
CGContextEndPage(pdfContext);
// Close pdf
CGContextRelease(pdfContext);
CGDataConsumerRelease(pdfConsumer);

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

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.

how to insert textField with tableView into PDF file

I'm using code to create a PDF File. Works.
But: I want my whole UITableView(I need to scroll) in my PDF File and not just the part of the view which is currently displayed on screen.
laso i need to add three textField to the same file PDF
this must be take all the content of view (three textField and the tableView with all cell) because this is an invoice
Is there a way to achieve this?
i use the following code
- (void)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, self.view.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[self.view.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];
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
}
but the problem it take as image to the view and the remaining cells for tableView didn't include PDF file
UIView *viewToRender = self.tableView;
CGPoint contentOffset = self.tableView.contentOffset;
UIGraphicsBeginImageContext(viewToRender.bounds.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
// KEY: need to translate the context down to the current visible portion of the tablview
CGContextTranslateCTM(ctx, 0, -contentOffset.y);
[viewToRender.layer renderInContext:ctx];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
try this

Resources