reworked line breaks
This commit is contained in:
@@ -98,21 +98,18 @@ def fit_text(size, text):
|
|||||||
font_size = round(text.font_size * min(size)) + 1
|
font_size = round(text.font_size * min(size)) + 1
|
||||||
font = FONTS[text.font]
|
font = FONTS[text.font]
|
||||||
t = ""
|
t = ""
|
||||||
while (text_size is None or text_size[1] >= max_height) and font_size > 1:
|
while (text_size is None or text_size[0] >= max_width or text_size[1] >= max_height) and font_size > 1:
|
||||||
font_size -= 1
|
font_size -= 1
|
||||||
font = font.font_variant(size=font_size)
|
font = font.font_variant(size=font_size)
|
||||||
words = text.text.split(" ")
|
k = 0 # number of lines
|
||||||
t = ""
|
while k == 0 or (t is not None and text_size[0] >= max_width):
|
||||||
for word in words:
|
k += 1
|
||||||
spacer = " "
|
t = utils.break_text(text.text, k)
|
||||||
if len(t) == 0:
|
if t is not None:
|
||||||
spacer = ""
|
|
||||||
text_size = font.getsize_multiline(t + spacer + word, stroke_width=text.stroke_width * font_size)
|
|
||||||
if text_size[0] >= max_width:
|
|
||||||
t += "\n" + word
|
|
||||||
else:
|
|
||||||
t += spacer + word
|
|
||||||
text_size = font.getsize_multiline(t, stroke_width=text.stroke_width * font_size)
|
text_size = font.getsize_multiline(t, stroke_width=text.stroke_width * font_size)
|
||||||
|
if t is None:
|
||||||
|
# max break attained
|
||||||
|
text_size = None # restart
|
||||||
return t, font
|
return t, font
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import os.path as path
|
import os.path as path
|
||||||
from Levenshtein import distance
|
from Levenshtein import distance
|
||||||
|
|
||||||
@@ -147,3 +148,84 @@ def find_nearest(word, wlist, threshold=5):
|
|||||||
if found[0] > threshold:
|
if found[0] > threshold:
|
||||||
return None
|
return None
|
||||||
return found[1]
|
return found[1]
|
||||||
|
|
||||||
|
|
||||||
|
def safe_index(src, pattern, start=0):
|
||||||
|
"""
|
||||||
|
:param (list|str) src:
|
||||||
|
:param pattern:
|
||||||
|
:param (int) start:
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return src.index(pattern, start)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_all(src, pattern):
|
||||||
|
"""
|
||||||
|
:param (str) src:
|
||||||
|
:param (str) pattern:
|
||||||
|
:rtype: list of int
|
||||||
|
"""
|
||||||
|
o = []
|
||||||
|
i = safe_index(src, pattern)
|
||||||
|
while i is not None:
|
||||||
|
o += [i]
|
||||||
|
i = safe_index(src, pattern, i + 1)
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def replace_at(src, pattern, indexes, remove):
|
||||||
|
"""
|
||||||
|
:param (str) src:
|
||||||
|
:param (str) pattern:
|
||||||
|
:param (list of int) indexes:
|
||||||
|
:param (int) remove:
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
o = ""
|
||||||
|
last = 0
|
||||||
|
for i in indexes:
|
||||||
|
o += src[last:i] + pattern
|
||||||
|
last = i + remove
|
||||||
|
o += src[last:]
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def break_text(src, n):
|
||||||
|
"""
|
||||||
|
:param (str) src:
|
||||||
|
:param (int) n:
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
spaces = find_all(src, " ")
|
||||||
|
if n - 1 > len(spaces):
|
||||||
|
return None
|
||||||
|
if n - 1 == len(spaces):
|
||||||
|
return replace_at(src, "\n", spaces, 1)
|
||||||
|
ideal = [k * (len(src) - 1) / n for k in range(1, n)]
|
||||||
|
indexes = best_fit(ideal, spaces)
|
||||||
|
return replace_at(src, "\n", indexes, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def best_fit(a, b):
|
||||||
|
"""
|
||||||
|
:param (list of float) a:
|
||||||
|
:param (list of int) b:
|
||||||
|
:rtype: list of int
|
||||||
|
"""
|
||||||
|
a = a[::]
|
||||||
|
o = []
|
||||||
|
dist = sys.maxsize
|
||||||
|
for i, value in enumerate(b):
|
||||||
|
if not len(a):
|
||||||
|
break
|
||||||
|
if dist < abs(value - a[0]):
|
||||||
|
o += [b[i - 1]]
|
||||||
|
a.pop(0)
|
||||||
|
else:
|
||||||
|
dist = abs(value - a[0])
|
||||||
|
if len(a):
|
||||||
|
o += [b[-1]]
|
||||||
|
return o
|
||||||
|
|||||||
+37
-2
@@ -80,11 +80,11 @@ class Test(TestCase):
|
|||||||
self.assertEqual(5, utils.read_key_safe(d, "test1"))
|
self.assertEqual(5, utils.read_key_safe(d, "test1"))
|
||||||
self.assertEqual([1, 3, ""], utils.read_key_safe(d, "test2"))
|
self.assertEqual([1, 3, ""], utils.read_key_safe(d, "test2"))
|
||||||
self.assertEqual("default", utils.read_key_safe(d, "test3", "default"))
|
self.assertEqual("default", utils.read_key_safe(d, "test3", "default"))
|
||||||
self.assertEqual(None, utils.read_key_safe(d, "test3"))
|
self.assertIsNone(utils.read_key_safe(d, "test3"))
|
||||||
|
|
||||||
def test_find_nearest(self):
|
def test_find_nearest(self):
|
||||||
self.assertEqual("test", utils.find_nearest("tost", ["test", "example", "what"]))
|
self.assertEqual("test", utils.find_nearest("tost", ["test", "example", "what"]))
|
||||||
self.assertEqual(None, utils.find_nearest("unknown", ["test", "example", "what"], threshold=2))
|
self.assertIsNone(utils.find_nearest("unknown", ["test", "example", "what"], threshold=2))
|
||||||
self.assertEqual("test", utils.find_nearest("unknown", ["test", "example", "what"], threshold=200))
|
self.assertEqual("test", utils.find_nearest("unknown", ["test", "example", "what"], threshold=200))
|
||||||
|
|
||||||
def test_parse_arguments(self):
|
def test_parse_arguments(self):
|
||||||
@@ -93,3 +93,38 @@ class Test(TestCase):
|
|||||||
self.assertEqual(["test1", "test2"], utils.parse_arguments("test1 test2"))
|
self.assertEqual(["test1", "test2"], utils.parse_arguments("test1 test2"))
|
||||||
self.assertEqual(["test1", "test 2", "test 3"], utils.parse_arguments("test1 'test 2' \"test 3\""))
|
self.assertEqual(["test1", "test 2", "test 3"], utils.parse_arguments("test1 'test 2' \"test 3\""))
|
||||||
self.assertEqual(["test1", "", ""], utils.parse_arguments("test1 '' \"\""))
|
self.assertEqual(["test1", "", ""], utils.parse_arguments("test1 '' \"\""))
|
||||||
|
|
||||||
|
def test_safe_index(self):
|
||||||
|
self.assertEqual(0, utils.safe_index("a", "a"))
|
||||||
|
self.assertEqual(0, utils.safe_index([0], 0))
|
||||||
|
self.assertEqual(2, utils.safe_index("cbaa", "a"))
|
||||||
|
self.assertEqual(3, utils.safe_index("cbaa", "a", 3))
|
||||||
|
self.assertEqual(1, utils.safe_index(["a", 0, 0], 0))
|
||||||
|
self.assertEqual(2, utils.safe_index(["a", 0, 0], 0, 2))
|
||||||
|
self.assertIsNone(utils.safe_index("a", "b"))
|
||||||
|
self.assertIsNone(utils.safe_index("a", "a", 2))
|
||||||
|
self.assertIsNone(utils.safe_index(["a", 0, 0], 0, 3))
|
||||||
|
|
||||||
|
def test_find_all(self):
|
||||||
|
self.assertEqual([], utils.find_all("abc", "n"))
|
||||||
|
self.assertEqual([0], utils.find_all("abc", "a"))
|
||||||
|
self.assertEqual([0, 2], utils.find_all("aba", "a"))
|
||||||
|
|
||||||
|
def test_replace_at(self):
|
||||||
|
self.assertEqual("abcd", utils.replace_at("abc", "d", [3], 0))
|
||||||
|
self.assertEqual("abd", utils.replace_at("abc", "d", [2], 1))
|
||||||
|
self.assertEqual("ddd", utils.replace_at("abc", "d", [0, 1, 2], 1))
|
||||||
|
self.assertEqual("a nice_plac_", utils.replace_at("a nice place", "_", [6, 11], 1))
|
||||||
|
|
||||||
|
def test_break_text(self):
|
||||||
|
self.assertIsNone(utils.break_text("abcd", 2))
|
||||||
|
self.assertIsNone(utils.break_text("abcd efgh", 3))
|
||||||
|
self.assertEqual("abcd", utils.break_text("abcd", 1))
|
||||||
|
self.assertEqual("abcd\nefgh", utils.break_text("abcd efgh", 2))
|
||||||
|
self.assertEqual("ab cd\nef gh", utils.break_text("ab cd ef gh", 2))
|
||||||
|
self.assertEqual("ab\ncd ef\ngh", utils.break_text("ab cd ef gh", 3))
|
||||||
|
|
||||||
|
def test_best_fit(self):
|
||||||
|
self.assertEqual([5, 9, 15], utils.best_fit([5.2, 14.3, 15.2], [3, 5, 9, 15, 18]))
|
||||||
|
self.assertEqual([5, 9, 15, 18], utils.best_fit([5.2, 14.3, 14.5, 15.2], [3, 5, 9, 15, 18]))
|
||||||
|
self.assertEqual([5, 9, 15, 18], utils.best_fit([5.2, 14.3, 14.5, 15.2], [3, 5, 9, 15, 18, 20]))
|
||||||
|
|||||||
Reference in New Issue
Block a user