I'm looking for a way to convert a piece of text to an image in Cocoa. Everything seems to describe converting an image to text not text to an image.
Simply put, I would like to take a word (such as "Kevin") and convert it into a bitmap image to manipulate and save as a JPEG.
The people giving answers are awesome. Thank you for three different and equally valid ways to do it(yes i've tested them)....Very cool I wish I could give you all correct answers.
EDIT: I misread the question and assumed you wanted Cocoa-touch code (I've left it at the end in case you did). Here is one way of doing it in Cocoa using CoreText (as another poster says there are a bunch of ways):
{
NSString* string = @"Kevin";
CGFloat fontSize = 12.0f;
// Create an attributed string with string and font information
CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica Light"), fontSize, nil);
NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(id)font, kCTFontAttributeName,
nil];
NSAttributedString* as = [[NSAttributedString alloc] initWithString:string attributes:attributes];
CFRelease(font);
// Figure out how big an image we need
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)as);
CGFloat ascent, descent, leading;
double fWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
// On iOS 4.0 and Mac OS X v10.6 you can pass null for data
size_t width = (size_t)ceilf(fWidth);
size_t height = (size_t)ceilf(ascent + descent);
void* data = malloc(width*height*4);
// Create the context and fill it with white background
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
CGContextRef ctx = CGBitmapContextCreate(data, width, height, 8, width*4, space, bitmapInfo);
CGColorSpaceRelease(space);
CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0); // white background
CGContextFillRect(ctx, CGRectMake(0.0, 0.0, width, height));
// Draw the text
CGFloat x = 0.0;
CGFloat y = descent;
CGContextSetTextPosition(ctx, x, y);
CTLineDraw(line, ctx);
CFRelease(line);
// Save as JPEG
CGImageRef imageRef = CGBitmapContextCreateImage(ctx);
NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithCGImage:imageRef];
NSAssert(imageRep, @"imageRep must not be nil");
NSData* imageData = [imageRep representationUsingType:NSJPEGFileType properties:nil];
NSString* fileName = [NSString stringWithFormat:@"Kevin.jpg"];
NSString* fileDirectory = NSHomeDirectory();
NSString* filePath = [fileDirectory stringByAppendingPathComponent:fileName];
[imageData writeToFile:filePath atomically:YES];
// Clean up
[imageRep release];
CGImageRelease(imageRef);
free(data);
}
This is the cocoa-touch version:
// Figure out the dimensions of the string in a given font.
NSString* kevin = @"Kevin";
UIFont* font = [UIFont systemFontOfSize:12.0f];
CGSize size = [kevin sizeWithFont:font];
// Create a bitmap context into which the text will be rendered.
UIGraphicsBeginImageContext(size);
// Render the text
[kevin drawAtPoint:CGPointMake(0.0, 0.0) withFont:font];
// Retrieve the image
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
// Convert to JPEG
NSData* data = UIImageJPEGRepresentation(image, 1.0);
// Figure out a safe path
NSArray *arrayPaths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *docDir = [arrayPaths objectAtIndex:0];
// Write the file
NSString *filePath = [docDir stringByAppendingPathComponent:@"Kevin.jpg"];
BOOL success = [data writeToFile:filePath atomically:YES];
if(!success)
{
NSLog(@"Failed to write to file. Perhaps it already exists?");
}
else
{
NSLog(@"JPEG file successfully written to %@", filePath);
}
// Clean up
UIGraphicsEndImageContext();
When I made started iOS programming I found the following things unintuitive or unusual. The methods to measure and draw strings are methods on NSString
(not the graphics context as in other systems). The method to save the data is a method on NSData
not a file class! The functions to create the graphics context are plain C functions and not part of any class.
Hope this helps!
There are, (fortunately or unfortunately), a number of different ways to do this.
Version 1: AppKit/Foundation only
NSString *text = ...;
NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys:
[NSFont fontWithName:@"Helvetica" size:24], NSFontAttributeName,
nil];
NSImage *img = [[NSImage alloc] initWithSize:NSMakeSize(250, 250)];
[img lockFocus];
[text drawAtPoint:NSMakePoint(10, 10) withAttributes:attr];
[img unlockFocus];
// when you want to write it to a JPEG
NSData *dat = [NSBitmapImageRep
representationOfImageRepsInArray:[img representations]
usingType:NSJPEGFileType
properties:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:0.9], NSImageCompressionFactor,
nil]];
Then you can write dat
to a file as you see fit.
Version 2:
The same can be accomplished using CGContextRef
(creating a bitmap context), and the equivalent Quartz APIs. This obviates the need for Objective C, but the code will be a little longer as a result. You can also use various mixtures of the Quartz (CGxxx
) and AppKit (NSxxx
) APIs, but the Quartz APIs are generally more cumbersome to use (because of their flexibility wrt. allocation and other concerns).
Version 3:
You can also use Quartz + Core Text, which is OS X 10.5+. This allows you a lot of flexibility in terms of exactly how your text is laid out, and also provides a relatively easy way to measure how big the text is before you draw it to a bitmap (so you can make the bitmap large enough).
Footnote: Things like skew are easy enough to apply before you draw the text. The text can be drawn with skew (see NSAffineTransform
and the Cocoa drawing guide).
I believe the function you want is CGContextShowTextAtPoint()
.
Example usage:
NSString *input = /* ... */;
CGContextRef context = /* create a graphics context */;
// make sure you have set up the font
CGContextShowTextAtPoint(context, 5, 5, [input UTF8String], [input length]);
Here's a minimal command line tool that does what you described. Pass it the path where you want save the result, e.g:
"./test foo.tiff
"
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[])
{
@autoreleasepool {
NSString *string = @"Hello, World!";
NSString *path = [[[NSProcessInfo processInfo] arguments] objectAtIndex:1];
NSDictionary *attributes =
@{ NSFontAttributeName : [NSFont fontWithName:@"Helvetica" size:40.0],
NSForegroundColorAttributeName : NSColor.blackColor};
NSImage *image = [[NSImage alloc] initWithSize:[string sizeWithAttributes:attributes]];
[image lockFocus];
[string drawAtPoint:NSZeroPoint withAttributes:attributes];
[image unlockFocus];
[[image TIFFRepresentation] writeToFile:path atomically:YES];
}
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With