From e1c2eacd8acba1a8147624fcf3c63d0d7dffc1b0 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Sun, 4 Mar 2018 19:36:22 -0500 Subject: [PATCH] better text handling --- axi/drawing.py | 2 +- axi/hershey.py | 90 ++++++++++++++++++++++++++++++++++-------------- examples/text.py | 4 +-- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/axi/drawing.py b/axi/drawing.py index 49e7e3a..1a03e52 100644 --- a/axi/drawing.py +++ b/axi/drawing.py @@ -243,7 +243,7 @@ class Drawing(object): paths.append(path) return Drawing(paths) - def render(self, scale=109, margin=1, line_width=0.5/25.4, + def render(self, scale=109, margin=1, line_width=0.35/25.4, bounds=None, show_bounds=True, use_axi_bounds=False, show_axi_bounds=False): if cairo is None: diff --git a/axi/hershey.py b/axi/hershey.py index ae4ac12..9ad0774 100644 --- a/axi/hershey.py +++ b/axi/hershey.py @@ -1,3 +1,5 @@ +from __future__ import division + from .hershey_fonts import * def text(string, font=FUTURAL, spacing=0, extra=0): @@ -18,29 +20,67 @@ def text(string, font=FUTURAL, spacing=0, extra=0): x += extra return result -def text_width(string, font=FUTURAL, spacing=0): - x = 0 - for ch in string: - index = ord(ch) - 32 - if index < 0 or index >= 96: - x += spacing - continue - lt, rt, coords = font[index] - x += rt - lt + spacing - return x +def _word_wrap(text, width, measure_func): + result = [] + for line in text.split('\n'): + fields = itertools.groupby(line, lambda x: x.isspace()) + fields = [''.join(g) for _, g in fields] + if len(fields) % 2 == 1: + fields.append('') + x = '' + for a, b in zip(fields[::2], fields[1::2]): + w, _ = measure_func(x + a) + if w > width: + if x == '': + result.append(a) + continue + else: + result.append(x) + x = '' + x += a + b + if x != '': + result.append(x) + result = [x.strip() for x in result] + return result -def justify_text(lines, font=FUTURAL, spacing=0): - widths = [text_width(x, font, spacing) for x in lines] - max_width = max(widths) - extras = [] - for i, (line, width) in enumerate(zip(lines, widths)): - spaces = line.count(' ') - if spaces == 0: - e = 0 - else: - e = float(max_width - width) / spaces - if i == len(lines) - 1: - e = 0 - extras.append(e) - print width, max_width, spaces, e - return [text(line, font, spacing, extra) for line, extra in zip(lines, extras)] +class Font(object): + def __init__(self, font, point_size): + self.font = font + self.max_height = axi.Drawing(axi.text(string.printable, font)).height + self.scale = (point_size / 72) / self.max_height + def text(self, text): + d = axi.Drawing(axi.text(text, self.font)) + d = d.scale(self.scale) + return d + def justify_text(self, text, width): + d = self.text(text) + w = d.width + spaces = text.count(' ') + if spaces == 0 or w >= width: + return d + e = ((width - w) / spaces) / self.scale + d = axi.Drawing(axi.text(text, self.font, extra=e)) + d = d.scale(self.scale) + return d + def measure(self, text): + return self.text(text).size + def wrap(self, text, width, line_spacing=1, align=0, justify=False): + lines = _word_wrap(text, width, self.measure) + ds = [self.text(line) for line in lines] + max_width = max(d.width for d in ds) + if justify: + jds = [self.justify_text(line, max_width) for line in lines] + ds = jds[:-1] + [ds[-1]] + spacing = line_spacing * self.max_height * self.scale + result = axi.Drawing() + y = 0 + for d in ds: + if align == 0: + x = 0 + elif align == 1: + x = max_width - d.width + else: + x = max_width / 2 - d.width / 2 + result.add(d.translate(x, y)) + y += spacing + return result diff --git a/examples/text.py b/examples/text.py index e998cb6..d78b34f 100644 --- a/examples/text.py +++ b/examples/text.py @@ -81,8 +81,8 @@ class Font(object): return result def main(): - font = Font(axi.FUTURAL, 18) - d = font.wrap(TEXT * 2, 12, 1.5, justify=True) + font = Font(axi.FUTURAL, 14) + d = font.wrap(TEXT, 11.5, 1.5, justify=True) d = d.center(12, 8.5) d.render(bounds=axi.V3_BOUNDS).write_to_png('out.png')