How to properly compensate CGContextRef coordinate system, so the origin is at the top left corner
Asked Answered
P

2

0

I'm trying to draw text using CoreText. I have the following code:

CGContextRef uiContext = UIGraphicsGetCurrentContext();

NSMutableDictionary<NSAttributedStringKey,id> *attributes = [NSMutableDictionary<NSAttributedStringKey,id> new];
[attributes setObject:UIColor.redColor forKey:NSForegroundColorAttributeName];
[attributes setObject:[UIFont systemFontOfSize:40] forKey:NSFontAttributeName];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:@"Str" attributes:attributes];

CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, nil, CGRectMake(0, 50, attrString.size.width, attrString.size.height));
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attrString.length), path, nil);

CTFrameDraw(frame, uiContext);

With the above code, text is placed properly, but is mirrored across y-direction proper origin, wrong text orientation

So, I tried to apply translate and scale transformation, which helped with text, but now it's placed at to bottom left corner, which isn't the desired behavior.

CGContextSetTextMatrix(uiContext, CGAffineTransformIdentity);
CGContextTranslateCTM(uiContext, 0.0, self.bounds.size.height);
CGContextScaleCTM(uiContext, 1.0, -1.0);

wrong origin, proper orientation

Is there any ideas what am I missing here? Thanks.

Predictor answered 22/4, 2019 at 18:16 Comment(0)
P
1

Where does this 50 value for the y origin come from on this line?

CGPathAddRect(path, nil, CGRectMake(0, 50, attrString.size.width, attrString.size.height));

If you're starting from the lower left y coordinate, that 50 value will start the path drawing y origin 50 up from that.

You should be able to adjust the y origin to be offset from the top by using the container view's height (that you'd like to draw the text into) minus the attrString.size.height.

Here's an example of what it might look like if you are doing the drawing a custom view's subclass' drawRect:rect:

CGPathAddRect(path, nil, CGRectMake(0, rect.size.height - attrString.size.height, attrString.size.width, attrString.size.height));

Which would result in this:

CoreTextDrawing upper left hand corner of container view

Petersham answered 22/4, 2019 at 22:44 Comment(2)
yep, that going to work, and actually, I was considering this as solution, but I though that there is more clear way of doing that, this will introduce such calculations for everything in the code, and I would like to omit that....Predictor
as for 50 value - it's used to offset from the top bottom of the screen, to omit overlapping with time at the very top of safe areaPredictor
C
0

How about this one:

swift:

   CGContextSetTextMatrix(uiContext, CGAffineTransform(a: 1.0, b: 0.0, c: 0.0, d: -1.0, tx: 0.0, ty: 0.0))

Objective c:

    CGContextSetTextMatrix(uiContext, CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0));

To simple setting, do the following:

   CGPathAddRect(path,nil, CGRectMake(0, 0, attrString.size.width, 1.5 * attrString.size.height));

instead of:

     CGPathAddRect(path, nil, CGRectMake(0, 50, attrString.size.width, attrString.size.height));
Cowgill answered 22/4, 2019 at 23:24 Comment(5)
in swift it behaves differently comparing to obj-cPredictor
I don't mean that I can't translate to obj-c from swift, the problem is that CoreText behaves differently (try it our for yourself)Predictor
@nrudnyk, you may try the simple one.Cowgill
The difference between this and yours is only adjusting the character transform not the frame.Cowgill
Such vertical shift is due to the origin of NSAttributeString is on its baseline by default, not in its middle vertical line. So the difference can be done by enlarging the drawing rects.Cowgill

© 2022 - 2024 — McMap. All rights reserved.