2017-05-07 174 views
1

我正在通過PIL在基本圖像上繪製文本。如果所有字符的合併寬度超過基礎圖像的寬度,則其中一個要求是溢出到下一行。根據字符寬度將字符串拆分爲多行(python)

當前我正在使用textwrap.wrap(text, width=16)來完成此操作。這裏width定義了在一行中容納的字符數。現在文本可以是任何東西,因爲它是用戶生成的。所以問題在於硬編碼width由於字體類型,字體大小和字符選擇而不會考慮width的變化。

我的意思是?

想象一下,我使用的是DejaVuSans.ttf,大小爲14. W的長度爲14,而'i'爲4.對於寬度爲400的基本圖像,最多爲100 i字符可以放在一行中。但只有29 W個字符。我需要制定一個更明智的方式來包裝到下一行,當字符寬度總和超過基本圖像寬度時,字符串被破壞。

有人可以幫我制定這個嗎?一個說明性的例子會很棒!

回答

1

你既然知道每個字符的寬度,你應該做的是爲一本字典,從中獲取的寬度來計算stringwidth:

char_widths = { 
    'a': 9, 
    'b': 11, 
    'c': 13, 
    # ...and so on 
} 

從這裏你可以查找每個字母,並使用該總和檢查你的寬度:

current_width = sum([char_widths[letter] for letter in word]) 
+0

或如何使用'font.getsize( c)[0]'在飛行中,爲每個字符'c'? –

+1

如果你這樣做的話,這可能會奏效:'current_width = sum([font.getsize(c)[0] for c in word])' - 如果你可以創建一個字典,訪問速度比做這個函數調用要快每一次。 – JacobIRR

+0

感謝您的提示。 –

1

如果您精確的問題,才能獲得真正的文本的寬度最好的方法是實際渲染它,因爲字體規格並不總是線性的,關於字距或字體大小(見例如,here)不容易預測。 我們可以接近與ImageFont方法get_size在內部使用核心字體渲染方法的最佳斷點(見PIL github

def break_text(txt, font, max_width): 

    # We share the subset to remember the last finest guess over 
    # the text breakpoint and make it faster 
    subset = len(txt) 
    letter_size = None 

    text_size = len(txt) 
    while text_size > 0: 

     # Let's find the appropriate subset size 
     while True: 
      width, height = font.getsize(txt[:subset]) 
      letter_size = width/subset 

      # min/max(..., subset +/- 1) are to avoid looping infinitely over a wrong value 
      if width < max_width - letter_size and text_size >= subset: # Too short 
       subset = max(int(max_width * subset/width), subset + 1) 
      elif width > max_width: # Too large 
       subset = min(int(max_width * subset/width), subset - 1) 
      else: # Subset fits, we exit 
       break 

     yield txt[:subset] 
     txt = txt[subset:] 
     text_size = len(txt) 

,並使用它,像這樣:

from PIL import Image 
from PIL import ImageFont 
img = Image.new('RGBA', (100, 100), (255,255,255,0)) 
draw = ImageDraw.Draw(img) 
font = ImageFont.truetype("Helvetica", 12) 
text = "This is a sample text to break because it is too long for the image" 

for i, line in enumerate(break_text(text, font, 100)): 
    draw.text((0, 16*i), line, (255,255,255), font=font) 
+0

謝謝。我實際上也使用'getsize'來解決問題 - 下面看看我的代碼如何最終變成如下形式:http://stackoverflow.com/q/43828154/4936905 –