SF.net SVN: docutils:[9950] trunk/docutils

milde--- via Docutils-checkins <[email protected]>
Newsgroups gmane.text.docutils.cvs
Message-ID <[email protected]>
Revision: 9950
          http://sourceforge.net/p/docutils/code/9950
Author:   milde
Date:     2024-10-15 11:24:32 +0000 (Tue, 15 Oct 2024)
Log Message:
-----------
Allow transient elements `<target>` and `<pending>` before a figure `<caption>`.

In rST, the "figure" directive's content must start with a paragraph (that
becomes the caption) or an empty comment (to get a legend without caption).

The change allows passing a "classes" or "names" attribute value to the
figure caption via the "class" directive rsp. an internal hyperlink target.

Change the `references.PropagateTargets` transform to remove a `<target>`
element followed by a `<caption>` (after propagating ids and names)
to keep the doctree valid.

Fix the HTML5 writer, to apply the attributes to the `<figcaption>` element,
(instead of the included `<p>`).

Fix LaTeX writer to handle "classes" and "ids" attributes of a `<caption>`
element.

Add test cases.
Remove misleading test case. Figures without caption are invalid and won't get
a "Figure 1."-style caption with the Docutils writers.

Modified Paths:
--------------
    trunk/docutils/docutils/parsers/rst/directives/images.py
    trunk/docutils/docutils/transforms/references.py
    trunk/docutils/docutils/writers/html5_polyglot/__init__.py
    trunk/docutils/docutils/writers/latex2e/__init__.py
    trunk/docutils/test/functional/expected/latex_memoir.tex
    trunk/docutils/test/functional/expected/standalone_rst_docutils_xml.xml
    trunk/docutils/test/functional/expected/standalone_rst_html4css1.html
    trunk/docutils/test/functional/expected/standalone_rst_html5.html
    trunk/docutils/test/functional/expected/standalone_rst_latex.tex
    trunk/docutils/test/functional/expected/standalone_rst_pseudoxml.txt
    trunk/docutils/test/functional/expected/standalone_rst_xetex.tex
    trunk/docutils/test/functional/input/data/standard.rst
    trunk/docutils/test/functional/tests/standalone_rst_docutils_xml.py
    trunk/docutils/test/test_parsers/test_rst/test_directives/test_figures.py

Modified: trunk/docutils/docutils/parsers/rst/directives/images.py
===================================================================
--- trunk/docutils/docutils/parsers/rst/directives/images.py	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/docutils/parsers/rst/directives/images.py	2024-10-15 11:24:32 UTC (rev 9950)
@@ -152,22 +152,29 @@
         if align:
             figure_node['align'] = align
         if self.content:
+            # optional caption (single paragraph or empty comment)
+            # + optional legend (arbitrary body elements).
             node = nodes.Element()          # anonymous container for parsing
             self.state.nested_parse(self.content, self.content_offset, node)
-            first_node = node[0]
-            if isinstance(first_node, nodes.paragraph):
-                caption = nodes.caption(first_node.rawsource, '',
-                                        *first_node.children)
-                caption.source = first_node.source
-                caption.line = first_node.line
-                figure_node += caption
-            elif not (isinstance(first_node, nodes.comment)
-                      and len(first_node) == 0):
+            for i, child in enumerate(node):
+                # skip temporary nodes that will be removed by transforms
+                if isinstance(child, (nodes.target, nodes.pending)):
+                    figure_node += child
+                    continue
+                if isinstance(child, nodes.paragraph):
+                    caption = nodes.caption(child.rawsource, '',
+                                            *child.children)
+                    caption.source = child.source
+                    caption.line = child.line
+                    figure_node += caption
+                    break
+                if isinstance(child, nodes.comment) and len(child) == 0:
+                    break
                 error = self.reporter.error(
-                      'Figure caption must be a paragraph or empty comment.',
-                      nodes.literal_block(self.block_text, self.block_text),
-                      line=self.lineno)
+                    'Figure caption must be a paragraph or empty comment.',
+                    nodes.literal_block(self.block_text, self.block_text),
+                    line=self.lineno)
                 return [figure_node, error]
-            if len(node) > 1:
-                figure_node += nodes.legend('', *node[1:])
+            if len(node) > i+1:
+                figure_node += nodes.legend('', *node[i+1:])
         return [figure_node]

Modified: trunk/docutils/docutils/transforms/references.py
===================================================================
--- trunk/docutils/docutils/transforms/references.py	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/docutils/transforms/references.py	2024-10-15 11:24:32 UTC (rev 9950)
@@ -76,11 +76,15 @@
                 getattr(target, 'expect_referenced_by_name', {}))
             next_node.expect_referenced_by_id.update(
                 getattr(target, 'expect_referenced_by_id', {}))
+            # Remove target node from places where it is invalid.
+            if isinstance(target.parent, nodes.figure) and isinstance(
+                    next_node, nodes.caption):
+                target.parent.remove(target)
+                continue
             # Set refid to point to the first former ID of target
             # which is now an ID of next_node.
             target['refid'] = target['ids'][0]
-            # Clear ids and names; they have been moved to
-            # next_node.
+            # Clear ids and names; they have been moved to next_node.
             target['ids'] = []
             target['names'] = []
             self.document.note_refid(target)

Modified: trunk/docutils/docutils/writers/html5_polyglot/__init__.py
===================================================================
--- trunk/docutils/docutils/writers/html5_polyglot/__init__.py	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/docutils/writers/html5_polyglot/__init__.py	2024-10-15 11:24:32 UTC (rev 9950)
@@ -150,8 +150,8 @@
     # use the <figcaption> semantic tag.
     def visit_caption(self, node) -> None:
         if isinstance(node.parent, nodes.figure):
-            self.body.append('<figcaption>\n')
-        self.body.append(self.starttag(node, 'p', ''))
+            self.body.append(self.starttag(node, 'figcaption'))
+        self.body.append('<p>')
 
     def depart_caption(self, node) -> None:
         self.body.append('</p>\n')
@@ -275,7 +275,7 @@
 
     # place inside HTML5 <figcaption> element (together with caption)
     def visit_legend(self, node) -> None:
-        if not isinstance(node.parent[1], nodes.caption):
+        if not isinstance(node.previous_sibling(), nodes.caption):
             self.body.append('<figcaption>\n')
         self.body.append(self.starttag(node, 'div', CLASS='legend'))
 

Modified: trunk/docutils/docutils/writers/latex2e/__init__.py
===================================================================
--- trunk/docutils/docutils/writers/latex2e/__init__.py	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/docutils/writers/latex2e/__init__.py	2024-10-15 11:24:32 UTC (rev 9950)
@@ -1794,8 +1794,11 @@
 
     def visit_caption(self, node) -> None:
         self.out.append('\n\\caption{')
+        self.out += self.ids_to_labels(node, set_anchor=False)
+        self.visit_inline(node)
 
     def depart_caption(self, node) -> None:
+        self.depart_inline(node)
         self.out.append('}\n')
 
     def visit_title_reference(self, node) -> None:

Modified: trunk/docutils/test/functional/expected/latex_memoir.tex
===================================================================
--- trunk/docutils/test/functional/expected/latex_memoir.tex	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/functional/expected/latex_memoir.tex	2024-10-15 11:24:32 UTC (rev 9950)
@@ -913,7 +913,7 @@
 \begin{DUclass}{figclass2}
 \begin{figure} % align = "left"
 \noindent\makebox[\linewidth][c]{\includegraphics[width=40px]{../../../docs/user/rst/images/biohazard.png}}
-\caption{This is the caption.}
+\caption{\label{caption-label}\DUrole{captionclass1}{\DUrole{captionclass2}{This is the caption.}}}
 \begin{DUlegend}
 This is the legend.
 

Modified: trunk/docutils/test/functional/expected/standalone_rst_docutils_xml.xml
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_docutils_xml.xml	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/functional/expected/standalone_rst_docutils_xml.xml	2024-10-15 11:24:32 UTC (rev 9950)
@@ -889,7 +889,7 @@
                 <paragraph>A left-aligned figure, 70% wide:</paragraph>
                 <figure align="left" classes="figclass1 figclass2" width="70%">
                     <image alt="reStructuredText, the markup syntax" classes="class1 class2" uri="../../../docs/user/rst/images/biohazard.png" width="40px"></image>
-                    <caption>This is the caption.</caption>
+                    <caption classes="captionclass1 captionclass2" ids="caption-label" names="caption-label">This is the caption.</caption>
                     <legend>
                         <paragraph>This is the legend.</paragraph>
                         <paragraph>The legend may consist of several paragraphs.</paragraph>

Modified: trunk/docutils/test/functional/expected/standalone_rst_html4css1.html
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_html4css1.html	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/functional/expected/standalone_rst_html4css1.html	2024-10-15 11:24:32 UTC (rev 9950)
@@ -643,7 +643,7 @@
 <p>A left-aligned figure, 70% wide:</p>
 <div class="figclass1 figclass2 figure align-left" style="width: 70%">
 <img alt="reStructuredText, the markup syntax" class="class1 class2" src="../../../docs/user/rst/images/biohazard.png" style="width: 40px;" />
-<p class="caption">This is the caption.</p>
+<p class="captionclass1 captionclass2 caption" id="caption-label">This is the caption.</p>
 <div class="legend">
 <p>This is the legend.</p>
 <p>The legend may consist of several paragraphs.</p>

Modified: trunk/docutils/test/functional/expected/standalone_rst_html5.html
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_html5.html	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/functional/expected/standalone_rst_html5.html	2024-10-15 11:24:32 UTC (rev 9950)
@@ -638,7 +638,7 @@
 <p>A left-aligned figure, 70% wide:</p>
 <figure class="figclass1 figclass2 align-left" style="width: 70%">
 <img alt="reStructuredText, the markup syntax" class="class1 class2" src="../../../docs/user/rst/images/biohazard.png" style="width: 40px;" />
-<figcaption>
+<figcaption class="captionclass1 captionclass2" id="caption-label">
 <p>This is the caption.</p>
 <div class="legend">
 <p>This is the legend.</p>

Modified: trunk/docutils/test/functional/expected/standalone_rst_latex.tex
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_latex.tex	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/functional/expected/standalone_rst_latex.tex	2024-10-15 11:24:32 UTC (rev 9950)
@@ -915,7 +915,7 @@
 \begin{DUclass}{figclass2}
 \begin{figure} % align = "left"
 \noindent\makebox[\linewidth][c]{\includegraphics[width=40px]{../../../docs/user/rst/images/biohazard.png}}
-\caption{This is the caption.}
+\caption{\label{caption-label}\DUrole{captionclass1}{\DUrole{captionclass2}{This is the caption.}}}
 \begin{DUlegend}
 This is the legend.
 

Modified: trunk/docutils/test/functional/expected/standalone_rst_pseudoxml.txt
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_pseudoxml.txt	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/functional/expected/standalone_rst_pseudoxml.txt	2024-10-15 11:24:32 UTC (rev 9950)
@@ -1374,7 +1374,7 @@
                     A left-aligned figure, 70% wide:
                 <figure align="left" classes="figclass1 figclass2" width="70%">
                     <image alt="reStructuredText, the markup syntax" classes="class1 class2" uri="../../../docs/user/rst/images/biohazard.png" width="40px">
-                    <caption>
+                    <caption classes="captionclass1 captionclass2" ids="caption-label" names="caption-label">
                         This is the caption.
                     <legend>
                         <paragraph>
@@ -2425,15 +2425,15 @@
         <system_message level="1" line="476" source="functional/input/data/standard.rst" type="INFO">
             <paragraph>
                 Hyperlink target "image-target-3" is not referenced.
-        <system_message level="1" line="640" source="functional/input/data/standard.rst" type="INFO">
+        <system_message level="1" line="643" source="functional/input/data/standard.rst" type="INFO">
             <paragraph>
                 Hyperlink target "target1" is not referenced.
-        <system_message level="1" line="641" source="functional/input/data/standard.rst" type="INFO">
+        <system_message level="1" line="644" source="functional/input/data/standard.rst" type="INFO">
             <paragraph>
                 Hyperlink target "target2" is not referenced.
-        <system_message level="1" line="686" source="functional/input/data/standard.rst" type="INFO">
+        <system_message level="1" line="689" source="functional/input/data/standard.rst" type="INFO">
             <paragraph>
                 Hyperlink target "docutils" is not referenced.
-        <system_message level="1" line="852" source="functional/input/data/standard.rst" type="INFO">
+        <system_message level="1" line="855" source="functional/input/data/standard.rst" type="INFO">
             <paragraph>
                 Hyperlink target "hyperlink targets" is not referenced.

Modified: trunk/docutils/test/functional/expected/standalone_rst_xetex.tex
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_xetex.tex	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/functional/expected/standalone_rst_xetex.tex	2024-10-15 11:24:32 UTC (rev 9950)
@@ -940,7 +940,7 @@
 \begin{DUclass}{figclass2}
 \begin{figure} % align = "left"
 \noindent\makebox[\linewidth][c]{\includegraphics[width=40\pdfpxdimen]{../../../docs/user/rst/images/biohazard.png}}
-\caption{This is the caption.}
+\caption{\label{caption-label}\DUrole{captionclass1}{\DUrole{captionclass2}{This is the caption.}}}
 \begin{DUlegend}
 This is the legend.
 

Modified: trunk/docutils/test/functional/input/data/standard.rst
===================================================================
--- trunk/docutils/test/functional/input/data/standard.rst	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/functional/input/data/standard.rst	2024-10-15 11:24:32 UTC (rev 9950)
@@ -550,6 +550,9 @@
    :width: 40 px
    :figwidth: 70 %
 
+   .. class:: captionclass1 captionclass2
+   .. _caption-label:
+
    This is the caption.
 
    This is the legend.

Modified: trunk/docutils/test/functional/tests/standalone_rst_docutils_xml.py
===================================================================
--- trunk/docutils/test/functional/tests/standalone_rst_docutils_xml.py	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/functional/tests/standalone_rst_docutils_xml.py	2024-10-15 11:24:32 UTC (rev 9950)
@@ -8,4 +8,5 @@
     'sectsubtitle_xform': True,
     # format output with indents and newlines
     'indents': True,
+    'validate': True,  # check conformance to Docutils Generic DTD
     }

Modified: trunk/docutils/test/test_parsers/test_rst/test_directives/test_figures.py
===================================================================
--- trunk/docutils/test/test_parsers/test_rst/test_directives/test_figures.py	2024-10-13 13:44:24 UTC (rev 9949)
+++ trunk/docutils/test/test_parsers/test_rst/test_directives/test_figures.py	2024-10-15 11:24:32 UTC (rev 9950)
@@ -41,6 +41,7 @@
 totest = {}
 
 totest['figures'] = [
+# Note: A figure with no caption nor legend is not valid according to the DTD.
 ["""\
 .. figure:: picture.png
 """,
@@ -94,6 +95,30 @@
                 A picture with a legend but no caption.
 """],
 ["""\
+.. figure:: picture.png
+
+   .. The comment replacing the caption must be empty.
+
+   This should be a legend.
+""",
+"""\
+<document source="test data">
+    <figure>
+        <image uri="picture.png">
+    <system_message level="3" line="1" source="test data" type="ERROR">
+        <paragraph>
+            Figure caption must be a paragraph or empty comment.
+        <literal_block xml:space="preserve">
+            .. figure:: picture.png
+            \n\
+               .. The comment replacing the caption must be empty.
+            \n\
+               This should be a legend.
+"""],
+# Passing a class value to the caption is done with a class directive
+# that inserts a pending node (to be removed by the ClassAttribute transform).
+# A hyperlink target before the caption is removed by a transform, too.
+["""\
 .. Figure:: picture.png
    :height: 100
    :width: 200
@@ -100,14 +125,24 @@
    :scale: 50
    :loading: embed
 
-   A picture with image options and a caption.
+   .. class:: custom
+   .. _figure:caption:
+
+   A picture with image options and a caption with class value and target.
 """,
 """\
 <document source="test data">
     <figure>
         <image height="100" loading="embed" scale="50" uri="picture.png" width="200">
+        <pending>
+            .. internal attributes:
+                 .transform: docutils.transforms.misc.ClassAttribute
+                 .details:
+                   class: ['custom']
+                   directive: 'class'
+        <target ids="figure-caption" names="figure:caption">
         <caption>
-            A picture with image options and a caption.
+            A picture with image options and a caption with class value and target.
 """],
 ["""\
 .. Figure:: picture.png
@@ -116,18 +151,19 @@
    :width: 200
    :scale: 50
    :loading: lazy
+   :class: image-class
    :figwidth: 300
    :figclass: class1 class2
    :name: fig:pix
 
-   A picture with image options on individual lines, and this caption.
+   A figure with options and this caption.
 """,
 """\
 <document source="test data">
     <figure classes="class1 class2" width="300px">
-        <image alt="alternate text" height="100" ids="fig-pix" loading="lazy" names="fig:pix" scale="50" uri="picture.png" width="200">
+        <image alt="alternate text" classes="image-class" height="100" ids="fig-pix" loading="lazy" names="fig:pix" scale="50" uri="picture.png" width="200">
         <caption>
-            A picture with image options on individual lines, and this caption.
+            A figure with options and this caption.
 """],
 ["""\
 .. figure:: picture.png
@@ -162,21 +198,7 @@
                A figure with wrong alignment.
 """],
 ["""\
-This figure lacks a caption. It may still have a
-"Figure 1."-style caption appended in the output.
-
 .. figure:: picture.png
-""",
-"""\
-<document source="test data">
-    <paragraph>
-        This figure lacks a caption. It may still have a
-        "Figure 1."-style caption appended in the output.
-    <figure>
-        <image uri="picture.png">
-"""],
-["""\
-.. figure:: picture.png
 
    A picture with a caption and a legend.
 

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.