iPhone - Using CGContextShowTextAtPoint to draw special characters - iphone

I am creating sub class for UILabel to adjust the character spacing. It works well.
But when I use special characters as in Spanish or Japanese language, its not writing. Only english characters are written properly. Any solution on how to display them?
- (void) drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSelectFont (context, [self.font.fontName cStringUsingEncoding:NSUTF8StringEncoding], 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.
const char* str = [self.text UTF8String];
CGPoint p = CGContextGetTextPosition(context);
float centeredY = (self.font.pointSize + (self.frame.size.height - self.font.pointSize)/2)-2;
CGContextShowTextAtPoint(context, 0, centeredY, str, [self.text length]);
CGPoint v = CGContextGetTextPosition(context);
float centeredX = 0;
if (self.centered) {
float width = v.x - p.x;
centeredX = (self.frame.size.width- width)/2;
}
// calculate width and draw second one.
CGContextSetFillColorWithColor(context, [self.textColor CGColor]);
CGContextShowTextAtPoint(context, centeredX, centeredY, str, [self.text length]);
}

CGContextShowTextAtPoint does not directly support those languages -- the MacRoman encoding may have been a hint. CG has only basic text layout/drawing.
two alternatives would be Cocoa's text drawing, or CoreText.

Better late than never;
Use CoreText to drawFonts instead (Special characters and Unicode)
http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_text/dq_text.html#//apple_ref/doc/uid/TP30001066-CH213-TPXREF101
"iOS 3.2 and later and Mac OS X both support Core Text, an advanced low-level technology for laying out text and handing fonts. Core Text is designed for high performance and ease of use and allows you to draw Unicode text directly to a graphics context. If you are writing an application that needs precise control over how text is displayed, see Core Text Programming Guide."

Related

Which method to use to display text ?Why?

When drawing using CoreGraphics to draw a string/label we have 2 methods
- (CGSize)drawInRect:(CGRect)rect withFont:(UIFont *)font lineBreakMode:(NSLineBreakMode)lineBreakMode alignment:(NSTextAlignment)alignment;
eg:
[value drawInRect:CGRectMake(xValue, rect.origin.y, objLegends.value *singleUnitWidth,heightOfBar)
withFont:[UIFont systemFontOfSize:10.0f]
lineBreakMode:NSLineBreakByClipping
alignment:NSTextAlignmentCenter];
and
CG_EXTERN void CGContextShowText(CGContextRef c, const char *string,
size_t length) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
eg
CGContextSaveGState(context);
// Tanslate and scale upside-down to compensate for Quartz's inverted coordinate system
CGContextSetTextMatrix(context, CGAffineTransformMake(1.0,0.0, 0.0, -1.0, 0.0, 0.0));
CGContextSelectFont(context, "Helvetica", 10.0f, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextSetTextPosition(context, xValue, rect.size.height-heightOfText);
if ([value canBeConvertedToEncoding:NSMacOSRomanStringEncoding])
{
CGContextShowText(context, [value cStringUsingEncoding:NSMacOSRomanStringEncoding],5);
// strlen([value cStringUsingEncoding:NSMacOSRomanStringEncoding]));
}
CGContextRestoreGState(context);
Which one is a good method and why?
The best method is the one that is simplest to achieve your goal. The higher the level of API you can use the better. If you can use the NSString method then you should - unless you can use UILabel (or similar) to do the job for you. Always try to write the least code possible and reuse code that's provided for you, dropping to use lower level API if your requirements or performance profiling show you need to.

CoreGraphic - display umlaut mark on an i

I'm to trying to display an umlaut mark on an i in my CoreGraphics function: ï.
I tried to use it's unicode representation (ï), but I don't manage to get it right.
here is my code:
void drawColorString(CGContextRef ctx, NSString *text, UIColor *color, CGPoint pos){
CGContextSaveGState(ctx);
[color setFill];
CGContextSelectFont(ctx, "Georgia-BoldItalic", 14, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(ctx, kCGTextFill);
CGAffineTransform xform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
CGContextSetTextMatrix(ctx, xform);
CGContextShowTextAtPoint(ctx, pos.x, pos.y, [text UTF8String], text.length);
CGContextRestoreGState(ctx);
}
it works very well for regular strings, but I get a square for each special char...
Can anyone help me?
Thanks.
R.
Drawing text in Quartz sucks. One reason is that it doesn't support Unicode: Your choices are plain old ASCII and MacRoman. You might be able to do it by looking up the glyphs for the characters, but that's not fun at all and I bet there are some easy ways to get it wrong.
You can draw a ï in MacRoman easily enough, but a far better solution is to switch to Core Text.

Iphone CGContextShowTextAtPoint for Japanese characters

I am working on an app where I am using CGContextShowTextAtPoint to display text to the screen. I want to also display Japanese characters, but CGContextShowTextAtPoint takes as its input a C string. So either A) How do I change Japanese characters into a C string? If this is not possible, B) How can I manually print Japanese characters to the screen (within the drawRect method).
Thanks in advance.
CoreText can help you:
CTFontGetGlyphsForCharacters (iOS 3.2 onwards) maps Unicode characters to glyphs
CTFontDrawGlyphs (iOS 4.2 onwards) draws the glyphs into a CGContext.
NB. CGContextShowGlyphs should work, but I never found a way to convert my UniChars to glyphs. More on that here:
Ancient, pre iOS 3.2 answer
you need to use UIKit for this.
Check out [NSString drawAtPoint:...] to get started.
This SO question is useful, too.
I don't know what they were thinking with the CoreGraphic text stuff, it's useless.
I was able to get this working by using a reimplementation of CGFontGetGlyphsForUnichars by Jens Egeblad: GlyphDrawing.mm
First load in a Japanese font as an otf file from the app bundle:
// Load font file from otf file
NSString *fontPath = [[NSBundle mainBundle] pathForResource:#"HStdNW8" ofType:#"otf"];
CGDataProviderRef fontDataProvider = CGDataProviderCreateWithFilename([fontPath UTF8String]);
CGFontRef _cgFont = CGFontCreateWithDataProvider(fontDataProvider);
CGDataProviderRelease(fontDataProvider);
Then you can convert your unichar text to glyphs and draw them:
NSString *text = #"日本語"
CGContextSetFont(context, _cgFont);
CGContextSetFontSize(context, 12);
CGGlyph textGlyphs[[text length]];
unichar textChars[[text length]];
for(int i = 0; i < [text length]; i++) {
textChars[i] = [text characterAtIndex:i];
}
CMFontGetGlyphsForUnichars(_cgFont, textChars, textGlyphs, [text length]);
CGContextShowGlyphsAtPoint(context, xCoord, yCoord, textGlyphs, [text length]);
For what it's worth, I spent a long time trying to get Japanese characters to work well in CoreGraphics, and didn't like where it left me.
In the end I switched to using UILabels to handle the text. All the CoreGraphics-like stuff I needed could be replicated using the transform & animation support, and in the end the resulting code was much simpler.
It may not be appropriate for your situation, but it's worth considering.
This maybe helps you %topic starter%. Thanks to Rhythmic Fistman for great advice!
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);
CFRelease(fontRef);

iPhone: Draw rotated text?

I want to draw some text in a view, rotated 90°. I'm pretty new to iPhone development, and poking around the web reveals a number of different solutions. I've tried a few and usually end up with my text getting clipped.
What's going on here? I am drawing in a fairly small space (a table view cell), but there has to be a "right" way to do this… right?
Edit: Here are a couple of examples. I'm trying to display the text "12345" along the black bar at the left.
First attempt, from RJShearman on the Apple Discussions
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSelectFont (context, "Helvetica-Bold", 16.0, kCGEncodingMacRoman);
CGContextSetTextDrawingMode (context, kCGTextFill);
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetTextMatrix (context, CGAffineTransformRotate(CGAffineTransformScale(CGAffineTransformIdentity, 1.f, -1.f ), M_PI/2));
CGContextShowTextAtPoint (context, 21.0, 55.0, [_cell.number cStringUsingEncoding:NSUTF8StringEncoding], [_cell.number length]);
CGContextRestoreGState(context);
(source: deeptechinc.com)
Second attempt, from zgombosi on iPhone Dev SDK. Identical results (the font was slightly smaller here, so there's less clipping).
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint point = CGPointMake(6.0, 50.0);
CGContextSaveGState(context);
CGContextTranslateCTM(context, point.x, point.y);
CGAffineTransform textTransform = CGAffineTransformMakeRotation(-1.57);
CGContextConcatCTM(context, textTransform);
CGContextTranslateCTM(context, -point.x, -point.y);
[[UIColor redColor] set];
[_cell.number drawAtPoint:point withFont:[UIFont fontWithName:#"Helvetica-Bold" size:14.0]];
CGContextRestoreGState(context);
Attempt two. There is almost identical clipping http://dev.deeptechinc.com/sidney/share/iphonerotation/attempt2.png
It turns out that the my table cell was always initialized 44px high regardless of the row height, so all of my drawing was getting clipped 44px from the top of the cell.
To draw larger cells it was necessary to set the content view's autoresizingMask with
cellContentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
or
cellContentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
…and drawRect is called with the correct size. In a way, this makes sense, because UITableViewCell's initWithStyle:reuseIdentifier: makes no mention of the size of the cell, and only the table view actually knows how big each row is going to be, based on its own size and its delegate's response to tableView:heightForRowAtIndexPath:.
I read the Quartz 2D Programming Guide until the drawing model and functions started to make sense, and the code to draw my rotated text became simple and obvious:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextRotateCTM(context, -(M_PI/2));
[_cell.number drawAtPoint:CGPointMake(-57.0, 5.5) withFont:[UIFont fontWithName:#"Helvetica-Bold" size:16.0]];
CGContextRestoreGState(context);
Thanks for the tips, it looks like I'm all set.
Use :-
label.transform = CGAffineTransformMakeRotation(- 90.0f * M_PI / 180.0f);
where label is the object of UILabel.
Here's a tip. I presume you're doing this drawing in drawRect. Why don't you draw a frame around drawRect to see how big the rect is and if that is why you get clipping.
An alternative is to put your text in a UILabel, and then rotate that 90 degrees when you make your cells in cellForRowAtIndexPath.
You know about the UITableViewDelegate method heightForRowAtIndexPath right?
Here's a simple tutorial on various graphics level methods. Presuming you know how big your text is you should be able to size your table view row size appropriately.
Also, I'd check to make sure that the bounds after any transform actually meet your expectations. (Either use a debugger or log statement to verify this).
to what #Sidnicious said, and what i collected through out stack overflow, i want to give a usage example - appended my code to completely draw a ruler to the left screen side, with numbers rotated:
RulerView : UIView
// simple testing for iPhones (check for device descriptions to get all iPhones + iPads)
- (float)getPPI
{
switch ((int)[UIScreen mainScreen].bounds.size.height) {
case 568: // iPhone 5*
case 667: // iPhone 6
return 163.0;
break;
case 736: // iPhone 6+
return 154.0;
break;
default:
return -1.0;
break;
}
}
- (void)drawRect:(CGRect)rect
{
[[UIColor blackColor] setFill];
float ppi = [self getPPI];
if (ppi == -1.0) // unable to draw, maybe an ipad.
return;
float linesDist = ppi/25.4; // ppi/mm per inch (regular size iPad would be 132.0, iPhone6+ 154.0)
float linesWidthShort = 15.0;
float linesWidthMid = 20.0;
float linesWidthLong = 25.0;
for (float i = 0, c = 0; i <= self.bounds.size.height; i = i + linesDist, c = c +1.0)
{
bool isMid = (int)c % 5 == 0;
bool isLong = (int)c % 10 == 0;
float linesWidth = isLong ? linesWidthLong : isMid ? linesWidthMid : linesWidthShort;
UIRectFillUsingBlendMode( (CGRect){0, i, linesWidth, .5} , kCGBlendModeNormal);
/* FONT: Numbers without rotation (yes, is short)
if (isLong && i > 0 && (int)c % 10 == 0)
[[NSString stringWithFormat:#"%d", (int)(c/10)] drawAtPoint:(CGPoint){linesWidthLong +2, i -5} withAttributes:#{
NSFontAttributeName: [UIFont systemFontOfSize:9],
NSBaselineOffsetAttributeName: [NSNumber numberWithFloat:1.0]
}];
*/
// FONT: Numbers with rotation (yes, requires more effort)
if (isLong && i > 0 && (int)c % 10 == 0)
{
NSString *str = [NSString stringWithFormat:#"%d", (int)(c/10)];
NSDictionary *attrs = #{
NSFontAttributeName: [UIFont systemFontOfSize:9],
NSBaselineOffsetAttributeName: [NSNumber numberWithFloat:0.0]
};
CGSize textSize = [str sizeWithAttributes:attrs];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextRotateCTM(context, +(M_PI/2));
[str drawAtPoint:(CGPoint){i - (textSize.width/2), -(linesWidthLong + textSize.height +2)} withAttributes:attrs];
CGContextRestoreGState(context);
}
}
}
After I discovered that I needed to add the following to the top of my file I liked Matt's approach. Very simple.
#define degreesToRadian(x) (M_PI * (x) / 180.0)
mahboudz's suggestion will probably be your path of least resistance. You can rotate the UILabel 90deg with this: [label setTransform:CGAffineTransformMakeRotation(DegreesToRadians(-90.0f))]; You'll just have to calculate your cell height based upon the label width. -Matt – Matt Long Nov 10 at 0:09

NSString drawInRect overlaps all characters

I am attempting to fill some text with a gradient-fill, where by I set the text drawing mode to clipping, and then paint a gradient-fill.
The problem is, whenever I set the text drawing mode to clip, every character of the text string is placed on top of each other, rather than being painted in a sequence to form a word - it is most bizarre!
My code looks like this:
CGRect r = CGRectInset(self.frame, 55, 8);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[8] = {44/255, 54/255, 66/255, 1.0
,75/255, 92/255, 111/255, 1.0};
CGFloat locations[2] = {0, 1};
// draw the text's gradient fill
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2);
CGContextSetTextDrawingMode(context, kCGTextClip);
[monthString drawInRect:r withFont:f lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentCenter];
CGContextFillRect(context, CGRectMake(0, 0, 320, 20));
It appears that the UIKit NSString additions that let you do drawInRect:withFont: and the like don't play well with the kCGTextClip drawing mode. I've seen the same behavior, and in this answer I provide code to fill text with a gradient using pure Quartz drawing calls. The downside to this approach is that you are limited to the MacRoman text encoding, which lacks support for many Unicode symbols.