Patching the docutils rst parser

Tweaking the HTML generation of restructured text

In an older post I described the problems I had with big pictures and what the possible solutions could be. Big, in this case, means pictures that are wider than the width my blog area has.

One of the options I mentioned was patching docutils by adding an additional option to the image directive and evaluate that option in the HTMLTranslator. Back then I wasn’t sure if it was really that easy or if I was overlooking something. As it turned out it was that easy, at elast for my purposes. What follows is my patch in diff format between. The original version of docutils was 0.13.1. I applied this patch directly to the docutils package of my systems’ Python installation.

diff --git a/docutils_0-13-1_bkp/parsers/rst/directives/images.py b/docutils_0-13-1_mod/parsers/rst/directives/images.py
index c813fa3..f6da1bd 100644
--- a/docutils_0-13-1_bkp/parsers/rst/directives/images.py
+++ b/docutils_0-13-1_mod/parsers/rst/directives/images.py
@@ -48,7 +48,8 @@ class Image(Directive):
                    'align': align,
                    'name': directives.unchanged,
                    'target': directives.unchanged_required,
-                   'class': directives.class_option}
+                   'class': directives.class_option,
+                   'scrollable': directives.flag }
 
     def run(self):
         if 'align' in self.options:
diff --git a/docutils_0-13-1_bkp/writers/_html_base.py b/docutils_0-13-1_mod/writers/_html_base.py
index a03231c..2a74132 100644
--- a/docutils_0-13-1_bkp/writers/_html_base.py
+++ b/docutils_0-13-1_mod/writers/_html_base.py
@@ -892,6 +892,10 @@ class HTMLTranslator(nodes.NodeVisitor):
             suffix = '\n'
         if 'align' in node:
             atts['class'] = 'align-%s' % node['align']
+        if 'scrollable' in node:
+            # Scrollable flag was given - embed the image tag in a `div` tag
+            # that allows for overflow
+            self.body.append('<div style="overflow: auto; width: auto; height: auto;">\n')
         if ext in self.object_image_types:
             # do NOT use an empty tag: incorrect rendering in browsers
             self.body.append(self.starttag(node, 'object', suffix, **atts) +
@@ -900,8 +904,9 @@ class HTMLTranslator(nodes.NodeVisitor):
             self.body.append(self.emptytag(node, 'img', suffix, **atts))
 
     def depart_image(self, node):
-        # self.body.append(self.context.pop())
-        pass
+        # close the `div` tag if we opened one
+        if 'scrollable' in node:
+            self.body.append('</div>\n')
 
     def visit_inline(self, node):
         self.body.append(self.starttag(node, 'span', ''))

From now on when I mark an image as “scrollable” the HTML img tag is enclosed by a div tag like so

<div style="overflow: auto; width: auto; height: auto;">
<img alt="error output of a Jenkins exception" class="big-image" src="images/jenkins_exception.png" />
</div>

If the image’s width does not fit the blog’s width, a vertical scrollbar appears.

Sum up

I was surprised that it was actually that easy to incorporate that change. I didn’t have to read very deep into the sources. The image directive already has a bunch of options, so it was easy to follow the pattern. Currently the height of the image won’t be limited, this could be changed in the future as a small improvement to above patch.