Ejemplo n.º 1
0
    def frame(self, lanes):
        dummy = elements.DiagramNode(None)
        dummy.xy = XY(0, 0)
        dummy.colwidth = self.colwidth
        dummy.colheight = self.colheight
        cell = self.cell(dummy, use_padding=False)

        headerbox = Box(
            cell.topleft.x - self.span_width // 2,
            (cell.topleft.y - self.node_height - self.span_height - 2),
            cell.topright.x + self.span_width // 2,
            cell.topright.y - self.span_height // 2)

        outline = Box(headerbox[0], headerbox[1], headerbox[2],
                      cell.bottom.y + self.span_height // 2)

        separators = [(XY(headerbox[0],
                          headerbox[3]), XY(headerbox[2], headerbox[3]))]

        for lane in lanes[:-1]:
            x = lane.xy.x + lane.colwidth + 1

            m = self.cell(lane, use_padding=False)
            span_width = self.spreadsheet.span_width[x] // 2
            x1 = m.right.x + span_width

            xy = (XY(x1, outline[1]), XY(x1, outline[3]))
            separators.append(xy)

        Frame = namedtuple('Frame', 'headerbox outline separators')
        return Frame(headerbox, outline, separators)
Ejemplo n.º 2
0
    def render_shape(self, drawer, _, **kwargs):
        if kwargs.get('shadow'):
            return

        m = self.metrics
        center = m.cell(self.node).center
        dots = [center]
        if self.node.group.orientation == 'landscape':
            pt = XY(center.x, center.y - m.node_height / 2)
            dots.append(pt)

            pt = XY(center.x, center.y + m.node_height / 2)
            dots.append(pt)
        else:
            pt = XY(center.x - m.node_width / 3, center.y)
            dots.append(pt)

            pt = XY(center.x + m.node_width / 3, center.y)
            dots.append(pt)

        r = m.cellsize / 2
        for dot in dots:
            box = Box(dot.x - r, dot.y - r, dot.x + r, dot.y + r)
            drawer.ellipse(box,
                           fill=self.node.linecolor,
                           outline=self.node.linecolor)
Ejemplo n.º 3
0
    def __init__(self, node, metrics=None):
        super(Actor, self).__init__(node, metrics)

        m = metrics.cell(node)
        if node.label:
            font = metrics.font_for(self.node)
            textsize = metrics.textsize(node.label, font)
            shortside = min(m.width, m.height - textsize.height)
        else:
            textsize = Size(0, 0)
            shortside = min(m.width, m.height)

        r = self.radius = shortside // 8  # radius of actor's head
        self.center = metrics.cell(node).center

        self.connectors[0] = XY(self.center.x, self.center.y - r * 9 // 2)
        self.connectors[1] = XY(self.center.x + r * 4, self.center.y)
        self.connectors[2] = XY(self.center.x,
                                self.center.y + r * 4 + textsize.height)
        self.connectors[3] = XY(self.center.x - r * 4, self.center.y)

        self.textbox = Box(m.left.x,
                           self.center.y + r * 4,
                           m.right.x,
                           self.connectors[2].y)
Ejemplo n.º 4
0
    def edge_shadow(self, edge):
        m = self.metrics
        dx, dy = m.shadow_offset

        if edge.leftnote:
            polygon = m.edge(edge).leftnoteshape
            shadow = [XY(pt.x + dx, pt.y + dy) for pt in polygon]
            if self.diagram.shadow_style == 'solid':
                self.drawer.polygon(shadow,
                                    fill=self.shadow,
                                    outline=self.shadow)
            else:
                self.drawer.polygon(shadow,
                                    fill=self.shadow,
                                    outline=self.shadow,
                                    filter='transp-blur')

        if edge.rightnote:
            polygon = m.edge(edge).rightnoteshape
            shadow = [XY(pt.x + dx, pt.y + dy) for pt in polygon]
            if self.diagram.shadow_style == 'solid':
                self.drawer.polygon(shadow,
                                    fill=self.shadow,
                                    outline=self.shadow)
            else:
                self.drawer.polygon(shadow,
                                    fill=self.shadow,
                                    outline=self.shadow,
                                    filter='transp-blur')
Ejemplo n.º 5
0
def dots_of_line(pt1, pt2):
    if pt1.x == pt2.x:  # vertical
        if pt1.y > pt2.y:
            pt2, pt1 = pt1, pt2

        for y in xrange(pt1.y, pt2.y + 1):
            yield XY(pt1.x, y)

    elif pt1.y == pt2.y:  # horizontal
        if pt1.x > pt2.x:
            pt2, pt1 = pt1, pt2

        for x in xrange(pt1.x, pt2.x + 1):
            yield XY(x, pt1.y)
    else:  # diagonal
        if pt1.x > pt2.x:
            pt2, pt1 = pt1, pt2

        # DDA (Digital Differential Analyzer) Algorithm
        m = float(pt2.y - pt1.y) / float(pt2.x - pt1.x)
        x = pt1.x
        y = pt1.y

        while x <= pt2.x + 1:
            yield XY(int(x), int(round(y)))
            x += 1
            y += m
Ejemplo n.º 6
0
        def __init__(self, node, metrics=None):
            super(NationalFlagImage, self).__init__(node, metrics)

            self.textalign = 'left'
            self.image_path = image_path

            box = metrics.cell(node).box
            bounded = (box[2] - box[0], box[3] - box[1])
            size = images.get_image_size(image_path)
            size = images.calc_image_size(size, bounded)

            pt = metrics.cell(node).center
            self.image_box = Box(pt.x - size[0] / 2, pt.y - size[1] / 2,
                                 pt.x + size[0] / 2, pt.y + size[1] / 2)

            width = metrics.node_width / 2 - size[0] / 2 + metrics.cellsize
            self.textbox = Box(pt.x + size[0] / 2, pt.y - size[1] / 2,
                               pt.x + size[0] / 2 + width, pt.y + size[1] / 2)

            size = self.metrics.textsize(node.label,
                                         self.metrics.font_for(None),
                                         self.textbox.width)

            self.connectors[0] = XY(pt.x, self.image_box[1])
            self.connectors[1] = XY(
                self.image_box.x2 + size.width + self.metrics.node_padding,
                pt.y)
            self.connectors[2] = XY(pt.x, self.image_box[3])
            self.connectors[3] = XY(self.image_box[0], pt.y)
Ejemplo n.º 7
0
 def xy(self):
     if len(self.edges) == 0:
         return XY(0, 0)
     else:
         x = min(e.left_node.xy.x for e in self.edges)
         y = min(e.order for e in self.edges) + 1
         return XY(x, y)
Ejemplo n.º 8
0
    def render_shape(self, drawer, _, **kwargs):
        fill = kwargs.get('fill')

        m = self.metrics.cell(self.node)
        r = self.metrics.cellsize * 3

        shape = [XY(m.topleft.x + r, m.topleft.y),
                 XY(m.topright.x, m.topright.y),
                 XY(m.bottomright.x - r, m.bottomright.y),
                 XY(m.bottomleft.x, m.bottomleft.y),
                 XY(m.topleft.x + r, m.topleft.y)]

        # draw outline
        if kwargs.get('shadow'):
            shape = self.shift_shadow(shape)
            if kwargs.get('style') == 'blur':
                drawer.polygon(shape, fill=fill, outline=fill,
                               filter='transp-blur')
            else:
                drawer.polygon(shape, fill=fill, outline=fill)
        elif self.node.background:
            drawer.polygon(shape, fill=self.node.color,
                           outline=self.node.color)
            drawer.image(self.textbox, self.node.background)
            drawer.polygon(shape, fill="none",
                           outline=self.node.linecolor, style=self.node.style)
        else:
            drawer.polygon(shape, fill=self.node.color,
                           outline=self.node.linecolor, style=self.node.style)
Ejemplo n.º 9
0
    def layout_nodes(self, group=None):
        networks = self.diagram.networks

        if group:
            nodes = (n for n in self.diagram.nodes if n.group == group)
        else:
            nodes = self.diagram.nodes

        for node in nodes:
            if node.layouted:
                continue

            joined = [g for g in node.networks if g.hidden is False]
            y1 = min(networks.index(g) for g in node.networks)
            if joined:
                y2 = max(networks.index(g) for g in joined)
            else:
                y2 = y1

            if node.group and node.group != self.diagram and group:
                starts = min(n.xy.x for n in group.nodes if n.layouted)
            else:
                nw = [n for n in node.networks if n.xy.y == y1][0]
                nodes = [n for n in self.diagram.nodes if nw in n.networks]
                layouted = [n for n in nodes if n.xy.x > 0]

                starts = 0
                if layouted:
                    layouted.sort(key=lambda a: a.xy.x)
                    basenode = layouted[0]
                    commonnw = set(basenode.networks) & set(node.networks)

                    if basenode.xy.y == y1:
                        starts = basenode.xy.x + 1
                    elif commonnw and list(commonnw)[0].hidden is True:
                        starts = basenode.xy.x
                    else:
                        starts = basenode.xy.x + 1 - len(nodes)

                if starts < 0:
                    starts = 0

            for x in range(starts, len(self.diagram.nodes)):
                points = [XY(x, y) for y in range(y1, y2 + 1)]
                if not set(points) & set(self.coordinates):
                    node.xy = XY(x, y1)
                    node.layouted = True
                    self.coordinates += points
                    break

            if node.group and node.group != self.diagram and group is None:
                self.layout_nodes(node.group)

        if group:
            self.set_coordinates(group)
Ejemplo n.º 10
0
    def rotate_diagram(cls, diagram):
        for node in diagram.traverse_nodes():
            node.xy = XY(node.xy.y, node.xy.x)
            node.colwidth, node.colheight = (node.colheight, node.colwidth)

        for lane in diagram.lanes:
            lane.xy = XY(lane.xy.y, lane.xy.x)
            lane.colwidth, lane.colheight = (lane.colheight, lane.colwidth)

        size = (diagram.colheight, diagram.colwidth)
        diagram.colwidth, diagram.colheight = size
Ejemplo n.º 11
0
    def __init__(self, node, metrics=None):
        super(MiniDiamond, self).__init__(node, metrics)

        r = metrics.cellsize
        m = metrics.cell(node)
        c = m.center
        self.connectors = (XY(c.x, c.y - r), XY(c.x + r,
                                                c.y), XY(c.x, c.y + r),
                           XY(c.x - r, c.y), XY(c.x, c.y - r))
        self.textbox = Box(m.top.x, m.top.y, m.right.x, m.right.y)
        self.textalign = 'left'
Ejemplo n.º 12
0
    def altblock(self, block):
        m = self.metrics.cell(block)
        self.drawer.rectangle(m, outline=block.linecolor)

        box = m.textbox
        line = [XY(box.x1, box.y2),
                XY(box.x2, box.y2),
                XY(box.x2 + self.metrics.cellsize * 2, box.y1)]
        self.drawer.line(line, fill=block.linecolor)
        self.drawer.textarea(box, block.type, fill=block.textcolor,
                             font=self.metrics.font_for(block))
Ejemplo n.º 13
0
    def __init__(self, node, metrics=None):
        super(Square, self).__init__(node, metrics)

        r = min(metrics.node_width, metrics.node_height) // 2 + \
            metrics.cellsize // 2
        pt = metrics.cell(node).center
        self.connectors = [XY(pt.x, pt.y - r),  # top
                           XY(pt.x + r, pt.y),  # right
                           XY(pt.x, pt.y + r),  # bottom
                           XY(pt.x - r, pt.y)]  # left
        self.textbox = Box(pt.x - r, pt.y - r, pt.x + r, pt.y + r)
Ejemplo n.º 14
0
    def __init__(self, node, metrics=None):
        super(EndPoint, self).__init__(node, metrics)

        m = metrics.cell(node)

        self.radius = metrics.cellsize
        self.center = m.center
        self.textbox = Box(m.top.x, m.top.y, m.right.x, m.right.y)
        self.textalign = 'left'
        self.connectors = [XY(self.center.x, self.center.y - self.radius),
                           XY(self.center.x + self.radius, self.center.y),
                           XY(self.center.x, self.center.y + self.radius),
                           XY(self.center.x - self.radius, self.center.y)]
Ejemplo n.º 15
0
    def __init__(self, node, metrics=None):
        super(Diamond, self).__init__(node, metrics)

        r = metrics.cellsize
        m = metrics.cell(node)
        self.connectors = [XY(m.top.x, m.top.y - r),
                           XY(m.right.x + r, m.right.y),
                           XY(m.bottom.x, m.bottom.y + r),
                           XY(m.left.x - r, m.left.y),
                           XY(m.top.x, m.top.y - r)]
        self.textbox = Box((self.connectors[0].x + self.connectors[3].x) // 2,
                           (self.connectors[0].y + self.connectors[3].y) // 2,
                           (self.connectors[1].x + self.connectors[2].x) // 2,
                           (self.connectors[1].y + self.connectors[2].y) // 2)
Ejemplo n.º 16
0
    def render_shape_background(self, drawer, **kwargs):
        fill = kwargs.get('fill')

        m = self.metrics.cell(self.node)
        r = self.metrics.cellsize * 2

        box = m.box
        ellipses = [
            Box(box[0], box[1], box[0] + r * 2, box[3]),
            Box(box[2] - r * 2, box[1], box[2], box[3])
        ]

        for e in ellipses:
            if kwargs.get('shadow'):
                e = self.shift_shadow(e)
                if kwargs.get('style') == 'blur':
                    drawer.ellipse(e,
                                   fill=fill,
                                   outline=fill,
                                   filter='transp-blur')
                else:
                    drawer.ellipse(e, fill=fill, outline=fill)
            else:
                drawer.ellipse(e,
                               fill=self.node.color,
                               outline=self.node.linecolor,
                               style=self.node.style)

        rect = Box(box[0] + r, box[1], box[2] - r, box[3])
        if kwargs.get('shadow'):
            rect = self.shift_shadow(rect)
            if kwargs.get('style') == 'blur':
                drawer.rectangle(rect,
                                 fill=fill,
                                 outline=fill,
                                 filter='transp-blur')
            else:
                drawer.rectangle(rect, fill=fill, outline=fill)
        else:
            drawer.rectangle(rect,
                             fill=self.node.color,
                             outline=self.node.color)

        lines = [(XY(box[0] + r, box[1]), XY(box[2] - r, box[1])),
                 (XY(box[0] + r, box[3]), XY(box[2] - r, box[3]))]
        for line in lines:
            if not kwargs.get('shadow'):
                drawer.line(line,
                            fill=self.node.linecolor,
                            style=self.node.style)
Ejemplo n.º 17
0
    def set_node_height(self, node, height=0):
        xy = XY(node.xy.x, height)
        if self.is_marked(node.lane, xy):
            return False
        node.xy = xy
        self.mark_xy(node)

        def cmp(x, y):
            if x.xy.x < y.xy.y:
                return -1
            elif x.xy.x == y.xy.y:
                return 0
            else:
                return 1

        count = 0
        children = self.get_child_nodes(node)
        children.sort(key=cmp_to_key(cmp))
        for child in children:
            if child.id in self.heightRefs:
                pass
            elif node is not None and node.xy.x >= child.xy.x:
                pass
            else:
                if node.lane == child.lane:
                    h = height
                else:
                    h = 0

                while True:
                    if self.set_node_height(child, h):
                        child.xy = XY(child.xy.x, h)
                        self.mark_xy(child)
                        self.heightRefs.append(child.id)

                        count += 1
                        break
                    elif node.lane != child.lane:
                        h += 1
                    else:
                        if count == 0:
                            return False

                        h += 1

                if node.lane == child.lane:
                    height = h + 1

        return True
Ejemplo n.º 18
0
    def labelbox(self):
        span = XY(self.span_width, self.span_height)
        node = XY(self.node_width, self.node_height)

        _dir = self.edge.direction
        node1 = self.cell(self.edge.node1, use_padding=False)
        node2 = self.cell(self.edge.node2, use_padding=False)

        if _dir == 'right':
            if self.edge.skipped:
                box = Box(node1.bottomright.x + span.x,
                          node1.bottomright.y,
                          node2.bottomleft.x - span.x,
                          node2.bottomleft.y + span.y // 2)
            else:
                box = Box(node1.topright.x, node1.topright.y - span.y // 8,
                          node2.left.x, node2.left.y - span.y // 8)

        elif _dir == 'right-up':
            box = Box(node2.left.x - span.x, node1.top.y - node.y // 2,
                      node2.bottomleft.x, node1.top.y)

        elif _dir == 'right-down':
            box = Box(node1.right.x, node2.topleft.y - span.y // 8,
                      node1.right.x + span.x, node2.left.y - span.y // 8)

        elif _dir in ('up', 'left-up', 'left', 'same'):
            if self.edge.node2.xy.y < self.edge.node1.xy.y:
                box = Box(node1.topright.x - span.x // 2 + span.x // 4,
                          node1.topright.y - span.y // 2,
                          node1.topright.x + span.x // 2 + span.x // 4,
                          node1.topright.y)
            else:
                box = Box(node1.top.x + span.x // 4,
                          node1.top.y - span.y,
                          node1.topright.x + span.x // 4,
                          node1.topright.y - span.y // 2)

        elif _dir in ('left-down', 'down'):
            box = Box(node2.top.x + span.x // 4,
                      node2.top.y - span.y,
                      node2.topright.x + span.x // 4,
                      node2.topright.y - span.y // 2)

        # shrink box
        box = Box(box[0] + span.x // 8, box[1],
                  box[2] - span.x // 8, box[3])

        return box
Ejemplo n.º 19
0
    def trunklines(self):
        metrics = self.metrics
        for network in self.diagram.networks:
            if network.hidden is False:
                self.trunkline(network)

                if (self.diagram.external_connector
                        and (network == self.diagram.networks[0])):
                    r = metrics.trunk_diameter // 2

                    pt = metrics.network(network).top
                    pt0 = XY(pt.x, pt.y - metrics.span_height * 2 // 3)
                    pt1 = XY(pt.x, pt.y - r)

                    self.drawer.line([pt0, pt1], fill=network.linecolor)
Ejemplo n.º 20
0
    def render_shape(self, drawer, format, **kwargs):
        super(Class, self).render_shape(drawer, format, **kwargs)

        fill = kwargs.get('fill')

        r = self.metrics.node_height / 3

        if not kwargs.get('shadow'):
            box = self.textbox

            line = (XY(box[0], box[1] + r), XY(box[2], box[1] + r))
            drawer.line(line, fill=fill)

            line = (XY(box[0], box[1] + r + 4), XY(box[2], box[1] + r + 4))
            drawer.line(line, fill=fill)
Ejemplo n.º 21
0
    def _node_topleft(self, node, use_padding=True):
        x, y = node.xy
        margin = self.page_margin
        padding = self.page_padding

        node_width = sum(self.node_width[i] for i in range(x))
        node_height = sum(self.node_height[i] for i in range(y))
        span_width = sum(self.span_width[i] for i in range(x + 1))
        span_height = sum(self.span_height[i] for i in range(y + 1))

        if use_padding:
            width = node.width or self.metrics.node_width
            xdiff = (self.node_width[x] - width) // 2
            if xdiff < 0:
                xdiff = 0

            height = node.height or self.metrics.node_height
            ydiff = (self.node_height[y] - height) // 2
            if ydiff < 0:
                ydiff = 0
        else:
            xdiff = 0
            ydiff = 0

        x1 = margin.x + padding[3] + node_width + span_width + xdiff
        y1 = margin.y + padding[0] + node_height + span_height + ydiff

        return XY(x1, y1)
Ejemplo n.º 22
0
    def _node_bottomright(self, node, use_padding=True):
        x = node.xy.x + node.colwidth - 1
        y = node.xy.y + node.colheight - 1
        margin = self.page_margin
        padding = self.page_padding

        node_width = sum(self.node_width[i] for i in range(x + 1))
        node_height = sum(self.node_height[i] for i in range(y + 1))
        span_width = sum(self.span_width[i] for i in range(x + 1))
        span_height = sum(self.span_height[i] for i in range(y + 1))

        if use_padding:
            width = node.width or self.metrics.node_width
            xdiff = (self.node_width[x] - width) // 2
            if xdiff < 0:
                xdiff = 0

            height = node.height or self.metrics.node_height
            ydiff = (self.node_height[y] - height) // 2
            if ydiff < 0:
                ydiff = 0
        else:
            xdiff = 0
            ydiff = 0

        x2 = margin.x + padding[3] + node_width + span_width - xdiff
        y2 = margin.y + padding[0] + node_height + span_height - ydiff

        return XY(x2, y2)
Ejemplo n.º 23
0
    def set_coordinates(self, group):
        self.set_group_size(group)

        xy = group.xy
        for i in range(xy.x, xy.x + group.colwidth):
            for j in range(xy.y, xy.y + group.colheight):
                self.coordinates.append(XY(i, j))
Ejemplo n.º 24
0
    def _scale(cls, value, ratio):
        if ratio == 1:
            return value

        klass = value.__class__
        if klass == XY:
            ret = XY(value.x * ratio, value.y * ratio)
        elif klass == Size:
            ret = Size(value.width * ratio, value.height * ratio)
        elif klass == Box:
            ret = Box(value[0] * ratio, value[1] * ratio,
                      value[2] * ratio, value[3] * ratio)
        elif klass == tuple:
            ret = tuple([cls.scale(x, ratio) for x in value])
        elif klass == list:
            ret = [cls.scale(x, ratio) for x in value]
        elif klass == EdgeLines:
            ret = EdgeLines()
            ret.polylines = cls.scale(value.polylines, ratio)
        elif klass == FontInfo:
            ret = FontInfo(value.familyname, value.path, value.size * ratio)
        elif klass == int:
            ret = value * ratio
        elif klass == str:
            ret = value
        else:
            ret = cls(value, ratio)

        return ret
Ejemplo n.º 25
0
    def fixiate_lanes(self):
        height = 0
        for lane in self.diagram.lanes:
            if self.coordinates[lane]:
                for node in self.diagram.nodes:
                    if node.lane == lane:
                        node.xy = XY(node.xy.x, node.xy.y + height)

                height += max(xy.y for xy in self.coordinates[lane]) + 1

            nodes = [n for n in self.diagram.nodes if n.lane == lane]
            x = min(n.xy.x for n in nodes)
            y = min(n.xy.y for n in nodes)
            lane.xy = XY(x, y)
            lane.colwidth = max(n.xy.x + n.colwidth for n in nodes) - x
            lane.colheight = max(n.xy.y + n.colheight for n in nodes) - y
Ejemplo n.º 26
0
 def baseline(self):
     dummy = elements.DiagramNode(None)
     dummy.xy = XY(0, 1)
     dummy.colwidth = self.metrics.node_count
     m = self.metrics.cell(dummy)
     r = self.metrics.cellsize * 3
     return (m.x1 - r, m.x2 + r)
Ejemplo n.º 27
0
 def bottomheight(self):
     height = len(self.edges) + len(self.separators)
     dummy = elements.DiagramNode(None)
     dummy.xy = XY(1, height)
     _, y = self.spreadsheet._node_bottomright(dummy, use_padding=False)
     y += self.spreadsheet.span_height[len(self.edges) + 1] // 2
     return y
Ejemplo n.º 28
0
    def render_shape(self, drawer, _, **kwargs):
        fill = kwargs.get('fill')

        m = self.metrics.cell(self.node)
        r = self.metrics.cellsize * 2

        # draw outline
        box = self.metrics.cell(self.node).box
        if kwargs.get('shadow'):
            box = self.shift_shadow(box)
            if kwargs.get('style') == 'blur':
                drawer.rectangle(box,
                                 fill=fill,
                                 outline=fill,
                                 filter='transp-blur')
            else:
                drawer.rectangle(box, fill=fill, outline=fill)
        elif self.node.background:
            drawer.rectangle(box,
                             fill=self.node.color,
                             outline=self.node.color)
            drawer.image(self.textbox, self.node.background)
            drawer.rectangle(box,
                             outline=self.node.linecolor,
                             style=self.node.style)
        else:
            drawer.rectangle(box,
                             fill=self.node.color,
                             outline=self.node.linecolor,
                             style=self.node.style)

        # draw flap
        if not kwargs.get('shadow'):
            flap = [m.topleft, XY(m.top.x, m.top.y + r), m.topright]
            drawer.line(flap, fill=self.node.linecolor, style=self.node.style)
Ejemplo n.º 29
0
    def shift(self, x, y):
        metrics = copy.copy(self)
        metrics.spreadsheet = copy.copy(self.spreadsheet)
        metrics.spreadsheet.metrics = metrics
        metrics.page_margin = XY(x, y)

        return metrics
Ejemplo n.º 30
0
    def lines(self):
        textsize = self.textsize(self._result, scaled=True)

        dx, _ = self.box.get_padding_for(textsize,
                                         halign=self.halign,
                                         padding=self.padding)

        width = self.box.width - dx + self.line_spacing
        base_xy = XY(self.box.x1, self.box.y1)
        for string in self._result:
            textsize = self.textsize(string, scaled=True)
            _, dy = self.box.get_padding_for(textsize,
                                             valign=self.valign,
                                             padding=self.line_spacing)

            height = dy
            width -= textsize.width + self.line_spacing
            for char in string:
                charsize = self.textsize(char, scaled=True)

                if self.adjustBaseline:
                    draw_xy = base_xy.shift(width, height + charsize.height)
                else:
                    draw_xy = base_xy.shift(width, height)

                yield char, draw_xy

                height += charsize.height + self.line_spacing