iOS emoji messed up in UILabel - ios

I am using a open source UILabel subclass STTweetLabel v2.22 (Github) and trying to show emoji in the label. During my test it seems that the code can handle most cases correctly but sometimes I see this:
Just wondering why this could happen, and what could be a possible fix I should look into..
Thanks!
-- Update (adding code used to decode strings from server) --
NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
NSString *decoded = [[NSString alloc] initWithData:data encoding:NSNonLossyASCIIStringEncoding];

Hi I have got one solution for that library you can find height with emoji.
Please use <CoreText/CoreText.h> framework and use below code.
- (CGFloat)heightStringWithEmojis:(NSString*)str fontType:(UIFont *)uiFont ForWidth:(CGFloat)width {
// Get text
CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), (CFStringRef) str );
CFIndex stringLength = CFStringGetLength((CFStringRef) attrString);
// Change font
CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef) uiFont.fontName, uiFont.pointSize, NULL);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, stringLength), kCTFontAttributeName, ctFont);
// Calc the size
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
CFRange fitRange;
CGSize frameSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(width, CGFLOAT_MAX), &fitRange);
CFRelease(ctFont);
CFRelease(framesetter);
CFRelease(attrString);
return frameSize.height +4;
}
Let me know thoughts....!!!

Several things:
Do not use encoding:NSNonLossyASCIIStringEncoding, emoji are not ASCII. Use NSUTF8StringEncoding.
Why are you converting to NSData and then back to an NSString? That makes no sense.
There is something else going on here.

Related

Set color CFAttributedStringRef

I have this methods for draw a table ant populated . What i want is to change the color for one word from each column , but i dont know how can i do it . Can somebeday help me ,please ? Any help will be appreciate .
in my app the user can select some attributes from segmente control... I want to export what he chosen in a pdf , like a table .So on each line a word will be selected
-(void)drawTableDataAt:(CGPoint)origin
withRowHeight:(int)rowHeight
andColumnWidth:(int)columnWidth
andRowCount:(int)numberOfRows
andColumnCount:(int)numberOfColumns
{
int padding = 1;
NSArray* headers = [NSArray arrayWithObjects:#"Grand", #"Taile ok", #"Petit", nil];
NSArray* invoiceInfo1 = [NSArray arrayWithObjects:#"Extra", #"Bon", #"Ordi", nil];
NSArray* invoiceInfo2 = [NSArray arrayWithObjects:#"Gras", #"Etat", #"Maigre", nil];
NSArray* invoiceInfo3 = [NSArray arrayWithObjects:#"Cru", #"Propre", #"Sale", nil];
NSArray* invoiceInfo4 = [NSArray arrayWithObjects:#"PLourd", #"PMoyen", #"PLeger", nil];
NSArray* invoiceInfo5 = [NSArray arrayWithObjects:#"CSup", #"CEgal", #"CInf", nil];
NSArray* allInfo = [NSArray arrayWithObjects:headers, invoiceInfo1, invoiceInfo2, invoiceInfo3, invoiceInfo4, invoiceInfo5,nil];
for(int i = 0; i < [allInfo count]; i++)
{
NSArray* infoToDraw = [allInfo objectAtIndex:i];
for (int j = 0; j < numberOfColumns; j++)
{
int newOriginX = origin.x + (j*columnWidth);
int newOriginY = origin.y + ((i+1)*rowHeight);
CGRect frame = CGRectMake(newOriginX + padding, newOriginY + padding, columnWidth, rowHeight);
[self drawText:[infoToDraw objectAtIndex:j] inFrame:frame];
}
}
}
-(void)drawText:(NSString*)textToDraw inFrame:(CGRect)frameRect
{
CFStringRef stringRef = (__bridge CFStringRef)textToDraw;
// Prepare the text using a Core Text Framesetter
CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, stringRef, NULL);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
// Get the frame that will do the rendering.
CFRange currentRange = CFRangeMake(0, 0);
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
CGPathRelease(framePath);
// Get the graphics context.
CGContextRef currentContext = UIGraphicsGetCurrentContext();
// Put the text matrix into a known state. This ensures
// that no old scaling factors are left in place.
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
// Core Text draws from the bottom-left corner up, so flip
// the current transform prior to drawing.
CGContextTranslateCTM(currentContext, 0, frameRect.origin.y*2);
CGContextScaleCTM(currentContext, 1.0, -1.0);
// Draw the frame.
CTFrameDraw(frameRef, currentContext);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGContextTranslateCTM(currentContext, 0, (-1)*frameRect.origin.y*2);
CFRelease(frameRef);
CFRelease(stringRef);
CFRelease(framesetter);
}
Based on the comments on the question, you mentioned that the words will never change. You could potentially create a whole bunch of if/else statements checking every word selected against every word in an array. I have put this down as a more efficient alternative and it should hopefully work. It may need some tweaking or even a loop to go through your chosen words, but this should hopefully put you in the right direction:
//declare your textToDraw as a new NSString
NSString *str = textToDraw;
//Make an Array of the str by adding objects that are separated by whitespace
NSArray *words = [str componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//create a BOOL to check if your selected word exists in the array
BOOL wordExists = [words containsObject: #"%#", yourSelectedWord];
CTFramesetterRef framesetter = null;
//if the word exists, make it red
if(wordExists){
NSUInteger indexOfTheString = [words indexOfObject: #"%#", yourSelectedWord];
CFAttributedStringRef currentText = CFAttributedStringCreate(NULL,str, NULL);
[currentText addAttribute:NSForegroundColorAttributeName
value:[UIColor redColor]
range:NSMakeRange(indexOfTheString, yourSelectedWord.length)];
framesetter = CTFramesetterCreateWithAttributedString(currentText);
}
This will match your selected word found against the right word in your array and highlight it red.

CoreText and right alignment

I'm writing a class to generate PDFs, I'll publish as I will finish it!
I'm unable to align text to the right, with the CTParagraphStyle, the text is always on the left. How does it is possible? What I'm getting wrong?
- (void)addText:(NSString *)text color:(UIColor *)color fontSize:(CGFloat)size floating:(BOOL)floating {
CGContextSaveGState(pdfContext);
// Prepare font
CTFontRef font = CTFontCreateWithName(CFSTR("Verdana"), size, NULL);
// Font color
CGColorRef fontColor = [color CGColor];
// Paragraph
CTTextAlignment alignment = kCTRightTextAlignment;
CTParagraphStyleSetting settings[] = {
{kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment}
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
// Create an attributed string
CFStringRef keys[] = { kCTFontAttributeName , kCTParagraphStyleAttributeName, kCTForegroundColorAttributeName};
CFTypeRef values[] = { font, paragraphStyle, fontColor};
CFDictionaryRef attr = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values,
sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFAttributedStringRef attrString = CFAttributedStringCreate(NULL, (CFStringRef)text, attr);
CFRelease(paragraphStyle);
CFRelease(attr);
// Draw the string
CTLineRef line = CTLineCreateWithAttributedString(attrString);
CGContextSetTextPosition(pdfContext, xPadding, [self relativeHeight:currentHeight+size]);
CTLineDraw(line, pdfContext);
// Clean up
CFRelease(line);
CFRelease(attrString);
CFRelease(font);
CGContextRestoreGState(pdfContext);
if(floating == NO) {
currentHeight += size;
}
}
Remove CTLineDraw() and its related code and use CTFrameDraw().
Try this:
// Create the Core Text framesetter using the attributed string.
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
// Create the Core Text frame using our current view rect bounds.
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), [path CGPath], NULL);
CTFrameDraw(frame, pdfContext);

iOS: Sample Code or Examples for CGPDFContextAddDocumentMetadata

I need to embed some custom meta data to a PDF file using the Apple's Objective C SDK. Is there any working sample code available that does that?
What I need is very basic. Open an existing pdf and add some meta data and then save it.
Example of information stored will be like.... a customer's name, contact number, items ordered.
I saw that this "CGPDFContextAddDocumentMetadata" supports metadata but I don't know how the meta data look like and how is it used passed to this function.
Any help will be greatly appreciated...:)
Metadata is metadata (of course, it is recommended by Adobe that you use XMP XML). So long as you create a valid CFDataRef and pass it into arg two, you're pretty much good to go with anything. For example, here's how to pass the string "Hello World" into a PDF's metadata:
void MakeAPDF()
{
CGRect mediaRect = CGRectMake(0, 0, 400, 600);
// use your own rect instead
CFMutableDataRef result = CFDataCreateMutable(kCFAllocatorDefault, 0);
CGDataConsumerRef PDFDataConsumer = CGDataConsumerCreateWithCFData(result);
// mark the PDF as coming from your program
CFMutableDictionaryRef auxInfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, NULL, NULL);
CFDictionaryAddValue(auxInfo, kCGPDFContextCreator, CFSTR("Your Programs Name"));
CFDictionaryRef auxillaryInformation = CFDictionaryCreateCopy(kCFAllocatorDefault, auxInfo);
CFRelease(auxInfo);
// create a context to draw into
CGContextRef graphicContext = CGPDFContextCreate(PDFDataConsumer, &mediaRect, auxillaryInformation);
CFRelease(auxillaryInformation);
CGDataConsumerRelease(PDFDataConsumer);
// actually make the call to embed your String
NSString* str= #"Hello World";
NSData* data=[str dataUsingEncoding:NSUTF8StringEncoding];
CFDataRef cfdata = CFDataCreate(NULL, [data bytes], [data length]);
CGPDFContextAddDocumentMetadata(graphicContext, cfdata);
CGContextBeginPage(graphicContext, &mediaRect);
// do your drawing, like this grey rectangle
CGContextSetGrayFillColor(graphicContext, 0.5, 0.5);
CGContextAddRect(graphicContext, mediaRect);
CGContextFillPath(graphicContext);
// end your drawing
CGContextEndPage(graphicContext);
CGContextFlush(graphicContext);
CGPDFContextClose(graphicContext);
}
An XMP file might look like this:
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c041">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:format>application/pdf</dc:format>
<dc:title>
<rdf:Alt>
<rdf:li />
</rdf:Alt>
</dc:title>
<dc:description>
<rdf:Alt>
<rdf:li />
</rdf:Alt>
</dc:description>
<dc:creator>
<rdf:Seq>
<rdf:li />
</rdf:Seq>
</dc:creator>
</rdf:Description>
<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
<pdf:Keywords />
<pdf:Producer />
</rdf:Description>
<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<xmp:CreatorTool />
<xmp:CreateDate>2011-02-10T11:41:05+02:00</xmp:CreateDate>
<xmp:ModifyDate>2011-02-10T11:41:06+02:00</xmp:ModifyDate>
</rdf:Description></rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
UIKit has some methods that make this simpler:
// use your own rect instead
CGRect pageRect = CGRectMake(0, 0, 400, 600);
// mark the PDF as coming from your program
NSDictionary *auxillaryInformation = #{(NSString *)kCGPDFContextCreator: #"Koedal, Inc."};
UIGraphicsBeginPDFContextToFile([[self URLOfPDF] path], pageRect, auxillaryInformation);
// Embed metadata into PDF, I'm pulling it from a textView
NSString *PDFMetadata = self.metadataTextView.text;
NSData *data = [PDFMetadata dataUsingEncoding:NSUTF8StringEncoding];
CGContextRef PDFContext = UIGraphicsGetCurrentContext();
CGPDFContextAddDocumentMetadata(PDFContext, (CFDataRef)data);
NSDictionary *textAttributes = #{ NSFontAttributeName : [UIFont boldSystemFontOfSize:20],
NSForegroundColorAttributeName : [UIColor blackColor]
};
// do your drawing, like drawing this string
UIGraphicsBeginPDFPage();
NSString *content = self.contentTextField.text;
[content drawAtPoint:CGPointMake(150, 280) withAttributes:textAttributes];
// end your drawing
UIGraphicsEndPDFContext();

iOS - Generating pdf from multiple files

I am attempting to convert some text files into a pdf. Now the easiest thing in the world right now is to simply combine all of the text from the files into a single string and then use the iOS documentation here to render it into a single pdf. The trouble is, these are large text files; together they can equal well over 90 pages. Therefore I will need to add in some hyperlinks so I can create a table of contents at the top and the user can quickly move to the beginning of each text file rather than have to scroll through 60 pages to get to where they want to go.
Problem is, if I combine the txt files into a single string, I have no way of knowing when each file will end while it is paginating, therefore I wanted to add the files separately to the pdf before finally publishing it. Problem is, at best only the last txt file will show up rendered, most likely because it is overwriting the previous ones. Below is my code, any ideas?
- (void)savePDFFile:(NSString *)file_Name
{
// NSArray *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *homeDir = NSHomeDirectory();
NSString *saveDirectory = [NSString stringWithFormat: #"%#/%#", homeDir, #"Documents/"];
NSArray *fileAr = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:saveDirectory error:nil];
// NSString *text = #"";
// NSMutableArray *textArray = [[NSMutableArray alloc] init];
NSInteger currentPage = 0;
NSString *completeString = #"";
for (NSString *string in fileAr) {
if([string hasSuffix:#"txt"]){
NSString *file = [NSString stringWithFormat: #"%#/%#", saveDirectory, string];
NSString *text =[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
completeString = [NSString stringWithFormat:#"%#%#", completeString, text];
}
}
for (NSString *string in fileAr) {
if([string hasSuffix:#"txt"]){
NSString *file = [NSString stringWithFormat: #"%#/%#", saveDirectory, string];
NSString *text =[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
// Prepare the text using a Core Text Framesetter
CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, (CFStringRef)text, NULL);
if (currentText) {
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)currentText);
// CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);
if (framesetter) {
NSString* pdfFileName = file_Name;
// Create the PDF context using the default page size of 612 x 792.
UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
CFRange currentRange = CFRangeMake(0, 0);
BOOL done = NO;
do {
// Mark the beginning of a new page.
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);
// Draw a page number at the bottom of each page
currentPage++;
[self drawPageNumber:currentPage];
// Render the current page and update the current range to
// point to the beginning of the next page.
currentRange = [self renderPage:currentPage withTextRange:currentRange andFramesetter:framesetter];
// If we're at the end of the text, exit the loop.
if (currentRange.location == CFAttributedStringGetLength((CFAttributedStringRef)currentText))
done = YES;
} while (!done);
// Release the framewetter.
CFRelease(framesetter);
CFRelease(currentText);
}
}
// Close the PDF context and write the contents out.
UIGraphicsEndPDFContext();
} else {
NSLog(#"Could not create the framesetter needed to lay out the atrributed string.");
}
// Release the attributed string.
}
}
// Use Core Text to draw the text in a frame on the page.
- (CFRange)renderPage:(NSInteger)pageNum withTextRange:(CFRange)currentRange
andFramesetter:(CTFramesetterRef)framesetter
{
// Get the graphics context.
CGContextRef currentContext = UIGraphicsGetCurrentContext();
// Put the text matrix into a known state. This ensures
// that no old scaling factors are left in place.
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
// Create a path object to enclose the text. Use 72 point
// margins all around the text.
CGRect frameRect = CGRectMake(72, 72, 468, 648);
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
// Get the frame that will do the rendering.
// The currentRange variable specifies only the starting point. The framesetter
// lays out as much text as will fit into the frame.
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
CGPathRelease(framePath);
// Core Text draws from the bottom-left corner up, so flip
// the current transform prior to drawing.
CGContextTranslateCTM(currentContext, 0, 792);
CGContextScaleCTM(currentContext, 1.0, -1.0);
// Draw the frame.
CTFrameDraw(frameRef, currentContext);
// Update the current range based on what was drawn.
currentRange = CTFrameGetVisibleStringRange(frameRef);
currentRange.location += currentRange.length;
currentRange.length = 0;
CFRelease(frameRef);
return currentRange;
}
I made progress and ran into another problem, so I asked an additional question here.
Eventually, with my own testing and a little bit of help, I solved the issue.

Issue with importing korean localization text into xcode

I am completing the final part of localizations for a project. The translated text has come back to me split between .txt and .docx formats.
The .txt once entered into the localizable.strings works fine, but that copied from word document doesn't work.
This is what I've tried so far:
save .docx as .txt and let word encode
save .txt as korean (Mac OS X),
then copy this text to XCode and reinterpret as korean (Mac OS X), then
convert to utf-16
Have tried many options to convert to utf-16, but just can't seem to crack it.
Any ideas would be much appreciated.
Here is the localized help view implementation:
helpText = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
NSLocalizedString(#" The Actions Tab", nil), kHelpTextKeyString,
#"Arial", kHelpTextKeyFontName,
[NSNumber numberWithInt:20], kHelpTextKeyFontSize,
[[UIColor blackColor] CGColor], kHelpTextKeyColor,
CGRectCreateDictionaryRepresentation(CGRectMake(30.0, 55.0, 200.0, 28.0)), kHelpTextKeyRect,
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
[NSArray arrayWithObjects:
NSLocalizedString(#"
- (void)displaySelectedHelpImage:(UIImage *)orgImage withTextArray:(NSArray *)textArr {
CGImageRef cgImage = [orgImage CGImage];
int pixelsWide = CGImageGetWidth(cgImage);
int pixelsHigh = CGImageGetHeight(cgImage);
int bitsPerComponent = CGImageGetBitsPerComponent(cgImage);//8; // fixed
int bitsPerPixel = CGImageGetBitsPerPixel(cgImage);//bitsPerComponent * numberOfCompnent;
int bytesPerRow = CGImageGetBytesPerRow(cgImage);//(pixelsWide * bitsPerPixel) // 8; // bytes
int byteCount = (bytesPerRow * pixelsHigh);
CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);//CGColorSpaceCreateDeviceRGB();
// Allocate data
NSMutableData *data = [NSMutableData dataWithLength:byteCount];
// Create a bitmap context
CGContextRef context = CGBitmapContextCreate([data mutableBytes], pixelsWide, pixelsHigh, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast); //kCGImageAlphaPremultipliedLast);//kCGImageAlphaNoneSkipLast); //kCGImageAlphaOnly);
// Set the blend mode to copy to avoid any alteration of the source data or to invert to invert image
CGContextSetBlendMode(context, kCGBlendModeCopy);
// Set alpha
CGContextSetAlpha(context, 1.0);
// Color image
//CGContextSetRGBFillColor(context, 1 ,1, 1, 1.0);
//CGContextFillRect(context, CGRectMake(0.0, 0.0, pixelsWide, pixelsHigh));
// Draw the image to extract the alpha channel
CGContextDrawImage(context, CGRectMake(0.0, 0.0, pixelsWide, pixelsHigh), cgImage);
// add text to image
// Changes the origin of the user coordinate system in a context
//CGContextTranslateCTM (context, pixelsWide, pixelsHigh);
// Rotate context upright
//CGContextRotateCTM (context, -180. * M_PI/180);
for (NSDictionary *dic in textArr) {
CGContextSelectFont (context,
//todo
[[dic objectForKey:kHelpTextKeyFontName] UTF8String],
[[dic objectForKey:kHelpTextKeyFontSize] intValue],
kCGEncodingMacRoman);
CGContextSetCharacterSpacing (context, 2);
CGContextSetTextDrawingMode (context, kCGTextFillStroke);
CGColorRef color = (CGColorRef)[dic objectForKey:kHelpTextKeyColor];
CGRect rect;
CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[dic objectForKey:kHelpTextKeyRect], &rect);
CGContextSetFillColorWithColor(context, color);
CGContextSetStrokeColorWithColor(context, color);
if ([[dic objectForKey:kHelpTextKeyString] isKindOfClass:[NSArray class]]) {
for (NSString *str in [dic objectForKey:kHelpTextKeyString]) {
CGContextShowTextAtPoint(context,
rect.origin.x,
pixelsHigh - rect.origin.y,
[str cStringUsingEncoding:[NSString defaultCStringEncoding]],
[str length]);
rect.origin.y += [[dic objectForKey:kHelpTextKeyFontSize] intValue];
}
For anyone facing this issue, it was solved by using the coretext foundation class.
What do the Word documents contain? What do you mean by "doesn't work?"
If they contain strings, couldn't you simply append them to the existing localizable.strings file? Since that works there is no encoding issue in this file, you could just copy/paste them from Word into the localizable.strings file in XCode.

Resources