From f6e937bd5c5bc9b648980a07cdb61a43eb2bf371 Mon Sep 17 00:00:00 2001
From: milde
Date: Fri, 3 Apr 2020 16:57:36 +0000
Subject: [PATCH] html5 writer: wrap image elements in
unless inline or in
figure.
In HTML, is by default an inline element.
The rST "image" directive generates block-level elements
(unless used in a "substitution" directive).
Wrapping in a container enables more advanced CSS styling.
A paragraph as wrapper fits well with Docutils wrapping
inline content in paragraphs by default.
No change for the "html4css1" writer (ensure backwards compatibility).
git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk@8510 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
---
docutils/docutils/writers/_html_base.py | 16 ++--
docutils/docutils/writers/html4css1/__init__.py | 99 ++++++++++++++++++++++
.../functional/expected/standalone_rst_html5.html | 40 ++++-----
3 files changed, 129 insertions(+), 26 deletions(-)
diff --git a/docutils/docutils/writers/_html_base.py b/docutils/docutils/writers/_html_base.py
index 24ee7cdb7..6fd2d91c8 100644
--- a/docutils/docutils/writers/_html_base.py
+++ b/docutils/docutils/writers/_html_base.py
@@ -957,13 +957,13 @@ class HTMLTranslator(nodes.NodeVisitor):
del atts[att_name]
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))):
- # Inline context or surrounded by ....
- suffix = ''
- else:
+ if isinstance(node.parent, (nodes.figure, nodes.compound)):
suffix = '\n'
+ elif not isinstance(node.parent, (nodes.TextElement, nodes.reference)):
+ self.body.append('
')
+ suffix = '
\n'
+ else:
+ suffix = ''
if 'align' in node:
atts['class'] = 'align-%s' % node['align']
if ext in self.object_image_types:
@@ -1325,12 +1325,16 @@ class HTMLTranslator(nodes.NodeVisitor):
atts['class'] += ' internal'
if not isinstance(node.parent, nodes.TextElement):
assert len(node) == 1 and isinstance(node[0], nodes.image)
+ if not isinstance(node.parent, (nodes.figure, nodes.compound)):
+ self.body.append('
')
atts['class'] += ' image-reference'
self.body.append(self.starttag(node, 'a', '', **atts))
def depart_reference(self, node):
self.body.append('')
if not isinstance(node.parent, nodes.TextElement):
+ if not isinstance(node.parent, (nodes.figure, nodes.compound)):
+ self.body.append('
')
self.body.append('\n')
self.in_mailto = False
diff --git a/docutils/docutils/writers/html4css1/__init__.py b/docutils/docutils/writers/html4css1/__init__.py
index 94c11a55d..2901674fe 100644
--- a/docutils/docutils/writers/html4css1/__init__.py
+++ b/docutils/docutils/writers/html4css1/__init__.py
@@ -15,10 +15,14 @@ for proper viewing with a modern graphical browser.
__docformat__ = 'reStructuredText'
import os.path
+import re
+import sys
+
import docutils
from docutils import frontend, nodes, writers, io
from docutils.transforms import writer_aux
from docutils.writers import _html_base
+from docutils.writers._html_base import PIL, url2pathname
class Writer(writers._html_base.Writer):
@@ -531,6 +535,75 @@ class HTMLTranslator(writers._html_base.HTMLTranslator):
object_image_types = {'.svg': 'image/svg+xml',
'.swf': 'application/x-shockwave-flash'}
+ # do not wrap block-level images in (for backwards compatibility)
+ def visit_image(self, node):
+ atts = {}
+ uri = node['uri']
+ ext = os.path.splitext(uri)[1].lower()
+ if ext in self.object_image_types:
+ atts['data'] = uri
+ atts['type'] = self.object_image_types[ext]
+ else:
+ atts['src'] = uri
+ atts['alt'] = node.get('alt', uri)
+ # image size
+ if 'width' in node:
+ atts['width'] = node['width']
+ if 'height' in node:
+ atts['height'] = node['height']
+ if 'scale' in node:
+ if (PIL and not ('width' in node and 'height' in node)
+ and self.settings.file_insertion_enabled):
+ imagepath = url2pathname(uri)
+ try:
+ img = PIL.Image.open(
+ imagepath.encode(sys.getfilesystemencoding()))
+ except (IOError, UnicodeEncodeError):
+ pass # TODO: warn?
+ else:
+ self.settings.record_dependencies.add(
+ imagepath.replace('\\', '/'))
+ if 'width' not in atts:
+ atts['width'] = '%dpx' % img.size[0]
+ if 'height' not in atts:
+ atts['height'] = '%dpx' % img.size[1]
+ del img
+ for att_name in 'width', 'height':
+ if att_name in atts:
+ match = re.match(r'([0-9.]+)(\S*)$', atts[att_name])
+ assert match
+ atts[att_name] = '%s%s' % (
+ float(match.group(1)) * (float(node['scale']) / 100),
+ match.group(2))
+ style = []
+ for att_name in 'width', 'height':
+ if att_name in atts:
+ if re.match(r'^[0-9.]+$', atts[att_name]):
+ # Interpret unitless values as pixels.
+ atts[att_name] += 'px'
+ style.append('%s: %s;' % (att_name, atts[att_name]))
+ del atts[att_name]
+ 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))):
+ # Inline context or surrounded by ....
+ suffix = ''
+ else:
+ suffix = '\n'
+ if 'align' in node:
+ atts['class'] = 'align-%s' % node['align']
+ 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) +
+ node.get('alt', uri) + '' + suffix)
+ else:
+ self.body.append(self.emptytag(node, 'img', suffix, **atts))
+
+ def depart_image(self, node):
+ pass
+
# use table for footnote text,
# context added in footnote_backrefs.
def visit_label(self, node):
@@ -665,6 +738,32 @@ class HTMLTranslator(writers._html_base.HTMLTranslator):
def depart_paragraph(self, node):
self.body.append(self.context.pop())
+ # do not wrap images in paragraphs (for backwards compatibility)
+ def visit_reference(self, node):
+ atts = {'class': 'reference'}
+ if 'refuri' in node:
+ atts['href'] = node['refuri']
+ if ( self.settings.cloak_email_addresses
+ and atts['href'].startswith('mailto:')):
+ atts['href'] = self.cloak_mailto(atts['href'])
+ self.in_mailto = True
+ atts['class'] += ' external'
+ else:
+ assert 'refid' in node, \
+ 'References must have "refuri" or "refid" attribute.'
+ atts['href'] = '#' + node['refid']
+ atts['class'] += ' internal'
+ if not isinstance(node.parent, nodes.TextElement):
+ assert len(node) == 1 and isinstance(node[0], nodes.image)
+ atts['class'] += ' image-reference'
+ self.body.append(self.starttag(node, 'a', '', **atts))
+
+ def depart_reference(self, node):
+ self.body.append('')
+ if not isinstance(node.parent, nodes.TextElement):
+ self.body.append('\n')
+ self.in_mailto = False
+
# ersatz for first/last pseudo-classes
def visit_sidebar(self, node):
self.body.append(
diff --git a/docutils/test/functional/expected/standalone_rst_html5.html b/docutils/test/functional/expected/standalone_rst_html5.html
index 86382bac3..3906cd610 100644
--- a/docutils/test/functional/expected/standalone_rst_html5.html
+++ b/docutils/test/functional/expected/standalone_rst_html5.html
@@ -567,31 +567,31 @@ document (a document-wide
A figure is an image with a caption and/or a legend. With page-based output
media, figures might float to a different position if this helps the page
layout.
Scalable vector graphics (SVG) images are the only standards-compliable way
to include vector graphics in HTML documents. However, they are not
supported by all backends/output formats. (E.g., LaTeX supports the
@@ -1273,30 +1273,30 @@ deficiencies or require plug-ins (i.e. don't support the <img> are
always scaled.
-
+
A scaling image (scales with the browser window), occupying 50% of the line
width. The viewBox setting in the image file enables auto-scaling also in
<object> tags and embedded SVG (if width and hight are set to 100% in the
SVG <image> tag).
-
+
A fixed-size image in a box 50% wide and 15 pixles high. This image is
scaled, if wrapped in an <img> tag but clipped in an <object> tag
or within SVG.
-
+
A right aligned, scaling image 50% wide and 1.5 em high. (This SVG image
keeps the aspect ratio.)
An inline image scaled to a height of 0.8 em.
-
+
A scaling image 1 em high, right aligned:
A scaling image 5 mm x 5 mm, centered, with hyperlink reference:
-
+
-
+
A fixed-size image in a 4 cm x 2 em box.
@@ -1304,12 +1304,12 @@ keeps the aspect ratio.)
konqueror) support the <img> tag but don't display contained bitmap
images in this case.
-
+
A small, fixed-size SVG image with embedded bitmap, The :width: is
set to 3 em in the rST source. Does not scale if wrapped in <object>
tags.
-
+
A scaling SVG image with embedded bitmap, 3 em wide.
@@ -1330,8 +1330,8 @@ support via a plugin. It is sometimes blocked due to privacy/security
concerns.
Images with extension .swf are placed inside <object> elements.
For complete control over display options use raw HTML.