Skip to content Skip to sidebar Skip to footer

How To Use My Own Bitmap Font In Pil.imagefont?

I created a bitmap font, basically a 256x256 png image where each character occupies 8x8 tile. I want to use it with Pillow as ImageFont but there's no info on this in Pillow docs.

Solution 1:

Not a complete answer, but too much for a comment, and it may be useful or spur someone else to work out the other 60% :-)

I may delete it if anyone else comes up with something better...

You can go to the Pillow repository on Github and download a ZIP file of the code.

If you go in there and nose around you will find two things that appear to work hand-in-hand, namely a .PIL file and a .PBM file.


In Tests/fonts there is a file called 10x20.pbm which is actually a PNG file if you look inside it. So, if you change its name to 10x20.png you can view it and it looks like this:

enter image description here

By the way, if you want to split that into 10x20 size chunks with one letter in each, you can use ImageMagick in Terminal like this:

convert 10x20.pbm -crop 10x20 char_%d.png

and you will get a bunch of files called char_0.png, char_1.png etc. The first 4 look like this:

enter image description here


If you look in src/PIL/FontFile.py there is this code that seems to know how to access/generate the metrics for a font:

## The Python Imaging Library# $Id$## base class for raster font file parsers## history:# 1997-06-05 fl   created# 1997-08-19 fl   restrict image width## Copyright (c) 1997-1998 by Secret Labs AB# Copyright (c) 1997-1998 by Fredrik Lundh## See the README file for information on usage and redistribution.#from __future__ import print_function

import os
from . import Image, _binary

WIDTH = 800defputi16(fp, values):
    # write network order (big-endian) 16-bit sequencefor v in values:
        if v < 0:
            v += 65536
        fp.write(_binary.o16be(v))


### Base class for raster font file handlers.classFontFile(object):

    bitmap = Nonedef__init__(self):

        self.info = {}
        self.glyph = [None] * 256def__getitem__(self, ix):
        return self.glyph[ix]

    defcompile(self):
        "Create metrics and bitmap"if self.bitmap:
            return# create bitmap large enough to hold all data
        h = w = maxwidth = 0
        lines = 1for glyph in self:
            if glyph:
                d, dst, src, im = glyph
                h = max(h, src[3] - src[1])
                w = w + (src[2] - src[0])
                if w > WIDTH:
                    lines += 1
                    w = (src[2] - src[0])
                maxwidth = max(maxwidth, w)

        xsize = maxwidth
        ysize = lines * h

        if xsize == 0and ysize == 0:
            return""

        self.ysize = h

        # paste glyphs into bitmap
        self.bitmap = Image.new("1", (xsize, ysize))
        self.metrics = [None] * 256
        x = y = 0for i inrange(256):
            glyph = self[i]
            if glyph:
                d, dst, src, im = glyph
                xx = src[2] - src[0]
                # yy = src[3] - src[1]
                x0, y0 = x, y
                x = x + xx
                if x > WIDTH:
                    x, y = 0, y + h
                    x0, y0 = x, y
                    x = xx
                s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0
                self.bitmap.paste(im.crop(src), s)
                self.metrics[i] = d, dst, s

    defsave(self, filename):
        "Save font"

        self.compile()

        # font data
        self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG")

        # font metricswithopen(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
            fp.write(b"PILfont\n")
            fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii'))  # HACK!!!
            fp.write(b"DATA\n")
            foridinrange(256):
                m = self.metrics[id]
                ifnot m:
                    puti16(fp, [0] * 10)
                else:
                    puti16(fp, m[0] + m[1] + m[2])

So hopefully someone has time/knowledge of how to put those two together to enable you to generate the metrics file for your PNG. I think you just need something that does the last 10 lines of that code for your PNG.

There appear to be 23 bytes of header which you can simply replicate, and then there are 256 "entries", i.e. 1 for each of 256 glyphs. Each entry has 10 numbers in it, and each number is 16-bit big endian.

Let's look at the header:

ddif=10x20.pilbs=23count=1|xxd-c23|more00000000:5049 4c666f6e740a3b3b3b3b3b3b3230 3b0a4441 5441 0aPILfont.;;;;;;20;.DATA.

Then you can see the entries using the command below to skip the header and group nicely:

ddif=10x20.pil bs=23 iseek=1| xxd -g2 -c20

which gives:

enter image description here

Column 1 appears to be the width of the glyph.

Column 7 is the x-offset of the left edge of the glyph in the image and column 9 is the x-offset of the right edge of the glyph in the image. So you will see that column 7 on each line is the same as column 9 on the previous line, i.e. that the glyphs abutt each other going across the image.

If you look at this extract from further down the file, you can see it starts a new row of glyphs in the output image in the middle of the extract (marked in red). That tells us that the bitmap should be no more than 800 pixels wide and that column 8 is the y-offset of the top of the glyph in the bitmap file and column 10 is the y-offset of the bottom of the glyph in the bitmap. You should see that when a new line row of glyphs starts in the bitmap file that x goes to zero and column 8 takes the previous value from column 10.

enter image description here

Post a Comment for "How To Use My Own Bitmap Font In Pil.imagefont?"