Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python PIL decrease letter spacing

How can I decrease the letter spacing of this text? I want to make the text more squished together by a few pixels.

I'm trying to make a transparent image, with text on it, that I want pushed together. Like this, but transparent:

image

from PIL import Image, ImageDraw, ImageFont

(W, H) = (140, 40)

#create transparent image
image = Image.new("RGBA", (140, 40), (0,0,0,0))

#load font
font = ImageFont.truetype("Arial.ttf", 30)
draw = ImageDraw.Draw(image)

text = "kpy7n"
w,h = font.getsize(text)

draw.text(((W-w)/2,(H-h)/2), text, font=font, fill=0)

image.save("transparent-image.png")
like image 999
toast Avatar asked Mar 28 '18 09:03

toast


2 Answers

This function will automate all the pain for you. It was written to emulate Photoshop values and can render leading (the space between lines) as well as tracking (the space between characters).

def draw_text_psd_style(draw, xy, text, font, tracking=0, leading=None, **kwargs):
    """
    usage: draw_text_psd_style(draw, (0, 0), "Test", 
                tracking=-0.1, leading=32, fill="Blue")

    Leading is measured from the baseline of one line of text to the
    baseline of the line above it. Baseline is the invisible line on which most
    letters—that is, those without descenders—sit. The default auto-leading
    option sets the leading at 120% of the type size (for example, 12‑point
    leading for 10‑point type).

    Tracking is measured in 1/1000 em, a unit of measure that is relative to 
    the current type size. In a 6 point font, 1 em equals 6 points; 
    in a 10 point font, 1 em equals 10 points. Tracking
    is strictly proportional to the current type size.
    """
    def stutter_chunk(lst, size, overlap=0, default=None):
        for i in range(0, len(lst), size - overlap):
            r = list(lst[i:i + size])
            while len(r) < size:
                r.append(default)
            yield r
    x, y = xy
    font_size = font.size
    lines = text.splitlines()
    if leading is None:
        leading = font.size * 1.2
    for line in lines:
        for a, b in stutter_chunk(line, 2, 1, ' '):
            w = font.getlength(a + b) - font.getlength(b)
            # dprint("[debug] kwargs")
            print("[debug] kwargs:{}".format(kwargs))
                
            draw.text((x, y), a, font=font, **kwargs)
            x += w + (tracking / 1000) * font_size
        y += leading
        x = xy[0]

It takes a font and a draw object, which can be obtained via:

font = ImageFont.truetype("Arial.ttf", 30)
draw = ImageDraw.Draw(image)
like image 126
Orwellophile Avatar answered Oct 18 '22 18:10

Orwellophile


You have to draw the text character by character and then change the x coordinate when drawing the next

Example of code:

w,h = font.getsize("k")
draw.text(((W,H),"K", font=font, fill=0)
draw.text(((W+w)*0.7,H),"p", font=font, fill=0)
draw.text(((W+w*2)*0.7,H),"y", font=font, fill=0)
draw.text(((W+w*3)*1,H),"7", font=font, fill=0)
draw.text(((W+w*4)*0.8,H),"n", font=font, fill=0)
like image 35
Christopher Chu Avatar answered Oct 18 '22 17:10

Christopher Chu