How to Convert Text to Image on iOS

How to convert text to Image on iOS?

Several approaches are possible.

  1. If you have an existing UITextField, UITextView or UILabel that you just want to render as an image, you can employ the traditional snapshot approaches, such as:

    - (UIImage *)imageForView:(UIView *)view
    {
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0);

    if ([view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)])
    [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES]; // if we have efficient iOS 7 method, use it ...
    else
    [view.layer renderInContext:UIGraphicsGetCurrentContext()]; // ... otherwise, fall back to tried and true methods

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
    }
  2. If you wanted a generic "create image from text" routine, in iOS 7, it would look like:

    - (UIImage *)imageFromString:(NSString *)string attributes:(NSDictionary *)attributes size:(CGSize)size
    {
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    [string drawInRect:CGRectMake(0, 0, size.width, size.height) withAttributes:attributes];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
    }

    The above will create an image whose size will vary based upon the text. Clearly, if you just want a fixed size image, then use constants frame, rather than dynamically building it.

    Anyway, you could then use the above like so:

    NSString *string = @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";

    NSDictionary *attributes = @{NSFontAttributeName : [UIFont systemFontOfSize:20],
    NSForegroundColorAttributeName : [UIColor blueColor],
    NSBackgroundColorAttributeName : [UIColor clearColor]};

    UIImage *image = [self imageFromString:string attributes:attributes size:self.imageView.bounds.size];
  3. If you need to support earlier iOS versions, you could use this technique:

    - (UIImage *)imageFromString:(NSString *)string font:(UIFont *)font size:(CGSize)size
    {
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    [string drawInRect:CGRectMake(0, 0, size.width, size.height) withFont:font lineBreakMode: NSLineBreakByWordWrapping];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
    }

There are many, many permutations of each of these. It just depends upon what you are trying to achieve.


Another approach is to simply have both UIImageView and UILabel/UITextView objects in the view, and if you have an image from the server, set the image of the UIImageView, and text, set the text of the UILabel/UITextView.

How can I convert text (String) to image (UIImage) in Swift?

There are a few issues with your code. First don't use NSString, Swift native string type is String. Second you need to specify your textFontAttributes type as [NSAttributedStringKey: Any] and don't force unwrap the result. Change the return type to optional image UIImage? You can also use defer to end graphics image context when your method finishes.

func textToImage(drawText text: String, inImage image: UIImage, atPoint point: CGPoint) -> UIImage? {
let textColor: UIColor = .red
let textFont = UIFont(name: "Arial Rounded MT Bold", size: 24)!
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(image.size, false, scale)
defer { UIGraphicsEndImageContext() }
let textFontAttributes: [NSAttributedStringKey: Any] = [.font: textFont, .foregroundColor: textColor]
image.draw(in: CGRect(origin: .zero, size: image.size))
let rect = CGRect(origin: point, size: image.size)
text.draw(in: rect, withAttributes: textFontAttributes)
return UIGraphicsGetImageFromCurrentImageContext()
}

How to generate an UIImage from custom text in Swift

You can use this function, you can send any text to this function, inside it i create UILabel and set text attribute as you like

func imageWith(name: String?) -> UIImage? {
let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
let nameLabel = UILabel(frame: frame)
nameLabel.textAlignment = .center
nameLabel.backgroundColor = .lightGray
nameLabel.textColor = .white
nameLabel.font = UIFont.boldSystemFont(ofSize: 40)
nameLabel.text = name
UIGraphicsBeginImageContext(frame.size)
if let currentContext = UIGraphicsGetCurrentContext() {
nameLabel.layer.render(in: currentContext)
let nameImage = UIGraphicsGetImageFromCurrentImageContext()
return nameImage
}
return nil
}

How to convert Text to Image in Cocoa Objective-C

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!

Image conversion from string data type to Image

If you look at the method signature for getImageData:, you'll see that it returns Data?. Conversely, your imagemovie property is expecting a UIImage?. See where the issue is?

func getImageData(by row: Int) -> Data? {

var imagemovie :UIImage?

Instead of assigning a value of type Data? to your imagemovie property, you first must convert the Data? to a UIImage? like so:

dc.imagemovie = UIImage(data: presenter.getImageData(by: row)!)

How to Convert Text With Attributes Directly Into a CIImage (Without Drawing to Screen)

Here's how to draw a NSAttributedString onto a NSImage. I didn't test the conversion to CIImage though (the last line), but it shouldn't be too difficult:

let string = NSAttributedString(string: "Hello World!", attributes: [NSFontAttributeName: NSFont.labelFontOfSize(10)])
let image = NSImage(size: string.size())
image.lockFocus()
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.currentContext()!.shouldAntialias = true
string.drawAtPoint(NSZeroPoint)
NSGraphicsContext.restoreGraphicsState()
image.unlockFocus()

let ciimage = CIImage(data: image.TIFFRepresentation!)!

How to convert Text into Emoji?

I would suggest you are using a simple bitmap technique. In a first step you build a black and white bitmap with the written text in the same dimensions as you like to have the final image.

Sample Image

In a second step you "divide" this image into a raster which is e.g. 20% smaller than the final emoji character to create the overlapping effect.

Sample Image

In each raster rectangle you calculate the black pixels. There you get a percentage between 0-100% black pixels. If this percentage e.g is over 40%, a random emoji is placed at the center of this rectangle.

You can improve the effect by adding a small amount of random displacement.

Final Thoughts

I implemented this and it worked great. I could improve the image even further by some small optimization:

  • For each rectangle with >40% black, I divide this rectangle into four zones (top left, top right, bottom left, bottom right).
  • If one of this zones has a black pixel in it, I count the zone as 1 otherwise as 0.
  • This creates 16 different patterns. For a few of these patterns I move displace the emoji even more from the center of the rectangle.

For example if I get 1000, I move it to the top left. If i get 1010 to the left.

Example Code

The following example code demonstrates the shown approach, without the tweaks in the "final thought" section. It is written in Swift for a Cocoa application. You see the application delegate of a simple application which provides a text field and an image view in the main window.

You can complete the application adding the text field and image view to the main window and bind the values to the outlets textField and imageView. Also you have to bind the text field value to the parameterText property.

The output of the application looks like this:

Sample Image

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

@IBOutlet weak var window: NSWindow!
@IBOutlet weak var textField: NSTextField!
@IBOutlet weak var imageView: NSImageView!

var updateTimer: NSTimer = NSTimer()

let canvasSize = CGSize(width: 1024, height: 768)
let canvasPadding: CGFloat = 32.0
let emojiSize = 20.0
let emojiSet: [NSString] = [",",","]
let emojiJitter = 4.0

var parameterText: NSString = "Hello!" {
didSet {
triggerImageUpdate()
}
}

func applicationDidFinishLaunching(aNotification: NSNotification) {
triggerImageUpdate()
}

func applicationWillTerminate(aNotification: NSNotification) {
}

func triggerImageUpdate() {
updateTimer.invalidate()
updateTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("updateImage"), userInfo: nil, repeats: false);
}

func updateImage() {
imageView.image = createEmojiImage(parameterText, size: canvasSize, padding: canvasPadding)
}

// This function creates a simple bitmap with the given text. The text
// is centered in the bitmap and scaled to the maximum size.
func createTextImage(text: NSString, size: CGSize, padding: CGFloat) -> NSImage {
let canvasRect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
let textRect = CGRectInset(canvasRect, padding, padding)
var textBitmap = NSImage(size: size)
textBitmap.lockFocus()
NSColor.whiteColor().setFill()
NSRectFill(canvasRect)
let textFont = NSFont(name: "Helvetica Bold", size: 100)
var textAttributes: [NSObject: AnyObject] = [
NSFontAttributeName: textFont!,
NSForegroundColorAttributeName: NSColor.blackColor()]
let textSize = text.sizeWithAttributes(textAttributes);
let scaleWidth = textRect.size.width/textSize.width
let scaleHeight = textRect.size.height/textSize.height
let scale: CGFloat
if (scaleWidth < scaleHeight) {
scale = scaleWidth
} else {
scale = scaleHeight
}
let scaledCanvasSize = CGSize(width: size.width/scale, height: size.height/scale)
let offset = CGPoint(x: (scaledCanvasSize.width - textSize.width)/2.0,
y: (scaledCanvasSize.height - textSize.height)/2.0)
CGContextScaleCTM(NSGraphicsContext.currentContext()!.CGContext, scale, scale)
text.drawAtPoint(offset, withAttributes: textAttributes)
textBitmap.unlockFocus()
return textBitmap
}

func createTextBitmap(text: NSString, size: CGSize, padding: CGFloat) -> NSBitmapImageRep {
let textImage = createTextImage(text, size: size, padding: padding)
let tiffImageData = textImage.TIFFRepresentation
return NSBitmapImageRep(data: tiffImageData!)!
}

// This is a class which represents a single field.
class Field {
let center: CGPoint
let black: Double
init(center: CGPoint, black: Double) {
self.center = center
self.black = black
}
}

// A function to get the black value from a certain rectangle in an image.
func blackValue(image: NSBitmapImageRep, rect: CGRect) -> Double {
let pixelInRect = Int(rect.size.width * rect.size.height)
var blackCount = 0;
for (var x = 0; x < Int(rect.size.width); ++x) {
for (var y = 0; y < Int(rect.size.height); ++y) {
var color = image.colorAtX(Int(rect.origin.x), y: Int(rect.origin.y))!
if (color.redComponent < 0.1) {
blackCount++
}
}
}
return Double(blackCount) / Double(pixelInRect)
}

// A function to rasterize the bitmap into single fields.
func rasterizeBitmap(image: NSBitmapImageRep, size: CGFloat) -> (width: Int, fields: [Field]) {
let width = Int(image.size.width/size)
let height = Int(image.size.height/size)
var fields = [Field]()
for (var x = 0; x < width; ++x) {
for (var y = 0; y < height; ++y) {
let rect = CGRect(x: CGFloat(x) * size, y: CGFloat(y) * size, width: size, height: size)
let center = CGPoint(x: floor(CGFloat(x) * size + size/2.0), y: image.size.height - floor(CGFloat(y) * size + size/2.0))
let black = blackValue(image, rect: rect)
var field = Field(center: center, black: black)
fields.append(field)
}
}
return (width, fields)
}

// Function to create the emoji image.
func createEmojiImage(text: NSString, size: CGSize, padding: CGFloat) -> NSImage {
// Create and rasterize the bitmap.
let textBitmap = self.createTextBitmap(text, size: size, padding: padding)
let (fieldsWidth, fields) = self.rasterizeBitmap(textBitmap, size: CGFloat(emojiSize*0.75))
// Create a new image
var result = NSImage(size: size)
result.lockFocus()
NSColor.whiteColor().setFill()
let canvasRect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
NSRectFill(canvasRect)
let textFont = NSFont(name: "Apple Color Emoji", size: CGFloat(self.emojiSize))
var textAttributes: [NSObject: AnyObject] = [NSFontAttributeName: textFont!]
NSColor.blackColor().setFill()
for field in fields {
let jitterX = CGFloat(Double(arc4random_uniform(101))/100.0*self.emojiJitter-self.emojiJitter/2.0)
let jitterY = CGFloat(Double(arc4random_uniform(101))/100.0*self.emojiJitter-self.emojiJitter/2.0)
if field.black > 0.4 {
var randomEmoji = self.emojiSet[Int(arc4random_uniform(UInt32(self.emojiSet.count)))]
var drawingPoint = CGPoint(x: field.center.x - CGFloat(self.emojiSize/2.0) + jitterX, y: field.center.y - CGFloat(self.emojiSize/2.0) + jitterY)
randomEmoji.drawAtPoint(drawingPoint, withAttributes: textAttributes)
}
}
result.unlockFocus()
return result
}
}


Related Topics



Leave a reply



Submit