I am trying to create a PDF from a UIView
which may contain images, labels, etc.
I have checked the reference ( GeneratingPDF) and I am also aware of the renderInContext:
method, which can be used to draw the UIView
's content onto the PDF context.
I used renderInContext:
to create a PDF. However, the images and the text lost their fidelity. i.e. The images were not selectable individually & the text could not be selected/copied, etc. in the resulting PDF. It was as good/bad as the PDF created from a snapshot of the UIView
.
My question is, do I have to draw the texts & images individually to achieve what I want (using CGContextShowTextAtPoint
& CGContextDrawImage
)?
If yes, then how do I go about scanning the UIView
for texts & images? Assume that I receive the UIView
from outside & am not aware of its contents.
You do indeed have to draw the texts/images individually into a CGContextRef
(look at CGPDFContextCreateWithURL
) using functions like CGContextShowTextAtPoint
. While you could theoretically scan your UIView for images/labels, it would be better if you just draw the layout from scratch. You must have some way to know what elements should be in the UIView. Although the code will be very different, logically you should approach it the same way you would if you were going to draw the UIView programmatically instead of loading it from a nib.
If you really want to scan your UIView, you could do something like this:
for(UIView *subview in parentView.subviews) {
if([subview isKindOfClass:[UIImageView class]]) {
...
} else if([subview isKindOfClass:[UITextView class]]) {
...
} else if([subview isKindOfClass:[UILabel class]]) {
...
}
}
I've had a moderate amount of luck just walking my view hierarchy, and calling -drawRect: myself. There's a bit of set up that you've got do before each, like translating the ctm and clearing the text matrix, but when you've done that for each view, you can do this:
UIGraphicsPushContext(ctx);
[self drawRect:self.frame];
UIGraphicsPopContext();
The PDF it generates is vector based -- not just a flat bitmap, so it scales nicely and you can select the text in a viewer/browser. This only works on plain vanilla custom drawing and very simple stuff like UILabels. I doubt it would work with more complex UIKit objects (I'm thinking table views and scroll views). Also, you would need extra work for UIImageViews (which I'm guessing set the contents property of the layer with a CGImageRef), and if you've got other layer based attributes and transforms -- even more work.
Hopefully Apple will give us a better solution than maintaining parallel draw code!
try,
{
//read the pdf file
CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("iPhoneAppProgrammingGuide.pdf"), NULL, NULL);
PDFfile = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
CFRelease(pdfURL)
CGPDFPageRef page = CGPDFDocumentGetPage(PDFfile,currentpage);
context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextFillRect(context,self.bounds);
CGContextTranslateCTM(context, -1.0, [self bounds].size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page, kCGPDFArtBox, [self bounds], 0, true));
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
//CGAffineTransform transform = aspectFit(CGPDFPageGetBoxRect(page, kCGPDFMediaBox),
CGContextGetClipBoundingBox(context));
// CGContextConcatCTM(context, transform);
UIGraphicsBeginImageContext(CGSizeMake(self.bounds.size.width, self.bounds.size.height));
}