Contex Drawing + Pagination - iphone

I am trying to draw contents of scrollview into a PDF context & I am facing problem with pagination.
Following Code I have used:
- (void)renderTheView:(UIView *)view inPDFContext:(CGContextRef)pdfContext
{
// Creating frame.
CGFloat heightOfPdf = [[[self attributes] objectForKey:SRCPdfHeight] floatValue];
CGFloat widthOfPdf = [[[self attributes] objectForKey:SRCPdfWidth] floatValue];
CGRect pdfFrame = CGRectMake(0, 0, widthOfPdf, heightOfPdf);
CGRect viewFrame = [view frame];
if ([view isKindOfClass:[UIScrollView class]])
{
viewFrame.size.height = ((UIScrollView *)view).contentSize.height;
[view setFrame:viewFrame];
}
// Calculates number of pages.
NSUInteger totalNumberOfPages = ceil(viewFrame.size.height/heightOfPdf);
// Start rendering.
for (NSUInteger pageNumber = 0; pageNumber<totalNumberOfPages; pageNumber++)
{
// Starts our first page.
CGContextBeginPage (pdfContext, &pdfFrame);
// Turn PDF upsidedown
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformMakeTranslation(0,view.bounds.size.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(pdfContext, transform);
// Calculate amount of y to be displace.
CGFloat ty = (heightOfPdf*(pageNumber));
CGContextTranslateCTM(pdfContext,0,-ty);
[view.layer renderInContext:pdfContext];
// We are done drawing to this page, let's end it.
CGContextEndPage (pdfContext);
}
}
It creates required number of pages but places the content wrongly. Following figure explains it.
Is there anything wrong in my code?

I don't think you need the code which flips the PDF upside down. I've done this before (manual pagination, but without a scroll view) and never had to flip the context vertically. My main concern is that you are doing it on every iteration - if you have a valid reason for flipping it, you probably don't need it in the loop, but just once before the loop begins. Also the code CGContextTranslateCTM(pdfContext,0,-ty); might need to be replaced with CGContextTranslateCTM(pdfContext,0,heightOfPdf);
If that doesn't work, try UIGraphicsBeginPDFPage(); instead of CGContextBeginPage(), that's the only major difference between your code and mine.

This github project can definitely help you.
- (void) renderPageAtIndex:(NSUInteger)index inContext:(CGContextRef)ctx {
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, index + 1);
CGAffineTransform transform = aspectFit(CGPDFPageGetBoxRect(page, kCGPDFMediaBox),
CGContextGetClipBoundingBox(ctx));
CGContextConcatCTM(ctx, transform);
CGContextDrawPDFPage(ctx, page);
}

Found this link to render a PDF, it says about multiple pages but haven't shown an implementation. It doesnt straight away answer to your question but it tells a simpler method to render a pdf with much less translation and transforms.
Render PDF
Generate Multipage PDF
-anoop

You could consider using a UIScrollView to layout your separate PDF pages. The following method loads each PDF page in a view (PDFView) and adds it to the scroll view content in a centered fashion:
- (void) loadPDFAtPath:(NSString *)path
{
NSURL *pdfUrl = [NSURL fileURLWithPath:path];
CGPDFDocumentRef document = CGPDFDocumentCreateWithURL((__bridge CFURLRef)pdfUrl);
float height = 0.0;
for(int i = 0; i < CGPDFDocumentGetNumberOfPages(document); i++) {
CGPDFPageRef page = CGPDFDocumentGetPage(document, i + 1);
PDFView *pdfView = [[PDFView alloc] initPdfViewWithPageRef:page];
CGRect pageSize = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
pv.frame = CGRectMake((self.frame.size.width / 2.0) - pageSize.size.width / 2.0, height, pageSize.size.width, pageSize.size.height);
height += pageSize.size.height + SOME_SPACE_IN_BETWEEN_PAGES;
// self is a subclass of UIScrollView
[self addSubview:pdfView];
[pdfView setNeedsDisplay];
}
CGPDFDocumentRelease(document);
[self setContentSize:CGSizeMake(self.frame.size.width, height)];
}
In this case, PDFView is responsible for rendering a single CGPDFPageRef in its drawRect or drawLayer implementation.

The portions you need to add to pdf pages put in seperate views inside the scroll and render tht view to the pdf context
OR
Just find the correct dimensions and draw using the CG method:
UIImage *Logo=[UIImage imageNamed:#"small-logo-left-top130_133.png"];
CGPoint drawingLogoOrgin = CGPointMake(5,5);
UIGraphicsBeginPDFContextToData(pdfData, CGRectZero, nil);
UIGraphicsBeginPDFPageWithInfo(pageFrame, nil);
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
[Logo drawAtPoint:drawingLogoOrgin];
There are a lot of drawing methods to draw pdf. You can use that to draw all the contents.

this might help you.
- (void) renderPageAtIndex:(NSUInteger)index inContext:(CGContextRef)ctx
{
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, index + 1);
CGAffineTransform transform = aspectFit(CGPDFPageGetBoxRect(page, kCGPDFMediaBox),
CGContextGetClipBoundingBox(ctx));
CGContextConcatCTM(ctx, transform);
CGContextDrawPDFPage(ctx, page);
}

Related

pdf generated upside down

I am creating pdf using below code
UIImage *CurImage=[UIImage imageWithContentsOfFile:[ImageArr objectAtIndex:i]];
UIView *ViewDraw=[[UIView alloc]initWithFrame:CGRectMake(0,10, pageSize, pageSize)];
ViewDraw.backgroundColor=[UIColor clearColor];
CGContextBeginPage (pdfContext,nil);
//turn PDF upsidedown
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformMakeTranslation(0,CurImage.size.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(pdfContext, transform);
int y = (pageSize - CurImage.size.height)/2;
int x = (pageSize - CurImage.size.width)/2;
[CurImage drawInRect:CGRectMake( (ViewDraw.frame.width - curImage.frame.width)/2, 24, size.width, curImage.frame.height)];
// CGContextDrawImage(pdfContext, CGRectMake(x,-y, CurImage.size.width, CurImage.size.height), CurImage.CGImage);
CGContextEndPage (pdfContext);
CurImage=nil;
but it is generating upside down i.e.180 degree with mirror image.
What is wrong here?
Turn PDF upside-down:
CGAffineTransform aCgAffTrans = CGAffineTransformMakeTranslation(0,892); //here the whole PDF page height should be given
aCgAffTrans = CGAffineTransformScale(aCgAffTrans, 1.0, -1.0);
CGContextConcatCTM(aCgPDFContextRef, aCgAffTrans);
Your are providing some random height while some part gets turned down and others remains the same. So a mirror like effect appears.

UIView Animations of custom view lagging

I created a UIView Subclass and are using drawing rounded rects with UIBezierpath and fill it with some gradient.
In short this is what the subclass draw rect looks like:
CGRect frame1 = //size of Frame 1
CGRect frame2 = //size of Frame 2
//gradientingStart
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
if (options == false) {
self.color = [self createRandomColor];
}
NSArray *gradientColors = [NSArray arrayWithObjects:
(id)[UIColor colorWithHue:color saturation:saturationVal brightness:brightnessVal alpha:1].CGColor,
(id)[UIColor colorWithHue:color+0.04 saturation:saturationVal+0.15 brightness:brightnessVal-0.15 alpha:1].CGColor, nil];
CGFloat gradientLocations2[] = {0.25,1};
CGGradientRef gradient2 = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradientColors, gradientLocations2);
CGContextSaveGState(context);
//gradientingEnd
UIBezierPath *result =
[UIBezierPath bezierPathWithRoundedRect: frame1 cornerRadius:radius];
[result appendPath:
[UIBezierPath bezierPathWithRoundedRect: frame2 cornerRadius:radius]];
[result setLineWidth:3];
[[UIColor blackColor]setStroke];
[result stroke];
[result fill];
[result addClip];
CGContextDrawLinearGradient(context, gradient2, CGPointMake(0, 0), CGPointMake(0, self.bounds.size.height), 0);
//important to prevent leaks
CGGradientRelease(gradient2);
CGColorSpaceRelease(colorSpace);
//-------
UIImage *texture = [UIImage imageNamed:#"texture2.png"];
[texture drawInRect:CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, self.bounds.size.height)];
Now I'm creating several instances of this in my view controller and trying the move them with animations like this:
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
costumview.alpha = 0;
[costumview setFrame:CGRectMake(320,0,320,420)];
[self reorderViewsFrom:costumview.intValue];
overlay.alpha = 0;
}completion:^(BOOL finished){
}];
But unfortunately the animation is on a device very laggy and the more (5-10) costum views are display the worse it gets.
How can i improve the performance and provide smooth animations? I think changing the costum view is not an alternative because this would destroy the sense of the application. Are there any faster ways to animate the costum view?
Your drawRect method is very intense. Drawing those gradients and rounded edges are performance heavy routines, and because your changing the frame of your image in the animation, your drawRect is getting called very often.
I would add an NSLog inside your draw rect and see how many times it gets called when you animate your custom view. You might be surprised.
You could try using:
view.layer.shouldRasterize = YES
This way you render a bitmap of the custom view before you animate it. After animating the frame change again, you should invoke drawRect again and disable shouldRasterize.
Read more about shouldRasterize here:
https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/CALayer_class/Introduction/Introduction.html#//apple_ref/occ/instp/CALayer/shouldRasterize
I pointed out that using some CALayer transformation (rounding corners) causing the lag - unfournately this wasn't in the snippet I posted.
I avoided it by creating the rounded corners with a own uiview subclass. This makes the app a lot smoother. All these rasterization stuff didn't do it for me...

How to draw a PDF to a CGContext with a higher resolution?

I wrote a view to render a PDF Page:
-(UIPDFRenderView*) initWithFrame:(CGRect)frame withDocument:(CGPDFDocumentRef*)document withPageNumber:(int)pageNumber
{
if (self)
{
page = CGPDFDocumentGetPage (*document, pageNumber);
pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
pdfScale = (CGRectGetWidth(frame)/pageRect.size.width);
rect = frame;
self = [super initWithFrame:CGRectMake(CGRectGetMinX(frame),
CGRectGetMinY(frame),
CGRectGetWidth(frame),
CGRectGetHeight(pageRect) * pdfScale)];
}
return self;
}
-(void) drawRect:(CGRect)rect
{
[self displayPDFPageWithContext:UIGraphicsGetCurrentContext()];
}
-(void) displayPDFPageWithContext:(CGContextRef)context
{
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context, self.bounds);
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, pdfScale,-1*pdfScale);
CGContextDrawPDFPage (context, page);
}
I use one of that view for every page of a pdf document and add them to a acrollview.
The problem is if i zoom in, the page is pixelig. No surprise because i am using the screen context to render the pdf. Is there any posibility to render that pdf with the double resolution to enable zoomin to a factor of 2?
Okay i got the solution, i render it in original size and initiate the scollview with a low scale that the width of the pdf fits with the width of the scrollview. With that methode i can zoom till original size (scale of 1).

How do I keep the text sharp while zooming when using a CATiledLayer to render a PDF

I have implemented a PDF viewer in my app. It's a scroll view which uses CATiledLayer for zooming.
A simpler approach would have been to use QLPreviewController, which supports PDF viewing with page control and zooming but I need fine control over the PDF page.
I have the viewer working but would like the redraw to kick in sooner. At the moment it only redraws the text when I zoom in to double the size (200%). So the text appears blurry until you zoom in far enough.
I have tried various combinations of levelOfDetail and levelOfDetailBias to try and get the re-drawing to occur sooner but to no avail.
Here is the code which creates the CATiledLayers:
//get the pdf crop box rectangle
CGPDFPageRef pageRef=[self.pdf getPdfPage:self.myPageNo];
CGRect cropBox = CGRectIntegral(CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox));
//scale to size of the screen
CGRect targetRect = self.view.bounds;
CGFloat xScale = targetRect.size.width / cropBox.size.width;
CGFloat yScale = targetRect.size.height / cropBox.size.height;
self.scale = xScale < yScale ? xScale : yScale;
//alway fill height of screen
self.scale = yScale;
//used to center the pdf horizontally
self.xOffSet = (targetRect.size.width - cropBox.size.width*self.scale)/2;
[self renderPDFPageToImage];
CGRect boundsRect = self.view.bounds;
//use a tiled layer for better performance
CATiledLayer *tiledLayer = [CATiledLayer layer];
tiledLayer.delegate = self;
tiledLayer.tileSize = CGSizeMake(1024.0, 1024.0);
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 4;
tiledLayer.frame = boundsRect;
self.myContentView = [[[UIView alloc] initWithFrame:boundsRect] autorelease];
[self.myContentView.layer addSublayer:tiledLayer];
CGRect viewFrame = self.view.frame;
viewFrame.origin = CGPointZero;
UIScrollView *_scrollView = [[UIScrollView alloc] initWithFrame:viewFrame];
_scrollView.delegate = self;
_scrollView.contentSize = boundsRect.size;
_scrollView.maximumZoomScale = 4;
[_scrollView addSubview:myContentView];
//retain it
self.scrollView=_scrollView;
[self.view addSubview:_scrollView];
[_scrollView release];
Please point me in the right direction.

iPhone: CoreGraphics and memory management

Can someone tell me what I am doing wrong here? I use this method to flip through pages in a PDF. But something in the code seems to not be released properly because every-time I pull a PDF page that contains an image my memory footprint increases. I am fairly new to CoreGraphics, and can't for the life of me figure out where this method would leak memory.
-(UIImage *)pageAtIndex:(NSInteger)pageNumber withWidth:(CGFloat)width andHeight:(CGFloat)height {
if((pageNumber>0) && (pageNumber<=pageCount)) {
CGFloat scaleRatio; // multiplier by which the PDF Page will be scaled
UIGraphicsBeginImageContext(CGSizeMake(width, height));
CGContextRef context = UIGraphicsGetCurrentContext();
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, pageNumber);
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFBleedBox);
//Figure out the orientation of the PDF page and set the scaleRatio accordingly
if(pageRect.size.width/pageRect.size.height < 1.0) {
scaleRatio = height/pageRect.size.height;
}
else {
scaleRatio = width/pageRect.size.width;
}
//Calculate the offset to center the image
CGFloat xOffset = 0.0;
CGFloat yOffset = height;
if(pageRect.size.width*scaleRatio<width) {
xOffset = (width/2)-(pageRect.size.width*scaleRatio/2);
}
else {
yOffset = height-((height/2)-(pageRect.size.height*scaleRatio/2));
}
CGContextTranslateCTM(context, xOffset, yOffset);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSaveGState(context);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFBleedBox, CGRectMake(0, 0, pageRect.size.width, pageRect.size.height), 0, true);
pdfTransform = CGAffineTransformScale(pdfTransform, scaleRatio, scaleRatio);
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, page);
UIImage *tempImage = UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(context);
UIGraphicsEndPDFContext();
UIGraphicsEndImageContext();
return tempImage;
}
return nil;
}
I think I resolved the problem thanks to the people on the apple core graphics mailing list. It seems that CGPDFDocument caches data between calls but never releases it. This appears to be a bug in CoreGraphics. I was told that the only real way around this is to load and unload the PDF every time I pull a page.
You probably weren't releasing something. Gotta check for things like CGPDFPageRetain(<CGPDFPageRef page>) and CGPDFPageRelease(<CGPDFPageRef page>).