How to get the visual length of a text string in python

匿名 (未验证) 提交于 2019-12-03 09:02:45

问题:

Similar to this question, I'm not asking how to find the number of characters in a string. I would like to determine the visual length of a string as rendered or compare it to another string.

For example, both 'iiii' and 'WWWW' have four characters. However, 'iiii' is shorter visually. I'm aware that this is determined by font, and I'm not working with monospaced fonts. So, for the purposes of this problem, I'll be using Arial 10pt.

Are there any built-in modules which will provide the visual dimensions of a string given a font?

回答1:

If you are using Windows, then the following approach could be used.

It uses the current screen as the output context and calculates the dimensions needed to display the given font at the given point size. It returns a tuple holding the text width and text height:

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)) 

This would display the following output:

....     Arial                (12, 15) WWWW     Arial                (44, 15) WWWW     Arial Narrow         (36, 16) ....     Courier New          (28, 15) WWWW     Courier New          (28, 15) Test     Unknown font         (24, 15) Test     Calibri              (23, 14) 

Arial being a proportional font shows different dimensions for .... and WWWW but Courier New being fixed width gives the same results. Arial Narrow gives 36 compared to 44 for Arial.

In the case of Unknown font, the Windows font mapper has automatically picked a default font.

Tested on Python 2.x.


Note for Python 3.x

As this is calling GetTextExtentPoint32A() in Windows, this expects ANSI text to be passed to it, as such the call could be changed as follows to fix this:

ctypes.windll.gdi32.GetTextExtentPoint32A(hdc, text.encode('cp1252'), len(text), ctypes.byref(size)) 

Alternatively, switch the code to use the wide versions, replace with these two:

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)) 


回答2:

Instead of rendering into an image buffer and counting pixels, you can calculate width directly by using the font metrics. There doesn't seem to be a font API distributed with core python, but there are plenty of third-party ones in various packages. Here's a pretty complete solution for Adobe font metrics, using 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)) >>> afm.string_width_height('What the heck?') (6220.0, 694) 

Another possibility is the tkFont module of tkinter. This page documents the function tkFont.Font.measure("some string"), but it seems you need a Tk window before you can use it; so I don't know how practical it is:

# 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 

Note: In python 2 (and in the page I mentioned above), tkinter is known as Tkinter, and tkinter.font is a top-level module, tkFont:

import Tkinter import tkFont 


回答3:

Use a graphic / font library like ImageFont. Draw the string and then use the getsize to get the width.

Note that some text like "AWAY" may be narrower than the sum of the individual letters due to kerning. So it would be difficult to lookup widths of each letter and add them.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!