The Cybook Gen 3 uses it’s own image file for thumbnails. The t2b file is generated by the device based upon the image found in the ebook. If there is no image a default one with the file name written across it is created.

One of the feature request for Cybook support in Calibre is for the t2b thumbnail files to be generated on the computer and moved to the device. This is much faster than having the Cybook generate the thumbnail itself.

The t2b file used by the Cybook is a 2-bit image. Meaning 2 bits represent 1 pixel. 2 bits can have a total of four combinations giving the image a total of four colours. There is no header or footer. The bits representing 0, 1, 2, 3 are written directly to the file. The image’s dimensions are 96x144.

Every t2b file will have 13,824 pixels. The file size will always be 3,456 bytes. The formula to determine this is: (height x width x 2 bits per pixel) / 8 bits per byte. (96 * 144 * 2) / 8 = 3,456.

Following are two python scripts for converting an image to a t2b file and for converting a t2b file into a pgm image.

image2t2b.py

#!/usr/bin/env python

import sys, Image

def reduce_color(c):
    if c <= 64:
        return 0
    elif c > 64 and c <= 128:
        return 1
    elif c > 128 and c <= 192:
        return 2
    else:
        return 3

def i2b(n):
    return "".join([str((n >> y) & 1) for y in range(1, -1, -1)])

def main():
    if len(sys.argv) != 3:
        raise Exception('Must have 2 arguments. %s input.image output.t2b' % sys.argv[0])

    outf = open(sys.argv[2], 'wb')

    im = Image.open(sys.argv[1]).convert("L")
    im.thumbnail((96, 144))

    newim = Image.new('L', (96, 144), 'white')

    x,y = im.size
    newim.paste(im, ((96-x)/2, (144-y)/2))

    px = []
    pxs = newim.getdata()
    for i in range(len(pxs)):
        px.append(pxs[i])
        if len(px) >= 4:
            binstr = i2b(reduce_color(px[0])) + i2b(reduce_color(px[1])) + i2b(reduce_color(px[2])) + i2b(reduce_color(px[3]))
            outf.write(chr(int(binstr, 2)))
            px = []

    outf.close()

if __name__ == '__main__':
    main()

t2b2pgm.py

#!/usr/bin/env python

import sys, os

def get_greys(b):
    b.zfill(8)
    b = "".join([str((ord(b) >> y) & 1) for y in range(7, -1, -1)])

    w = str(int(b[0:2],2))
    x = str(int(b[2:4],2))
    y = str(int(b[4:6],2))
    z = str(int(b[6:8],2))

    return [w, x, y, z]

def main():
    if len(sys.argv) != 3:
        raise Exception('Must have 2 arguments. %s input.t2b output.pgm' % sys.argv[0])

    t2bfile = open(sys.argv[1], 'rb')
    pgmfile = open(sys.argv[2], 'w')

    pgmfile.write('P2n96 144n3n')

    for i in range(144):
        for j in range(24):
            b = t2bfile.read(1)

            if b != '':
                vals = get_greys(b)
                pgmfile.write('%s %s %s %s ' % (vals[0], vals[1], vals[2], vals[3]))
        pgmfile.write('n')

    pgmfile.close()
    t2bfile.close()

if __name__ == '__main__':
    main()