[blockdiag] 04/29: Import Upstream version 1.1.4

Andreas Tille tille at debian.org
Tue Jan 10 21:35:57 UTC 2017


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository blockdiag.

commit fdb8942a1d234431d73001f81b2f3904cbbd09ef
Author: Andreas Tille <tille at debian.org>
Date:   Tue Jan 10 11:08:02 2017 +0100

    Import Upstream version 1.1.4
---
 PKG-INFO                                          | 14 +++-
 blockdiag.1                                       |  6 ++
 src/README.txt                                    | 12 +++
 src/blockdiag.egg-info/PKG-INFO                   | 14 +++-
 src/blockdiag.egg-info/SOURCES.txt                |  2 +
 src/blockdiag/__init__.py                         |  2 +-
 src/blockdiag/builder.py                          | 19 ++---
 src/blockdiag/command.py                          |  3 +-
 src/blockdiag/drawer.py                           | 10 +--
 src/blockdiag/elements.py                         | 49 +++++++++--
 src/blockdiag/imagedraw/pdf.py                    |  6 ++
 src/blockdiag/imagedraw/png.py                    | 14 +++-
 src/blockdiag/imagedraw/svg.py                    |  6 ++
 src/blockdiag/metrics.py                          | 76 ++++++++++++++++-
 src/blockdiag/noderenderer/__init__.py            |  2 +-
 src/blockdiag/noderenderer/actor.py               | 16 ++--
 src/blockdiag/parser.py                           |  5 +-
 src/blockdiag/tests/diagrams/circular_ref2.diag   |  4 +
 src/blockdiag/tests/diagrams/edge_datamodels.diag |  3 +
 src/blockdiag/tests/test_boot_params.py           | 17 ++++
 src/blockdiag/tests/test_builder.py               | 12 ++-
 src/blockdiag/tests/test_rst_directives.py        | 99 ++++++++++++++++++++++-
 src/blockdiag/utils/bootstrap.py                  | 13 ++-
 src/blockdiag/utils/rst/directives.py             | 32 +++++++-
 24 files changed, 384 insertions(+), 52 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 89eb60a..6575733 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: blockdiag
-Version: 1.1.2
+Version: 1.1.4
 Summary: blockdiag generate block-diagram image file from spec-text file.
 Home-page: http://blockdiag.com/
 Author: Takeshi Komiya
@@ -121,6 +121,18 @@ Description: `blockdiag` generate block-diagram image file from spec-text file.
         History
         =======
         
+        1.1.4 (2012-03-15)
+        ------------------
+        * Add new edge.hstyles: oneone, onemany, manyone, manymany
+        * Add edge attribute: description (for build description-tables)
+        * Fix bugs
+        
+        1.1.3 (2012-02-13)
+        ------------------
+        * Add new edge type for data-models (thanks to David Lang)
+        * Add --no-transparency option
+        * Fix bugs
+        
         1.1.2 (2011-12-26)
         ------------------
         * Support font-index for TrueType Font Collections (.ttc file)
diff --git a/blockdiag.1 b/blockdiag.1
index 3f0b711..511029a 100644
--- a/blockdiag.1
+++ b/blockdiag.1
@@ -53,6 +53,9 @@ write diagram to FILE
 .B \-f FONT, \-\-font=FONT
 use FONT to draw diagram
 .TP
+.B \-\-fontmap=FONT
+use FONTMAP file to draw diagram
+.TP
 .B \-s, \-\-separate
 Separate diagram images for each group
 .TP
@@ -61,6 +64,9 @@ Output diagram as TYPE format
 .TP
 .B \-\-nodoctype
 Do not output doctype definition tags (SVG only)
+.TP
+.B \-\-no-transparency
+do not make transparent background of diagram (PNG only)
 .SH SEE ALSO
 The programs are documented fully by
 .br
diff --git a/src/README.txt b/src/README.txt
index fbb9010..cf3744b 100644
--- a/src/README.txt
+++ b/src/README.txt
@@ -113,6 +113,18 @@ Apache License 2.0
 History
 =======
 
+1.1.4 (2012-03-15)
+------------------
+* Add new edge.hstyles: oneone, onemany, manyone, manymany
+* Add edge attribute: description (for build description-tables)
+* Fix bugs
+
+1.1.3 (2012-02-13)
+------------------
+* Add new edge type for data-models (thanks to David Lang)
+* Add --no-transparency option
+* Fix bugs
+
 1.1.2 (2011-12-26)
 ------------------
 * Support font-index for TrueType Font Collections (.ttc file)
diff --git a/src/blockdiag.egg-info/PKG-INFO b/src/blockdiag.egg-info/PKG-INFO
index 89eb60a..6575733 100644
--- a/src/blockdiag.egg-info/PKG-INFO
+++ b/src/blockdiag.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: blockdiag
-Version: 1.1.2
+Version: 1.1.4
 Summary: blockdiag generate block-diagram image file from spec-text file.
 Home-page: http://blockdiag.com/
 Author: Takeshi Komiya
@@ -121,6 +121,18 @@ Description: `blockdiag` generate block-diagram image file from spec-text file.
         History
         =======
         
+        1.1.4 (2012-03-15)
+        ------------------
+        * Add new edge.hstyles: oneone, onemany, manyone, manymany
+        * Add edge attribute: description (for build description-tables)
+        * Fix bugs
+        
+        1.1.3 (2012-02-13)
+        ------------------
+        * Add new edge type for data-models (thanks to David Lang)
+        * Add --no-transparency option
+        * Fix bugs
+        
         1.1.2 (2011-12-26)
         ------------------
         * Support font-index for TrueType Font Collections (.ttc file)
diff --git a/src/blockdiag.egg-info/SOURCES.txt b/src/blockdiag.egg-info/SOURCES.txt
index fadb6a7..77aa2f1 100644
--- a/src/blockdiag.egg-info/SOURCES.txt
+++ b/src/blockdiag.egg-info/SOURCES.txt
@@ -90,6 +90,7 @@ src/blockdiag/tests/diagrams/background_url_image.diag
 src/blockdiag/tests/diagrams/beginpoint_color.diag
 src/blockdiag/tests/diagrams/branched.diag
 src/blockdiag/tests/diagrams/circular_ref.diag
+src/blockdiag/tests/diagrams/circular_ref2.diag
 src/blockdiag/tests/diagrams/circular_ref_and_parent_node.diag
 src/blockdiag/tests/diagrams/circular_ref_to_root.diag
 src/blockdiag/tests/diagrams/circular_skipped_edge.diag
@@ -98,6 +99,7 @@ src/blockdiag/tests/diagrams/diagram_attributes.diag
 src/blockdiag/tests/diagrams/diagram_attributes_order.diag
 src/blockdiag/tests/diagrams/diagram_orientation.diag
 src/blockdiag/tests/diagrams/edge_attribute.diag
+src/blockdiag/tests/diagrams/edge_datamodels.diag
 src/blockdiag/tests/diagrams/edge_label.diag
 src/blockdiag/tests/diagrams/edge_layout_landscape.diag
 src/blockdiag/tests/diagrams/edge_layout_portrait.diag
diff --git a/src/blockdiag/__init__.py b/src/blockdiag/__init__.py
index 40e1275..c070b78 100644
--- a/src/blockdiag/__init__.py
+++ b/src/blockdiag/__init__.py
@@ -13,4 +13,4 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
-__version__ = '1.1.2'
+__version__ = '1.1.4'
diff --git a/src/blockdiag/builder.py b/src/blockdiag/builder.py
index 4481585..4639319 100644
--- a/src/blockdiag/builder.py
+++ b/src/blockdiag/builder.py
@@ -172,13 +172,13 @@ class DiagramLayoutManager:
     def do_layout(self):
         self.detect_circulars()
 
-        self.set_node_width()
+        self.set_node_xpos()
         self.adjust_node_order()
 
         height = 0
         for node in self.diagram.nodes:
             if node.xy.x == 0:
-                self.set_node_height(node, height)
+                self.set_node_ypos(node, height)
                 height = max(xy.y for xy in self.coordinates) + 1
 
     def get_related_nodes(self, node, parent=False, child=False):
@@ -236,7 +236,8 @@ class DiagramLayoutManager:
         for child in self.get_child_nodes(node):
             if child in parents:
                 i = parents.index(child)
-                self.circulars.append(parents[i:])
+                if parents[i:] not in self.circulars:
+                    self.circulars.append(parents[i:])
             else:
                 self.detect_circulars_sub(child, parents + [child])
 
@@ -266,7 +267,7 @@ class DiagramLayoutManager:
 
         return False
 
-    def set_node_width(self, depth=0):
+    def set_node_xpos(self, depth=0):
         for node in self.diagram.nodes:
             if node.xy.x != depth:
                 continue
@@ -283,7 +284,7 @@ class DiagramLayoutManager:
 
         depther_node = [x for x in self.diagram.nodes if x.xy.x > depth]
         if len(depther_node) > 0:
-            self.set_node_width(depth + 1)
+            self.set_node_xpos(depth + 1)
 
     def adjust_node_order(self):
         for node in list(self.diagram.nodes):
@@ -380,7 +381,7 @@ class DiagramLayoutManager:
             for h in range(height):
                 self.coordinates.append(XY(xy.x + w, xy.y + h))
 
-    def set_node_height(self, node, height=0):
+    def set_node_ypos(self, node, height=0):
         for x in range(node.colwidth):
             for y in range(node.colheight):
                 xy = XY(node.xy.x + x, height + y)
@@ -406,7 +407,7 @@ class DiagramLayoutManager:
                 pass
             else:
                 if isinstance(node, NodeGroup):
-                    parent_height = self.get_parent_node_height(node, child)
+                    parent_height = self.get_parent_node_ypos(node, child)
                     if parent_height and parent_height > height:
                         height = parent_height
 
@@ -417,7 +418,7 @@ class DiagramLayoutManager:
                         height = max(coord) + 1
 
                 while True:
-                    if self.set_node_height(child, height):
+                    if self.set_node_ypos(child, height):
                         child.xy = XY(child.xy.x, height)
                         self.mark_xy(child.xy, child.colwidth, child.colheight)
                         self.heightRefs.append(child.id)
@@ -455,7 +456,7 @@ class DiagramLayoutManager:
 
         return ret
 
-    def get_parent_node_height(self, parent, child):
+    def get_parent_node_ypos(self, parent, child):
         heights = []
         for e in DiagramEdge.find(parent, child):
             y = parent.xy.y
diff --git a/src/blockdiag/command.py b/src/blockdiag/command.py
index a8c5e00..3d67a80 100644
--- a/src/blockdiag/command.py
+++ b/src/blockdiag/command.py
@@ -53,7 +53,8 @@ class BlockdiagApp(Application):
                 draw = DiagramDraw(self.options.type, group, outfile,
                                    fontmap=self.fontmap,
                                    antialias=self.options.antialias,
-                                   nodoctype=self.options.nodoctype)
+                                   nodoctype=self.options.nodoctype,
+                                   transparency=self.options.transparency)
                 draw.draw()
                 draw.save()
 
diff --git a/src/blockdiag/drawer.py b/src/blockdiag/drawer.py
index 2ad6deb..5c3b6bd 100644
--- a/src/blockdiag/drawer.py
+++ b/src/blockdiag/drawer.py
@@ -49,7 +49,8 @@ class DiagramDraw(object):
             self.shadow = kwargs.get('shadow', (0, 0, 0))
 
         kwargs = dict(nodoctype=kwargs.get('nodoctype'),
-                      scale_ratio=self.scale_ratio)
+                      scale_ratio=self.scale_ratio,
+                      transparency=kwargs.get('transparency'))
         drawer = imagedraw.create(self.format, self.filename,
                                   self.pagesize(), **kwargs)
         if drawer is None:
@@ -72,13 +73,10 @@ class DiagramDraw(object):
 
     @property
     def edges(self):
-        for edge in (e for e in self.diagram.edges  if e.style != 'none'):
+        edges = self.diagram.traverse_edges(preorder=True)
+        for edge in (e for e in edges  if e.style != 'none'):
             yield edge
 
-        for group in self.groups:
-            for edge in (e for e in group.edges  if e.style != 'none'):
-                yield edge
-
     def pagesize(self, scaled=False):
         if scaled:
             metrics = self.metrics
diff --git a/src/blockdiag/elements.py b/src/blockdiag/elements.py
index 557c8f0..c0a0982 100644
--- a/src/blockdiag/elements.py
+++ b/src/blockdiag/elements.py
@@ -216,10 +216,9 @@ class DiagramNode(Element):
         self.linecolor = images.color_to_rgb(color)
 
     def set_shape(self, value):
-        try:
-            noderenderer.get(value)
+        if noderenderer.get(value):
             self.shape = value
-        except:
+        else:
             msg = "WARNING: unknown node shape: %s\n" % value
             raise AttributeError(msg)
 
@@ -308,6 +307,19 @@ class NodeGroup(Element):
             else:
                 yield node
 
+    def traverse_edges(self, preorder=False):
+        if preorder:
+            for edge in self.edges:
+                yield edge
+
+        for group in self.traverse_groups(preorder):
+            for edge in group.traverse_edges(preorder):
+                yield edge
+
+        if not preorder:
+            for edge in self.edges:
+                yield edge
+
     def traverse_groups(self, preorder=False):
         for node in self.traverse_nodes(preorder=preorder):
             if isinstance(node, NodeGroup):
@@ -438,6 +450,7 @@ class DiagramEdge(Base):
         self.skipped = 0
 
         self.label = None
+        self.description = None
         self.dir = 'forward'
         self.color = self.basecolor
         self.hstyle = None
@@ -460,6 +473,15 @@ class DiagramEdge(Base):
         value = value.lower()
         if value in ('back', 'both', 'none', 'forward'):
             self.dir = value
+        elif value == '-<':
+            self.dir = 'forward'
+            self.hstyle = 'onemany'
+        elif value == '>-':
+            self.dir = 'back'
+            self.hstyle = 'manyone'
+        elif value == '>-<':
+            self.dir = 'both'
+            self.hstyle = 'manymany'
         elif value == '->':
             self.dir = 'forward'
         elif value == '<-':
@@ -479,6 +501,18 @@ class DiagramEdge(Base):
         value = value.lower()
         if value in ('generalization', 'composition', 'aggregation'):
             self.hstyle = value
+        elif value == 'oneone':
+            self.dir = 'none'
+            self.hstyle = value
+        elif value == 'onemany':
+            self.dir = 'forward'
+            self.hstyle = value
+        elif value == 'manyone':
+            self.dir = 'back'
+            self.hstyle = value
+        elif value == 'manymany':
+            self.dir = 'both'
+            self.hstyle = value
         else:
             msg = "WARNING: unknown edge hstyle: %s\n" % value
             raise AttributeError(msg)
@@ -521,6 +555,10 @@ class DiagramEdge(Base):
 
         return dir
 
+    def to_desctable(self):
+        label = "%s -> %s" % (self.node1.label, self.node2.label)
+        return [label, self.description]
+
 
 class Diagram(NodeGroup):
     _DiagramNode = DiagramNode
@@ -562,10 +600,9 @@ class Diagram(NodeGroup):
         plugins.load(modules, diagram=self)
 
     def set_default_shape(self, value):
-        try:
-            noderenderer.get(value)
+        if noderenderer.get(value):
             DiagramNode.set_default_shape(value)
-        except:
+        else:
             msg = "WARNING: unknown node shape: %s\n" % value
             raise AttributeError(msg)
 
diff --git a/src/blockdiag/imagedraw/pdf.py b/src/blockdiag/imagedraw/pdf.py
index b6d7856..377ae08 100644
--- a/src/blockdiag/imagedraw/pdf.py
+++ b/src/blockdiag/imagedraw/pdf.py
@@ -149,10 +149,16 @@ class PDFImageDraw(object):
             outline = kwargs.get('outline')
             self.rectangle(lines.outlinebox, fill='white', outline=outline)
 
+        rendered = False
         for string, xy in lines.lines:
             self.text(xy, string, font, **kwargs)
+            rendered = True
         self.canvas.restoreState()
 
+        if not rendered and font.size > 0:
+            font.size = int(font.size * 0.8)
+            self.textarea(box, string, font, **kwargs)
+
     def line(self, xy, **kwargs):
         self.set_stroke_color(kwargs.get('fill', 'none'))
         self.set_style(kwargs.get('style'), kwargs.get('thick'))
diff --git a/src/blockdiag/imagedraw/png.py b/src/blockdiag/imagedraw/png.py
index c87dd35..0624e5a 100644
--- a/src/blockdiag/imagedraw/png.py
+++ b/src/blockdiag/imagedraw/png.py
@@ -111,8 +111,9 @@ class ImageDrawEx(object):
             self.image = Image.new('RGB', size, (256, 256, 256))
 
             # set transparency to background
-            alpha = Image.new('L', size, 1)
-            self.image.putalpha(alpha)
+            if kwargs.get('transparency'):
+                alpha = Image.new('L', size, 1)
+                self.image.putalpha(alpha)
 
         self.filename = filename
         self.scale_ratio = kwargs.get('scale_ratio', 1)
@@ -298,7 +299,8 @@ class ImageDrawEx(object):
             else:
                 _box = box
 
-            text = ImageDrawEx(None, _box.size, parent=self, mode=self.mode)
+            text = ImageDrawEx(None, _box.size, parent=self, mode=self.mode,
+                               transparency=True)
             textbox = (0, 0, _box.width, _box.height)
             text.textarea(textbox, string, font, **kwargs)
 
@@ -313,8 +315,14 @@ class ImageDrawEx(object):
             outline = kwargs.get('outline')
             self.rectangle(lines.outlinebox, fill='white', outline=outline)
 
+        rendered = False
         for string, xy in lines.lines:
             self.text(xy, string, font, **kwargs)
+            rendered = True
+
+        if not rendered and font.size > 0:
+            font.size = int(font.size * 0.8)
+            self.textarea(box, string, font, **kwargs)
 
     def loadImage(self, filename, box):
         box_width = box[2] - box[0]
diff --git a/src/blockdiag/imagedraw/svg.py b/src/blockdiag/imagedraw/svg.py
index 86c608d..122dea3 100644
--- a/src/blockdiag/imagedraw/svg.py
+++ b/src/blockdiag/imagedraw/svg.py
@@ -137,8 +137,14 @@ class SVGImageDrawElement(object):
             outline = kwargs.get('outline')
             self.rectangle(lines.outlinebox, fill='white', outline=outline)
 
+        rendered = False
         for string, xy in lines.lines:
             self.text(xy, string, font, **kwargs)
+            rendered = True
+
+        if not rendered and font.size > 0:
+            font.size = int(font.size * 0.8)
+            self.textarea(box, string, font, **kwargs)
 
     def line(self, xy, **kwargs):
         fill = kwargs.get('fill')
diff --git a/src/blockdiag/metrics.py b/src/blockdiag/metrics.py
index 8f5d86b..7563cc7 100644
--- a/src/blockdiag/metrics.py
+++ b/src/blockdiag/metrics.py
@@ -425,6 +425,34 @@ class EdgeMetrics(object):
             head.append(XY(xy.x + cell * 2, xy.y))
             head.append(XY(xy.x + cell, xy.y + cell / 2))
             head.append(XY(xy.x + 1, xy.y))
+        elif direct == 'rup':
+            xy = node.bottom
+            head.append(XY(xy.x, xy.y + cell))
+            head.append(XY(xy.x - cell, xy.y + 1))
+            head.append(XY(xy.x, xy.y + 1 * 2))
+            head.append(XY(xy.x + cell, xy.y + 1))
+            head.append(XY(xy.x, xy.y + cell))
+        elif direct == 'rdown':
+            xy = node.top
+            head.append(XY(xy.x, xy.y - cell))
+            head.append(XY(xy.x - cell, xy.y - 1))
+            head.append(XY(xy.x, xy.y - 1 * 2))
+            head.append(XY(xy.x + cell, xy.y - 1))
+            head.append(XY(xy.x, xy.y - cell))
+        elif direct == 'rright':
+            xy = node.left
+            head.append(XY(xy.x - cell, xy.y))
+            head.append(XY(xy.x - 1, xy.y - cell))
+            head.append(XY(xy.x - 1 * 2, xy.y))
+            head.append(XY(xy.x - 1, xy.y + cell))
+            head.append(XY(xy.x - cell, xy.y))
+        elif direct == 'rleft':
+            xy = node.right
+            head.append(XY(xy.x + cell, xy.y))
+            head.append(XY(xy.x + 1, xy.y - cell))
+            head.append(XY(xy.x + 1 * 2, xy.y))
+            head.append(XY(xy.x + 1, xy.y + cell))
+            head.append(XY(xy.x + cell, xy.y))
 
         if self.edge.hstyle not in ('composition', 'aggregation'):
             head.pop(2)
@@ -447,6 +475,14 @@ class EdgeMetrics(object):
                 lines.polylines[0].insert(0, XY(pt.x + cell, pt.y))
             elif head1 == 'down':
                 lines.polylines[0].insert(0, XY(pt.x, pt.y - cell))
+            elif head1 == 'rup':
+                lines.polylines[0].insert(0, XY(pt.x, pt.y + cell))
+            elif head1 == 'rright':
+                lines.polylines[0].insert(0, XY(pt.x - cell, pt.y))
+            elif head1 == 'rleft':
+                lines.polylines[0].insert(0, XY(pt.x + cell, pt.y))
+            elif head1 == 'rdown':
+                lines.polylines[0].insert(0, XY(pt.x, pt.y - cell))
 
         if head2:
             pt = lines.polylines[-1].pop()
@@ -458,6 +494,14 @@ class EdgeMetrics(object):
                 lines.polylines[-1].append(XY(pt.x + cell, pt.y))
             elif head2 == 'down':
                 lines.polylines[-1].append(XY(pt.x, pt.y - cell))
+            elif head2 == 'rup':
+                lines.polylines[-1].append(XY(pt.x, pt.y + cell))
+            elif head2 == 'rright':
+                lines.polylines[-1].append(XY(pt.x - cell, pt.y))
+            elif head2 == 'rleft':
+                lines.polylines[-1].append(XY(pt.x + cell, pt.y))
+            elif head2 == 'rdown':
+                lines.polylines[-1].append(XY(pt.x, pt.y - cell))
 
         return lines
 
@@ -486,6 +530,9 @@ class LandscapeEdgeMetrics(EdgeMetrics):
                     heads.append('left')
                 else:
                     heads.append('up')
+
+            if self.edge.hstyle in ('manyone', 'manymany'):
+                heads[-1] = 'r' + heads[-1]
         else:
             heads.append(None)
 
@@ -496,6 +543,9 @@ class LandscapeEdgeMetrics(EdgeMetrics):
                 heads.append('up')
             elif dir in ('left-up', 'left', 'left-down', 'down', 'same'):
                 heads.append('down')
+
+            if self.edge.hstyle in ('onemany', 'manymany'):
+                heads[-1] = 'r' + heads[-1]
         else:
             heads.append(None)
 
@@ -680,6 +730,9 @@ class PortraitEdgeMetrics(EdgeMetrics):
                     heads.append('left')
                 else:
                     heads.append('up')
+
+            if self.edge.hstyle in ('manyone', 'manymany'):
+                heads[-1] = 'r' + heads[-1]
         else:
             heads.append(None)
 
@@ -693,6 +746,9 @@ class PortraitEdgeMetrics(EdgeMetrics):
                 heads.append('down')
             elif dir in ('left-up', 'left', 'left-down', 'down', 'right-down'):
                 heads.append('down')
+
+            if self.edge.hstyle in ('onemany', 'manymany'):
+                heads[-1] = 'r' + heads[-1]
         else:
             heads.append(None)
 
@@ -840,12 +896,18 @@ class FlowchartLandscapeEdgeMetrics(LandscapeEdgeMetrics):
 
         if self.edge.direction == 'right-down':
             if self.edge.dir in ('back', 'both'):
-                heads.append('up')
+                if self.edge.hstyle in ('manyone', 'manymany'):
+                    heads.append('rup')
+                else:
+                    heads.append('up')
             else:
                 heads.append(None)
 
             if self.edge.dir in ('forward', 'both'):
-                heads.append('right')
+                if self.edge.hstyle in ('onemany', 'manymany'):
+                    heads.append('rright')
+                else:
+                    heads.append('right')
             else:
                 heads.append(None)
         else:
@@ -909,12 +971,18 @@ class FlowchartPortraitEdgeMetrics(PortraitEdgeMetrics):
 
         if self.edge.direction == 'right-down':
             if self.edge.dir in ('back', 'both'):
-                heads.append('left')
+                if self.edge.hstyle in ('manyone', 'manymany'):
+                    heads.append('left')
+                else:
+                    heads.append('left')
             else:
                 heads.append(None)
 
             if self.edge.dir in ('forward', 'both'):
-                heads.append('down')
+                if self.edge.dir in ('onemany', 'manymany'):
+                    heads.append('rdown')
+                else:
+                    heads.append('down')
             else:
                 heads.append(None)
         else:
diff --git a/src/blockdiag/noderenderer/__init__.py b/src/blockdiag/noderenderer/__init__.py
index ad3d3b2..0165c21 100644
--- a/src/blockdiag/noderenderer/__init__.py
+++ b/src/blockdiag/noderenderer/__init__.py
@@ -46,7 +46,7 @@ def get(shape):
         if name in renderers:
             return renderers[name]
 
-    return renderers[shape]
+    return renderers.get(shape)
 
 
 class NodeShape(object):
diff --git a/src/blockdiag/noderenderer/actor.py b/src/blockdiag/noderenderer/actor.py
index cae08b6..d1ada53 100644
--- a/src/blockdiag/noderenderer/actor.py
+++ b/src/blockdiag/noderenderer/actor.py
@@ -24,18 +24,18 @@ class Actor(NodeShape):
 
         shortside = min(self.node.width or metrics.node_height,
                         self.node.height or metrics.node_height)
-        self.radius = shortside / 8  # radius of actor's head
+        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 - self.radius * 4)
-        self.connectors[1] = XY(self.center.x + self.radius * 4, self.center.y)
-        self.connectors[2] = XY(self.center.x, self.center.y + self.radius * 4)
-        self.connectors[3] = XY(self.center.x - self.radius * 4, self.center.y)
+        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)
+        self.connectors[3] = XY(self.center.x - r * 4, self.center.y)
 
     def head_part(self):
-        r = self.radius
-        pt = self.metrics.cell(self.node).center
-        return Box(pt.x - r, pt.y - r * 4, pt.x + r, pt.y - r * 2)
+        r = self.radius * 3 / 2
+        pt = self.metrics.cell(self.node).center.shift(y=-self.radius * 3)
+        return Box(pt.x - r, pt.y - r, pt.x + r, pt.y + r)
 
     def body_part(self):
         r = self.radius
diff --git a/src/blockdiag/parser.py b/src/blockdiag/parser.py
index 34a85f7..e9d57d0 100644
--- a/src/blockdiag/parser.py
+++ b/src/blockdiag/parser.py
@@ -69,7 +69,7 @@ def tokenize(str):
         ('Space',   (r'[ \t\r\n]+',)),
         ('Name',    (ur'[A-Za-z_0-9\u0080-\uffff]'
                      ur'[A-Za-z_\-.0-9\u0080-\uffff]*',)),
-        ('Op',      (r'[{};,=\[\]]|(<->)|(<-)|(--)|(->)',)),
+        ('Op',      (r'[{};,=\[\]]|(<->)|(<-)|(--)|(->)|(>-<)|(-<)|(>-)',)),
         ('Number',  (r'-?(\.[0-9]+)|([0-9]+(\.[0-9]*)?)',)),
         ('String',  (r'(?P<quote>"|\').*?(?<!\\)(?P=quote)', DOTALL)),
     ]
@@ -111,7 +111,8 @@ def parse(seq):
     # We use a forward_decl becaue of circular definitions like (stmt_list ->
     # stmt -> group -> stmt_list)
     group = forward_decl()
-    edge_rhs = (op('->') | op('--') | op('<-') | op('<->')) + node_list
+    edge_rhs = (op('->') | op('--') | op('<-') | op('<->') |
+                op('>-') | op('-<') | op('>-<')) + node_list
     edge_stmt = (
         node_list +
         edge_rhs +
diff --git a/src/blockdiag/tests/diagrams/circular_ref2.diag b/src/blockdiag/tests/diagrams/circular_ref2.diag
new file mode 100644
index 0000000..8e8115b
--- /dev/null
+++ b/src/blockdiag/tests/diagrams/circular_ref2.diag
@@ -0,0 +1,4 @@
+{
+  A -> B -> C -> D, E -> F -> C;
+  Z;
+}
diff --git a/src/blockdiag/tests/diagrams/edge_datamodels.diag b/src/blockdiag/tests/diagrams/edge_datamodels.diag
new file mode 100644
index 0000000..a1dff47
--- /dev/null
+++ b/src/blockdiag/tests/diagrams/edge_datamodels.diag
@@ -0,0 +1,3 @@
+{
+  A >- B >-< C -< D;
+}
diff --git a/src/blockdiag/tests/test_boot_params.py b/src/blockdiag/tests/test_boot_params.py
index 1555fee..a4b1053 100644
--- a/src/blockdiag/tests/test_boot_params.py
+++ b/src/blockdiag/tests/test_boot_params.py
@@ -65,6 +65,23 @@ class TestBootParams(unittest2.TestCase):
         sys.argv = ['', '-Tpdf', '--nodoctype', 'input.diag']
         self.parser.parse()
 
+    @assertRaises(RuntimeError)
+    @argv_wrapper
+    def test_svg_notransparency_option(self):
+        sys.argv = ['', '-Tsvg', '--no-transparency', 'input.diag']
+        self.parser.parse()
+
+    @argv_wrapper
+    def test_png_notransparency_option(self):
+        sys.argv = ['', '-Tpng', '--no-transparency', 'input.diag']
+        self.parser.parse()
+
+    @assertRaises(RuntimeError)
+    @argv_wrapper
+    def test_pdf_notransparency_option(self):
+        sys.argv = ['', '-Tpdf', '--no-transparency', 'input.diag']
+        self.parser.parse()
+
     @argv_wrapper
     def test_config_option(self):
         try:
diff --git a/src/blockdiag/tests/test_builder.py b/src/blockdiag/tests/test_builder.py
index 3608cc8..d96942a 100644
--- a/src/blockdiag/tests/test_builder.py
+++ b/src/blockdiag/tests/test_builder.py
@@ -34,16 +34,22 @@ def test_diagram_attributes_order_diagram():
 
 def test_circular_ref_to_root_diagram():
     positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
-                  'D': (2, 1), 'Z': (0, 2)}
+                 'D': (2, 1), 'Z': (0, 2)}
     __validate_node_attributes('circular_ref_to_root.diag', xy=positions)
 
 
 def test_circular_ref_diagram():
     positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0),
-                  'D': (2, 1), 'Z': (0, 2)}
+                 'D': (2, 1), 'Z': (0, 2)}
     __validate_node_attributes('circular_ref.diag', xy=positions)
 
 
+def test_circular_ref2_diagram():
+    positions = {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'D': (3, 0),
+                 'E': (3, 1), 'F': (4, 0), 'Z': (0, 2)}
+    __validate_node_attributes('circular_ref2.diag', xy=positions)
+
+
 def test_circular_ref_and_parent_node_diagram():
     positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 1),
                  'D': (2, 1), 'Z': (0, 2)}
@@ -59,7 +65,7 @@ def test_labeled_circular_ref_diagram():
 
 def test_twin_forked_diagram():
     positions = {'A': (0, 0), 'B': (1, 0), 'C': (1, 2), 'D': (2, 0),
-                  'E': (3, 0), 'F': (3, 1), 'G': (4, 1), 'Z': (0, 3)}
+                 'E': (3, 0), 'F': (3, 1), 'G': (4, 1), 'Z': (0, 3)}
     __validate_node_attributes('twin_forked.diag', xy=positions)
 
 
diff --git a/src/blockdiag/tests/test_rst_directives.py b/src/blockdiag/tests/test_rst_directives.py
index 73b9783..dcdb205 100644
--- a/src/blockdiag/tests/test_rst_directives.py
+++ b/src/blockdiag/tests/test_rst_directives.py
@@ -232,6 +232,61 @@ class TestRstDirectives(unittest2.TestCase):
         self.assertEqual(0, len(tbody[1][1]))
 
     @use_tmpdir
+    def test_rst_directives_with_block_desctable_using_node_group(self, path):
+        directives.setup(format='SVG', outputdir=path)
+        text = ".. blockdiag::\n   :desctable:\n\n   { A -> B; group { A } }"
+        doctree = publish_doctree(text)
+        self.assertEqual(2, len(doctree))
+        self.assertEqual(nodes.image, type(doctree[0]))
+        self.assertEqual(nodes.table, type(doctree[1]))
+
+        self.assertEqual(1, len(doctree[1]))
+        self.assertEqual(nodes.tgroup, type(doctree[1][0]))
+
+        # tgroup
+        self.assertEqual(4, len(doctree[1][0]))
+        self.assertEqual(nodes.colspec, type(doctree[1][0][0]))
+        self.assertEqual(nodes.colspec, type(doctree[1][0][1]))
+        self.assertEqual(nodes.thead, type(doctree[1][0][2]))
+        self.assertEqual(nodes.tbody, type(doctree[1][0][3]))
+
+        # colspec
+        self.assertEqual(0, len(doctree[1][0][0]))
+        self.assertEqual(50, doctree[1][0][0]['colwidth'])
+
+        self.assertEqual(0, len(doctree[1][0][1]))
+        self.assertEqual(50, doctree[1][0][1]['colwidth'])
+
+        # thead
+        thead = doctree[1][0][2]
+        self.assertEqual(1, len(thead))
+        self.assertEqual(2, len(thead[0]))
+
+        self.assertEqual(1, len(thead[0][0]))
+        self.assertEqual(1, len(thead[0][0][0]))
+        self.assertEqual('Name', thead[0][0][0][0])
+
+        self.assertEqual(1, len(thead[0][1]))
+        self.assertEqual(1, len(thead[0][1][0]))
+        self.assertEqual('Description', thead[0][1][0][0])
+
+        # tbody
+        tbody = doctree[1][0][3]
+        self.assertEqual(2, len(tbody))
+
+        self.assertEqual(2, len(tbody[0]))
+        self.assertEqual(1, len(tbody[0][0]))
+        self.assertEqual(1, len(tbody[0][0][0]))
+        self.assertEqual('A', tbody[0][0][0][0])
+        self.assertEqual(0, len(tbody[0][1]))
+
+        self.assertEqual(2, len(tbody[1]))
+        self.assertEqual(1, len(tbody[1][0]))
+        self.assertEqual(1, len(tbody[1][0][0]))
+        self.assertEqual('B', tbody[1][0][0][0])
+        self.assertEqual(0, len(tbody[1][1]))
+
+    @use_tmpdir
     def test_rst_directives_with_block_desctable_with_description(self, path):
         directives.setup(format='SVG', outputdir=path)
         text = ".. blockdiag::\n   :desctable:\n\n" + \
@@ -312,7 +367,6 @@ class TestRstDirectives(unittest2.TestCase):
 
         self.assertEqual('B', tbody[1][0][0][0])
         self.assertEqual(4, len(tbody[1][1][0]))
-        print tbody[1][1][0]
         self.assertEqual(nodes.strong, type(tbody[1][1][0][0]))
         self.assertEqual(nodes.Text, type(tbody[1][1][0][0][0]))
         self.assertEqual('foo', str(tbody[1][1][0][0][0]))
@@ -363,3 +417,46 @@ class TestRstDirectives(unittest2.TestCase):
         self.assertEqual('2', tbody[1][0][0][0])
         self.assertEqual('A', tbody[1][1][0][0])
         self.assertEqual(0, len(tbody[1][2]))
+
+    @use_tmpdir
+    def test_rst_directives_with_block_desctable_for_edges(self, path):
+        directives.setup(format='SVG', outputdir=path)
+        text = ".. blockdiag::\n   :desctable:\n\n" + \
+               "   { A -> B [description = \"foo\"]; " + \
+               "     C -> D [description = \"bar\"]; " + \
+               "     C [label = \"label_C\"]; " + \
+               "     D [label = \"label_D\"]; }"
+        doctree = publish_doctree(text)
+        self.assertEqual(3, len(doctree))
+        self.assertEqual(nodes.image, type(doctree[0]))
+        self.assertEqual(nodes.table, type(doctree[1]))
+        self.assertEqual(nodes.table, type(doctree[2]))
+
+        # tgroup
+        self.assertEqual(4, len(doctree[2][0]))
+        self.assertEqual(nodes.colspec, type(doctree[2][0][0]))
+        self.assertEqual(nodes.colspec, type(doctree[2][0][1]))
+        self.assertEqual(nodes.thead, type(doctree[2][0][2]))
+        self.assertEqual(nodes.tbody, type(doctree[2][0][3]))
+
+        # colspec
+        self.assertEqual(25, doctree[2][0][0]['colwidth'])
+        self.assertEqual(50, doctree[2][0][1]['colwidth'])
+
+        # thead
+        thead = doctree[2][0][2]
+        self.assertEqual(2, len(thead[0]))
+        self.assertEqual('Name', thead[0][0][0][0])
+        self.assertEqual('Description', thead[0][1][0][0])
+
+        # tbody
+        tbody = doctree[2][0][3]
+        self.assertEqual(2, len(tbody))
+        self.assertEqual('A -> B', tbody[0][0][0][0])
+        self.assertEqual(1, len(tbody[0][1][0]))
+        self.assertEqual(nodes.Text, type(tbody[0][1][0][0]))
+        self.assertEqual('foo', str(tbody[0][1][0][0]))
+        self.assertEqual('label_C -> label_D', tbody[1][0][0][0])
+        self.assertEqual(1, len(tbody[1][1][0]))
+        self.assertEqual(nodes.Text, type(tbody[1][1][0][0]))
+        self.assertEqual('bar', str(tbody[1][1][0][0]))
diff --git a/src/blockdiag/utils/bootstrap.py b/src/blockdiag/utils/bootstrap.py
index 2856308..0a0a2fc 100644
--- a/src/blockdiag/utils/bootstrap.py
+++ b/src/blockdiag/utils/bootstrap.py
@@ -31,6 +31,8 @@ class Application(object):
 
             parsed = self.parse_diagram()
             return self.build_diagram(parsed)
+        except SystemExit, e:
+            return e
         except UnicodeEncodeError, e:
             msg = "ERROR: UnicodeEncodeError caught " + \
                   "(check your font settings)\n"
@@ -64,7 +66,8 @@ class Application(object):
         drawer = DiagramDraw(self.options.type, diagram,
                              self.options.output, fontmap=self.fontmap,
                              antialias=self.options.antialias,
-                             nodoctype=self.options.nodoctype)
+                             nodoctype=self.options.nodoctype,
+                             transparency=self.options.transparency)
         drawer.draw()
         drawer.save()
 
@@ -97,6 +100,10 @@ class Options(object):
                      help='use FONT to draw diagram', metavar='FONT')
         p.add_option('--fontmap',
                      help='use FONTMAP file to draw diagram', metavar='FONT')
+        p.add_option('--no-transparency', dest='transparency',
+                     default=True, action='store_false',
+                     help='do not make transparent background of diagram ' +\
+                          '(PNG only)')
         p.add_option('-T', dest='type', default='PNG',
                      help='Output diagram as TYPE format')
         p.add_option('--nodoctype', action='store_true',
@@ -134,6 +141,10 @@ class Options(object):
             msg = "--nodoctype option work in SVG images."
             raise RuntimeError(msg)
 
+        if self.options.transparency is False and self.options.type != 'PNG':
+            msg = "--no-transparency option work in PNG images."
+            raise RuntimeError(msg)
+
         if self.options.config and not os.path.isfile(self.options.config):
             msg = "config file is not found: %s" % self.options.config
             raise RuntimeError(msg)
diff --git a/src/blockdiag/utils/rst/directives.py b/src/blockdiag/utils/rst/directives.py
index 707ca0a..07d4440 100644
--- a/src/blockdiag/utils/rst/directives.py
+++ b/src/blockdiag/utils/rst/directives.py
@@ -132,7 +132,7 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
 
         if 'desctable' in node['options']:
             del node['options']['desctable']
-            results.append(self.description_table(diagram))
+            results += self.description_tables(diagram)
 
         results[0] = self.node2image(node, diagram)
 
@@ -201,14 +201,26 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
 
         return filename
 
-    def description_table(self, diagram):
-        nodes = diagram.traverse_nodes
+    def description_tables(self, diagram):
+        tables = []
+        desctable = self.node_description_table(diagram)
+        if desctable:
+            tables.append(desctable)
+
+        desctable = self.edge_description_table(diagram)
+        if desctable:
+            tables.append(desctable)
+
+        return tables
+
+    def node_description_table(self, diagram):
+        nodes = diagram.traverse_nodes()
         klass = diagram._DiagramNode
 
         widths = [25] + [50] * (len(klass.desctable) - 1)
         headers = [klass.attrname[n] for n in klass.desctable]
 
-        descriptions = [n.to_desctable() for n in nodes()]
+        descriptions = [n.to_desctable() for n in nodes  if n.drawable]
         descriptions.sort(cmp_node_number)
 
         for i in range(len(headers) - 2, -1, -1):
@@ -222,6 +234,18 @@ class BlockdiagDirective(BlockdiagDirectiveBase):
 
         return self._description_table(descriptions, widths, headers)
 
+    def edge_description_table(self, diagram):
+        edges = diagram.traverse_edges()
+
+        widths = [25, 50]
+        headers = ['Name', 'Description']
+        descriptions = [e.to_desctable() for e in edges  if e.style != 'none']
+
+        if any(desc[1] for desc in descriptions):
+            return self._description_table(descriptions, widths, headers)
+        else:
+            return None
+
     def _description_table(self, descriptions, widths, headers):
         # generate table-root
         tgroup = nodes.tgroup(cols=len(widths))

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/blockdiag.git



More information about the debian-science-commits mailing list