Document thumbnails generated from preview content - iphone

I'm creating a grid of cells that represent downloaded files of various types (PDF, Images, Videos, HTML, .pages, .numbers, Word, etc.) in an ios application and am looking for a way to preemptively create preview thumbnails for these documents to show in the cells. I'm currently using the UIDocumentInteractionController for previewing these documents after the user selects one and was hoping that the icons array would return the preview images. Unfortunately, it only returns a generic icon.
I see that Pages and Numbers do this, but they own the documents and the format. I was hoping that the Quicklook framework would provide a solution, but I haven't yet found it. Has anybody else found a way to easily generate these thumbnails?
I'd prefer to use an actual UIImage and not a UIWebView as others have suggested. That solution seems to be a hack and I have to believe there's a better solution out there.
My next option is to generate a preview after they open the document by essentially capturing the view as an image, but that still seems hackish.
Anybody have any thoughts or clearer options?
Thanks,

My solution:
I never found a complete solution this particular problem, but I was able to handle PDF's fairly efficiently. I honestly don't remember if I wrote this or found an example. If it's your code that was posted somewhere else, thanks! The end result here is docImage is either null, or contains the first page of the PDF as an image. When loading my table rows, I set the image preview to a default image and then run this code on a background thread using blocks. Once complete, I simply animate the preview back into the cell.
NSURL *pdfFileURL = [NSURL fileURLWithPath:localFilePath];
CGPDFDocumentRef pdfDoc = CGPDFDocumentCreateWithURL((__bridge CFURLRef) pdfFileURL);
NSUInteger numPages = CGPDFDocumentGetNumberOfPages(pdfDoc);
if(numPages > 0) {
docImage = [UIImage imageFromPDFDocumentRef:pdfDoc];
}
CGPDFDocumentRelease(pdfDoc);
UIImage Category:
#implementation UIImage (CGPDFDocument)
+(UIImage *)imageFromPDFDocumentRef:(CGPDFDocumentRef)documentRef {
return [self imageFromPDFDocumentRef:documentRef page:1];
}
+(UIImage *)imageFromPDFDocumentRef:(CGPDFDocumentRef)documentRef page:(NSUInteger)page {
CGPDFPageRef pageRef = CGPDFDocumentGetPage(documentRef, page);
CGRect pageRect = CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox);
UIGraphicsBeginImageContext(pageRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, CGRectGetMinX(pageRect),CGRectGetMaxY(pageRect));
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, -(pageRect.origin.x), -(pageRect.origin.y));
CGContextDrawPDFPage(context, pageRef);
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}
#end

Related

Text Formatting in PDF Reader and Writer in iOS

In my application I have to show a PDF reader (PDF from a server) and copy everything to another PDF. I am using Reader to show the PDF.
But it is unable to format the text inside the PDF. Bold text is shown in some different narrow font other than Bold. Can you please give me a solution?
Again, while writing I am using Saving a PDF document to disk using Quartz. But here I am facing the same problem. The new PDF is showing the font other than what is used in the source PDF.
I tried the code below also. Still I am getting the same O/P. Please help me with this. I have not tested it with another text font like italic / underline. It should work for these fonts also.
- (void) generatePdfWithFilePath: (NSString *)thefilePath {
UIGraphicsBeginPDFContextToFile(thefilePath, CGRectZero, NULL);
BOOL done = NO;
do {
//Start a new page.
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, pageSize.width, pageSize.height), nil);
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"MyPdf" ofType:#"pdf"];
CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:filePath];
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL(url);
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
CGRect paperSize = CGPDFPageGetBoxRect(CGPDFDocumentGetPage (pdf, 1), kCGPDFMediaBox);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, paperSize.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawPDFPage(context, page);
done = YES;
} while (!done);
UIGraphicsEndPDFContext();
}
It is happening if the source PDF is generated from a source other than an iOS device.
A couple of thoughts:
The font you're looking to display is not in any of the iOS devices, nor is it embedded in the PDF. Thus, it substitutes a font it thinks is similar.
You don't have to use Reader to display a PDF, you can use a web view, the UIDocumentInteraction Controller, or CGPDFController. You might have more luck using one of these built-in displays, rather than Reader.

Adding tags to Images a cross between facebook & map pointers

I intend to make an app that adds a layer of text or pointers to images.
Kinda like adding another layer text/pointer over an image displayed in iphone.
ALSO, upon exporting, i want that image with the added text over it to be clubbed with the new "exported" image.
Im puzzeled as to how to start this..?
any ideas or references would be greatly appreciated.
Well, the easiest way is to add all the load the uiimageview, and the other views on a single parent view and
#implementation UIView (Imaging)
-(UIImage *) getSnapshotImage
{
UIGraphicsBeginImageContext(CGSizeMake(self.bounds.size.width, self.bounds.size.height));
CGColorSpaceRef color = CGColorSpaceCreateDeviceRGB();
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
CGColorSpaceRelease(color);
UIGraphicsEndImageContext();
return outputImage;
}
#end
once you have the UIImage, convert it to JPEG or PNG using
NSData * UIImageJPEGRepresentation (
UIImage *image,
CGFloat compressionQuality
);
which you can then proceed to store it in a file
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag

iOS SDK - Programmatically generate a PDF file

Using the CoreGraphics framework is tedious work, in my honest opinion, when it comes to programmatically drawing a PDF file.
I would like to programmatically create a PDF, using various objects from views throughout my app.
I am interested to know if there are any good PDF tutorials around for the iOS SDK, maybe a drop in library.
I've seen this tutorial, PDF Creation Tutorial, but it was mostly written in C. Looking for more Objective-C style. This also seems like a ridiculous way to write to a PDF file, having to calculate where lines and other objects will be placed.
void CreatePDFFile (CGRect pageRect, const char *filename)
{
// This code block sets up our PDF Context so that we can draw to it
CGContextRef pdfContext;
CFStringRef path;
CFURLRef url;
CFMutableDictionaryRef myDictionary = NULL;
// Create a CFString from the filename we provide to this method when we call it
path = CFStringCreateWithCString (NULL, filename,
kCFStringEncodingUTF8);
// Create a CFURL using the CFString we just defined
url = CFURLCreateWithFileSystemPath (NULL, path,
kCFURLPOSIXPathStyle, 0);
CFRelease (path);
// This dictionary contains extra options mostly for 'signing' the PDF
myDictionary = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(myDictionary, kCGPDFContextTitle, CFSTR("My PDF File"));
CFDictionarySetValue(myDictionary, kCGPDFContextCreator, CFSTR("My Name"));
// Create our PDF Context with the CFURL, the CGRect we provide, and the above defined dictionary
pdfContext = CGPDFContextCreateWithURL (url, &pageRect, myDictionary);
// Cleanup our mess
CFRelease(myDictionary);
CFRelease(url);
// Done creating our PDF Context, now it's time to draw to it
// Starts our first page
CGContextBeginPage (pdfContext, &pageRect);
// Draws a black rectangle around the page inset by 50 on all sides
CGContextStrokeRect(pdfContext, CGRectMake(50, 50, pageRect.size.width - 100, pageRect.size.height - 100));
// This code block will create an image that we then draw to the page
const char *picture = "Picture";
CGImageRef image;
CGDataProviderRef provider;
CFStringRef picturePath;
CFURLRef pictureURL;
picturePath = CFStringCreateWithCString (NULL, picture,
kCFStringEncodingUTF8);
pictureURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), picturePath, CFSTR("png"), NULL);
CFRelease(picturePath);
provider = CGDataProviderCreateWithURL (pictureURL);
CFRelease (pictureURL);
image = CGImageCreateWithPNGDataProvider (provider, NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease (provider);
CGContextDrawImage (pdfContext, CGRectMake(200, 200, 207, 385),image);
CGImageRelease (image);
// End image code
// Adding some text on top of the image we just added
CGContextSelectFont (pdfContext, "Helvetica", 16, kCGEncodingMacRoman);
CGContextSetTextDrawingMode (pdfContext, kCGTextFill);
CGContextSetRGBFillColor (pdfContext, 0, 0, 0, 1);
const char *text = "Hello World!";
CGContextShowTextAtPoint (pdfContext, 260, 390, text, strlen(text));
// End text
// We are done drawing to this page, let's end it
// We could add as many pages as we wanted using CGContextBeginPage/CGContextEndPage
CGContextEndPage (pdfContext);
// We are done with our context now, so we release it
CGContextRelease (pdfContext);
}
EDIT: Here's an example on GitHub using libHaru in an iPhone project.
A couple things...
First, there is a bug with CoreGraphics PDF generation in iOS that results in corrupted PDFs. I know this issue exists up to and including iOS 4.1 (I haven't tested iOS 4.2). The issue is related to fonts and only shows up if you include text in your PDF. The symptom is that, when generating the PDF, you'll see errors in the debug console that look like this:
<Error>: can't get CIDs for glyphs for 'TimesNewRomanPSMT'
The tricky aspect is that the resulting PDF will render fine in some PDF readers, but fail to render in other places. So, if you have control over the software that will be used to open your PDF, you may be able to ignore this issue (e.g., if you only intend to display the PDF on the iPhone or Mac desktops, then you should be fine using CoreGraphics). However, if you need to create a PDF that works anywhere, then you should take a closer look at this issue. Here's some additional info:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/15505-pdf-font-problem-cant-get-cids-glyphs.html#post97854
As a workaround, I've used libHaru successfully on iPhone as a replacement for CoreGraphics PDF generation. It was a little tricky getting libHaru to build with my project initially, but once I got my project setup properly, it worked fine for my needs.
Second, depending on the format/layout of your PDF, you might consider using Interface Builder to create a view that serves as a "template" for your PDF output. You would then write code to load the view, fill in any data (e.g., set text for UILabels, etc.), then render the individual elements of the view into the PDF. In other words, use IB to specify coordinates, fonts, images, etc. and write code to render various elements (e.g., UILabel, UIImageView, etc.) in a generic way so you don't have to hard-code everything. I used this approach and it worked out great for my needs. Again, this may or may not make sense for your situation depending on the formatting/layout needs of your PDF.
EDIT: (response to 1st comment)
My implementation is part of a commercial product meaning that I can't share the full code, but I can give a general outline:
I created a .xib file with a view and sized the view to 850 x 1100 (my PDF was targeting 8.5 x 11 inches, so this makes it easy to translate to/from design-time coordinates).
In code, I load the view:
- (UIView *)loadTemplate
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ReportTemplate" owner:self options:nil];
for (id view in nib) {
if ([view isKindOfClass: [UIView class]]) {
return view;
}
}
return nil;
}
I then fill in various elements. I used tags to find the appropriate elements, but you could do this other ways. Example:
UILabel *label = (UILabel *)[templateView viewWithTag:TAG_FIRST_NAME];
if (label != nil) {
label.text = (firstName != nil) ? firstName : #"None";
Then I call a function to render the view to the PDF file. This function recursively walks the view hierarchy and renders each subview. For my project, I need to support only Label, ImageView, and View (for nested views):
- (void)addObject:(UIView *)view
{
if (view != nil && !view.hidden) {
if ([view isKindOfClass:[UILabel class]]) {
[self addLabel:(UILabel *)view];
} else if ([view isKindOfClass:[UIImageView class]]) {
[self addImageView:(UIImageView *)view];
} else if ([view isKindOfClass:[UIView class]]) {
[self addContainer:view];
}
}
}
As an example, here's my implementation of addImageView (HPDF_ functions are from libHaru):
- (void)addImageView:(UIImageView *)imageView
{
NSData *pngData = UIImagePNGRepresentation(imageView.image);
if (pngData != nil) {
HPDF_Image image = HPDF_LoadPngImageFromMem(_pdf, [pngData bytes], [pngData length]);
if (image != NULL) {
CGRect destRect = [self rectToPDF:imageView.frame];
float x = destRect.origin.x;
float y = destRect.origin.y - destRect.size.height;
float width = destRect.size.width;
float height = destRect.size.height;
HPDF_Page page = HPDF_GetCurrentPage(_pdf);
HPDF_Page_DrawImage(page, image, x, y, width, height);
}
}
}
Hopefully that gives you the idea.
It's a late reply, but as i struggled a lot with pdf generation, i considered it worthwhile to share my views. Instead of Core graphics, to create a context, you can also use UIKit methods to generate a pdf.
Apple has documented it well in the drawing and printing guide.
The PDF functions on iOS are all CoreGraphics based, which makes sense, because the draw primitives in iOS are also CoreGraphics based. If you want to be rendering 2D primitives directly into a PDF, you'll have to use CoreGraphics.
There are some shortcuts for objects that live in UIKit as well, like images. Drawing a PNG to a PDF context still requires the call to CGContextDrawImage, but you can do it with the CGImage that you can get from a UIImage, e.g.:
UIImage * myPNG = [UIImage imageNamed:#"mypng.png"];
CGContextDrawImage (pdfContext, CGRectMake(200, 200, 207, 385), [myPNG CGImage]);
If you want more guidance on overall design, please be more explicit about what you mean by "various objects throughout your app" and what you're trying to accomplish.
Late, but possibly helpful to others. It sounds like a PDF template style of operation might be the approach you want rather than building the PDF in code. Since you want to email it off anyway, you are connected to the net so you could use something like the Docmosis cloud service which is effectively a mail-merge service. Send it the data/images to merge with your template. That type of approach has the benefit of a lot less code and offloading most of the processing from your iPad app. I've seen it used in an iPad app and it was nice.

Annotate PDF within iPhone SDK

I have managed to implement a very basic PDF viewer within my application, but was wondering if it was possible to add annotations to the PDF. I have looked through the SDK docs, but not found anything. I have 2 questions really:
Is it possible to do this?
What is the best approach to take?
Is there a framework or library that I can include to assist with this?
Thanks.
You can do annotation by reading in a PDF page, drawing it onto a new PDF graphics context, then drawing extra content onto that graphic context. Here is some code that adds the words 'Example annotation' at position (100.0,100.0) to an existing PDF. The method getPDFFileName returns the path of the original PD. getTempPDFFileName returns the path of the new PDF, the one that is the original plus the annotation.
To vary the annotations, just add in more drawing code in place of the drawInRect:withFont: method. See the Drawing and Printing Guide for iOS for more on how to do that.
- (void) exampleAnnotation;
{
NSURL* url = [NSURL fileURLWithPath:[self getPDFFileName]];
CGPDFDocumentRef document = CGPDFDocumentCreateWithURL ((CFURLRef) url);// 2
size_t count = CGPDFDocumentGetNumberOfPages (document);// 3
if (count == 0)
{
NSLog(#"PDF needs at least one page");
return;
}
CGRect paperSize = CGRectMake(0.0,0.0,595.28,841.89);
UIGraphicsBeginPDFContextToFile([self getTempPDFFileName], paperSize, nil);
UIGraphicsBeginPDFPageWithInfo(paperSize, nil);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
// flip context so page is right way up
CGContextTranslateCTM(currentContext, 0, paperSize.size.height);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGPDFPageRef page = CGPDFDocumentGetPage (document, 1); // grab page 1 of the PDF
CGContextDrawPDFPage (currentContext, page); // draw page 1 into graphics context
// flip context so annotations are right way up
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGContextTranslateCTM(currentContext, 0, -paperSize.size.height);
[#"Example annotation" drawInRect:CGRectMake(100.0, 100.0, 200.0, 40.0) withFont:[UIFont systemFontOfSize:18.0]];
UIGraphicsEndPDFContext();
CGPDFDocumentRelease (document);
}
I am working in the stuff and created a GitHub project. Please check it out here.
I don't think PDFAnnotation or PDFKit were ported to iPhone from the desktop.... probably a great excuse to file a radar. However, Haru may get you by in the meantime.

Computing a UIImage to be saved to the photo album

I basically want to automatically create a tiled image from a bunch of source images, and then save that to the user's photo album. I'm not having any success drawing a bunch of small UIImage's into one big UIImage. What's the best way to accomplish this? Currently I'm using UIGraphicsBeginImageContext() and [UIImage drawAtPoint], etc. All I ever end up with is a 512x512 black square. How should I be doing this? I'm looking to CGLayer's, etc. seems there are a lot of options but none that work particularly easily.
Let me actually put my code in:
CGSize size = CGSizeMake(512, 512);
UIGraphicsBeginImageContext(size);
UIGraphicsPushContext(UIGraphicsGetCurrentContext());
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
UIImage *image = [self getImageAt:i:j];
[image drawAtPoint:CGPointMake(i*128,j*128)];
}
}
UIGraphicsPopContext();
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(result, nil, nil, nil);
I should note that actually the above is not what happens in my code exactly. What really happens is that I call every line before and including to UIGraphicsPushContext, then in an animation timer I slowly increment the drawing and draw to the context. Then after it's all done I call everything from UIGraphicsPopContext onward.
Oh, then you can just save the onscreen view after it has been rendered on screen:
UIGraphicsBeginImageContext(myBigView.bounds.size);
[view drawRect:myBigView.bounds];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
are you storing it back to an image?
UIImage *myBigImage = UIGraphicsGetImageFromCurrentImageContext();
To do exactly what I wanted to do...
Make your GLView as big as the total thing you want. Also make sure glOrtho and your viewport have the right size. Then just draw whatever you want wherever you want, and take a single OpenGL screenshot. Then you don't need to worry about combining to a single UIImage over multiple OpenGL rendering passes, which is no doubt what was causing my issue.