SF.net SVN: docutils:[10219] trunk/docutils
milde--- via Docutils-checkins <[email protected]> Thu, 21 Aug 2025 15:18:06 +0000
| Newsgroups | gmane.text.docutils.cvs |
|---|---|
| Message-ID | <[email protected]> |
Revision: 10219
http://sourceforge.net/p/docutils/code/10219
Author: milde
Date: 2025-08-21 15:18:05 +0000 (Thu, 21 Aug 2025)
Log Message:
-----------
LaTeX writer: write "label" commands for elements with "ids".
Fix [bugs:#503].
Currently, internal cross-references to most body elements (admonitions, lists,
block_quotes, container, compound, ...) fail, because the "ids"
attribute of these elements is ignored.
Calls `ids_to_labels()` for (almost) all elements that may have one or more IDs.
TODO:
* test/revise citations and footnotes
* class handling for image and list items
(use DUclass environment or DUrole function?)
* Currently, there is no way to specify a target name or custom class for
the "docinfo" in the rST source.
Modified Paths:
--------------
trunk/docutils/HISTORY.rst
trunk/docutils/docutils/writers/latex2e/__init__.py
trunk/docutils/test/functional/expected/latex_cornercases.tex
trunk/docutils/test/functional/expected/latex_leavevmode.tex
trunk/docutils/test/functional/expected/latex_memoir.tex
trunk/docutils/test/functional/expected/standalone_rst_latex.tex
trunk/docutils/test/functional/expected/standalone_rst_xetex.tex
trunk/docutils/test/test_writers/test_latex2e.py
trunk/docutils/test/test_writers/test_latex2e_parts.py
Modified: trunk/docutils/HISTORY.rst
===================================================================
--- trunk/docutils/HISTORY.rst 2025-08-20 12:04:54 UTC (rev 10218)
+++ trunk/docutils/HISTORY.rst 2025-08-21 15:18:05 UTC (rev 10219)
@@ -43,7 +43,8 @@
* docutils/writers/latex2e/__init__.py
- - Prepend ``\phantomsection`` to labelled math-blocks.
+ - Add cross-reference anchors (``\phantomsection\label{...}``)
+ for elements with IDs (fixes bug #503).
- Fix cross-reference anchor placement in figures, images,
literal-blocks, tables, and (sub)titles.
- Simplify code for nested image.
Modified: trunk/docutils/docutils/writers/latex2e/__init__.py
===================================================================
--- trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-20 12:04:54 UTC (rev 10218)
+++ trunk/docutils/docutils/writers/latex2e/__init__.py 2025-08-21 15:18:05 UTC (rev 10219)
@@ -1714,6 +1714,8 @@
self.provide_fallback('admonition')
if 'error' in node['classes']:
self.provide_fallback('error')
+ if not isinstance(node, nodes.system_message):
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
self.out.append('\\begin{DUadmonition}')
@@ -1746,6 +1748,7 @@
self.depart_docinfo_item(node)
def visit_block_quote(self, node) -> None:
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
self.out.append('\\begin{quote}')
@@ -1754,6 +1757,7 @@
self.duclass_close(node)
def visit_bullet_list(self, node) -> None:
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
self.out.append('\\begin{itemize}')
@@ -1876,6 +1880,7 @@
def visit_compound(self, node) -> None:
if isinstance(node.parent, nodes.compound):
self.out.append('\n')
+ self.out += self.ids_to_labels(node, pre_nl=True)
node['classes'].insert(0, 'compound')
self.duclass_open(node)
@@ -1889,6 +1894,7 @@
self.depart_docinfo_item(node)
def visit_container(self, node) -> None:
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
def depart_container(self, node) -> None:
@@ -1920,6 +1926,7 @@
pass
def visit_definition_list(self, node) -> None:
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
self.out.append('\\begin{description}\n')
@@ -1928,7 +1935,7 @@
self.duclass_close(node)
def visit_definition_list_item(self, node) -> None:
- pass
+ self.out += self.ids_to_labels(node, newline=True)
def depart_definition_list_item(self, node) -> None:
if node.next_node(descend=False, siblings=True) is not None:
@@ -2239,6 +2246,7 @@
label = r'%s\%s{%s}%s' % (prefix, enumtype, counter_name, suffix)
self._enumeration_counters.append(label)
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
if enum_level <= 4:
self.out.append('\\begin{enumerate}')
@@ -2263,8 +2271,8 @@
self._enumeration_counters.pop()
def visit_field(self, node) -> None:
+ self.out += self.ids_to_labels(node, pre_nl=True)
# output is done in field_body, field_name
- pass
def depart_field(self, node) -> None:
pass
@@ -2278,6 +2286,7 @@
self.out.append(r'\\'+'\n')
def visit_field_list(self, node) -> None:
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
if self.out is not self.docinfo:
self.provide_fallback('fieldlist')
@@ -2447,6 +2456,7 @@
def visit_image(self, node) -> None:
# <image> can be inline element, body element, or nested in a <figure>
# in all three cases the <image> may also be nested in a <reference>
+ # TODO: "classes" attribute currently ignored!
self.requirements['graphicx'] = self.graphicx_package
attrs = node.attributes
# convert image URI to filesystem path, do not adjust relative path:
@@ -2560,6 +2570,7 @@
'\\begin{DUlineblock}{\\DUlineblockindent}\n')
# In rST, nested line-blocks cannot be given class arguments
else:
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
self.out.append('\\begin{DUlineblock}{0em}\n')
self.insert_align_declaration(node)
@@ -2569,6 +2580,7 @@
self.duclass_close(node)
def visit_list_item(self, node) -> None:
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.out.append('\n\\item ')
def depart_list_item(self, node) -> None:
@@ -2784,6 +2796,7 @@
def visit_option_list(self, node) -> None:
self.provide_fallback('providelength', '_providelength')
self.provide_fallback('optionlist')
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
self.out.append('\\begin{DUoptionlist}\n')
@@ -2792,7 +2805,7 @@
self.duclass_close(node)
def visit_option_list_item(self, node) -> None:
- pass
+ self.out += self.ids_to_labels(node, newline=True)
def depart_option_list_item(self, node) -> None:
pass
@@ -2927,10 +2940,13 @@
self.provide_fallback('rubric')
# class wrapper would interfere with ``\section*"`` type commands
# (spacing/indent of first paragraph)
- self.out.append('\n\\DUrubric{')
+ self.out += self.ids_to_labels(node, pre_nl=True)
+ self.duclass_open(node)
+ self.out.append('\\DUrubric{')
def depart_rubric(self, node) -> None:
self.out.append('}\n')
+ self.duclass_close(node)
def visit_section(self, node) -> None:
# Update counter-prefix for compound enumerators
@@ -2972,6 +2988,7 @@
self.section_level -= 1
def visit_sidebar(self, node) -> None:
+ self.out += self.ids_to_labels(node, pre_nl=True)
self.duclass_open(node)
self.requirements['color'] = PreambleCmds.color
self.provide_fallback('sidebar')
@@ -2988,12 +3005,15 @@
def visit_attribution(self, node) -> None:
prefix, suffix = self.attribution_formats[self.settings.attribution]
- self.out.append('\\nopagebreak\n\n\\raggedleft ')
- self.out.append(prefix)
+ self.out.append('\\nopagebreak\n')
+ self.out += self.ids_to_labels(node, pre_nl=True)
+ self.duclass_open(node)
+ self.out.append(f'\\raggedleft {prefix}')
self.context.append(suffix)
def depart_attribution(self, node) -> None:
self.out.append(self.context.pop() + '\n')
+ self.duclass_close(node)
def visit_status(self, node) -> None:
self.visit_docinfo_item(node)
@@ -3281,7 +3301,7 @@
# labels and PDF bookmark (sidebar entry)
self.out.append('\n') # start new paragraph
- if node['names']: # don't add labels just for auto-ids
+ if len(node['names']) > 1: # don't add labels just for the auto-id
self.out += self.ids_to_labels(node, newline=True)
if (isinstance(node.next_node(), nodes.title)
and 'local' not in node['classes']
Modified: trunk/docutils/test/functional/expected/latex_cornercases.tex
===================================================================
--- trunk/docutils/test/functional/expected/latex_cornercases.tex 2025-08-20 12:04:54 UTC (rev 10218)
+++ trunk/docutils/test/functional/expected/latex_cornercases.tex 2025-08-21 15:18:05 UTC (rev 10219)
@@ -139,7 +139,6 @@
These tests contain syntax elements and combinations which may cause
trouble for the LaTeX writer.
-\phantomsection\label{contents}
\pdfbookmark[1]{Contents}{contents}
\tableofcontents
Modified: trunk/docutils/test/functional/expected/latex_leavevmode.tex
===================================================================
--- trunk/docutils/test/functional/expected/latex_leavevmode.tex 2025-08-20 12:04:54 UTC (rev 10218)
+++ trunk/docutils/test/functional/expected/latex_leavevmode.tex 2025-08-21 15:18:05 UTC (rev 10219)
@@ -360,6 +360,7 @@
\item[{Comment and Target}] \leavevmode
% This is ignored.
+\phantomsection\label{foo}
\begin{itemize}
\item Comments and other “Invisible” nodes (substitution definitions,
targets, pending) must be skipped when determining whether a
Modified: trunk/docutils/test/functional/expected/latex_memoir.tex
===================================================================
--- trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-20 12:04:54 UTC (rev 10218)
+++ trunk/docutils/test/functional/expected/latex_memoir.tex 2025-08-21 15:18:05 UTC (rev 10219)
@@ -271,7 +271,6 @@
\pagebreak[4] % start ToC on new page
-\phantomsection\label{table-of-contents}
\renewcommand{\contentsname}{Table of Contents}
\tableofcontents
Modified: trunk/docutils/test/functional/expected/standalone_rst_latex.tex
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-20 12:04:54 UTC (rev 10218)
+++ trunk/docutils/test/functional/expected/standalone_rst_latex.tex 2025-08-21 15:18:05 UTC (rev 10219)
@@ -268,7 +268,6 @@
\pagebreak[4] % start ToC on new page
-\phantomsection\label{table-of-contents}
\pdfbookmark[1]{Table of Contents}{table-of-contents}
\renewcommand{\contentsname}{Table of Contents}
\tableofcontents
Modified: trunk/docutils/test/functional/expected/standalone_rst_xetex.tex
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-20 12:04:54 UTC (rev 10218)
+++ trunk/docutils/test/functional/expected/standalone_rst_xetex.tex 2025-08-21 15:18:05 UTC (rev 10219)
@@ -135,7 +135,6 @@
\pagebreak[4] % start ToC on new page
-\phantomsection\label{table-of-contents}
\pdfbookmark[1]{Table of Contents}{table-of-contents}
\begin{DUclass}{contents}
@@ -787,7 +786,6 @@
\label{directives}%
}
-\phantomsection\label{contents}
\begin{DUclass}{contents}
\begin{DUclass}{local}
Modified: trunk/docutils/test/test_writers/test_latex2e.py
===================================================================
--- trunk/docutils/test/test_writers/test_latex2e.py 2025-08-20 12:04:54 UTC (rev 10218)
+++ trunk/docutils/test/test_writers/test_latex2e.py 2025-08-21 15:18:05 UTC (rev 10219)
@@ -316,9 +316,11 @@
\\hline
\\end{longtable*}
"""],
+])
+
# Test handling of IDs and custom class values
# --------------------------------------------
-# targets with ID
+samples['IDs and classes'] = ({}, [
["""\
A paragraph with _`inline target`.
@@ -335,6 +337,177 @@
\phantomsection\label{block-target}
\DUrole{custom}{\DUrole{paragraph}{Next paragraph.}}
"""],
+# admonition
+["""
+.. class:: cls1
+.. _label1:
+.. hint::
+ :name: label2
+ :class: cls2
+
+ Don't forget to breathe.
+""",
+r"""
+\phantomsection\label{label2}\label{label1}
+\begin{DUclass}{cls2}
+\begin{DUclass}{cls1}
+\begin{DUclass}{hint}
+\begin{DUadmonition}
+\DUtitle{Hint}
+
+Don't forget to breathe.
+\end{DUadmonition}
+\end{DUclass}
+\end{DUclass}
+\end{DUclass}
+"""],
+# block quote
+["""
+.. class:: cls1
+.. _label1:
+
+ Exlicit is better than implicit.
+
+ .. class:: attribute-cls cute
+ .. _a-tribution:
+
+ -- Zen of Python
+""",
+r"""
+\phantomsection\label{label1}
+\begin{DUclass}{cls1}
+\begin{quote}
+Exlicit is better than implicit.
+\nopagebreak
+
+\phantomsection\label{a-tribution}
+\begin{DUclass}{attribute-cls}
+\begin{DUclass}{cute}
+\raggedleft —Zen of Python
+\end{DUclass}
+\end{DUclass}
+\end{quote}
+\end{DUclass}
+"""],
+# bullet list
+["""
+.. class:: cls1
+.. _bullet1:
+
+* list item
+
+ .. class:: bullet-class
+ .. _b-item:
+
+* second bullet list item
+""",
+r"""
+\phantomsection\label{bullet1}
+\begin{DUclass}{cls1}
+\begin{itemize}
+\item list item
+
+\phantomsection\label{b-item}
+\item second bullet list item
+\end{itemize}
+\end{DUclass}
+"""],
+# definition list
+["""
+.. class:: def-list-class
+.. _def-list:
+
+definition
+ list
+
+ .. class:: def-item-class
+ .. _def-item:
+
+term
+ definition
+""",
+"""
+\\phantomsection\\label{def-list}
+\\begin{DUclass}{def-list-class}
+\\begin{description}
+\\item[{definition}] \n\
+list
+
+\\phantomsection\\label{def-item}
+\\item[{term}] \n\
+definition
+\\end{description}
+\\end{DUclass}
+"""],
+# enumerated list
+["""
+.. class:: cls1
+.. _enumerated1:
+
+#. list item
+
+ .. class:: e-item-class
+ .. _e-item:
+
+#. enumerated list item
+""",
+r"""
+\phantomsection\label{enumerated1}
+\begin{DUclass}{cls1}
+\begin{enumerate}
+\item list item
+
+\phantomsection\label{e-item}
+\item enumerated list item
+\end{enumerate}
+\end{DUclass}
+"""],
+# field list
+["""\
+Not a docinfo.
+
+.. class:: fieldlist-class
+.. _f-list:
+
+:field: list
+
+ .. class:: field-class
+ .. _f-list-item:
+
+:name: body
+""",
+r"""
+Not a docinfo.
+
+\phantomsection\label{f-list}
+\begin{DUclass}{fieldlist-class}
+\begin{DUfieldlist}
+\item[{field:}]
+list
+
+\phantomsection\label{f-list-item}
+\item[{name:}]
+body
+\end{DUfieldlist}
+\end{DUclass}
+"""],
+# line block
+["""\
+.. class:: lineblock-class
+.. _line-block:
+
+| line block
+| second line
+""",
+r"""
+\phantomsection\label{line-block}
+\begin{DUclass}{lineblock-class}
+\begin{DUlineblock}{0em}
+\item[] line block
+\item[] second line
+\end{DUlineblock}
+\end{DUclass}
+"""],
# literal block
["""\
.. class:: cls1
@@ -354,6 +527,28 @@
\end{quote}
\end{DUclass}
"""],
+# option list
+["""\
+.. class:: o-list-class
+.. _o-list:
+
+--an option list
+
+ .. class:: option-class
+ .. _o-item:
+
+--another option
+""",
+r"""
+\phantomsection\label{o-list}
+\begin{DUclass}{o-list-class}
+\begin{DUoptionlist}
+\item[-{}-an] option list
+\phantomsection\label{o-item}
+\item[-{}-another] option
+\end{DUoptionlist}
+\end{DUclass}
+"""],
# table with IDs and custom + special class values
["""\
.. class:: cls1
@@ -377,6 +572,145 @@
\end{DUclass}
\end{DUclass}
"""],
+# directives
+["""\
+.. compound::
+ :class: compoundclass
+ :name: com-pound
+
+ Compound paragraph 1
+
+ Compound paragraph 2
+
+.. container:: containerclass
+ :name: con-tainer
+
+ Container paragraph 1
+
+ Container paragraph 2
+""",
+r"""
+\phantomsection\label{com-pound}
+\begin{DUclass}{compound}
+\begin{DUclass}{compoundclass}
+Compound paragraph 1
+
+Compound paragraph 2
+\end{DUclass}
+\end{DUclass}
+
+\phantomsection\label{con-tainer}
+\begin{DUclass}{containerclass}
+Container paragraph 1
+
+Container paragraph 2
+\end{DUclass}
+"""],
+# figures and images
+["""\
+.. figure:: parrot.png
+ :figclass: figureclass
+ :figname: fig-ure
+
+ .. class:: f-caption-class
+ .. _f-caption:
+
+ A figure with caption
+
+ .. class:: legend-class
+ .. _le-gend:
+
+ A figure legend
+
+.. image:: parrot.png
+ :class: imgclass TODO ignored!
+ :name: i-mage
+ :target: example.org/parrots
+""",
+r"""
+\phantomsection\label{fig-ure}
+\begin{DUclass}{figureclass}
+\begin{figure}
+\noindent\makebox[\linewidth][c]{\includegraphics{parrot.png}}
+\caption{\label{f-caption}\DUrole{f-caption-class}{A figure with caption}}
+\begin{DUlegend}
+\phantomsection\label{le-gend}
+\DUrole{legend-class}{A figure legend}
+\end{DUlegend}
+\end{figure}
+\end{DUclass}
+
+\phantomsection\label{i-mage}
+\href{example.org/parrots}{\includegraphics{parrot.png}}
+"""],
+["""\
+.. math:: x = 2^4
+ :class: mathclass
+ :name: math-block
+
+.. note:: a specific admonition
+ :class: noteclass
+ :name: my-note
+
+.. _my-raw:
+.. raw:: latex pseudoxml xml
+ :class: rawclass
+
+ \\LaTeX
+
+.. sidebar:: sidebar title
+ :class: sideclass
+ :name: side-bar
+
+ sidebar content
+
+.. topic:: topic heading
+ :class: topicclass
+ :name: to-pic
+
+ topic content
+""",
+r"""%
+\phantomsection
+\DUrole{mathclass}{%
+\begin{equation*}
+x = 2^4
+\label{math-block}
+\end{equation*}
+}
+\phantomsection\label{my-note}
+\begin{DUclass}{noteclass}
+\begin{DUclass}{note}
+\begin{DUadmonition}
+\DUtitle{Note}
+
+a specific admonition
+\end{DUadmonition}
+\end{DUclass}
+\end{DUclass}
+
+\phantomsection\label{my-raw}\DUrole{rawclass}{\LaTeX}
+
+\phantomsection\label{side-bar}
+\begin{DUclass}{sideclass}
+\DUsidebar{
+\DUtitle{sidebar title}
+
+sidebar content
+}
+\end{DUclass}
+
+\phantomsection\label{to-pic}
+\begin{DUclass}{topic}
+\begin{DUclass}{topicclass}
+\begin{quote}
+\DUtitle{topic heading}
+
+topic content
+\end{quote}
+\end{DUclass}
+\end{DUclass}
+"""],
])
samples['latex_sectnum'] = ({'sectnum_xform': False}, [
Modified: trunk/docutils/test/test_writers/test_latex2e_parts.py
===================================================================
--- trunk/docutils/test/test_writers/test_latex2e_parts.py 2025-08-20 12:04:54 UTC (rev 10218)
+++ trunk/docutils/test/test_writers/test_latex2e_parts.py 2025-08-21 15:18:05 UTC (rev 10219)
@@ -214,7 +214,6 @@
------------------
""",
{'body': r"""
-\phantomsection\label{contents}
\pdfbookmark[1]{Contents}{contents}
\tableofcontents
@@ -235,7 +234,6 @@
-------------
""",
{'body': r"""
-\phantomsection\label{contents}
\pdfbookmark[1]{Contents}{contents}
\tableofcontents
@@ -256,7 +254,6 @@
-------------
""",
{'body': r"""
-\phantomsection\label{contents}
\pdfbookmark[1]{Contents}{contents}
\setcounter{tocdepth}{1}
\tableofcontents
@@ -286,7 +283,6 @@
\label{section-with-local-toc}%
}
-\phantomsection\label{contents}
\mtcsettitle{secttoc}{}
\secttoc
@@ -596,7 +592,6 @@
-------------
""",
{'body': r"""
-\phantomsection\label{contents}
\pdfbookmark[1]{Contents}{contents}
\setcounter{tocdepth}{0}
\tableofcontents
@@ -703,7 +698,6 @@
Paragraph 2.
""",
{'body': r"""
-\phantomsection\label{table-of-contents}
\pdfbookmark[1]{Table of Contents}{table-of-contents}
\begin{DUclass}{contents}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
_______________________________________________
Docutils-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/docutils-checkins