Creating Graphics Files Programatically

Introduction

Recently I wanted to create an image with a number of makers on it. These markers were red circles with a number in the middle. Like this:

This is a pretty simple graphic to make in PhotoShop or whatever else, but it would be tedious to make it 24 times. Even if writing an application to do it for me would take the same amount of time as using a graphics application, writing code is much more fun and reusable.

This is a really simple application, so simple that I almost didn't want to write this article. But there are two interesting things here that you might not know, so perhaps that will redeem me.

The Simple Stuff

I'm going to gloss over the simple stuff, which is the actual drawing. Use the NSBezierPath object and its helpers to draw the circle, and NSAttributedString to draw the text. Like this:
#define DIMENSIONS 20
#define STRING_ATTR [NSDictionary dictionaryWithObjectsAndKeys: [NSColor whiteColor], NSForegroundColorAttributeName, nil]

	// draw the circle
[[NSColor redColor] set];
NSBezierPath* path = [NSBezierPath bezierPathWithOvalInRect:NSMakeRect(0,0,DIMENSIONS, DIMENSIONS)];
[path fill];

	// create the string
NSAttributedString* stringToDraw = [[NSAttributedString alloc] initWithString:marker attributes:STRING_ATTR];

	// figure out how big the string is going to be so we can center it
NSSize stringSize = [stringToDraw size];

	// my experience shows that the size is reported as slightly taller than it really is going to be.
stringSize.height -= 1;

	// figure out where to draw the string.  Centered in the circle.  
NSPoint destPoint = NSMakePoint((DIMENSIONS - stringSize.width) / 2, ((DIMENSIONS - stringSize.height) / 2));

	// draw the string
[stringToDraw drawAtPoint:destPoint];
So there you can see the actual drawing commands to draw a circle, and then draw our text centered in it.

Where

So that code draws circles and text to nowhere. We need a destination to draw our circles and text into. So we can create an NSImage, and lock focus on that. Then, all drawing gets rendered into the NSImage until we unlock focus. This is like an offscreen graphics port.

	// create a new image to draw into
NSImage* destImage = [[NSImage alloc] initWithSize:NSMakeSize(DIMENSIONS, DIMENSIONS)];

	// direct all drawing into this new image
[destImage lockFocus];

	/* ... insert drawing code here ... */
	
	// finished drawing into the image.  Unlock focus.
[destImage unlockFocus];

Turn it into a TIFF

Plug in the filename of your choice.
	// create a TIFF file
NSData* tiffData = [destImage TIFFRepresentation];
[tiffData writeToFile:[NSString stringWithFormat:@"/tmp/%@.tiff", marker] atomically:NO];

Preventing An Exception

The application I wrote was a simple command-line tool. As soon as I tried to create the NSImage, an NSInternalInconsistencyException exception would get thrown:

*** Uncaught exception: <NSInternalInconsistencyException> Error (1002) creating CGSWindow

When you use NSApplication, some Cocoa things are initialized. Since I was not using NSApplication and just doing this drawing from main(), bad things would happen. I need to initialize some Cocoa stuff myself. Fortunately, this is just one line of code:

NSApplicationLoad();
So there you have it. I did all the work, and now you can read free code. Lucky you. Consider sending me money.

Extreme Mac OS X Programming. Copyright 2005 John A. Vink