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

2.14.2 Images and Figures

An image directive (also clickable -- a hyperlink reference):

-../../../docs/user/rst/images/title.png +

../../../docs/user/rst/images/title.png

Image with multiple IDs:

-../../../docs/user/rst/images/title.png +

../../../docs/user/rst/images/title.png

A centered image:

-../../../docs/user/rst/images/biohazard.png +

../../../docs/user/rst/images/biohazard.png

A left-aligned image:

-../../../docs/user/rst/images/biohazard.png +

../../../docs/user/rst/images/biohazard.png

This paragraph might flow around the image. The specific behavior depends upon the style sheet and the browser or rendering software used.

A right-aligned image:

-../../../docs/user/rst/images/biohazard.png +

../../../docs/user/rst/images/biohazard.png

This paragraph might flow around the image. The specific behavior depends upon the style sheet and the browser or rendering software used.

For inline images see Substitution Definitions.

Image size:

An image 2 em wide:

-../../../docs/user/rst/images/biohazard.png +

../../../docs/user/rst/images/biohazard.png

An image 2 em wide and 15 pixel high:

-../../../docs/user/rst/images/biohazard.png +

../../../docs/user/rst/images/biohazard.png

An image occupying 50% of the line width:

-../../../docs/user/rst/images/title.png +

../../../docs/user/rst/images/title.png

An image 2 cm high:

-../../../docs/user/rst/images/biohazard.png +

../../../docs/user/rst/images/biohazard.png

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.

@@ -1238,7 +1238,7 @@ crunchy, now would it?

3 HTML specific

3.1 SVG Images

-../../../docs/user/rst/images/biohazard.svg +

../../../docs/user/rst/images/biohazard.svg

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.

@@ -1304,12 +1304,12 @@ keeps the aspect ratio.)

konqueror) support the <img> tag but don't display contained bitmap images in this case.

@@ -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.

- -[biohazard.swf] +

+[biohazard.swf]

An SWF image in a 4 cm x 2 em box, left aligned.

An inline SWF image inline-swf scaled to 0.8 em x 0.8 em.

-- 2.11.4.GIT