Efficient thumbnail creation for PDF files like iBooks - iphone

How is iBooks able to create PDF page thumbnails so fast on first load? I tried using CGContext functions to draw the page and then resize it to get a thumbnail. But this approach takes way to long. Is there an efficient way to get thumbnails of PDF pages?
Thanks in advance,
Anupam

First get all your PDF files Path in an array[here ie:pdfs].Then if you want to show all these PDF thumbnails in UICollectionView,just pass the index obtained from the collection view Delegate Method "CellForRowAtIndexPath" to the following,
-(UIImage *)GeneratingIcon:(int)index
{
NSURL* pdfFileUrl = [NSURL fileURLWithPath:[pdfs objectAtIndex:index]];
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((__bridge CFURLRef)pdfFileUrl);
CGPDFPageRef page;
CGRect aRect = CGRectMake(0, 0, 102, 141); // thumbnail size
UIGraphicsBeginImageContext(aRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
UIImage* IconImage;
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, aRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetGrayFillColor(context, 1.0, 1.0);
CGContextFillRect(context, aRect);
// Grab the first PDF page
page = CGPDFDocumentGetPage(pdf, 1);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFMediaBox, aRect, 0, true);
// And apply the transform.
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, page);
// Create the new UIImage from the context
IconImage = UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(context);
UIGraphicsEndImageContext();
CGPDFDocumentRelease(pdf);
return IconImage;
}
Hope this would be fast enough to create thumbnail images for the PDF.

Disclaimer: I have not checked if this is faster or not.
There are some built in methods in ImageIO that are specialized in creating thumbnails. These methods should be optimized for creating thumbnails. You would need to add ImageIO.framework to your project and #import <ImageIO/ImageIO.h> in your code.
// Get PDF-data
NSData *pdfData = [NSData dataWithContentsOfURL:myFileURL];
// Get reference to the source
// NOTE: You are responsible for releasing the created image source
CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)pdfData, NULL);
// Configure how to create the thumbnail
// NOTE: You should change the thumbnail size depending on how large thumbnails you need.
// 512 pixels is probably way too big. Smaller sizes will be faster.
NSDictionary* thumbnailOptions =
#{(id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue,
(id)kCGImageSourceCreateThumbnailFromImageIfAbsent: (id)kCFBooleanTrue,
(id)kCGImageSourceThumbnailMaxPixelSize: #512}; // no more than 512 px wide or high
// Create thumbnail
// NOTE: You are responsible for releasing the created image
CGImageRef imageRef =
CGImageSourceCreateThumbnailAtIndex(imageSourceRef,
0, // index 0 of the source
(__bridge CFDictionaryRef)thumbnailOptions);
// Do something with the thumbnail ...
// Release the imageRef and imageSourceRef
CGImageRelease (imageRef);
CFRelease(imageSourceRef);

Related

Drawing UIImage with CGRect in a pdf from Core Data

I'm generating a PDF document on the fly using data that's been outputted from my Core Data graph, however this is proving to be quite difficult.
I'm using the assetlibrary to get the filepath of the image out of the core data graph by using the following:
NSURL *url = [[NSURL alloc] initWithString:bird.photo];
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library assetForURL:url resultBlock:^(ALAsset *asset) {
_pdfBirdmage = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
} failureBlock:^(NSError *error) {
NSLog(#"Couldn't load asset %# => %#", error, [error localizedDescription]);
}];
And this is showing that the image is as the following:
birdphotoview = <UIImage: 0x1caac0>
Obviously, I would like this to be in an image format, such as PNG or JPG in order to write it to the PDF document, using the following code:
[_pdfBirdImage drawInRect:CGRectMake( (pageSize.width - _pdfBirdImage.size.width/2)/2, 350, _pdfBirdImage.size.width/2, _pdfBirdImage.size.height/2)];
However, this is not printing anything to the pdf.
Any help would be brilliant.
Are you using the CGPDF based routines? I think the Y dimension is opposite in that event. I've displayed PDFs before and overlaid data on top (and subsequently then rendered the entire screen back to disk but as an image). If that is the case, I had to transform the existing PDF first. Doesn't sound like your problem, but I don't see any PDF code referenced here, so hard to tell. Regardless, was something along:
UIGraphicsBeginImageContext(pdfPageRectScaled.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// First fill the background with white.
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f);
CGContextFillRect(context, pdfPageRectScaled);
CGContextSaveGState(context);
// Flip the context so that the PDF page is rendered
// right side up.
CGContextTranslateCTM(context, 0.0f, pdfPageRectScaled.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);
// Scale the context so that the PDF page is rendered
// at the correct size for the zoom level.
CGContextScaleCTM(context, pdfScale, pdfScale);
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);

saving image from a page of CGPDfDocument is not perfectly fitted in UIImageview

I am having some trouble with saving a PDF page as UIImage...the pdf is loaded from the internet and it has one page(original PDF has been splitted in sever)...but the converted image sometimes is cropped...sometimes it is small and leave white space when it is putted on UIImageview...
here is the code
-(UIImage *)imageFromPdf:(NSString *) pdfUrl{
NSURL *pdfUrlStr=[NSURL URLWithString:pdfUrl];
CFURLRef docURLRef=(CFURLRef)pdfUrlStr;
UIGraphicsBeginImageContext(CGSizeMake(768, 1024)); //840, 960
NSLog(#"save begin");
CGContextRef context = UIGraphicsGetCurrentContext();
//CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("/file.pdf"), NULL, NULL);
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL(docURLRef);
NSLog(#"save complete");
CGContextTranslateCTM(context, 0.0, 900);//320
CGContextScaleCTM(context, 1.0, -1.0);
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
CGContextSaveGState(context);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, CGRectMake(0, 0, 768, 1024), 0, true);
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultingImage;
}
btw I have prepared my UIImageview by coding like this
self.PDFImageVIew.contentMode = UIViewContentModeScaleAspectFit;
self.PDFImageVIew.clipsToBounds = YES;
I just want this image perfectly fitted on UIImageview and may be its reducing the quality of image...can you have suggesion how can I keep the quality also? please help and give me some suggestion
thanks
CGContextTranslateCTM(context, 0.0, 900);//320
Here generally last parameter of translate operation should be the height of context or height of rectangle for which you creating image. So, i think it should be 1024(You have taken height of image context is 1024 so here i am assuming that status bar is not present). This may eliminate the issue of cropping. Some more things that i have noted on your code you should have to save the state of graphics before any operation on context. You have are saving it but after few operations.
Above code will try to make it height fit so if height of actual page is bigger than your context height then it will be scaled down. so you can obviously see white space around page.
One more thing if your original pdf page have white space in it then there is no way to eliminate it as far as i know.

Rendering UIWebView as Image/PDF Has Visual Artifacts

I'm rendering a UIWebView's layer into a graphics context and then using the UIGraphicsBeginPDFPageWithInfo() family of functions to include it in a PDF.
My problem is that the output includes an extra set of gray lines that aren't part of my data set. I'm hoping someone can shed some light on where they're coming from.
An example of the output is included below. The HTML document that is being rendered contains nothing but the text 'THIS IS A TEST' - the boxes you see are coming from the rendering process somewhere. When rendered on the screen, it's just black text on a white screen - no lines/boxes.
Anyone have any ideas what's going on? Thanks!
Here's the code I'm using to render this web view as a PDF:
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, CGRectZero, nil);
CGRect viewBounds = webView.bounds;
UIGraphicsBeginPDFPageWithInfo(viewBounds, nil);
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
[webView.layer renderInContext:pdfContext];
UIGraphicsEndPDFContext();
Also, here's a screenshot of what I'm seeing for output:
I ran into the same issue. It would happen whenever trying to render the UIWebView into the PDF context, and frame width > 512. I was not able to diagnose the exact issue, but worked around it by rendering the UIWebView into a UIImage, and then rendering the UIImage into the pdf context.
Code as:
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, kWidth, kHeight), nil);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
UIImage* image = nil;
UIGraphicsPushContext(currentContext);
UIGraphicsBeginImageContext(self.webview.frame.size);
{
[self.webview.layer renderInContext: UIGraphicsGetCurrentContext()];
image = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
UIGraphicsPopContext();
[image drawInRect:CGRectMake(0, 0, kWidth, kHeight)];

What's the correct code to save a CGLayer as a PNG file?

Please note that this question is about CGLayer (which you typically use to draw offscreen), it is not about CALayer.
In iOS, what's the correct code to save a CGLayer as a PNG file? Thanks!
Again, that's CGLayer, not CALayer.
Note that you CAN NOT use UIGraphicsGetImageFromCurrentImageContext.
(From the documentation, "You can call UIGraphicsGetImageFromCurrentImageContext only when a bitmap-based graphics context is the current graphics context.")
Note that you CAN NOT use renderInContext:. renderInContext: is strictly for CALayers. CGLayers are totally different.
So, how can you actually convert a CGLayer to a PNG image? Or indeed, how to render a CGLayer in to a bitmap in some way (of course you can then easily save as an image).
Later ... Ken has answered this difficult question. I will paste in a long example code that may help people. Thanks again Ken! Amazing!
-(void)drawingExperimentation
{
// this code uses the ASTOUNDING solution by KENNYTM -- Oct/Nov2010
//
// create a CGLayer for offscreen drawing
// note. for "yourContext", ideally it should be a context from your screen, ie the
// context you "normally get" in one of your drawRect routines associated with
// drawing to the screen normally.
// UIGraphicsGetCurrentContext() also normally works but you could have colorspace woes
// so create the CGLayer called notepad...
CGLayerRef notepad = CGLayerCreateWithContext(yourContext,CGSizeMake(1500,1500), NULL);
CGContextRef notepadContext = CGLayerGetContext(notepad);
// you can for example write an image in to notepad
CGImageRef imageExamp = [[UIImage imageWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:#"smallTestImage" ofType:#"png"] ] CGImage];
CGContextDrawImage( notepadContext, CGRectMake(100,100, 50,50), imageExamp);
// setting the colorspace may or may not be relevant to you
CGContextSetFillColorSpace( notepadContext, CGColorSpaceCreateDeviceRGB() );
// you can draw to notepad as much as you like in the normal way
// don't forget to push it's context on and off your work space so you can draw to it
UIGraphicsPushContext(notepadContext);
// set the colors
CGContextSetRGBFillColor(notepadContext, 0.15,0.25,0.35, 0.45);
// draw rects
UIRectFill(CGRectMake(x,y,w,h));
// draw ovals, filled stroked or whatever you wish
UIBezierPath* d = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(x,y,w,h)];
[d fill];
// draw cubic and other curves
UIBezierPath *longPath;
longPath.lineWidth = 42;
longPath.lineCapStyle = kCGLineCapRound;
longPath.lineJoinStyle = kCGLineJoinRound;
[longPath moveToPoint:p];
[longPath addCurveToPoint:q controlPoint1:r controlPoint2:s];
[longPath addCurveToPoint:a controlPoint1:b controlPoint2:c];
[longPath addCurveToPoint:m controlPoint1:n controlPoint2:o];
[longPath closePath];
[longPath stroke];
UIGraphicsPopContext();
// so now you have a nice CGLayer.
// how to save it to a file?
// you can save it to a file using the amazing KENNY-TM-METHOD !!!
UIGraphicsBeginImageContext( CGLayerGetSize(notepad) );
CGContextRef rr = UIGraphicsGetCurrentContext();
CGContextDrawLayerAtPoint(rr, CGPointZero, notepad);
UIImage* ii = UIGraphicsGetImageFromCurrentImageContext();
NSData* pp = UIImagePNGRepresentation(ii);
[pp writeToFile:#"foo.png" atomically:YES];
UIGraphicsEndImageContext();
// you may prefer to look at it like this:
UIGraphicsBeginImageContext( CGLayerGetSize(notepad) );
CGContextDrawLayerAtPoint(UIGraphicsGetCurrentContext(), CGPointZero, notepad);
[UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext()) writeToFile:#"foo.png" atomically:YES];
UIGraphicsEndImageContext();
// there are three clever steps in the KENNY-TM-METHOD:
// - start a new UIGraphics image context
// - CGContextDrawLayerAtPoint which can, in fact, draw a CGLayer
// - just use the usual UIImagePNGRepresentation to convert to a png
// done! a miracle
// if you are testing on your mac-simulator, you'll find the file
// simply in the main drive directory
return;
}
For iPhone OS, it should be possible to draw a CGLayer on a CGContext and then convert into a UIImage, which can then be encoded into PNG and saved.
CGSize size = CGLayerGetSize(layer);
UIGraphicsBeginImageContext(size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextDrawLayerAtPoint(ctx, CGPointZero, layer);
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
NSData* pngData = UIImagePNGRepresentation(image);
[pngData writeToFile:... atomically:YES];
UIGraphicsEndImageContext();
(not tested)

CGPDFDocument scaling

I am just starting to build an app that will display PDF documents. I've been experimenting, subclassing UIView and using the code from Apples demo. I have a PDF document that contains an image that is 1024 x 748 pixels at 131 ppi, so that it SHOULD fill the iPad screen in landscape view.
When I run the app the pdf is scaled to approximately .25% of its full size, centered in the iPad screen. Why isn't the PDF being displayed full sized?
Code from my custom UIView:
-(id)initWithFrame:(CGRect)frame PDFName:(NSString *)pdfName
{
self = [super initWithFrame:frame];
if(self != nil)
{
self.backgroundColor = [UIColor blueColor];
self.opaque = YES;
self.clearsContextBeforeDrawing = YES;
CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), (CFStringRef)pdfName, NULL, NULL);
pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
CFRelease(pdfURL);
}
return self;
}
- (void)drawRect:(CGRect)rect {
// PDF page drawing expects a Lower-Left coordinate system, so we flip the coordinate system
// before we start drawing.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// Grab the first PDF page
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
// We're about to modify the context CTM to draw the PDF page where we want it, so save the graphics state in case we want to do more drawing
CGContextSaveGState(context);
// CGPDFPageGetDrawingTransform provides an easy way to get the transform for a PDF page. It will scale down to fit, including any
// base rotations necessary to display the PDF page correctly.
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFMediaBox, self.bounds, 0, true);
// And apply the transform.
CGContextConcatCTM(context, pdfTransform);
// Finally, we draw the page and restore the graphics state for further manipulations!
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
}
Answer was easy. Changed the ppi of the image in the PDF to 72 ppi (still 1024 x 748). Now it fills the screen correctly. I thought that I needed to match the iPads pixel density, but I guess not.
Jk