1 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
12 for word in text.split():
14 words[-1] += ' ' + word
17 conj = len(word.lstrip('(').lstrip('[')) == 1
21 class DoesNotFit(Exception):
26 def __init__(self, width, height, texts,
27 font, lines, leading, tracking,
34 self.font_fallbacks = font_fallbacks
36 self.leading = leading
37 self.tracking = tracking
38 self.align_h = align_h
39 self.align_v = align_v
41 self.margin_top = self.font.getbbox('l')[1]
43 self.glue = self.get_length(' ')
46 (self.get_length(word), word)
47 for word in self.texts
50 self.grouping = self.find_grouping(groups, self.lines, self.glue)
51 if self.grouping is None:
54 def get_font_for_char(self, c):
55 if self.font_fallbacks:
56 for char_range, font in self.font_fallbacks.items():
57 if char_range[0] <= c <= char_range[1]:
61 def get_length(self, text):
62 text = bidi.get_display(text, base_dir='L')
65 font = self.get_font_for_char(c)
66 if groups and font is groups[-1][0]:
69 groups.append([font, c])
73 for (font, t) in groups
74 ) + self.tracking * len(text)
76 def text_with_tracking(self, draw, pos, text, fill=None):
78 for c in bidi.get_display(text, base_dir='L'):
79 cfont = self.get_font_for_char(c)
80 width = cfont.getlength(c)
81 draw.text((x, y), c, fill=fill, font=cfont)
82 x += width + self.tracking
84 def as_pil_image(self, color):
85 img = PIL.Image.new('RGBA', (self.width, self.height + 2 * self.margin_top))
86 draw = PIL.ImageDraw.ImageDraw(img)
88 font_letter_height = self.font.getmetrics()[0] - self.margin_top
90 y = self.align_v * (self.height - ((self.lines - 1) * self.leading + font_letter_height))
91 for group in self.grouping:
92 x = (self.width - group[0] + self.tracking) * self.align_h
93 self.align_h * - group[0] / 2
95 self.text_with_tracking(
104 def find_grouping(self, groups, ngroups, glue):
110 mean = sum(g[0] for g in groups) + (len(groups) - ngroups) * glue
111 if mean > self.width * ngroups:
114 for grouping in self.all_groupings(groups, ngroups, glue):
115 if max(g[0] for g in grouping) > self.width:
117 var = sum((g[0] - mean) ** 2 for g in grouping)
118 if best is None or best_var > var:
119 best, best_var = grouping, var
123 def all_groupings(self, groups, ngroups, glue):
126 yield [(groups[0][0], groups)]
128 next_groups = groups[1:]
129 for grouping in self.all_groupings(next_groups, ngroups, glue):
132 groups[0][0] + glue + grouping[0][0],
133 [groups[0]] + grouping[0][1]
137 for grouping in self.all_groupings(next_groups, ngroups - 1, glue):
139 (groups[0][0], [groups[0]])