I'm trying to write prices on an Image using two different fonts on the same line, one for the numbers, and one for the currency symbol (in this case, €), so I am wondering what is the proper way to do it using PIL. What I'm currently trying is to use font.getsize()
on the first font, and add the X axis result of the getsize to the coordinates of the text in order to get the offset needed to write the symbol:
d_price = "248"
d_price_font = ImageFont.truetype("Gotham Black Regular.ttf", 58)
symbol_font = ImageFont.truetype("Myriad Pro Regular.ttf", 70)
d_price_size = d_price_font.getsize(d_price)
d_price_coords = (677, 519)
draw.text(d_price_coords, d_price, (0, 255, 255), font=d_price_font)
symbol_coords = (d_price_coords[0] d_price_size[0], d_price_coords[1])
draw.text(symbol_coords, "€", (255, 255, 255), font=symbol_font)
This seem to work, however this would require me to calculate the size of the text everytime I want to write a symbol next to a price, and also adjust the font size, which is different to the one I'm using to the prices.
CodePudding user response:
I think your approach is OK, but there's a nicer way to write the code that will avoid the repetition and make it easier to add more text in varied font:
from PIL import ImageFont, Image, ImageDraw
def draw_text_in_font(d: ImageDraw, coords: tuple[int, int],
content: list[tuple[str, tuple[int, int, int], str, int]]):
fonts = {}
for text, color, font_name, font_size in content:
font = fonts.setdefault(font_name, ImageFont.truetype(font_name, font_size))
d.text(coords, text, color, font)
coords = (coords[0] font.getsize(text)[0], coords[1])
with Image.open("white.png") as im:
draw = ImageDraw.Draw(im)
draw_text_in_font(draw, (10, 10), [
('248', (255, 255, 0), 'C:/Windows/Fonts/Consola.ttf', 70),
('€', (255, 0, 255), 'C:/Windows/Fonts/Arial.ttf', 56)
])
im.show()
This just bundles all you were doing in a single, fairly readable function, while calling it is pretty clear as well. You can string on more text in other fonts, and it will reuse a font once it's created it.
It might be nicer to use a list[dict[str, Any]]
for the content, or even define a dataclass for it, so you could make it clearer in your code what the values are that you're passing to draw_text_in_font
, but the idea would be the same.
Also, if you wrote a class instead, you could cache the fonts you use in class attributes, to avoid creating them every time you write a text, but that's all just niceties.
Note that I like making the types explicit, as it helps whoever is using the code to provide the right arguments in a decent editor or IDE. But this could would work the same, just not as nice to the coder:
def draw_text_in_font(d, coords, content):
fonts = {}
for text, color, font_name, font_size in content:
font = fonts.setdefault(font_name, ImageFont.truetype(font_name, font_size))
d.text(coords, text, color, font)
coords = (coords[0] font.getsize(text)[0], coords[1])