Commits

Peter Ward committed 43d5375

Rip out the single slide text rendering stuff and put it into shinypress.
Still needs to be tidied up.

  • Participants
  • Parent commits 0cd3e37

Comments (0)

Files changed (5)

shinypress/render.py

     :type scaling_mode: shinypress.render.ScalingMode
     """
     pass
-
-def render_text(ctx, text, size, **todo_fill_in_these_kwargs):
-    """
-    Render some text onto the given Cairo context.
-
-    :param ctx: a Cairo context
-    :param text: the text to be rendered
-    :type text: unicode
-    :param size: the dimensions of the text box
-    :type size: a (width, height) tuple
-    """
-    pass

shinypress/text/single.py

+from functools import partial
+
+from gi.repository import Pango
+from gi.repository import PangoCairo
+
+def get_box_cost(actual_size, target_size):
+    aw, ah = actual_size
+    tw, th = target_size
+    if aw > tw or ah > th:
+        return float('inf')
+    height_penalty = 1 - (float(ah) / th) ** 4
+    width_penalty = 1 - (float(aw) / tw) ** 2
+    return (width_penalty + height_penalty) / 2.0
+
+def layout_text(ctx, text, target_size, size):
+    font = Pango.FontDescription()
+    font.set_family('Droid Sans')
+    font.set_size(size * Pango.SCALE)
+
+    layout = PangoCairo.create_layout(ctx)
+    layout.set_font_description(font)
+    layout.set_justify(True)
+    layout.set_text(text, -1)
+    layout.set_width(target_size[0] * Pango.SCALE)
+    layout.set_spacing(size * 0.2 * Pango.SCALE)
+
+    actual_size = layout.get_pixel_size()
+    cost = get_box_cost(actual_size, target_size)
+
+    return cost, layout
+
+def generate_sizes(min_size, max_size, scale):
+    if not 0 < scale < 1:
+        scale = 1.0 / scale
+    assert 0 < scale < 1
+
+    sizes = []
+    # generate the minor scale
+    size = min_size
+    while size < max_size:
+        sizes.append(size)
+        size /= scale
+    # generate the major scale
+    size = max_size
+    while size > min_size:
+        sizes.append(size)
+        size *= scale
+
+    return sorted(set(sizes))
+
+def minimise(sizes, fn):
+    """Find the maximum size with the lowest cost."""
+    best_cost = float('inf')
+    best = None
+
+    while len(sizes) > 1:
+        # find the middle element
+        i = len(sizes) // 2
+        size = sizes[i]
+        cost, layout = fn(size)
+
+        # if this beat our current best, we should only try sizes larger than
+        # this one.
+        if cost <= best_cost:
+            best_cost = cost
+            best = (size, layout)
+            sizes = sizes[i + 1:]
+        # otherwise, this sizes and anything larger is bad
+        else:
+            sizes = sizes[:i]
+
+    return best_cost, best
+
+def render_text(
+    ctx, text, size,
+    min_font_size, max_font_size, factor,
+):
+    """
+    Render text on a Cairo context.
+
+    This method takes a minimum and maximum font size, and scales both by
+    ``factor`` in order to determine the range of font sizes that should be
+    tried.
+
+    The font size which produces the lowest cost according to layout_text will
+    be used.
+
+    :param ctx: the Cairo context to draw on
+    :param text: the text to draw
+    :type text: unicode
+    :param size: the dimensions of the text box
+    :type size: a (width, height) tuple
+    :param min_font_size: minimum font size, in points
+    :param max_font_size: maximum font size, in points
+    :param factor: amount to scale by
+    :type factor: float
+    """
+    fn = partial(layout_text, ctx, text, size)
+    sizes = generate_sizes(min_font_size, max_font_size, factor)
+
+    cost, (size, layout) = minimise(sizes, fn)
+    PangoCairo.show_layout(ctx, layout)

slide-breaking/main.py

-from functools import partial
+import cairo
 
-import cairo
-from gi.repository import PangoCairo
-
-from text.single import generate_sizes, layout_text
-from utils import minimise
+from text.single import render_text
 
 TEXT = open('sample.txt', 'rU').read()
 
 surf = cairo.PDFSurface('blah.pdf', WIDTH, HEIGHT)
 ctx = cairo.Context(surf)
 
-fn = partial(layout_text, ctx, TEXT, (WIDTH, HEIGHT))
-sizes = generate_sizes(18, 48, 1.25)
-
-score, (size, layout) = minimise(sizes, fn)
-PangoCairo.show_layout(ctx, layout)
+render_text(ctx, TEXT, (WIDTH, HEIGHT), 18, 48, 1.25)
 surf.show_page()

slide-breaking/text/single.py

-from gi.repository import Pango
-from gi.repository import PangoCairo
-
-def score_box(actual_size, target_size):
-    aw, ah = actual_size
-    tw, th = target_size
-    if aw > tw or ah > th:
-        return float('inf')
-    height_penalty = 1 - (float(ah) / th) ** 4
-    width_penalty = 1 - (float(aw) / tw) ** 2
-    return (width_penalty + height_penalty) / 2.0
-
-def layout_text(ctx, text, target_size, size):
-    font = Pango.FontDescription()
-    font.set_family('Droid Sans')
-    font.set_size(size * Pango.SCALE)
-
-    layout = PangoCairo.create_layout(ctx)
-    layout.set_font_description(font)
-    layout.set_justify(True)
-    layout.set_text(text, -1)
-    layout.set_width(target_size[0] * Pango.SCALE)
-    layout.set_spacing(size * 0.2 * Pango.SCALE)
-
-    actual_size = layout.get_pixel_size()
-    score = score_box(actual_size, target_size)
-
-    return score, layout
-
-def generate_sizes(min_size, max_size, scale):
-    if not 0 < scale < 1:
-        scale = 1.0 / scale
-    assert 0 < scale < 1
-
-    sizes = []
-    # generate the minor scale
-    size = min_size
-    while size < max_size:
-        sizes.append(size)
-        size /= scale
-    # generate the major scale
-    size = max_size
-    while size > min_size:
-        sizes.append(size)
-        size *= scale
-
-    return sorted(set(sizes))

slide-breaking/utils.py

-def minimise(sizes, fn):
-    """Find the maximum size with the lowest score."""
-    best_score = float('inf')
-    best = None
-
-    while len(sizes) > 1:
-        # find the middle element
-        i = len(sizes) // 2
-        size = sizes[i]
-        score, layout = fn(size)
-
-        # if this beat our current best, we should only try sizes larger than
-        # this one.
-        if score <= best_score:
-            best_score = score
-            best = (size, layout)
-            sizes = sizes[i + 1:]
-        # otherwise, this sizes and anything larger is bad
-        else:
-            sizes = sizes[:i]
-
-    return best_score, best
-