How do I reduce the number of colors used in ImageDraw.text

Viewed 117

I have a folder with .ttf and .otf fonts and would like to write them on my ImageDraw object but with NO shading. A single RGB only. I have tried bitmap fonts but they A) don't look nice and B) use more than one color anyway.

I have read that there is a library for converting .bdf to .pil. If I convert arial.ttf to arial.bdf and then to arial.pil, will this be what I'm looking for? The text will almost always be dropped onto a background--so should I consider writing the text first on a blank canvas, do a color reduction, and then paste that canvas onto my background?

I have previously made this program using Java and it writes text very nicely on my bitmaps. One color, symmetrical, etc. Image below.

Java font rendering

Below are the two attempts with python. The blockier one is a bitmap font, the other is regular arial.ttf.

Python Arial Bitmap

Python Arial

Here is my code:

def personalize(self):

    names = self.personalize_entry.get("1.0", 'end-1c').split('\n')
    num_names = len(names)
    num_grids = math.ceil(num_names/20)
    answer = ask_grid_background()
    separator = Image.new('RGB', (473, 1), color_dict['P'])
    background = Image.new('RGB', (473, 821), color_dict['.'])

    if answer:
        showinfo("Bitmap", "Give me the design.")
        file_path = filedialog.askopenfilename()
        filename = path_leaf(file_path)
        filename = filename[:-4]
        __, __, center = read(file_path)
        if center == 0:
            messagebox.showinfo("Hmmm", f"I couldn't find a center...are you sure this is a basic set up?")
            return False
        img = Image.open(file_path)
        size_num = img.size
        section = img.crop((5, (size_num[1] - 55 - center), 478, (size_num[1] - center - 15)))
        background.paste(separator, (0, 0))
        for i in range(20):
            background.paste(section, (0, (41 * i + 1)))
            background.paste(separator, (0, (41 * i) + 41))
    else:
        background.paste(separator, (0, 0))
        for i in range(20):
            # background.paste(section,(0,(41*i+1)))
            background.paste(separator, (0, (41 * i) + 41))

    draw = ImageDraw.Draw(background)
    fnt = ImageFont.truetype("Fonts/PIXEAB__.ttf",36)
    draw.text((10, 10), names[0], font=fnt, fill=(0, 0, 0))
    background.show()
1 Answers

ImageDraw has an undocumented member fontmode, which can be set to '1' (cf. Pillow's image modes) to turn off the anti-aliasing of the rendered text.

Let's compare common rendered text, draw.fontmode is implicitly set to 'L':

from PIL import Image, ImageDraw, ImageFont

image = Image.new('RGB', (800, 200), (255, 255, 255))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('arial.ttf', 150)
draw.text((10, 10), 'Hello World', font=font, fill=(0, 0, 0))
image.save('image.png')

With anti-aliasing

Now, let's explicitly set draw.fontmode = '1':

from PIL import Image, ImageDraw, ImageFont

image = Image.new('RGB', (800, 200), (255, 255, 255))
draw = ImageDraw.Draw(image)
draw.fontmode = '1'
font = ImageFont.truetype('arial.ttf', 150)
draw.text((10, 10), 'Hello World', font=font, fill=(0, 0, 0))
image.save('image.png')

Without anti-aliasing

Et voilà – no anti-aliasing, all pixels are solid black.

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.16299-SP0
Python:        3.9.1
PyCharm:       2021.1
Pillow:        8.2.0
----------------------------------------
Related