与此问题类似,我不是在问如何查找字符串中的字符数.我想确定渲染字符串的可视长度或将其与另一个字符串进行比较.
例如,'iiii'和'WWWW'都有四个字符.但是,'iiii'在视觉上较短.我知道这是由字体决定的,我不使用等宽字体.因此,出于这个问题的目的,我将使用Arial 10pt.
是否有任何内置模块可以提供给定字体的字符串的视觉尺寸?
您可以使用字体度量直接计算宽度,而不是渲染到图像缓冲区和计算像素。好像没有core python自带的字体API,但是各种包里有很多第三方的。这是Adobe 字体度量的一个非常完整的解决方案,使用matplotlib:
>>> from matplotlib import rcParams
>>> import os.path
>>> afm_filename = os.path.join(rcParams['datapath'], 'fonts', 'afm', 'ptmr8a.afm')
>>>
>>> from matplotlib.afm import AFM
>>> afm = AFM(open(afm_filename, "rb"))
>>> afm.string_width_height('What the heck?')
(6220.0, 694)
Run Code Online (Sandbox Code Playgroud)
度量以所用字体的比例因子(磅值)的 1/1000 为单位报告。(感谢@JacobLee 挖掘这个信息。)
另一种可能性是tkFont的模块tkinter。 该页面记录了该功能tkFont.Font.measure("some string"),但您似乎需要一个 Tk 窗口才能使用它;所以我不知道它有多实用:
# Python 3 names -- see Note below
import tkinter
from tkinter import font as tkFont
tkinter.Frame().destroy() # Enough to initialize resources
arial36b = tkFont.Font(family='Arial', size=36, weight='bold')
width = arial36b.measure("How wide is this?")
print(width) # Prints: 404
Run Code Online (Sandbox Code Playgroud)
注意:在 python 2(以及我上面提到的页面中)中,tkinter被称为Tkinter,并且tkinter.font是一个顶级模块,tkFont:
import Tkinter
import tkFont
Run Code Online (Sandbox Code Playgroud)
如果您使用的是 Windows,则可以使用以下方法。
它使用当前屏幕作为输出上下文并计算以给定点大小显示给定字体所需的尺寸。它返回一个包含文本宽度和文本高度的元组:
import ctypes
def GetTextDimensions(text, points, font):
class SIZE(ctypes.Structure):
_fields_ = [("cx", ctypes.c_long), ("cy", ctypes.c_long)]
hdc = ctypes.windll.user32.GetDC(0)
hfont = ctypes.windll.gdi32.CreateFontA(-points, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, font)
hfont_old = ctypes.windll.gdi32.SelectObject(hdc, hfont)
size = SIZE(0, 0)
ctypes.windll.gdi32.GetTextExtentPoint32A(hdc, text, len(text), ctypes.byref(size))
ctypes.windll.gdi32.SelectObject(hdc, hfont_old)
ctypes.windll.gdi32.DeleteObject(hfont)
return (size.cx, size.cy)
for text, font in [
('....', 'Arial'),
('WWWW', 'Arial'),
('WWWW', 'Arial Narrow'),
('....', 'Courier New'),
('WWWW', 'Courier New'),
("Test", "Unknown font"),
('Test', 'Calibri')]:
print '{:8} {:20} {}'.format(text, font, GetTextDimensions(text, 12, font))
Run Code Online (Sandbox Code Playgroud)
这将显示以下输出:
import ctypes
def GetTextDimensions(text, points, font):
class SIZE(ctypes.Structure):
_fields_ = [("cx", ctypes.c_long), ("cy", ctypes.c_long)]
hdc = ctypes.windll.user32.GetDC(0)
hfont = ctypes.windll.gdi32.CreateFontA(-points, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, font)
hfont_old = ctypes.windll.gdi32.SelectObject(hdc, hfont)
size = SIZE(0, 0)
ctypes.windll.gdi32.GetTextExtentPoint32A(hdc, text, len(text), ctypes.byref(size))
ctypes.windll.gdi32.SelectObject(hdc, hfont_old)
ctypes.windll.gdi32.DeleteObject(hfont)
return (size.cx, size.cy)
for text, font in [
('....', 'Arial'),
('WWWW', 'Arial'),
('WWWW', 'Arial Narrow'),
('....', 'Courier New'),
('WWWW', 'Courier New'),
("Test", "Unknown font"),
('Test', 'Calibri')]:
print '{:8} {:20} {}'.format(text, font, GetTextDimensions(text, 12, font))
Run Code Online (Sandbox Code Playgroud)
Arial作为比例字体显示不同的尺寸....,WWWW但Courier New固定宽度会产生相同的结果。与for相比Arial Narrow给出。3644Arial
在 的情况下Unknown font,Windows 字体映射器已自动选择默认字体。
在 Python 2.x 上测试。
Python 3.x 的注意事项
由于这是GetTextExtentPoint32A()在 Windows中调用,因此需要将 ANSI 文本传递给它,因此可以按如下方式更改调用以解决此问题:
ctypes.windll.gdi32.GetTextExtentPoint32A(hdc, text.encode('cp1252'), len(text), ctypes.byref(size))
Run Code Online (Sandbox Code Playgroud)
或者,将代码切换为使用宽版本,替换为以下两个:
hfont = ctypes.windll.gdi32.CreateFontW(-points, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, font)
ctypes.windll.gdi32.GetTextExtentPoint32W(hdc, text, len(text), ctypes.byref(size))
Run Code Online (Sandbox Code Playgroud)