I have attached the code snippet that I am using for displaying a PDF. The following code displays the PDF, but it seems that it is either squeezed or not using the full size of the iPad display, resulting in a page that is too small.
How can I display a PDF that fits in the boundary of an iPad display or in a zoomed state? I have tried using a different approach (approach-2), but it creates a problem with the PDF appearing rotated at a 90-degree angle.
Approach-1:
CGContextSaveGState(ctx);
CGContextTranslateCTM(ctx, 0.0, [self.view bounds].size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextConcatCTM(ctx,
CGPDFPageGetDrawingTransform(page, kCGPDFCropBox,
[self.view bounds], 0, true));
CGContextDrawPDFPage(ctx, page);
CGContextRestoreGState(ctx);
Approach-2:
CGPDFPageRef page = CGPDFDocumentGetPage(pdfdocument, PageNo+1);
if(page){
CFRetain(page);
}
CGRect pageRect =CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
int angle= CGPDFPageGetRotationAngle(page);
float pdfScale = self.bounds.size.width/pageRect.size.width;
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,self.bounds);
CGContextSaveGState(context);
// Flip the context so that the PDF page is rendered
// right side up.
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// 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);
Can anyone suggest to me a solution that allows any PDF of any size and any angle to be displayed full screen on an iPad in both orientations? It would be great if you can provide me a code snippet or pseudo-code.
Thanks
Hopefully this helps. It shows how to render the first page of a PDF referenced by an URL.
This code is a collection of snippets of my own codebase so don't just copy paste it in 1 file and expect it to build and run. I put some comments so you see what belongs where, and you'll need to declare some ivars for it to work.
// helper function
CGRect CGRectScaleAspectFit(CGRect sourceRect,CGRect fitRect)
{
if ( sourceRect.size.width > fitRect.size.width)
{
float scale = fitRect.size.width / sourceRect.size.width;
sourceRect.size.width = fitRect.size.width;
sourceRect.size.height = (int)(sourceRect.size.height * scale);
}
if ( sourceRect.size.height > fitRect.size.height)
{
float scale = fitRect.size.height / sourceRect.size.height;
sourceRect.size.height = fitRect.size.height;
sourceRect.size.width = (int)(sourceRect.size.width * scale);
}
return sourceRect;
}
// in your UIView subclass init method
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
pdfPage = CGPDFDocumentGetPage(pdf, 1);
CGRect fitrect = CGRectMake(0, 0, self.frame.size.width,self.frame.size.height);
CGRect pageRect = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
CGRect f;
f.origin.x=0;
f.origin.y=0;
f.size.height = self.frame.size.height;
f.size.width = self.frame.size.height * pageRect.size.width/pageRect.size.height;
f = CGRectScaleAspectFit(f,fitrect); // this is the actual pdf frame rectangle to fill the screen as much as possible
pdfScale = f.size.height/pageRect.size.height;
// in your UIView subclass drawRect method
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, 1.0, -1.0);
CGContextScaleCTM(context, pdfScale,pdfScale);
CGContextDrawPDFPage(context, pdfPage);
CGContextRestoreGState(context);
This will display your PDF Full Screen : not sure this is the best way to handle PDF though
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
//PDF might be transparent, assume white paper - set White Background
[[UIColor whiteColor] set];
CGContextFillRect(ctx, rect);
//Flip coordinates
CGContextGetCTM(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -rect.size.height);
//PDF File Path
NSURL *pdfURL = [[NSBundle mainBundle] URLForResource:#"TEST" withExtension:#"pdf"];
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((__bridge CFURLRef)pdfURL);
CGPDFPageRef page1 = CGPDFDocumentGetPage(pdf, 1);
//Get the rectangle of the cropped inside
CGRect mediaRect = CGPDFPageGetBoxRect(page1, kCGPDFCropBox);
CGContextScaleCTM(ctx, rect.size.width / mediaRect.size.width,
rect.size.height / mediaRect.size.height);
//Draw PDF
CGContextDrawPDFPage(ctx, page1);
CGPDFDocumentRelease(pdf);
}
Copy the PDF you want to display into your project library,
in the example above the name of the PDF file is "TEST" : you want to name yours with the name of your file
This works for me to display the PDF in full screen within a UIView,
I am not sure this is your best option though : there are issues : i.e. you need to handle zooming, and if you do that, you need to handle panning. Also Orientation is a big mess (when you flip the device to landscape mode) the file loses its Aspect Ration (and gets squished)
Play a round with it .....
Though jumping into PDF handling in IOS is nothing but a pain . . . .
Hope this helps you out a little
Related
What is wrong with this method? The problem is that rotated image has transparent box after rotation. How can I get rid of it?
#implementation UIImage (RotationMethods)
static CGFloat getRadianFromDegree(CGFloat degrees)
{return degrees * M_PI / 180;};
- (UIImage *) rotateImageByDegree:(CGFloat)degrees
{
UIView *rotatedImageView = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(getRadianFromDegree(degrees));
rotatedImageView.transform = t;
CGSize rotatedSize = rotatedImageView.frame.size;
UIGraphicsBeginImageContextWithOptions(rotatedSize, YES, self.scale);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
CGContextRotateCTM(bitmap, getRadianFromDegree(degrees));
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]);
UIImage *rotatedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return rotatedImage;
}
#end
EDIT1
EDIT2
Here is the code of rotate action:
- (IBAction)rotateAction:(id)sender {
self.imageView.image = [self.imageView.image rotateImageByDegree:22];
}
The problem is caused by code you are not showing us.
How do I know? I ran your code, the code you did show — and there is no gap between the rotated image and the image boundary:
In that screen shot, the main view background is red, and I have deliberately set the image view background to white so that you can see that the corners of the rotated kittens picture meet the containing image edges.
If we set the image view background to clear, the white disappears and we see the rotated kittens on the red background.
Thus we may conclude that the "shrinkage" you are complaining about is something you are doing, something that you have not revealed in your question.
I have a PDF file, I need to extract each page from the PDF as a UIImage and then change the colour of the UIImage based on the user settings and load this to a UICollectionViewCell. I had some how managed to do this and to avoid the jerky scroll I am preloading some images and releasing them as the pages are passed. But it takes a considerable amount of memory which comes down once the images are rendered and also considerable amount of time for rendering. Please have a look at the piece of code I had done
-(UIImage *)getImageFromPDF:(int)pg withColor:(int)color{
// create PDF document
CGPDFDocumentRef document = CGPDFDocumentCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:pdfPath]);
// get the first page
CGPDFPageRef page = CGPDFDocumentGetPage(document, pg);
// create a bitmap context
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
CGFloat pdfScale = 1.0f;//self.view.frame.size.width/pageRect.size.width;
pageRect.size = CGSizeMake(pageRect.size.width * pdfScale, pageRect.size.height * pdfScale);
UIGraphicsBeginImageContextWithOptions(pageRect.size, YES, pdfScale);
// flip the context
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, pageRect.size.height);
CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1, -1);
// draw the page into the bitmap context
CGContextDrawPDFPage(UIGraphicsGetCurrentContext(), page);
CGPDFDocumentRelease(document);
// get the image from the context
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if(color==1)
return image;
else
{
CGRect contextRect;
contextRect.origin.x = 0.0f;
contextRect.origin.y = 0.0f;
contextRect.size = [image size];
// Retrieve source image and begin image context
CGSize itemImageSize = [image size];
CGPoint itemImagePosition;
itemImagePosition.x = ceilf((contextRect.size.width - itemImageSize.width) / 2);
itemImagePosition.y = ceilf((contextRect.size.height - itemImageSize.height) );
UIGraphicsBeginImageContext(contextRect.size);
CGContextRef c = UIGraphicsGetCurrentContext();
// Setup shadow
// Setup transparency layer and clip to mask
//CGContextBeginTransparencyLayer(c, NULL);
CGContextScaleCTM(c, 1.0, -1.0);
CGContextClipToMask(c, CGRectMake(itemImagePosition.x, -itemImagePosition.y, itemImageSize.width, -itemImageSize.height), [image CGImage]);
switch (color) {
case 1:
CGContextSetRGBFillColor(c, 1, 1, 1, 1);
break;
case 2:
CGContextSetRGBFillColor(c, 0, 0, 0, 1);
break;
case 3:
CGContextSetRGBFillColor(c, 0.95686, 0.929412, 0.85098, 1);
break;
default:
CGContextSetRGBFillColor(c, 1, 1, 1, 1);
break;
}
contextRect.size.height = -contextRect.size.height;
contextRect.size.height -= 15;
// Fill and end the transparency layer
CGContextFillRect(c, contextRect);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
}
Can anyone help me to reduce the memory usage and rendering time, so that the user can scroll smoothly through the pages. I am passing the page number and the colour option to the function
-(UIImage *)getImageFromPDF:(int)pg withColor:(int)color
The colour option 1 is the default colour of the image extracted from the PDF so the colouring section is skipped and the UIImage is returned straight away.
This is working but takes considerable amount of time. Any help is really appreciated
instead of creating CGPDFDocumentRef document and CGContextRef for each pass of function, create them once, using either static or private properties. Because creating them will be lot of memory overhead, if you create once, next time it will be quick.
What's a right way to draw text in a PDF file? First, I tried:
NSString* string = #"Some Text";
const char* chstr = [string UTF8String];
CGContextRef pdfContext = CGPDFContextCreate(pdfConsumer, &pdfPageRect, NULL);
...
CGContextShowTextAtPoint(pdfContext, x, y, chstr, strlen(chstr));
...
CGContextRelease(pdfContext);
This worked but did not show unicode text correctly. Also the API CGContextShowTextAtPoint has been deprecated in iOS7. So I tried:
NSDictionary *textAttributes = #{NSFontAttributeName: [UIFont systemFontOfSize:8.0]};
...
UIGraphicsPushContext(pdfContext);
[string drawAtPoint:CGPointMake(x, y) withAttributes:textAttributes];
UIGraphicsPopContext();
...
CGContextRelease(pdfContext);
As suggested by some SO posts. But the app crashes on CGContextRelease(pdfContext). If I remove CGContextRelease(pdfContext), it does not draw anything in PDF. Am I missing anything?
** Additional information **
I figured out the crash in CGContextRelease happens only when NSString contains unicode text and here is the image of the call stack at the time of the crash. I even moved the test code of PDF generation in [AppDelegate didFinishLaunchingWithOptions] but it still crashes.
You can try this! I have test it and it work.
//Ready to begin
UIGraphicsBeginPDFContextToFile(tempPath, CGRectZero, nil);
CGPDFPageRef page = CGPDFDocumentGetPage (doc, i);
//grab page i of the PDF
CGRect bounds = [ReaderDocument boundsForPDFPage:page];
//Create a new page
UIGraphicsBeginPDFPageWithInfo(bounds, nil);
CGContextRef context = UIGraphicsGetCurrentContext();
// flip context so page is right way up
CGContextTranslateCTM(context, 0, bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawPDFPage (context, page); // draw the page into graphics context
//Flip back right-side up
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -bounds.size.height);
//Draw text
UIGraphicsPushContext(context);
CGContextSetTextMatrix(context, CGAffineTransformMake(1.0,0.0, 0.0, -1.0, 0.0, 0.0));
CGContextSetTextDrawingMode(context, kCGTextFill); // This is the default
CGContextSetFillColorWithColor(context, [[UIColor blackColor] CGColor]);
CGRect newTextFrame = CGRectMake(100,100,100,30);
[#"Your Text......" drawInRect:newTextFrame withAttributes:#{NSFontAttributeName:self.font}];
UIGraphicsPopContext();
UIGraphicsEndPDFContext();
CGPDFDocumentRelease(doc);
I have read numerous answers related to this, but I still can't get it to work.
I have a view where a user can sign their name. Here's how it looks: http://d.pr/i/McuE
I can successfully retrieve this image and save it to the file system, but I need to rotate it 90 degrees before saving so the signature reads from left-to-right.
// Grab the image
UIGraphicsBeginImageContext(self.signArea.bounds.size);
[self.signArea drawRect: self.signArea.bounds];
UIImage *signatureImage = UIGraphicsGetImageFromCurrentImageContext();
//-- ? -- Rotate the image (this doesn't work) -- ? --
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextRotateCTM(context, M_PI_2);
UIGraphicsEndImageContext();
//Save the image as a PNG in Documents folder
[UIImagePNGRepresentation(signatureImage) writeToFile:[[PPHelpers documentsPath] stringByAppendingPathComponent:#"signature-temp.png"] atomically:YES];
How can I rotate the image prior to saving?
Thanks in advance for your help. I'm on Xcode 5 using the iOS 7 SDK.
I finally got this to work using one of the answers on this post: How to Rotate a UIImage 90 degrees?
I used this method:
- (UIImage *)imageRotatedByDegrees:(UIImage*)oldImage deg:(CGFloat)degrees{
//Calculate the size of the rotated view's containing box for our drawing space
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,oldImage.size.width, oldImage.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(degrees * M_PI / 180);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
//Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
//Rotate the image context
CGContextRotateCTM(bitmap, (degrees * M_PI / 180));
//Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-oldImage.size.width / 2, -oldImage.size.height / 2, oldImage.size.width, oldImage.size.height), [oldImage CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
And then called it like this:
//Rotate it
UIImage *rotatedImage = [self imageRotatedByDegrees:signatureImage deg:90];
Thanks, everyone.
I'm working in a function for scaling and crop image (similar to camara app) for iOS, the code below works fine, just that the resulting image came up side down, and I would like to understand why.
Thanks
- (UIImage*)imageByCropping:(UIImageView *)imageViewToCrop toRect:(CGRect)rect
{
//create a context to do our clipping in
CGRect newRect = CGRectApplyAffineTransform(rect, imageViewToCrop.transform);
UIImage *imageToCrop = imageViewToCrop.image;
UIGraphicsBeginImageContext(newRect.size);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
//create a rect with the size we want to crop the image to
//the X and Y here are zero so we start at the beginning of our
//newly created context
CGRect clippedRect = CGRectMake(0, 0, rect.size.width, rect.size.height);
CGContextClipToRect( currentContext, clippedRect);
//draw the image to our clipped context using our offset rect
CGContextDrawImage(currentContext, newRect, imageToCrop.CGImage);
//pull the image from our cropped context
UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();
//pop the context to get back to the default
UIGraphicsEndImageContext();
return cropped;
}
I've also came across this issue without explanation. But have found a workaround that solves that problem. Just put the following two lines in your code and try it again. It works for me.
CGContextTranslateCTM(context, 0.0, newRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);//flip context