In case it might be useful to anyone, here is an almost complete Python implementation for Golly which I hacked up years ago. Unless there are other issues, the only thing missing is representing spaceships in the standard form according to the spec for direction traveled. It encodes the pattern in the current layer rather than converting rle and copies the sof representation of the pattern to the clipboard. If you want an rle converter it should be just a matter of inserting a few lines to place the pattern in a new layer.

`# Canonical small object format representation`

# Representation for CGoL objects devised by Heinrich Koenig

# Format description at http://conwaylife.com/wiki/SOF

# Further information at http://www.pentadecathlon.com/objects/definitions/definitions.php

# Determine the canonical small object format representation of the current

# pattern using the comparison rules. Special case rules for spaceships are

# not yet implemented.

#

# By Arie Paap

# Oct. 2014

import golly as g

# Obtains a canonical representation of the current pattern

# Uses algorithm by Adam P. Goucher

def sof_canonise(duration):

sof = ""

# We need to compare each phase to find the one used to determine the

# pattern's standard form:

for t in xrange(duration):

if (duration > 1):

g.run(1)

rect = g.getrect()

if (len(rect) == 0):

return "0"

ox = rect[0]

oy = rect[1]

x_length = rect[2]

y_breadth = rect[3]

# Choose the orientation which comes first according to sof comparison rules:

sof = sof_representation(x_length, y_breadth, ox, oy, 1, 0, 0, 1)

sof = sof_compare(sof, sof_representation(x_length, y_breadth, ox+x_length-1, oy, -1, 0, 0, 1)) # flip_x

sof = sof_compare(sof, sof_representation(x_length, y_breadth, ox, oy+y_breadth-1, 1, 0, 0, -1)) # flip_y

sof = sof_compare(sof, sof_representation(x_length, y_breadth, ox+x_length-1, oy+y_breadth-1, -1, 0, 0, -1)) # 180

sof = sof_compare(sof, sof_representation(y_breadth, x_length, ox, oy, 0, 1, 1, 0)) # swap_xy

sof = sof_compare(sof, sof_representation(y_breadth, x_length, ox+x_length-1, oy, 0, -1, 1, 0)) # cw 90

sof = sof_compare(sof, sof_representation(y_breadth, x_length, ox, oy+y_breadth-1, 0, 1, -1, 0)) # ccw 90

sof = sof_compare(sof, sof_representation(y_breadth, x_length, ox+x_length-1, oy+y_breadth-1, 0, -1, -1, 0)) # swap_xy_flip

return sof

# Determine sof for current pattern in specified orientation:

def sof_representation(length, breadth, ox, oy, a, b, c, d):

sof = ""

for v in xrange(breadth):

value = 1

run = 0

blank = True

if (v != 0):

sof += "-"

for u in xrange(length+1):

x = ox + a*u + b*v

y = oy + c*u + d*v

if (g.getcell(x,y) == value):

# Continue the run

run += 1

else:

# Encode the run, unless a run of zeros reaches the boundary

if (u < length or value == 1):

while (run > 78):

run -= 78

sof += "~0"

sof += chr(run + ord('0'))

run = 1

value = 1 - value # Toggle value

if (value==1):

blank = False

if (blank == True):

# Remove unnecessary '0'

sof = sof[:-2] + '-'

sof += "."

return sof

# Compares two sof patterns according to sof comparison rules.

def sof_compare(a, b):

alines = str.split(a, '-')

blines = str.split(b, '-')

aline = ''

bline = ''

# Find first line with different cell

for line in xrange(0, len(a)):

aline = alines[line]

bline = blines[line]

if not (aline == bline):

break

# Find first difference

value = 1

ii = 0

for ii in xrange(0, min(len(aline), len(bline))):

if not (aline[ii] == bline[ii]):

break

value = 1 - value

if (value == 1):

if int(aline[ii]) > int(bline[ii]):

return a

else:

return b

else:

if int(aline[ii]) > int(bline[ii]):

return b

else:

return a

g.warn("Did not compare")

return '#'

p = int(g.getstring("Enter period of pattern", "1"))

canonPatt = sof_canonise(p)

g.setclipstr(canonPatt)

g.show("SOF representation of pattern is: " + canonPatt + " (copied to clipboard).")