cString fails when given Russian characters on iPhone - iphone

I think this is pretty basic. I have the following code which writes text to a file for me:
float curpo = CGContextGetTextPosition(ref).x;
float newpo = 0;
float textw = 0;
const char *text = [userName cStringUsingEncoding:NSUTF8StringEncoding];
CGContextSetTextDrawingMode(ref, kCGTextInvisible);
CGContextShowTextAtPoint (ref, 0, pos, text, strlen(text));
newpo = CGContextGetTextPosition(ref).x;
textw = newpo - curpo;
It works fine with traditional english text, but when I pass in Russian or other cyrillic characters, the text comes out all jumbled. How can I fix this?

For each byte in text, CGContextShowTextAtPoint() is going to map it to a glyph in the CGContextRef's current font. This is at a lower level than you probably want.
To draw a string which may contain any glyphs from any language, use the string drawing methods in UIStringDrawing.h . Something like this:
UIGraphicsPushContext(context);
[userName drawAtPoint:pos withFont:[UIFont systemFontOfSize:16]];
UIGraphicsPopContext();
If you need finer-grain control, you can look at the CoreText frame and create a CFAttributedString from your NSString, then create a CTFramesetterRef via CTFramesetterCreateWithAttributedString(), create a CTFrameRef from that, and draw it to the context using CTFrameDraw().

Related

Placing button as subview just next to the UILable/UITextView's last character

I have used four UITextviews to place each paragraphs in. Now I want to add a UIButton as subview for each UITextview.
I realized creating subview with frame CGRectMake(textview.frame.width-50,textview.frame.height-20,50,20) is not a ideal solution because the sentence may end anywhere, I mean the last character of any paragraph may end anywhere, its position is not constant though.
So the bottom line is, I want to add UIButton within the UITextView,just next to lats word. How do I do that?
Any solution will be greatly appreciated. :)
It's not so simple.
Using:
CGSize sizeOfString = [string sizeWithFont:self.aTextView.font constrainedToSize:self.aTextView.frame.size];
you can obtain a CGSize for the given string.
But, giving the entire string, will result in a size equal to your UITextView size (so, the bottom-right corner)
You have to use this method on the last line of each UITextField...and this is the difficult part. The word wrap is done by CoreText, UITextView or UILabel doesn't give you information about the single lines, positions, etc.
You have to calculate it by yourself doing something similar (the code is not so clean, I'll help later if you have problem understanding):
NSAttributedString* text = self.aTextView.attributedText;
CTFramesetterRef fs =
CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)text);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,self.aTextView.frame.size.width - 16,100000)); // why the -16? the frame should be the REAL TEXT FRAME, not the UITextView frame. If you look, there is a space between the view margin and the text. This is the baseline. Probably there is a method to calculate it programatically, but I can't check now. In my case it seems like 8px (*2)
CTFrameRef f = CTFramesetterCreateFrame(fs, CFRangeMake(0, 0), path, NULL);
CTFrameDraw(f, NULL);
NSRange lastRange;
NSArray* lines = (__bridge NSArray*)CTFrameGetLines(f);
id lastLineInArray = [lines lastObject];
CTLineRef theLine = (__bridge CTLineRef)lastLineInArray;
CFRange range = CTLineGetStringRange(theLine);
lastRange.length = range.length;
lastRange.location = range.location - 1;
NSLog(#"%ld %ld", range.location, range.length);
CGPathRelease(path);
CFRelease(f);
CFRelease(fs);
// this is the last line
NSString *lastLine = [self.aTextView.text substringWithRange:lastRange];
Now, you can use:
CGSize sizeOfString = [lastLine sizeWithFont:self.aTextView.font constrainedToSize:self.aTextView.frame.size];
you will obtain the width and the height of the string, and then the final position of your button (for the y position: number of lines taken from the lines array count * string height)
EDIT: a comment about the left space in the UITextView (the reason why there is a -16 in this line)
CGPathAddRect(path, NULL, CGRectMake(0,0,self.aTextView.frame.size.width - 16,100000));
It happens because the text is not really inside the UITextView, but inside one of its subviews: an UIWebDocumentView (private class) which adds an insets. After some searching, I can't find any method to obtain (legally) the value of this insets, necessary to pass the correct rect to the CGPathAddRect() function.
You can do some tests to be sure that it's always 8pt :-) or switch to UILabel, which doesn't have that content insets
You should consider either using UIWebView or some third party attributed text views that support such features.
UITextView internally uses a web view, making it a nontrivial job to figure out the exact layout of text.
I think, that better way to use tableView. One paragraph in one cell. Make cell without border and set clearColor to cells background. In this way you can easy add button.

iPhone - Using CGContextShowTextAtPoint to draw special characters

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."

Convert pdf file to text file

Hi all I am working on Objective-C. my previous Question was How can I edit PDF files in an iOS application?
after a lot of googling I found out the following. display the pdf in UIWebView, extract the data using C/javascript and edit it. I am still not sure about this procedure. now what I have planned is
1) display the pdf
2) when user wants to edit the pdf I covert the pdf to text and allow him to edit it
3) tryin to save wil convert the content back to pdf.
is this a gud way to proceed?? im k with step 1. now how do i convert pdf--> text and text-->pdf.
thanks in advance
When you load a custom document type (doc, ppt, pdf, etc) into a UIWebView, the webview returns a nil HTML string, even via javascript. There's a few suggestions for extracting PDF text here.
But turning the string back into a PDF is different. If you want to retain the formatting of the original PDF, I'm rather sure that's impossible because NSAttributedString on iOS doesn't do much. But this will work for plain text or NSAttributedString, if its possible:
NSData *PDFDataFromString(NSString *str) {
NSMutableData *data = [NSMutableData data];
//Create an NSAttributedString for CoreText. If you find a way to translate
//PDF into an NSAttributedString, you can skip this step and simply use an
//NSAttributedString for this method's argument.
NSAttributedString* string = [[[NSAttributedString alloc] initWithString:str] autorelease];
//612 and 792 are the dimensions of the paper in pixels. (8.5" x 11")
CGRect paperRect = CGRectMake(0.0, 0.0, 612, 792);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef) string);
CGSize requiredSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, [string length]), NULL, CGSizeMake(paperRect.size.width - 144, 1e40), NULL);
//Subtract the top and bottom margins (72 and 72), so they aren't factored in page count calculations.
NSUInteger pageCount = ceill(requiredSize.height / (paperRect.size.height - 144));
CFIndex resumePageIndex = 0;
UIGraphicsBeginPDFContextToData(data, paperRect, nil);
for(NSUInteger i = 0; i < pageCount; i++)
{
//After calculating the required number of pages, break up the string and
//draw them into sequential pages.
UIGraphicsBeginPDFPage();
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState (currentContext);
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
CGMutablePathRef framePath = CGPathCreateMutable();
//72 and 72 are the X and Y margins of the page in pixels.
CGPathAddRect(framePath, NULL, CGRectInset(paperRect, 72.0, 72.0));
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(resumePageIndex, 0), framePath, NULL);
resumePageIndex += CTFrameGetVisibleStringRange(frameRef).length;
CGPathRelease(framePath);
CGContextTranslateCTM(currentContext, 0, paperRect.size.height);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CTFrameDraw(frameRef, currentContext);
CFRelease(frameRef);
CGContextRestoreGState (currentContext);
}
CFRelease(framesetter);
UIGraphicsEndPDFContext();
return data;
}

how to write formatted text in a rectangle using quartz framework in iphone

I m a newbie in iphone.
Currently m doing a project to create a pdf having tables.
I created that. I can even write the text in table cells.
But now the problem is the data will come dynamically from database. That is also not a big deal if i m using drawinrect function. But now i want to display a rich text i.e. The text can be bold or italic.
How to write such rich text in a bounded rectangle...???
I can have text in html format.
Any solution for this???
// Initialize an attributed string.
NSString *html = text;
NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding];
// Create attributed string from HTML
NSAttributedString *string = [[NSAttributedString alloc] initWithHTML:data baseURL:nil documentAttributes:NULL];
// CGColorRef red = CGColorCreate(rgbColorSpace, components); CGColorSpaceRelease(rgbColorSpace);
// CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 50),kCTForegroundColorAttributeName, red);
// Create the framesetter with the attributed string.
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(string);
// Create the frame and draw it into the graphics context
CTFrameRef frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0, 0), path, NULL);
CFRelease(framesetter);
CTFrameDraw(frame, pdfContext);
CFRelease(frame);
This solved my issue :)

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);