CGContextSelectFont equivalent - iphone

In iOS7 CGContextSelectFont is deprecated. Deprecation message says that I have to use Core Text, but I don't know which is the exact equivalent of this piece of code:
CGContextSelectFont(context, "Helvetica", kBarLabelSize, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextSetRGBFillColor(context, 0, 0, 0, 1.0);
CGContextSetTextMatrix (context, CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0));
CGContextShowTextAtPoint(context, barX, barY, [#"Some text" cStringUsingEncoding:NSUTF8StringEncoding], [barValue length]);
I've been able to create the font with this code:
CFMutableAttributedStringRef attrStr = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica"), kBarLabelSize, NULL);
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTFontAttributeName, font);
But now haw can I "draw" a text with this font into the context?

As best I can understand from your code, the exact equivalent is:
CGContextSetTextDrawingMode(context, kCGTextFill); // This is the default
[[UIColor blackColor] setFill]; // This is the default
[#"Some text" drawAtPoint:CGPointMake(barX, barY)
withAttributes:#{NSFontAttributeName:[UIFont fontWithName:#"Helvetica"
size:kBarLabelSize]
}];
Note that your calls to CGContextSetTextDrawingMode and CGContextSetRGBFillColor are setting the values to the defaults. Your call to CGContextSetTextMatrix is not needed when using UIKit drawing like this.
I have no idea what [barValue length] is here, however. I'm assuming that you simply incorrectly used this for the length of #"Some text". (length is not the number of bytes which is what you need. What you probably meant was [barValue lengthOfBytesUsingEncoding:NSUTF8StringEncoding]).
Note that UIKit string drawing (seen here) wraps Core Text.

I have found, at least in my case, the problem with the new NSString.drawAtPoint interface is that it may draw upside down, depending on how you are using the context.
An alternate is to use the Core Text methods, specifically the CTLine interface thusly:
NSDictionary *attribs = #{NSFontAttributeName:[UIFont fontWithName:#"Helvetica" size:14.0]};
NSAttributedString *fontStr = [[NSAttributedString alloc] initWithString:#"some text" attributes:attribs];
CTLineRef displayLine = CTLineCreateWithAttributedString( (__bridge CFAttributedStringRef)fontStr );
CGContextSetTextPosition( ctx, xPosition, yPosition );
CTLineDraw( displayLine, ctx );
CFRelease( displayLine );

You probably can use the following to replace it.
CGContextSetFont
CGContextSetFontSize

Related

How to set font color and style for PDF using core text?

If I create a PDF using core text framework, it will not allow me to set color and font style.
Is there any problem with "core text" framework?
I am using below code for setting color :
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(currentContext, 1.0, 0.0, 0.0, 1.0); //This is not working.
Try below code, in this code i've changed my font style and size with CTFontRef
+(void)drawText:(NSString*)textToDraw inFrame:(CGRect)frameRect
{
CFStringRef stringRef = ( CFStringRef)textToDraw;
CGColorSpaceCreateWithName(stringRef);
NSMutableAttributedString *string = [[NSMutableAttributedString alloc]
initWithString:textToDraw];
CTFontRef helveticaBold;
helveticaBold = CTFontCreateWithName(CFSTR("Helvetica-Bold"), 24.0, NULL);
[string addAttribute:(id)kCTFontAttributeName
value:(id)helveticaBold
range:NSMakeRange(0, [string length])];
[string addAttribute:(id)kCTForegroundColorAttributeName
value:(id)[UIColor whiteColor].CGColor
range:NSMakeRange(0, [string length])];
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
CFRange currentRange = CFRangeMake(0, 0);
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
CGPathRelease(framePath);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
CGContextTranslateCTM(currentContext, 0, frameRect.origin.y*2);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CTFrameDraw(frameRef, currentContext);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGContextTranslateCTM(currentContext, 0, (-1)*frameRect.origin.y*2);
CFRelease(frameRef);
CFRelease(string);
CFRelease(framesetter);
}
you can follow this Link also
Example
Hope this helps you, Happy Coding
You can create color object like this:
UIColor *colour = [UIColor colorWithRed:0 green:1 blue:0 alpha:0];
And fill colour:
CGContextSetFillColorWithColor(ctx, colour.CGColor);
If you are not using ARC, you need to retain and release color appropriately.

iPhone sdk: NSMutableAttributedString buggy behavior when using CTFramesetterCreateFrame

I'm trying to draw text with some part of it in different color
First issue it that when using the following code:
NSMutableAttributedString *test1 = [[NSMutableAttributedString alloc] initWithString:#"fi"];
// Should color only the first character "f"
[test1 addAttribute:(id)kCTForegroundColorAttributeName value:(id)[[UIColor redColor] CGColor] range:NSMakeRange(0, 1)];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)test1);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height));
CTFrameRef textFrame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, test1.string.length), path, NULL);
And then drawing the frame:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
// Flip the coordinate system
CGContextScaleCTM(context, 1.0, -1.0);
CTFrameDraw(textFrame, context);
CGContextRestoreGState(context);
For some reason it shows both "f" and "i" colored in red. When I change the text to "f1" , "f!", or anything else it works OK. Even when I use "afi" and range as (1,1) it colors both "f" and "i" as if it treats it as a single character.
Second issue, when drawing inside a frame with width which is less that the width of the text:
NSMutableAttributedString *test2 = [[NSMutableAttributedString alloc] initWithString:#"aaaaaaaaaaaaaaaaaaaa"];
CTLineBreakMode lineBreakMode;
lineBreakMode = kCTLineBreakByTruncatingTail;
// Apply selected truncation
CTParagraphStyleSetting paragraphStyleSetting = {kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &lineBreakMode};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(&paragraphStyleSetting, 1);
[test1 addAttribute:(NSString *)kCTParagraphStyleAttributeName value:(__bridge id)paragraphStyle range:NSMakeRange(0, test2.string.length)];
[test1 addAttribute:(id)kCTForegroundColorAttributeName value:(id)[[UIColor redColor] CGColor] range:NSMakeRange(7, 16)];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)test2);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height));
CTFrameRef textFrame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, test1.string.length), path, NULL);
There are total 20 characters,
the ones at indexes 7 to 16 appear colored in red (as they should), CoreText adds a truncation mark as I requested but the problem is that it colored in red too and I want it to remain black (as the remaining truncated characters color is black).
I did some study and it seems that when increasing the frame size by some (random/font specific?) value in the command:
CGPathAddRect(path, NULL, CGRectMake(0, 0, self.frame.size.width + 10.0f, self.frame.size.height));
It keeps the truncation mark black, but because of the extra space sometimes the truncation mark is clipped
NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString:#"firstsecondthird"];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0,5)];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(5,6)];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(11,5)];
This sounds like an issue with ligatures. A good font has special characters for some combinations of characters that look better than the individual characters. For example, in a lot of fonts To doesn't look good if rendered individually since there's a lot of space between the vertical bar of T and the start of the o. So ligatures are provided that have a more appealing spacing. Sometimes the characters even get connect, your fi is a good example: some fonts provide a ligature that makes the top of the f double as the dot for the i.
So you may want to turn the ligatures off but setting the attribute kCTLigatureAttributeName to 0.

Standard Output Method

I know you can use a text field to display output but there is a limit on the size of the box. I can I display a message to the screen, without using text field? I'm not really finding anything useful so far in a couple books.
I think you can do this using,
CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextSetFillColorWithColor(context, strokeColor);
CGContextSelectFont(context, "Helvetica", fontSize, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextSetTextPosition(context, 0.0f, round(fontSize / 4.0f));
CGContextShowText(context, [text UTF8String], strlen([text UTF8String]));

drawing text using CGContextShowTextAtPoint but text shown is inverted,

- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSelectFont(context, "Arial", 24, kCGEncodingMacRoman);
CGContextShowTextAtPoint(context, 80, 80, "I am Rahul", 7);
}
I am trying to draw text as shown above "I am Rahul" but when i execute the code,
text gets shown, but it is inverted, why it is happening I am not getting !! help!!
You can use this code :
CGContextSetTextMatrix(context, CGAffineTransformMake(1.0,0.0, 0.0, -1.0, 0.0, 0.0));
This is easier:
- (void)drawRect:(CGRect)rect {
NSString* string = #"I am Rahul";
[string drawAtPoint:CGPointMake(80, 80)
withFont:[UIFont systemFontOfSize:24]];
}

Is it possible to alter the letter-spacing/kerning of a font with Cocoa Touch?

I've been scouring the Internet for a while now for information on how one can alter the letter-spacing/kerning of a font within UIKit.
My fear is, that like using your own custom fonts, you simply can't. Which would be terrible news.
I know Apple is protecting us from bad design with these constraints, but they're also preventing us from implementing really great design too.
What do people suggest I do?
Use the font with the standard kerning, people probably wouldn't notice the difference anyway.
Find someone's hacked class to get the look the user deserves after parting with their hard earned cash.
Use the method that I've somehow overlooked, and do it with UIKit, pledging my eternal gratitude to the person who imparts this hidden nugget of knowledge.
The accepted answer lost a lot of the formatting, and user363349's answer mostly worked for me but text alignment wasn't working. I modified the code to fix that:
- (void) drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSelectFont (context, [self.font.fontName cStringUsingEncoding:NSASCIIStringEncoding], self.font.pointSize, kCGEncodingMacRoman);
CGContextSetCharacterSpacing(context, characterSpacing);
CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
CGAffineTransform myTextTransform = CGAffineTransformScale(CGAffineTransformIdentity, 1.f, -1.f );
CGContextSetTextMatrix (context, myTextTransform);
// draw 1 but invisbly to get the string length.
CGPoint p =CGContextGetTextPosition(context);
float centeredY = (self.font.pointSize + (self.frame.size.height- self.font.pointSize)/2)-2;
CGContextShowTextAtPoint(context, 0, centeredY, [self.text cStringUsingEncoding:NSASCIIStringEncoding], [self.text length]);
CGPoint v =CGContextGetTextPosition(context);
// calculate width and draw second one.
float width = v.x - p.x;
float destX = 0;
// calculate X position based on alignment
if([self textAlignment] == UITextAlignmentLeft){
destX = 0;
}else if([self textAlignment] == UITextAlignmentCenter){
destX = (self.frame.size.width- width)/2;
}else if([self textAlignment] == UITextAlignmentRight){
destX = self.frame.size.width - width;
}
CGContextSetFillColorWithColor(context, [self.textColor CGColor]);
CGContextShowTextAtPoint(context, destX, centeredY, [self.text cStringUsingEncoding:NSASCIIStringEncoding], [self.text length]);
}
I landed here from Google trying to do the same with UITextField. I implemented something that'll work generically for UILabel below.
#interface AdjustableUILable : UILabel {
CGFloat characterSpacing;
}
#property CGFloat characterSpacing;
#end
#implementation AdjustableUILable
#synthesize characterSpacing;
- (void)drawTextInRect:(CGRect)rect
{
if (characterSpacing)
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat size = self.font.pointSize;
CGContextSelectFont (context, [self.font.fontName UTF8String], size, kCGEncodingMacRoman);
CGContextSetCharacterSpacing (context, characterSpacing);
CGContextSetTextDrawingMode (context, kCGTextFill);
// Rotate text to not be upside down
CGAffineTransform xform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
CGContextSetTextMatrix(context, xform);
const char *cStr = [self.text UTF8String];
CGContextShowTextAtPoint (context, rect.origin.x, rect.origin.y + size, cStr, strlen(cStr));
}
else
{
// no character spacing provided so do normal drawing
[super drawTextInRect:rect];
}
}
#end
Then to use it, just set up the label in viewDidLoad or something
- (void)viewDidLoad
{
myLabel.characterSpacing = 2; // point size
}
I tried it with UITextField, but unfortunately it only adds the character spacing after the user has edited the field. -drawTextInRect is only called after the -textFieldShouldEndEditing delegate method. From other forums some suggested this was because UITextField needs to know the rendering of the font in order to display the cursor. Never found a solution for that piece...
You may be able to do what you need using Quartz 2D. Specifically, the CGContextSetCharacterSpacing property can control spacing between letters. I'm not sure about kerning though.
Quartz 2D Programming Guide: Text
I've extended UILabel to change the character spacing. This should work out the box and pulls font, text, color etc from the UILabel itself (proper coding!).
You may notice I draw the text twice, first with clear color. This is to auto center the text in the label. Whilst this may be inefficient - isn't it nice to be auto centered?
Enjoy!
#interface RALabel : UILabel {
}
#end
#implementation RALabel
- (void) drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSelectFont (context, [self.font.fontName cStringUsingEncoding:NSASCIIStringEncoding], self.font.pointSize, kCGEncodingMacRoman);
CGContextSetCharacterSpacing(context, 1);
CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
CGAffineTransform myTextTransform = CGAffineTransformScale(CGAffineTransformIdentity, 1.f, -1.f );
CGContextSetTextMatrix (context, myTextTransform);
// draw 1 but invisbly to get the string length.
CGPoint p =CGContextGetTextPosition(context);
float centeredY = (self.font.pointSize + (self.frame.size.height- self.font.pointSize)/2)-2;
CGContextShowTextAtPoint(context, 0, centeredY, [self.text cStringUsingEncoding:NSASCIIStringEncoding], [self.text length]);
CGPoint v =CGContextGetTextPosition(context);
// calculate width and draw second one.
float width = v.x - p.x;
float centeredX =(self.frame.size.width- width)/2;
CGContextSetFillColorWithColor(context, [self.textColor CGColor]);
CGContextShowTextAtPoint(context, centeredX, centeredY, [self.text cStringUsingEncoding:NSASCIIStringEncoding], [self.text length]);
}
The main problem of CGContextShowText methods - they are diaplays only ASCII strings.
To display UTF strings you need to map your string symbols to glyphs and show glyphs.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSelectFont (context, [self.font.fontName cStringUsingEncoding:NSASCIIStringEncoding], self.font.pointSize, kCGEncodingMacRoman);
CGContextSetCharacterSpacing(context, characterSpacing);
CGContextSetFillColorWithColor(context, [self.textColor CGColor]);
CGAffineTransform myTextTransform = CGAffineTransformScale(CGAffineTransformIdentity, 1.f, -1.f );
CGContextSetTextMatrix (context, myTextTransform);
CGGlyph glyphs[self.text.length];
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)self.font.fontName, self.font.pointSize, NULL);
CTFontGetGlyphsForCharacters(fontRef, (const unichar*)[self.text cStringUsingEncoding:NSUnicodeStringEncoding], glyphs, self.text.length);
float centeredY = (self.font.pointSize + (self.frame.size.height- self.font.pointSize)/2)-2;
CGContextShowGlyphsAtPoint(context, rect.origin.x, centeredY, (const CGGlyph *)glyphs, self.text.length);