|
From: <mi...@us...> - 2024-10-15 11:24:34
|
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.
|