How to override draw method on PDFAnnotation IOS-PDFKIT - objective-c

I followed another StackOverflow post that explains how i could override the draw method of a PDFAnnotation so i could draw a picture instead of a traditional PDFAnnotation.
But sadly i was not able to achieve that and the annotation that is drawn on top of my pdf is still a regular one.
This is the code that i used :
#implementation PDFImageAnnotation { UIImage * _picture;
CGRect _bounds;};
-(instancetype)initWithPicture:(nonnull UIImage *)picture bounds:(CGRect) bounds{
self = [super initWithBounds:bounds
forType:PDFAnnotationSubtypeWidget
withProperties:nil];
if(self){
_picture = picture;
_bounds = bounds;
}
return self;
}
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
[_picture drawInRect:_bounds];
CGContextRestoreGState(context);
UIGraphicsPushContext(context);
};
#end
Does someone know how i could override the draw method so i could draw a custom Annotation ?
Thank You !
ps: i also tried to followed the tutorial on the apple dev site.
UPDATE :
Now i'm able to draw pictures using CGContextDrawImage but i'm not able to flip coordinates back in place. when i do that mi pictures are not drawn and it seems that they are put outside of the page but i'm not sure.
This is my new code :
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, _pdfView.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, _bounds, _picture.CGImage);
CGContextRestoreGState(context);
UIGraphicsPopContext();
}

I also tried to follow the tutorial on the Apple dev site.
Which one?
Custom Graphics
Adding Custom Graphics to a PDF
Because both include UIGraphicsPushContext(context) & CGContextSaveGState(context) calls, but your code doesn't. Do not blindly copy & paste examples, try to understand them. Read what these two calls do.
Fixed code:
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
[_picture drawInRect:_bounds];
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
The image was drawn with CGRectMake(20, 20, 100, 100). It's upside down, because PDFPage coordinates are flipped (0, 0 = bottom/left). Leaving it as an exercise for OP.
Rotation
Your rotation code is wrong:
CGContextTranslateCTM(context, 0.0, _pdfView.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, _bounds, _picture.CGImage);
It's based on _pdfView bounds, but it should be based on the image bounds (_bounds). Here's the correct one:
- (void)drawWithBox:(PDFDisplayBox) box
inContext:(CGContextRef)context {
[super drawWithBox:box inContext:context];
UIGraphicsPushContext(context);
CGContextSaveGState(context);
CGContextTranslateCTM(context, _bounds.origin.x, _bounds.origin.y + _bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
[_picture drawInRect:CGRectMake(0, 0, _bounds.size.width, _bounds.size.height)];
CGContextRestoreGState(context);
UIGraphicsPopContext();
}

Related

How to move line drawn in CGContext by touch event in Objective-C?

I used following code to draw line which works fine but How to move that drawn line within context by touch event?
- (void) drawLineFrom:(CGPoint)from to:(CGPoint)to width:(CGFloat)width
{
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextScaleCTM(ctx, 1.0f, -1.0f);
CGContextTranslateCTM(ctx, 0.0f, -self.frame.size.height);
if (drawImage != nil) {
CGRect rect = CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height);
CGContextDrawImage(ctx, rect, drawImage.CGImage);
}
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, width);
CGContextSetStrokeColorWithColor(ctx, self.drawColor.CGColor);
CGContextMoveToPoint(ctx, from.x, from.y);
CGContextAddLineToPoint(ctx, to.x, to.y);
CGContextStrokePath(ctx);
CGContextFlush(ctx);
drawImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
drawLayer.contents = (id)drawImage.CGImage;
}
How to get reference of drawn line from CGContext ? thanks in advance.
UIGraphicsBeginImageContext() begins an image context — one that is a grid of pixels.
drawImage = UIGraphicsGetImageFromCurrentImageContext(); and drawLayer.contents = (id)drawImage.CGImage; set the contents of a layer to a captured form of that grid of pixels.
The intermediate UIGraphicsEndImageContext() ends the context you had. The context no longer exists.
So to answer you question literally:
you can't ask an image context to tell you what was drawn to it and expect it to have any higher-level insight than the individual pixels that were plotted;
you can't ask the context you drew to anything, because it no longer exists.
The normal thing to do would be to create a UIView with a bunch of properties that describe whatever you want about the line. Implement -drawRect: and in there draw the line based on the properties. When you want to update the line, update the properties. Ensure the property setters make a call to setNeedsDisplay.
In UIKit things that are interactive should be subclasses of UIView. Views draw when requested to by the system. The normal pattern is pull, not push.

CGContextFillRect not working

This code appears to do nothing. Build successful, no errors. No rectangle drawn on screen.
- (void)viewDidLoad
{
[super viewDidLoad];
UIColor *reliantMagenta = [UIColor colorWithRed:208.0f / 255.0f green:27.0f / 255.0f blue:124.0f / 255.0f alpha:1];
CALayer *reliantCanvasLayer = [CALayer layer];
reliantCanvasLayer.frame = CGRectMake(0, 0, 640, 960);
[[[self view] layer] addSublayer:reliantCanvasLayer];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect leftRect = CGRectMake(0, 0, 200, 300);
CGContextSaveGState(ctx);
CGContextSetFillColorWithColor(ctx, reliantMagenta.CGColor);
CGContextFillRect(ctx, leftRect);
CGContextRestoreGState(ctx);
}
I am just learning Quartz and really thrashing with it. If you want to explain the relationship between UIViews, CALayers, CGLayers, and context that would be a big help too, but not required, just having trouble understanding what is going on.
If you're starting out with Quartz, then you should start with the Quartz 2D Programming Guide, which walks through all of this. Your key mistake here is that there is no context available in viewDidLoad. Drawing of this kind is generally done in drawRect:. Your call to UIGraphicsGetCurrentContext() returns NULL at this point.
After reading through the Programming Guide, you may have more questions, but that's where you should start to learn about custom drawing.

iOS: Generate PDF from UIView makes text blurry

Generate PDF from UIView by rendering looses quality iOS
I have a custom UIView called TTT_WantsToBeRazorSharpView.
This view does nothing, but draw a text with
NSString*txtPleaseHelp = NSLocalizedString(#"Hello, am I blurry again?",#"");
CGContextShowTextAtPoint(ctx, 10, 50, [txtPleaseHelp cStringUsingEncoding:NSMacOSRomanStringEncoding], [txtPleaseHelp length]);
Now the View is drawn three times to an UIViewController (one time in IB) and two times in code with these lines (compare to image below):
TTT_WantsToBeRazorSharpView *customViewSharp = [[TTT_WantsToBeRazorSharpView alloc] initWithFrame:CGRectMake(10,250, 300, 80)];
[self.view addSubview:customViewSharp];
TTT_WantsToBeRazorSharpView *customViewBlurryButIKnowWhy = [[TTT_WantsToBeRazorSharpView alloc] initWithFrame:CGRectMake(361.5, 251.5, 301.5, 80.5)];
[self.view addSubview:customViewBlurryButIKnowWhy];
the first code drawn view is razorsharp, while the second isn't, but that is ok, because of rect's comma values (361.5, 251.5, 301.5, 80.5).
See this picture:
But my problem is now, if I render the view into an pdf document it is blurry!
And don't know why, see here:
and the PDF file itself Test.pdf
https://raw.github.com/florianbachmann/GeneratePDFfromUIViewButDontWantToLooseQuality/master/Test.pdf
and the lines to render the view into the pdf:
//this is blurry, but why? it can't be the comma coordinates
CGRect customFrame1 = CGRectMake(10,50, 300, 80);
TTT_WantsToBeRazorSharpView *customViewSharp = [[TTT_WantsToBeRazorSharpView alloc] initWithFrame:customFrame1];
CGContextSaveGState(context);
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, (int)customFrame1.origin.x, (int)customFrame1.origin.y);
[customViewSharp.layer renderInContext:context];
CGContextRestoreGState(context);
So why is the renderInContext:context text blurry?
I appreciate all your hints and help, I even made an GitHub project for the brave of you (with source): https://github.com/florianbachmann/GeneratePDFfromUIViewButDontWantToLooseQuality
Try this:
- (void)initValues {
UIColor *gray = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.3f];
CALayer *l = [self layer];
l.masksToBounds = YES;
l.cornerRadius = 10.0f;
l.borderWidth = 3.0f;
self.backgroundColor = gray;
l.backgroundColor = gray.CGColor;
l.borderColor = gray.CGColor;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"TTT_WantsToBeRazorSharpView drawRect[%3.2f,%3.2f][%3.2f,%3.2f]",rect.origin.x,rect.origin.y,rect.size.width,rect.size.height);
// get the graphic context
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetShouldSmoothFonts(ctx,YES);
CGColorRef colorWhite = CGColorRetain([UIColor redColor].CGColor);
CGContextSetFillColorWithColor(ctx, colorWhite);
CGContextSelectFont(ctx, "Helvetica-Bold", 24, kCGEncodingMacRoman);
CGAffineTransform tranformer = CGAffineTransformMakeScale(1.0, -1.0);
CGContextSetTextMatrix(ctx, tranformer);
//why is this sucker blurry?
NSString*txtPleaseHelp = NSLocalizedString(#"Hello, am I blurry again?",#"");
CGContextShowTextAtPoint(ctx, 10, 50, [txtPleaseHelp cStringUsingEncoding:NSMacOSRomanStringEncoding], [txtPleaseHelp length]);
CGColorRelease(colorWhite);
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
if (!layer.shouldRasterize && !CGRectIsEmpty(UIGraphicsGetPDFContextBounds())) {
[self drawRect:self.bounds];
}
else {
[super drawLayer:layer inContext:ctx];
}
}
I had to change the text color to red to make it visible. Because the text is written first, then the transparent grey button is placed over it, it would make the white text disappear. When adding the drawLayer method, everything that does not need to be rasterized is written as vector to pdf, making the text also selectable.
The text in the view is rendered as bitmap in the PDF and not as vector graphics and this causes the blurry appearance. My guess is iOS rendering engine draws the text on an intermediary bitmap (for color composition purposes I think) and then this bitmap is rendered on the target context.
I have used the following command to fix blurry text:
self.frame = CGRectIntegral(self.frame);
The reason for blurry text was that alignment was not ideal, i.e. fraction values in the point coordinates, so the text appear blurry due to excessive antialiasing.

Drawings in drawRect not being displayed correctly

I want to implement freeform drawing in my app. First, I tried the code inside drawLayer:inContext: and it gave me the result I wanted.
Drawing in CALayer:
But when I decided to implement the code inside drawRect:, this happened:
Even if I draw inside the white space, the drawing is rendered outside as shown above. The code I used is exactly the same. I copy-pasted it from drawLayer:inContext: to drawRect:. I didn't change a thing, so why is this happening?
The Code:
CGContextSaveGState(ctx);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, 1.0);
CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, prevPoint.x, prevPoint.y);
for (NSValue *r in drawnPoints){
CGPoint pt = [r CGPointValue];
CGContextAddLineToPoint(ctx, pt.x, pt.y);
}
CGContextStrokePath(ctx);
CGContextRestoreGState(ctx);
I see you are using app in full screen mode where the view is centered and does not take full width of the screen.
It may be that CALayer has transform applied to it that translates the drawing from the left side of the screen to the center. This may not be the case with drawRect:. Try setting CGContext's transform matrix:
CGContextSaveGState(ctx);
CGFloat xOffset = CGRectGetMidX(screenFrame) - CGRectGetMidX(viewFrame);
CGContextTranslateCTM(ctx, xOffset, 0.0f);
// rest of drawing code
// ...
CGContextRestoreGState(ctx);

Why does occur difference origin point in quartz framework?

I've made methods like follow and excute code.
I am confusing about origin of quartz framework after looking follow result image.
Please look at follow code and image.
[in ViewController.m]
Excute a follow method when press UIButton.
- (void)test {
[customView drawCustom];
[customView setNeedsDisplay];
}
[in CustomView.m]
- (void)drawRect:(CGRect)rect {
cgc_Context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(cgc_Context, 5.0);
CGContextSetStrokeColorWithColor(cgc_Context, [UIColor redColor].CGColor);
CGContextMoveToPoint(cgc_Context, 0, 0);
CGContextAddLineToPoint(cgc_Context, 200, 50);
CGContextStrokePath(cgc_Context);
}
- (void)drawCustom {
CGContextSetLineWidth(cgc_Context, 5.0);
CGContextSetStrokeColorWithColor(cgc_Context, [UIColor yellowColor].CGColor);
CGContextMoveToPoint(cgc_Context, 0, 0);
CGContextAddLineToPoint(cgc_Context, 200, 50);
CGContextStrokePath(cgc_Context);
}
[Result screen]
Red Line : result of drawRect
Yellow Line : result of drawCustom
Why does occur different origin point?
You must read the apple's documentation about Quartz 2D Coordinate Systems