Python Pillow text vertical align - python

I'm trying to position 1 symbol in the top left corner of the given bounding box.
draw = ImageDraw.Draw(img)
font = ImageFont.truetype('LiberationSans-Regular.ttf', 150)
draw.text((x0, y0), "€", "green", font=font)
But when I place text, for example at (0, 0) of the box, it appears with some padding at the top. Also it seems padding size depends on font size.
Is there a way to calculate size of this padding? And maybe move it on that exact amount of pixels upwards.
Basically top pixel of the given symbol must be at y0 of the bounding box, regardless of font and font size.

Related

how to measure the width of a tkinter canvas text

I was wondering how to measure the width of a text in tkinter canvas. I have a text displayed somewhere:
myCanvas.create_text(400,410, text="This is my text", tags="my_tag")
and at some point there is a shape (polygon line) that may overlap when the text gets longer (text will be changed with .itemconfigure()). In this case I wanted to break the text in lines, which works well with the width option.
To figure out if objects overlap there are a couple of possible methods: find_overlapping() or find_enclosed(), however, they do require 4 coordinates and myCanvas.coords("my_tag") only returns 2 coordinates. How could I figure out the other x2, y2?
An alternative was to use find_closest(), however, there are a bunch of shapes closer than the overlapping one, so that is not accurate.
Another alternative was that I could figure out the intersecting coordinate and then just take the distance to the x coordinate of the text and double that as a width (since text gets drawn from the center), for example:
intersection point at (350,405) => width = 2 * (400-350)
However, since the shape is a line (part of a polygon) that goes diagonal, I'm not sure how get the intersection point other than having a loop that creates a line that slowly increases till it overlaps with an object, for example:
text_center = (400,410)
start_x = text_center[0]
start_y = text_center[1]
extender = 1
while(myCanvas.find_overlapping(start_x, start_y, start_x+entender, start_y == ())
myCanvas.delete("mytestline")
extender += 1
myCanvas.create_line(start_x, start_y, start_x+extender, start_y, tags="mytestline")
overlapping_point = (start_x+extender, start_y)
Is there an easier way?
The bbox method will return the bounding box of any item or group of items on the canvas. The bounding box is the smallest rectangle that contains the elements, give or take a pixel or two. The returned value is a tuple of the two coordinates.
The following example creates a text item, gets and displays the bounding box, and also uses the coordinates to draw a rectangle around the text so you can visually see that the numbers are correct.
import tkinter as tk
root = tk.Tk()
myCanvas = tk.Canvas(root)
myCanvas.pack(fill="both", expand=True)
item_id = myCanvas.create_text(100,50, text="This is my text", tags="my_tag")
bbox = myCanvas.bbox(item_id)
myCanvas.create_rectangle(bbox, outline="red")
myCanvas.create_text(100, 70, text=f"bbox: {bbox}")
root.mainloop()

Fill PIL ImageDraw Line partially

I'm trying to fill a line by a different color increasingly like a progress bar. This is the image:
It was created with this code
from PIL import Image, ImageDraw
image = Image.new("RGBA", (300, 300), color="black")
draw = ImageDraw.Draw(image)
width = 7
image_w, image_h = image.size
coord_a = image_w / 2, width
coord_b = width, image_h / 2
coord_c = image_w / 2, image_h - width
coord_d = image_w - width, image_h / 2
draw.line([coord_a, coord_b, coord_c, coord_d, coord_a], fill="red", width=width, joint="curve")
image.show()
image.save("test.png")
I'm trying to fill it with different color like this:
Should I just fill each line separately and combine them all?
Interesting question! You could have lots of fun thinking up ways to do this.
As you suggest, you could draw the rhombus as four separate lines. You would have to calculate the point where the red and blue portion met using sin/cos but that's not too hard.
You could draw it much more simply as the four sides of a square with its sides initially horizontal and vertical, then rotate it 45 degrees into place when you are finished drawing. I think I would go for this option.
You could draw a single long horizontal red line, and then overdraw the correct percentage in blue. Then cut it into four pieces, rotate and paste onto the black square background.
You could get the coordinates of all the points on the rhombus using scikit-image draw.polygon_perimeter() as documented here. Then colour the first however many percent blue and the remainder in red. You could make the lines thicker using morphological dilation.

Wand - Right aligning text with offset

Gravity seemed like what I wanted but it does not let me change the x offset.
bg.caption(f"{xp} / {req_xp}", left=200, top=165, font=montserrat_bold, gravity='north_east')
Correctly offsets in the y direction but no matter what I put for left it remains glued to the right. Is this a bug with wand? I saw several examples of pure imagemagick where alignment seemed possible.
Captions are created by building a new bounding box (width x height), and renders the text to fit within the box -- while respecting the gravity parameter. After the text has been rendered, the bonding box is composited at the left x top coordinates of the image.
[...] it remains glued to the right. Is this a bug with wand?
With gravity set to "north_east" & width undefined, the text will remain "glued to the right".
Try the following ...
bg.caption(f"{xp} / {req_xp}",
left=200,
top=165,
width=100,
height=50,
font=montserrat_bold,
gravity='north_east')
Adjust the width= & height= parameter values to respect the pointsize of montserrat_bold variable.

Can't center characters using Pillow and Python

I am trying to write characters in specific locations in an image. I am using Pillow v6, Python 3.6.
Here is my code, I draw char by char, passing the top left point that I calculated.
font = ImageFont.truetype('platechar.tff', 500)
def draw_single_char(img, font, val, tl): #tl = (x,y)
pil_img = Image.fromarray(np.uint8(img))
draw = ImageDraw.Draw(pil_img)
draw.text(tl, val, (0,0,0), font=font)
img = np.array(pil_img)
return img
The output is not centered, I got the character width and height from the font, then with my top left point I draw the rectangle enclosing the character. The character is not centered inside the rectangle.
Font: https://drive.google.com/open?id=1N9rN-AgjK83U9ZDycLKxjeMP3o36vbfg
I want it to be like this (another font)
EDIT
Using Bidu Font editor I was able to remove the horizontal space (blue line). How can I center it vertically?.
Result so far ...
It looks like the font you are using contains non-centered numbers inside originally. So you should choose another font or you can modify your placechar.tff in a special editor for fonts.
Also you can calculate coordinate offsets for each symbol manually, store them into a dictionary and apply it for your text before drawing. It doesn't look like a good idea, but it would work also.
Calculate the width and height of the text to be drawn:
from PIL import Image, ImageDraw, ImageFont
txt='7'
font = ImageFont.truetype('platechar.ttf', 250)
(W, H) = font.getsize(txt)
image = Image.new('RGB', (256, 256), (63, 63, 63, 0))
drawer = ImageDraw.Draw(image)
(offset_w, offset_h) = font.getoffset(txt)
(x, y, W_mask, H_mask) = font.getmask(txt).getbbox()
drawer.text((10, 10 - offset_h), txt, align='center', font=font)
drawer.rectangle((10, 10, W + offset_w, 10 + H - offset_h), outline='black')
drawer.rectangle((x+10, y+10, W_mask+10, H_mask+10), outline='red')
image.show()
image.save('example.png', 'PNG')
After taking the path that #Fomalhaut suggested, using font editor. I found Bidu font editor (link in the question). I was able to fix the horizontal space (also shown in the question). For vertical space, after searching the menus, I found setting option to change the ascent.
I decreased it to 1440, and it worked.

Kivy Interpolation on Canvas

I am new to kivy and python. I am trying to have an texture (256x256) shown pixelated through my canvas, but it is blurred, and through some googling i think it is interpolated? I want it to show the pixels with sharp edging and not blurred. It also blends in color from surrounding tiles in the texture when i use texture.get_region(...) to select one tile (64x64) and display that, so i get a border from the other tiles in my new texture. I guess i am just really bad at reading Kivy's documentation.
texture = Image('template.png').texture //getting texture
topright = texture.get_region(64, 64, 64, 64) //Selecting a specific Tile
with self.canvas:
Rectangle(texture = texture, pos = self.pos, size = (512,512))
Set
texture.mag_filter = 'nearest'
texture.min_filter = 'nearest'
You might only need one of these, I don't remember, but you can test and see the doc at http://kivy.org/docs/api-kivy.graphics.texture.html

Categories