2006-07-10

I wanted to convert a directory of Windows bitmaps to PNG images today. I must be missing something in Graphic Converter. (I can get to the batch window, but how do I make it convert?) After a few minutes, I decided just to write something for the command line. Apple's image source/destination abstractions make it easy. My program is less than forty lines:

/* Compile with:
	gcc -o imageToPNG imageToPNG.m -framework Foundation -framework Carbon
*/
#import <Foundation/Foundation.h>
#import <stdio.h>

int main( int argc, char *argv[] ) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	int i;
	for (i = 1; i < argc; i++) {
		NSString *spath = [NSString stringWithUTF8String: argv[i]];
		CGImageSourceRef sref = CGImageSourceCreateWithURL( (CFURLRef) [NSURL fileURLWithPath: spath], NULL );
		if (sref == NULL) {
			fprintf( stderr, "Could not create image source for file %s\n", argv[i] );
			continue;
		}
		NSString *dpath = [[[spath stringByExpandingTildeInPath]
			stringByDeletingPathExtension]
			stringByAppendingPathExtension: @"png"];

		CGImageDestinationRef dref = CGImageDestinationCreateWithURL(
			(CFURLRef)[NSURL fileURLWithPath: dpath],
			(CFStringRef) @"public.png",
			1, NULL );
		if (dref == NULL) {
			fprintf( stderr, "Could not create image destination for file %s\n", argv[i] );
		} else {
			CGImageDestinationAddImageFromSource( dref, sref, 0, NULL );
			if (!CGImageDestinationFinalize(dref)) {
				fprintf( stderr, "Error converting %s to %s\n", argv[i], [dpath UTF8String] );
			}
			CFRelease(dref);
		}
		CFRelease(sref);
	}
	[pool release];
	return 0;
}

2007-01-29

The Cocoa equivalent of the above program is:

/* Compile with:
	gcc -o imageToPNG2 imageToPNG2.m -framework AppKit
*/
#import <Cocoa/Cocoa.h>

int main( int argc, char *argv[] ) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSApplicationLoad();	// initializes state before calling AppKit functions from non-AppKit code
	int i;
	for (i = 1; i < argc; i++) {
		NSString *spath = [[NSString stringWithUTF8String: argv[i]] stringByExpandingTildeInPath];
		NSImage *image = [[NSImage alloc] initByReferencingFile: spath];

		NSSize size = [image size];
		[image lockFocus];
		NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:
			NSMakeRect(0,0,size.width,size.height)];
		[image unlockFocus];
		NSData *data = [rep representationUsingType: NSPNGFileType properties: nil];

		NSString *dpath = [[spath stringByDeletingPathExtension] stringByAppendingPathExtension: @"png"];
		[data writeToFile: dpath atomically: NO];

		[rep release];
		[image release];
		printf( "Completed %s -> %s\n", argv[i], [dpath UTF8String]);
	}
	[pool release];
	return 0;
}

You can use the program above to convert handwritten Postscript to a PNG file.

If you feed it a document with a bounding box, the -[initWithFocusedViewRect:] method will use that box as the pixel dimensions of the output. Save the following text as an EPS file, and imageToPNG2 will convert it to a 250x400 PNG:

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 0 250 400
/Optima findfont 18 scalefont setfont
0 1 0 setrgbcolor
50 350 [ (Hello) (world) (from) (Houston,) (Texas!) ]
{ % stack: x y string
  3 copy pop moveto show
  % stack: x y
  20 sub exch 12 add exch
  % stack: x+12 y-20
} forall
pop pop

If I do not specify a bounding box, my system defaults to 692x792 pixels, the point size of an 8.5 inch x 11 inch sheet of paper. I wonder if it defaults to A4 in Europe? Also, be careful of your backgrounds. PostScript assumes the background is white while most bitmap viewers display their contents over a black background. If it matters, explicitly fill your viewable rectangle with your desired background before doing any other output.