Error drawing text on NSImage in PyObjC
Question: I'm trying to overlay an image with some text using PyObjC, while striving to
answer my question, ["Annotate images using tools built into OS
X"](http://stackoverflow.com/questions/1229171/annotate-images-using-tools-
built-into-os-x). By referencing [CocoaMagic](http://www.rubycocoa.com/cocoa-
magic-for-gruff-graphs/2/), a RubyObjC replacement for
[RMagick](http://rmagick.rubyforge.org/), I've come up with this:

    
    
    #!/usr/bin/env python
    
    from AppKit import *
    
    source_image = "/Library/Desktop Pictures/Nature/Aurora.jpg"
    final_image = "/Library/Desktop Pictures/.loginwindow.jpg"
    font_name = "Arial"
    font_size = 76
    message = "My Message Here"
    
    app = NSApplication.sharedApplication()  # remove some warnings
    
    # read in an image
    image = NSImage.alloc().initWithContentsOfFile_(source_image)
    image.lockFocus()
    
    # prepare some text attributes
    text_attributes = NSMutableDictionary.alloc().init()
    font = NSFont.fontWithName_size_(font_name, font_size)
    text_attributes.setObject_forKey_(font, NSFontAttributeName)
    text_attributes.setObject_forKey_(NSColor.blackColor, NSForegroundColorAttributeName)
    
    # output our message
    message_string = NSString.stringWithString_(message)
    size = message_string.sizeWithAttributes_(text_attributes)
    point = NSMakePoint(400, 400)
    message_string.drawAtPoint_withAttributes_(point, text_attributes)
    
    # write the file
    image.unlockFocus()
    bits = NSBitmapImageRep.alloc().initWithData_(image.TIFFRepresentation)
    data = bits.representationUsingType_properties_(NSJPGFileType, nil)
    data.writeToFile_atomically_(final_image, false)
    

When I run it, I get this:

    
    
    Traceback (most recent call last):
      File "/Users/clinton/Work/Problems/TellAtAGlance/ObviouslyTouched.py", line 24, in <module>
        message_string.drawAtPoint_withAttributes_(point, text_attributes)
    ValueError: NSInvalidArgumentException - Class OC_PythonObject: no such selector: set
    

Looking in the docs for drawAtPoint:withAttributes:, it says, "You should only
invoke this method when an NSView has focus." NSImage is not a subclass of
NSView, but I would hope this would work, and something very similar seems to
work in the Ruby example.

What do I need to change to make this work?

* * *

I rewrote the code, converting it faithfully, line for line, into an
Objective-C Foundation tool. It works, without problems. [I would be happy to
post if here if there is a reason to do so.]

The question then becomes, how does:

    
    
    [message_string drawAtPoint:point withAttributes:text_attributes];
    

differ from

    
    
    message_string.drawAtPoint_withAttributes_(point, text_attributes)
    

? Is there a way to tell which "OC_PythonObject" is raising the
NSInvalidArgumentException?


Answer: Here are the problems in the above code:

    
    
    text_attributes.setObject_forKey_(NSColor.blackColor, NSForegroundColorAttributeName)
    ->
    text_attributes.setObject_forKey_(NSColor.blackColor(), NSForegroundColorAttributeName)
    
    bits = NSBitmapImageRep.alloc().initWithData_(image.TIFFRepresentation)
    data = bits.representationUsingType_properties_(NSJPGFileType, nil)
    ->
    bits = NSBitmapImageRep.imageRepWithData_(image.TIFFRepresentation())
    data = bits.representationUsingType_properties_(NSJPEGFileType, None)
    

Minor typos indeed.

Note that the middle portion of the code can be replaced with this more-
readable variant:

    
    
    # prepare some text attributes
    text_attributes = { 
        NSFontAttributeName : NSFont.fontWithName_size_(font_name, font_size),
        NSForegroundColorAttributeName : NSColor.blackColor() 
    }
    
    # output our message 
    NSString.drawAtPoint_withAttributes_(message, (400, 400), text_attributes)
    

I learned this by looking at the source code to [NodeBox](http://nodebox.net),
the twelve lines of
[psyphography.py](http://dev.nodebox.net/browser/nodebox/trunk/play/psyphography.py)
and
[cocoa.py](http://dev.nodebox.net/browser/nodebox/trunk/src/nodebox/graphics/cocoa.py),
particularly the save and _getImageData methods.

