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

milde--- via Docutils-checkins <[email protected]> Wed, 20 Aug 2025 12:04:33 +0000
Newsgroups gmane.text.docutils.cvs
Message-ID <[email protected]>
Revision: 10216
          http://sourceforge.net/p/docutils/code/10216
Author:   milde
Date:     2025-08-20 12:04:32 +0000 (Wed, 20 Aug 2025)
Log Message:
-----------
LaTeX writer: fix anchor placement for figures, images, literal blocks, tables.

New attribute "pre_nl" for `ids_to_labels()`: prepend newline to label
definitions if there are labels.

Move anchor and label definition(s) for images, figures, literal blocks,
and tables before class wrappers and element.
Now, after activating a link to the element, it is fully visible
in the PDF viewer.

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_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

Modified: trunk/docutils/HISTORY.rst
===================================================================
--- trunk/docutils/HISTORY.rst	2025-08-20 12:04:19 UTC (rev 10215)
+++ trunk/docutils/HISTORY.rst	2025-08-20 12:04:32 UTC (rev 10216)
@@ -44,6 +44,8 @@
 * docutils/writers/latex2e/__init__.py
 
   - Prepend ``\phantomsection`` to labelled math-blocks.
+  - Fix cross-reference anchor placement in figures, images,
+    literal-blocks, and tables.
 
 
 Release 0.22 (2026-07-29)

Modified: trunk/docutils/docutils/writers/latex2e/__init__.py
===================================================================
--- trunk/docutils/docutils/writers/latex2e/__init__.py	2025-08-20 12:04:19 UTC (rev 10215)
+++ trunk/docutils/docutils/writers/latex2e/__init__.py	2025-08-20 12:04:32 UTC (rev 10216)
@@ -1560,19 +1560,25 @@
                                    id for id in node['ids']))
 
     def ids_to_labels(self, node, set_anchor=True, protect=False,
-                      newline=False) -> list[str]:
+                      newline=False, pre_nl=False) -> list[str]:
         """Return label definitions for all ids of `node`.
 
         If `set_anchor` is True, an anchor is set with \\phantomsection.
         If `protect` is True, the \\label cmd is made robust.
         If `newline` is True, a newline is added if there are labels.
+        If `pre_nl` is True, a newline is prepended if there are labels.
+
+        Provisional.
         """
         prefix = '\\protect' if protect else ''
-        labels = [prefix + '\\label{%s}' % id for id in node['ids']]
-        if set_anchor and labels:
-            labels.insert(0, '\\phantomsection')
-        if newline and labels:
-            labels.append('\n')
+        labels = [f'{prefix}\\label{{{id}}}' for id in node['ids']]
+        if labels:
+            if set_anchor:
+                labels.insert(0, '\\phantomsection')
+            if newline:
+                labels.append('\n')
+            if pre_nl:
+                labels.insert(0, '\n')
         return labels
 
     def set_align_from_classes(self, node) -> None:
@@ -2297,6 +2303,7 @@
 
     def visit_figure(self, node) -> None:
         self.requirements['float'] = PreambleCmds.float
+        self.out += self.ids_to_labels(node, pre_nl=True)
         self.duclass_open(node)
         # The 'align' attribute sets the "outer alignment",
         # for "inner alignment" use LaTeX default alignment (similar to HTML)
@@ -2308,7 +2315,6 @@
             self.out.append('\\begin{figure} %% align = "%s"\n' % alignment)
         else:
             self.out.append('\\begin{figure}\n')
-        self.out += self.ids_to_labels(node, newline=True)
 
     def depart_figure(self, node) -> None:
         self.out.append('\\end{figure}\n')
@@ -2482,6 +2488,7 @@
         if 'width' in attrs:
             include_graphics_options.append(
                 f"width={self.to_latex_length(attrs['width'], node)}")
+        pre.append(''.join(self.ids_to_labels(node, newline=True)))
         if not (self.is_inline(node)
                 or isinstance(node.parent, (nodes.figure, nodes.compound))):
             pre.append('\n')
@@ -2501,7 +2508,7 @@
         self.out.extend(post)
 
     def depart_image(self, node) -> None:
-        self.out += self.ids_to_labels(node, newline=True)
+        pass
 
     def visit_inline(self, node) -> None:
         # This function is also called by the visiting functions for
@@ -2623,8 +2630,8 @@
         _use_listings = (literal_env == 'lstlisting') and _use_env
 
         # Labels and classes:
+        self.out += self.ids_to_labels(node, pre_nl=True)
         self.duclass_open(node)
-        self.out += self.ids_to_labels(node, newline=True)
         # Highlight code?
         if (not _plaintext
             and 'code' in node['classes']
@@ -3058,7 +3065,6 @@
         self.depart_admonition(node)
 
     def visit_table(self, node) -> None:
-        self.duclass_open(node)
         self.requirements['table'] = PreambleCmds.table
         if not self.settings.legacy_column_widths:
             self.requirements['table1'] = PreambleCmds.table_columnwidth
@@ -3090,9 +3096,9 @@
         # if it has no caption/title.
         # See visit_thead() for tables with caption.
         if not self.active_table.caption:
-            self.out.extend(self.ids_to_labels(
-                node, set_anchor=len(self.table_stack) != 1,
-                newline=True))
+            set_anchor = (len(self.table_stack) != 1)
+            self.out += self.ids_to_labels(node, set_anchor, pre_nl=True)
+        self.duclass_open(node)
         # TODO: Don't use a longtable or add \noindent before
         #       the next paragraph, when in a "compound paragraph".
         #       Start a new line or a new paragraph?

Modified: trunk/docutils/test/functional/expected/latex_cornercases.tex
===================================================================
--- trunk/docutils/test/functional/expected/latex_cornercases.tex	2025-08-20 12:04:19 UTC (rev 10215)
+++ trunk/docutils/test/functional/expected/latex_cornercases.tex	2025-08-20 12:04:32 UTC (rev 10216)
@@ -894,8 +894,8 @@
 \hline
 \end{longtable}
 
+\phantomsection\label{figure-label}
 \begin{figure}
-\phantomsection\label{figure-label}
 \noindent\makebox[\linewidth][c]{\includegraphics{../../../docs/user/rst/images/biohazard.png}}
 \caption{Figure with %
 \label{hypertarget-in-figure-caption}hypertarget in figure caption.}
@@ -905,8 +905,8 @@
 \end{DUlegend}
 \end{figure}
 
+\phantomsection\label{image-label}
 \includegraphics{../../../docs/user/rst/images/biohazard.png}
-\phantomsection\label{image-label}
 
 See \hyperref[hypertarget-in-plain-text]{hypertarget in plain text},
 \hyperref[table-label]{table label}, \hyperref[hypertarget-in-table-title]{hypertarget in table title},

Modified: trunk/docutils/test/functional/expected/latex_memoir.tex
===================================================================
--- trunk/docutils/test/functional/expected/latex_memoir.tex	2025-08-20 12:04:19 UTC (rev 10215)
+++ trunk/docutils/test/functional/expected/latex_memoir.tex	2025-08-20 12:04:32 UTC (rev 10216)
@@ -824,8 +824,8 @@
 
 Image with multiple IDs:
 
+\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1}
 \includegraphics{../../../docs/user/rst/images/biohazard.png}
-\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1}
 
 A centered image:
 

Modified: trunk/docutils/test/functional/expected/standalone_rst_latex.tex
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_latex.tex	2025-08-20 12:04:19 UTC (rev 10215)
+++ trunk/docutils/test/functional/expected/standalone_rst_latex.tex	2025-08-20 12:04:32 UTC (rev 10216)
@@ -818,8 +818,8 @@
 
 Image with multiple IDs:
 
+\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1}
 \includegraphics{../../../docs/user/rst/images/biohazard.png}
-\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1}
 
 A centered image:
 

Modified: trunk/docutils/test/functional/expected/standalone_rst_xetex.tex
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_xetex.tex	2025-08-20 12:04:19 UTC (rev 10215)
+++ trunk/docutils/test/functional/expected/standalone_rst_xetex.tex	2025-08-20 12:04:32 UTC (rev 10216)
@@ -843,8 +843,8 @@
 
 Image with multiple IDs:
 
+\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1}
 \includegraphics{../../../docs/user/rst/images/biohazard.png}
-\phantomsection\label{image-target-3}\label{image-target-2}\label{image-target-1}
 
 A centered image:
 

Modified: trunk/docutils/test/test_writers/test_latex2e.py
===================================================================
--- trunk/docutils/test/test_writers/test_latex2e.py	2025-08-20 12:04:19 UTC (rev 10215)
+++ trunk/docutils/test/test_writers/test_latex2e.py	2025-08-20 12:04:32 UTC (rev 10216)
@@ -133,19 +133,27 @@
 ["""
 .. image:: larch-mini.jpg
    :target: larch.jpg
+   :name: the-larch
+   :class: currently ignored
    :align: center
 """,
 r"""
+\phantomsection\label{the-larch}
 \noindent\makebox[\linewidth][c]{\href{larch.jpg}{\includegraphics{larch-mini.jpg}}}
 """],
 ["""\
+.. _fig:larch:
+
 .. figure:: larch-mini.jpg
    :target: larch.jpg
+   :name: the-larch
 
    The larch
 """,
 r"""
+\phantomsection\label{fig-larch}
 \begin{figure}
+\phantomsection\label{the-larch}
 \noindent\makebox[\linewidth][c]{\href{larch.jpg}{\includegraphics{larch-mini.jpg}}}
 \caption{The larch}
 \end{figure}
@@ -327,6 +335,25 @@
 \phantomsection\label{block-target}
 \DUrole{custom}{\DUrole{paragraph}{Next paragraph.}}
 """],
+# literal block
+["""\
+.. class:: cls1
+.. _block1:
+
+::
+
+   1^2_3
+""",
+r"""
+\phantomsection\label{block1}
+\begin{DUclass}{cls1}
+\begin{quote}
+\begin{alltt}
+1^2_3
+\end{alltt}
+\end{quote}
+\end{DUclass}
+"""],
 # table with IDs and custom + special class values
 ["""\
 .. class:: cls1
@@ -341,9 +368,9 @@
    = =
 """,
 r"""
+\phantomsection\label{label2}\label{label1}
 \begin{DUclass}{cls2}
 \begin{DUclass}{cls1}
-\phantomsection\label{label2}\label{label1}
 \begin{longtable*}{ll}
 Y & N \\
 \end{longtable*}

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.