chiark / gitweb /
m.images: support width/height/scale options.
authorVladimír Vondruš <mosra@centrum.cz>
Fri, 22 Feb 2019 19:40:44 +0000 (20:40 +0100)
committerVladimír Vondruš <mosra@centrum.cz>
Sat, 23 Feb 2019 00:00:22 +0000 (01:00 +0100)
doc/plugins/images.rst
pelican-plugins/m/htmlsanity.py
pelican-plugins/m/images.py
pelican-plugins/m/test/images/page.html
pelican-plugins/m/test/images/page.rst

index fc33ace895c3175f8bd161ba0f15fe57ab26b63b..b5900d98100f47c8cda78c07f5b928e55c2e2c03 100644 (file)
@@ -58,9 +58,9 @@ presence of `m.htmlsanity <{filename}/plugins/htmlsanity.rst>`_.
     PLUGINS += ['m.htmlsanity', 'm.images']
     M_IMAGES_REQUIRE_ALT_TEXT = False
 
-To use the image grid feature (see below), in addition you need the
-`Pillow <https://pypi.python.org/pypi/Pillow>`_ library installed. Get it via
-``pip`` or your distribution package manager:
+To use the image grid feature and image/figure :rst:`:scale:` option (see
+below), in addition you need the `Pillow <https://pypi.python.org/pypi/Pillow>`_
+library installed. Get it via ``pip`` or your distribution package manager:
 
 .. code:: sh
 
@@ -77,8 +77,13 @@ directives and:
 -   Adds :css:`.m-image` / :css:`.m-figure` CSS classes to them so they have
     the expected m.css `image <{filename}/css/components.rst#images>`_ and
     `figure <{filename}/css/components.rst#figures>`_ styling.
--   Removes the :rst:`:align:`, :rst:`:figwidth:` and :rst:`:scale:` options,
-    as this is better handled by m.css features.
+-   Removes the :rst:`:align:` option, as this is better handled by m.css
+    features and removes the redundant :rst:`:figwidth:` option (use
+    :rst:`:width:` instead).
+-   The original :rst:`:width:`, :rst:`:height:` and :rst:`:scale:` options are
+    supported, only converted to a CSS ``style`` attribute instead of using
+    deprecated HTML attributes. The width/height options take CSS units, the
+    scale takes a percentage.
 -   To maintain accessibility easier, makes it possible to enforce :rst:`:alt:`
     text for every image and figure by setting :py:`M_IMAGES_REQUIRE_ALT_TEXT`
     to :py:`True`.
index 144b4fbe27f96404096e792f9636f4555fb2c72a..86af90a7c504fc82426d7244264f03f56db4880b 100644 (file)
@@ -263,8 +263,8 @@ class SaneHtmlTranslator(HTMLTranslator):
     def depart_abbreviation(self, node):
         self.body.append('</abbr>')
 
-    # Remove useless cruft from images, such as width, height, scale; don't put
-    # URI in alt text.
+    # Convert outdated width/height attributes to CSS styles, handle the scale
+    # directly inside m.images; don't put URI in alt text.
     def visit_image(self, node):
         atts = {}
         uri = node['uri']
@@ -275,6 +275,13 @@ class SaneHtmlTranslator(HTMLTranslator):
         else:
             atts['src'] = uri
             if 'alt' in node: atts['alt'] = node['alt']
+        style = []
+        if node.get('width'):
+            style += ['width: {}'.format(node['width'])]
+        if node.get('height'):
+            style += ['height: {}'.format(node['height'])]
+        if style:
+            atts['style'] = '; '.join(style)
         if (isinstance(node.parent, nodes.TextElement) or
             (isinstance(node.parent, nodes.reference) and
              not isinstance(node.parent.parent, nodes.TextElement))):
@@ -412,8 +419,11 @@ class SaneHtmlTranslator(HTMLTranslator):
         atts = {}
         if node.get('id'):
             atts['ids'] = [node['id']]
+        style = []
         if node.get('width'):
-            atts['style'] = 'width: %s' % node['width']
+            style += ['width: {}'.format(node['width'])]
+        if style:
+            atts['style'] = '; '.join(style)
         self.body.append(self.starttag(node, 'figure', **atts))
 
     def depart_figure(self, node):
index 462248395d643655647aaf7e011b83daf1f55902..e6c834d395453f88af15e2d2e14063bb83202aae 100644 (file)
@@ -33,7 +33,7 @@ from pelican import signals
 from pelican import StaticGenerator
 
 # If Pillow is not available, it's not an error unless one uses the image grid
-# functionality
+# functionality (or :scale: option for Image)
 try:
     import PIL.Image
     import PIL.ExifTags
@@ -47,8 +47,9 @@ class Image(Directive):
 
     Copy of docutils.parsers.rst.directives.Image with:
 
-    -   the align, scale, width, height options removed (handled better by
-        m.css)
+    -   the align option removed (handled better by m.css)
+    -   moved the implementation of scale here instead of it being handled in
+        the HTML writer
     -   .m-image CSS class added
     -   adding a outer container for clickable image to make the clickable area
         cover only the image
@@ -60,6 +61,9 @@ class Image(Directive):
     final_argument_whitespace = True
     option_spec = {'alt': directives.unchanged_required,
                    'name': directives.unchanged,
+                   'height': directives.length_or_unitless,
+                   'width': directives.length_or_percentage_or_unitless,
+                   'scale': directives.percentage,
                    'class': directives.class_option,
                    'target': directives.unchanged_required}
 
@@ -89,12 +93,32 @@ class Image(Directive):
                 messages.append(data)       # data is a system message
             del self.options['target']
 
+        width = None
+        height = None
+        # If scaling requested, open the files and calculate the scaled size
+        # Support both {filename} (3.7.1) and {static} (3.8) placeholders. In
+        # all cases use only width and not both so the max-width can correctly
+        # scale the image down on smaller screen sizes.
+        # TODO: implement ratio-preserving scaling to avoid jumps on load using
+        # the margin-bottom hack
+        if 'scale' in self.options:
+            file = os.path.join(os.getcwd(), settings['PATH'])
+            absuri = reference.format(filename=file, static=file)
+            im = PIL.Image.open(absuri)
+            width = "{}px".format(int(im.width*self.options['scale']/100.0))
+        elif 'width' in self.options:
+            width = self.options['width']
+        elif 'height' in self.options:
+            height = self.options['height'] # TODO: convert to width instead?
+
         # Remove the classes from the image element, will be added either to it
         # or to the wrapping element later
         set_classes(self.options)
         classes = self.options.get('classes', [])
         if 'classes' in self.options: del self.options['classes']
-        image_node = nodes.image(self.block_text, **self.options)
+        if 'width' in self.options: del self.options['width']
+        if 'height' in self.options: del self.options['height']
+        image_node = nodes.image(self.block_text, width=width, height=height, **self.options)
 
         if not 'alt' in self.options and settings['M_IMAGES_REQUIRE_ALT_TEXT']:
             error = self.state_machine.reporter.error(
@@ -123,7 +147,9 @@ class Figure(Image):
 
     Copy of docutils.parsers.rst.directives.Figure with:
 
-    -   the align, figwidth options removed (handled better by m.css)
+    -   the align option removed (handled better by m.css)
+    -   the redundant figwidth option removed (use width/scale/height from the
+        Image directive instead)
     -   .m-figure CSS class added
     """
 
index 005d09cc7881a776ddd6f199d9261906980f2f63..3e16d9fa57de0ba09b61ca2347caebc8f20dcc04 100644 (file)
 <div class="m-image m-fullwidth">
 <a href="./ship.jpg"><img src="./ship.jpg" /></a>
 </div>
+<p>Image with custom width, height and scale (scale gets picked):</p>
+<img class="m-image" src="./ship.jpg" style="width: 384px" />
+<p>Image with custom width and height (width gets picked):</p>
+<img class="m-image" src="./ship.jpg" style="width: 300px" />
+<p>Image with custom height:</p>
+<img class="m-image" src="./ship.jpg" style="height: 100px" />
 <p>Figure:</p>
 <figure class="m-figure">
 <img src="./ship.jpg" />
@@ -42,9 +48,9 @@
 <span>
 Yes.</span>
 </figure>
-<p>Figure with link and only a caption:</p>
+<p>Figure with link, scale and only a caption:</p>
 <figure class="m-figure">
-<a href="./ship.jpg"><img src="./ship.jpg" /></a>
+<a href="./ship.jpg"><img src="./ship.jpg" style="width: 568px" /></a>
 <figcaption>A Ship</figcaption>
 </figure>
 <p>Figure with link and class on top:</p>
@@ -52,6 +58,16 @@ Yes.</span>
 <a href="./ship.jpg"><img src="./ship.jpg" /></a>
 <figcaption>A Ship</figcaption>
 </figure>
+<p>Figure with a width:</p>
+<figure class="m-figure">
+<img src="./ship.jpg" style="width: 250px" />
+<figcaption>A Ship</figcaption>
+</figure>
+<p>Figure with a height:</p>
+<figure class="m-figure">
+<a href="./ship.jpg"><img src="./ship.jpg" style="height: 200px" /></a>
+<figcaption>A Ship</figcaption>
+</figure>
 <p>Image grid:</p>
 <div class="m-imagegrid m-container-inflate">
 <div>
index 3dbbf5b5c7539c82b9086ce118e832e0df22610a..1aea1188cb9799e92480fa7ad4f85ee5dcb5af62 100644 (file)
@@ -24,6 +24,24 @@ Image with link, class on top:
     :target: {static}/ship.jpg
     :class: m-fullwidth
 
+Image with custom width, height and scale (scale gets picked):
+
+.. image:: {static}/ship.jpg
+    :width: 4000px
+    :height: 8000px
+    :scale: 25%
+
+Image with custom width and height (width gets picked):
+
+.. image:: {static}/ship.jpg
+    :width: 300px
+    :height: 8000px
+
+Image with custom height:
+
+.. image:: {static}/ship.jpg
+    :height: 100px
+
 Figure:
 
 .. figure:: {static}/ship.jpg
@@ -32,10 +50,11 @@ Figure:
 
     Yes.
 
-Figure with link and only a caption:
+Figure with link, scale and only a caption:
 
 .. figure:: {static}/ship.jpg
     :target: {static}/ship.jpg
+    :scale: 37%
 
     A Ship
 
@@ -47,6 +66,21 @@ Figure with link and class on top:
 
     A Ship
 
+Figure with a width:
+
+.. figure:: {static}/ship.jpg
+    :width: 250px
+
+    A Ship
+
+Figure with a height:
+
+.. figure:: {static}/ship.jpg
+    :target: {static}/ship.jpg
+    :height: 200px
+
+    A Ship
+
 Image grid:
 
 .. image-grid::