Clip part of the page of PDF while drawing in iOS - ios

I am over-riding the drawLayer method to draw a particular page of the pdf. How can I clip a part of the page and show the same.
- (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)context
{
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // White
CGContextFillRect(context, CGContextGetClipBoundingBox(context)); // Fill
CGContextTranslateCTM(context, 0.0f, self.bounds.size.height); CGContextScaleCTM(context, 1.0f, -1.0f);
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(_PDFPageRef, kCGPDFCropBox, self.bounds, 0, true));
CGContextDrawPDFPage(context, _PDFPageRef);
}

You can clip a part of the page if you set a clipping region on the context before drawing the PDF page.

Related

How to rotate and crop iOS CGImage from file

I'm having some trouble with image rotation in iOS. I'm performing some image manipulation in the background of an app... I would like to rotate and crop the images. Currently, the rotation seems to be working correctly, but no matter what I have tried, the crop is off. I have performed these same operations in GIMP and found that the images crop correctly, so I believe it has something to do with the Quartz coordinate system. Furthermore, the greater the radians in either direction, the further "off" the crop becomes. Here is the code I am using to rotate and crop:
+(UIImage*)imageWithImageFile:(NSString*)imageFile
radians:(float)radians
imageSize:(CGSize)imageSize
croppedToRect:(CGRect)croppedToRect
{
UIImage* returnImg = nil;
#autoreleasepool {
CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename([imageFile UTF8String]);
CGImageRef image = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, YES, kCGRenderingIntentDefault);
UIGraphicsBeginImageContext(croppedToRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShouldAntialias(context, YES);
CGContextSetAllowsAntialiasing(context, YES);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextTranslateCTM(context, imageSize.width * 0.5,
imageSize.height * 0.5);
CGContextRotateCTM(context, radians);
CGContextTranslateCTM(context, -imageSize.width * 0.5, -imageSize.height * 0.5);
//Translate and scale upside-down to compensate for Quartz's inverted coordinate system
CGContextTranslateCTM(context, 0.0, imageSize.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, (CGRect) {croppedToRect.origin, imageSize}, image);
returnImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGDataProviderRelease(dataProvider);
CGImageRelease(image);
}
return returnImg;
}
I believe this is the solution: Since the context is a different size and has a different origin inside the original image, you must offset the point where you "paint" the image on the context (canvas for those of you more visual people). You first find the origin of the centered context, then compare it to the origin of the desired crop. The difference (or offset) can be subtracted from the center point of the context, which will offset the point at which the image is painted.
CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename([imageFile UTF8String]);
CGImageRef image = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, YES, kCGRenderingIntentDefault);
UIGraphicsBeginImageContext(croppedToRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint contextCenter = CGPointMake(croppedToRect.size.width * 0.5f,
croppedToRect.size.height * 0.5f);
CGPoint centerOfImage = CGPointMake(imageSize.width * 0.5f,
imageSize.height * 0.5f);
CGPoint contextOriginInImage = CGPointMake(centerOfImage.x - contextCenter.x,
centerOfImage.y - contextCenter.y);
CGPoint desiredOrigin = croppedToRect.origin;
CGPoint offset = CGPointMake(desiredOrigin.x - contextOriginInImage.x,
desiredOrigin.y - contextOriginInImage.y);
CGContextTranslateCTM(context, contextCenter.x - offset.x, contextCenter.y - offset.y);
CGContextRotateCTM(context, radians);
CGContextScaleCTM(context, 1.0f, -1.0f);
CGContextDrawImage(context, CGRectMake(-imageSize.width * 0.5f,
-imageSize.height * 0.5f,
imageSize.width,
imageSize.height), image);
returnImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGDataProviderRelease(dataProvider);
CGImageRelease(image);

core graphics text clipping

I have the following code working that draws a white rectangle and clips it the string "TEXT".
CGContextSaveGState(c);
CGContextTranslateCTM(c, 0.0f, rect.size.height);
CGContextScaleCTM(c, 1.0f, -1.0f);
CGContextSelectFont(c, "Helvetica-Bold", 16, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(c, kCGTextClip);
CGContextShowTextAtPoint(c, x, y, "TEXT", strlen("TEXT"));
CGContextClip(c);
CGContextFillRect(c, fillRect);
CGContextRestoreGState(c);
But I want it to clip so that it only draws outside of the text. How do I do that?

getting wrong CropBox rect in this PDF i will get like (-396,-306,792,612)?

Are there some issue with this pdf ?
what to do to get rid of this ?
here is my sample code
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(drawPDFPageRef, kCGPDFCropBox, self.bounds, angle, true));
appdel.widthScale=scale;
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f);
CGContextFillRect(context, cropBoxRect);
//CGContextClipToRect(context, cropBoxRect);
CGContextDrawPDFPage(context, drawPDFPageRef);
Pdf name is Xoom Pad 4.pdf
http://www.motorola.com/staticfiles/Support/US-EN/TABLETS/XOOM-with-Wi-Fi/Documents/StaticFiles/XOOM_wifi_GSG_US_68016036001A.pdf
Well it's certainly possible that the cropbox has negative values from pdf coordinate space.

Can not stroke path on mapkit overlay view

I am using mapkit on iPhone with iOS 4.
I am using a custom overlay and a custom overlay view, to draw shapes on the map.
At the moment, shapes are just rectangles, but I am planning something more sophisticated. This is why I am not using the MKPolygon overlay type.
This is the code for my overlay view drawing method:
-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
// Clip context to bounding rectangle
MKMapRect boundingMapRect = [[self overlay] boundingMapRect];
CGRect boundingRect = [self rectForMapRect:boundingMapRect];
CGContextAddRect(context, boundingRect);
CGContextClip(context);
// Define shape
CGRect shapeRect = CGRectMake(0.5f, 0.5f, boundingRect.size.width - 1.0f, boundingRect.size.height - 1.0f);
// Fill
CGContextSetRGBFillColor(context, 0.5f, 0.5f, 0.5f, 0.5f);
CGContextFillRect(context, shapeRect);
// Stroke
CGContextSetRGBStrokeColor(context, 0, 0, 0, 0.75f);
CGContextSetLineWidth(context, 1.0f);
CGContextStrokeRect(context, shapeRect);
}
The problem is that rectangles get correctly filled (so it appears their bounding rect is correctly set), but they don't get stroked.
Can anyone help?
Thanks!
As reported in some previous comments, the problem is with line width. More generally, all drawing is automatically scaled to follow the map zooming, so if you want some of your drawing metrics to be zoom-independent, you have to divide it by zoomScale.
Here is the new code, that works correctly on my iPhone 4:
-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
// Clip context to bounding rectangle
MKMapRect boundingMapRect = [[self overlay] boundingMapRect];
CGRect boundingRect = [self rectForMapRect:boundingMapRect];
CGContextAddRect(context, boundingRect);
CGContextClip(context);
// Define shape
CGRect shapeRect = CGRectInset(boundingRect, 2.0f / zoomScale, 2.0f / zoomScale);
// Fill
CGContextSetRGBFillColor(context, 0.5f, 0.5f, 0.5f, 0.5f);
CGContextFillRect(context, shapeRect);
// Stroke
CGContextSetRGBStrokeColor(context, 0, 0, 0, 0.75f);
CGContextSetLineWidth(context, 4.0f / zoomScale);
CGContextStrokeRect(context, shapeRect);
}
I will also report the code I am using in the overlay to calculate and return the bounding rectangle, because I think it can help:
-(MKMapRect)boundingMapRect
{
// Overlay bounds
CLLocationCoordinate2D topLeftcoordinate = <the top-left coordinate of overlay>;
CLLocationCoordinate2D bottomRightCoordinate = <the bottom-right coordinate of overlay>;
// Convert them to map points
MKMapPoint topLeftPoint = MKMapPointForCoordinate(topLeftcoordinate);
MKMapPoint bottomRightPoint = MKMapPointForCoordinate(bottomRightCoordinate);
// Calculate map rect
return MKMapRectMake(topLeftPoint.x, topLeftPoint.y, bottomRightPoint.x - topLeftPoint.x, topLeftPoint.y - bottomRightPoint.y);
}
Thank you all for your comments and suggestions.
Currently, we should use MKOverlayRenderer instead of MKOverlayView.
In method -(void)drawMapRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context,
use scaleFactor for configuring the stroke width of lines or other
attributes that might be affected by the scale of the map’s
contents.
Refer to Apple official site drawMapRect:zoomScale:inContext: of MKOverlayRenderer.

Drawing horizontal line in printed pdf using UIPrintPageRenderer

I'm using UIPrintPageRenderer sub-class to print html content on a pdf. How can i add a horizontal line on my printed content (both on header and footer)?
CGContextAddLineToPoint doesn't seem to work in UIPrintPageRenderer methods. Specifically those used to draw header and footer. NSString's drawAtPoint is working perfectly.
Here's what i've tried so far:
- (void)drawHeaderForPageAtIndex:(NSInteger)pageIndex inRect:(CGRect)headerRect {
...
// Attempt 1 (Doesn't work!)
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1);
CGContextSetRGBStrokeColor(context, 1.0f, 1.0f, 1.0f, 1);
CGContextMoveToPoint(context, 10.0, 20.0);
CGContextAddLineToPoint(context, 310.0, 20.0);
// Attempt 2 (Doesn't work!)
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1);
CGContextSetRGBStrokeColor(context, 1.0f, 1.0f, 1.0f, 1);
CGContextTranslateCTM(context, 0, headerRect.size.height);
CGContextScaleCTM(context, 1, -1);
CGContextMoveToPoint(context, 10.0, 20.0);
CGContextAddLineToPoint(context, 310.0, 20.0);
}
In Core Graphics, a logical graphics elements are added to the context and then drawn. I see you adding a path to the context with e.g. CGContextAddLineToPoint(context, 310.0, 20.0);
This creates the path in memory, but to composite it to the screen you need to either fill or stroke the context's path. Try adding CGContextStrokePath(context); after to actually stoke the path.
Just as regular drawing
- (void)drawHeaderForPageAtIndex:(NSInteger)pageIndex inRect:(CGRect)headerRect {
CGContextRef context = UIGraphicsGetCurrentContext();
CContextMoveToPoint(context, CGRectGetMinX(headerRect), 70);
CGContextAddLineToPoint(context, CGRectGetMaxX(headerRect), 70);
CGFloat grayScale = 0.5f;
CGContextSetRGBStrokeColor(context, grayScale, grayScale, grayScale, 1);
CGContextStrokePath(context);
}
Don't forget to CGStrokePath(...)
So, for now i've applied an alternate solution. I would still love to know how to do this using CGContext (without having to load an image). Here's my solution:
// Draw horizontal ruler in the header
UIImage *horizontalRule = [UIImage imageNamed:#"HorizontalRule.png"];
horizontalRule = [horizontalRule stretchableImageWithLeftCapWidth:0.5 topCapHeight:0];
CGFloat rulerX = CGRectGetMinX(headerRect) + HEADER_LEFT_TEXT_INSET;
CGFloat rulerY = self.printableRect.origin.y + fontSize.height + HEADER_FOOTER_MARGIN_PADDING + PRINT_RULER_MARGIN_PADDING;
CGFloat rulerWidth = headerRect.size.width - HEADER_LEFT_TEXT_INSET - HEADER_RIGHT_TEXT_INSET;
CGFloat rulerHeight = 1;
CGRect ruleRect = CGRectMake(rulerX, rulerY, rulerWidth, rulerHeight);
[horizontalRule drawInRect:ruleRect blendMode:kCGBlendModeNormal alpha:1.0];

Resources