我正在创建一个web应用程序,它可以提供动态图像和文本。绘制的每一个字符串都可以是多种颜色。
到目前为止,我已经创建了一个解析方法和一个呈现方法。解析方法只接受字符串,并从字符串中解析颜色,它们的格式如下:"§aThis是绿色§rthis是白色的“(是的,它是我的世界)。这就是我的字体模块的样子:
# Imports from pillow
from PIL import Image, ImageDraw, ImageFont
# Load the fonts
font_regular = ImageFont.truetype("static/font/regular.ttf", 24)
font_bold = ImageFont.truetype("static/font/bold.ttf", 24)
font_italics = ImageFont.truetype("static/font/italics.ttf", 24)
font_bold_italics = ImageFont.truetype("static/font/bold-italics.ttf", 24)
max_height = 21 # 9, from FONT_HEIGHT in FontRederer in MC source, multiplied by
# 3, because each virtual pixel in the font is 3 real pixels
# This number is also returned by:
# font_regular.getsize("ABCDEFGHIJKLMNOPQRSTUVWXYZ")[1]
# Create the color codes
colorCodes = [0] * 32 # Empty array, 32 slots
# This is ported from the original MC java source:
for i in range(0, 32):
j = int((i >> 3 & 1) * 85)
k = int((i >> 2 & 1) * 170 + j)
l = int((i >> 1 & 1) * 170 + j)
i1 = int((i >> 0 & 1) * 170 + j)
if i == 6:
k += 85
if i >= 16:
k = int(k/4)
l = int(l/4)
i1 = int(i1/4)
colorCodes[i] = (k & 255) << 16 | (l & 255) << 8 | i1 & 255
def _get_colour(c):
''' Get the RGB-tuple for the color
Color can be a string, one of the chars in: 0123456789abcdef
or an int in range 0 to 15, including 15
'''
if type(c) == str:
if c == 'r':
c = int('f', 16)
else:
c = int(c, 16)
c = colorCodes[c]
return ( c >> 16 , c >> 8 & 255 , c & 255 )
def _get_shadow(c):
''' Get the shadow RGB-tuple for the color
Color can be a string, one of the chars in: 0123456789abcdefr
or an int in range 0 to 15, including 15
'''
if type(c) == str:
if c == 'r':
c = int('f', 16)
else:
c = int(c, 16)
return _get_colour(c+16)
def _get_font(bold, italics):
font = font_regular
if bold and italics:
font = font_bold_italics
elif bold:
font = font_bold
elif italics:
font = font_italics
return font
def parse(message):
''' Parse the message in a format readable by render
this will return a touple like this:
[((int,int),str,str)]
so if you where to send it directly to the rederer you have to do this:
render(pos, parse(message), drawer)
'''
result = []
lastColour = 'r'
total_width = 0
bold = False
italics = False
for i in range(0,len(message)):
if message[i] == '§':
continue
elif message[i-1] == '§':
if message[i] in "01234567890abcdef":
lastColour = message[i]
if message[i] == 'l':
bold = True
if message[i] == 'o':
italics = True
if message[i] == 'r':
bold = False
italics = False
lastColour = message[i]
continue
width, height = _get_font(bold, italics).getsize(message[i])
total_width += width
result.append(((width, height), lastColour, bold, italics, message[i]))
return result
def get_width(message):
''' Calculate the width of the message
The message has to be in the format returned by the parse function
'''
return sum([i[0][0] for i in message])
def render(pos, message, drawer):
''' Render the message to the drawer
The message has to be in the format returned by the parse function
'''
x = pos[0]
y = pos[1]
for i in message:
(width, height), colour, bold, italics, char = i
font = _get_font(bold, italics)
drawer.text((x+3, y+3+(max_height-height)), char, fill=_get_shadow(colour), font=font)
drawer.text((x, y+(max_height-height)), char, fill=_get_colour(colour), font=font)
x += width它确实有效,但是应该在字体的底线以下的字符,比如g、y和q,都是在底线上呈现的,所以看起来很奇怪,下面是一个例子:

我有什么想法能让他们表现得很好吗?还是我必须自己制作胶印表,在那里手动放置?
发布于 2013-10-06 20:04:27
考虑到您无法从PIL获得偏移量,您可以通过分割图像来实现这一点,因为PIL适当地组合了多个字符。这里我有两种方法,但我认为第一种方法更好,尽管这两种方法都只是几行。第一种方法给出了这个结果(它也放大了一个小字体,这就是为什么它会被像素化):

为了在这里解释这个概念,比如我想要字母' j‘,而不是只做'j’的图像,我做‘o j’的图像,因为这将保持‘j’的正确对齐。然后我裁剪我不想要的部分,只保留'j‘(在’o‘和’o‘上使用textsize )。
import Image, ImageDraw
from random import randint
make_color = lambda : (randint(50, 255), randint(50, 255), randint(50,255))
image = Image.new("RGB", (1200,20), (0,0,0)) # scrap image
draw = ImageDraw.Draw(image)
image2 = Image.new("RGB", (1200, 20), (0,0,0)) # final image
fill = " o "
x = 0
w_fill, y = draw.textsize(fill)
x_draw, x_paste = 0, 0
for c in "The quick brown fox jumps over the lazy dog.":
w_full = draw.textsize(fill+c)[0]
w = w_full - w_fill # the width of the character on its own
draw.text((x_draw,0), fill+c, make_color())
iletter = image.crop((x_draw+w_fill, 0, x_draw+w_full, y))
image2.paste(iletter, (x_paste, 0))
x_draw += w_full
x_paste += w
image2.show()顺便说一句,我使用“o”,而不是仅仅使用“o”,因为相邻的字母似乎彼此有点腐败。
第二种方法是制作整个字母表的图像,将其分割,然后将其组合在一起。比听起来容易多了。下面是一个示例,构建字典和连接到图像中都只有几行代码:
import Image, ImageDraw
import string
A = " ".join(string.printable)
image = Image.new("RGB", (1200,20), (0,0,0))
draw = ImageDraw.Draw(image)
# make a dictionary of character images
xcuts = [draw.textsize(A[:i+1])[0] for i in range(len(A))]
xcuts = [0]+xcuts
ycut = draw.textsize(A)[1]
draw.text((0,0), A, (255,255,255))
# ichars is like {"a":(width,image), "b":(width,image), ...}
ichars = dict([(A[i], (xcuts[i+1]-xcuts[i]+1, image.crop((xcuts[i]-1, 0, xcuts[i+1], ycut)))) for i in range(len(xcuts)-1)])
# Test it...
image2 = Image.new("RGB", (400,20), (0,0,0))
x = 0
for c in "This is just a nifty text string":
w, char_image = ichars[c]
image2.paste(char_image, (x, 0))
x += w下面是结果字符串的(放大)图像:

以下是整个字母表的图像:

这里的一个窍门是,我必须在我原来的字母表图像中的每个字符之间留出一个空格,否则我就会看到相邻的字符相互影响。
我想如果你需要为有限的字体和字符做a,这将是一个好主意,预先计算一个字母表图像字典。
或者,对于另一种方法,使用像numpy这样的工具,您可以很容易地在上面的ichar字典中确定每个字符的y偏移量(例如,取每个水平行的最大值,然后在非零索引上找到最大值和最小值)。
发布于 2022-04-18 22:03:44
我只是这样解决了这个问题:
image = Image.new("RGB", (1000,1000), (255,255,255)) # 1000*1000 white empty image
# image = Image.fromarray(frame) # or u can get image from cv2 frame
draw = ImageDraw.Draw(image)
fontpath = "/etc/fonts/bla/bla/comic-sans.ttf"
font = ImageFont.truetype(fontpath, 35) # U can use default fonts
x = 20 # image draw start pixel x position
y = 100 # image draw start pixel y position
xDescPxl = draw.textsize("Descrition", font= font)[0]
draw.text((x, y), "Descrition" , font = font, fill = (0, 255, 0, 0)) # Green Color
draw.text((x + xDescPxl, y), ": u can do bla bla", font = font, fill = (0, 0, 0, 0)) # Black Color结果:
Description: u can do bla bla(20 Part空间)-(绿色部分)-(黑色部分)
https://stackoverflow.com/questions/19206840
复制相似问题