[英]How to get width of a truetype font character in 1200ths of an inch with Python?
我可以使用 PIL 以像素為單位獲得字符的高度和寬度(見下文),但是(除非我弄錯了)像素大小取決於屏幕的 DPI,這可能會有所不同。 相反,我想做的是以絕對單位(如英寸或 1200 分之一英寸(“wordperfect 單位”))計算字符的寬度。
>>> # Getting pixels width with PIL
>>> font = ImageFont.truetype('/blah/Fonts/times.ttf' , 12)
>>> font.getsize('a')
(5, 14)
我想要這樣做的原因是創建一個用於編寫二進制 Word Perfect 文檔的自動換行功能。 Word Perfect 要求在整個文本的有效點處插入軟換行符,否則文件將損壞且無法打開。 問題是在哪里為可變寬度字體添加它們。
我意識到我並不完全理解像素與屏幕分辨率和字體大小之間的關系。 我在這一切都錯了嗎?
原始文本的寬度通常在計算排版的點,但由於為字體定義的目的點定義為1/72英寸,您可以輕松地將其轉換成其他任何單位。
要獲得字符的設計寬度(以em單位表示),您需要訪問字體的低級數據。 最簡單的方法是pip install fonttools
,它可以在字體定義的最低級別工作。
安裝 fontTools 后,您可以:
加載字體數據——這需要實際字體文件的路徑;
字符寬度存儲為字形寬度,這意味着您必須檢索“字符到字形”映射; 這是在字體的cmap
表中:
一種。 為您的字體加載cmap
。 最有用的是 Unicode 映射——一種字體可能包含其他字體。 灣加載為您的字體設置的字形。 這是該字體中字形的名稱列表。
然后,對於每個 Unicode 字符,首先查找其名稱,然后使用該名稱以設計單位檢索其寬度。
不要忘記“設計單位”基於字體的整體“設計寬度”。 這可以是標准值1000
(Type 1 字體的典型值)、 2048
(TrueType 字體的典型值)或任何其他值。
這導致了這個功能:
from fontTools.ttLib import TTFont
from fontTools.ttLib.tables._c_m_a_p import CmapSubtable
font = TTFont('/Library/Fonts/Arial.ttf')
cmap = font['cmap']
t = cmap.getcmap(3,1).cmap
s = font.getGlyphSet()
units_per_em = font['head'].unitsPerEm
def getTextWidth(text,pointSize):
total = 0
for c in text:
if ord(c) in t and t[ord(c)] in s:
total += s[t[ord(c)]].width
else:
total += s['.notdef'].width
total = total*float(pointSize)/units_per_em;
return total
text = 'This is a test'
width = getTextWidth(text,12)
print ('Text: "%s"' % text)
print ('Width in points: %f' % width)
print ('Width in inches: %f' % (width/72))
print ('Width in cm: %f' % (width*2.54/72))
print ('Width in WP Units: %f' % (width*1200/72))
結果是:
Text: "This is a test"
Width in points: 67.353516
Width in inches: 0.935465
Width in cm: 2.376082
Width in WP Units: 1122.558594
並且與 Adobe InDesign 報告的內容進行比較時是正確的。 (請注意,這里不應用每個字符的字距調整!那將需要更多的代碼。)
字體中未定義的字符會被默默忽略,並且通常會使用.notdef
字形的寬度。 如果您希望將此報告為錯誤,請刪除函數中的if
測試。
函數getTextWidth
中的float
getTextWidth
因此適用於 Python 2.7 和 3.5,但請注意,如果您使用 Python 2.7 和更大值的 Unicode 字符(非純 ASCII),則需要重寫該函數以正確使用 UTF8 字符。
這對我來說效果更好:
def pixel_width(unicode_text):
width=len(unicode_text)*50
height=100
back_ground_color=(0,0,0)
font_size=64
font_color=(255,255,255)
im = Image.new ( "RGB", (width,height), back_ground_color )
draw = ImageDraw.Draw (im)
unicode_font = ImageFont.truetype("./usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", font_size)
draw.text ( (0,0), unicode_text, font=unicode_font, fill=font_color )
im.save("/dev/shm/text.png")
box = Image.open("/dev/shm/text.png").getbbox()
return box[2] - box[0]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.