Menu

#176 LaTeX writer: Append \leavevmode to non-docinfo field names

None
closed-fixed
nobody
None
5
2021-04-03
2020-10-26
No

Currently, if one converts an input like the following into LaTeX:

This line prevents the field list from being treated as docinfo.

:field name:
    - List item 1
    - List item 2

then the text in the final rendered PDF will look like:

This line prevents the field list from being treated as docinfo.

field name:  List item 1
     List item 2

This is ugly & undesirable, and it can be fixed by inserting \leavevmode after the field name — as is already done for definition lists — thereby causing the rendered text to instead look like:

This line prevents the field list from being treated as docinfo.

field name:
     List item 1
     List item 2

The attached patch causes the LaTeX & XeTeX writers to add \leavevmode after each field name in non-docinfo field lists.

1 Attachments

Discussion

  • Günter Milde

    Günter Milde - 2020-10-28

    Thank you for the analysis and patch.

    For a cleaner LaTeX source, it would be an added bonus, if the \leavevmode would only be added if required. The latex writer has an example in visit_table() where a test with isinstance() is done before inserting. (While adding such a test in depart_term() is a TODO item. Maybe there are further instances where \leavevmode is required - then an auxiliary function to insert it given the right conditions may be best.)

     
  • John Thorvald Wodder II

    I've written a new patch that makes both depart_field_name() and depart_term() only insert \leavevmode if necessary. The method for determining whether an insertion is necessary is described in the comments. Which constructs require \leavevmode was determined by typesetting all possible field body constructs in LaTeX and seeing which ones were wonky in a way that could be fixed with \leavevmode; the reStructuredText source used for that was then converted into the new tests leavevmode_deflist.txt and leavevmode_fieldlist.txt, so if you're wondering if a given \leavevmode is necessary, you can fiddle with the corresponding .tex files and see the results.

     
  • Günter Milde

    Günter Milde - 2020-11-27

    Thank you for the updated patch with the comprehensive test document.
    I believe, the actual fix can and should be made much simpler:
    your research showed, that almost all block-level elements require the \leavevmode safeguard. Exceptions are "paragraph" and "math-block" (did I miss something)?
    Images and Tables are block-level elements in Docutils (exception: inline-images via substitution). Therefore we want them rendered a such and \leavevmode is required (no matter whether alignment is defined or not).
    The Docutils document model requires a block-level element as field body (see doctuisl.dtd).
    From previous experience, we know that an unnecessary \leavevmode does no harm to the output, so a false positive test is tolerable (especially for seldom used combinations).

    IMV, a simple test whether the next node is paragraph or math-block should suffice. If skipping nodes like comment, footnote, citation, substitution-reference, ... and testing the next node can be implemented in just a few lines, we might add this but this is actually not necessary. "Compound" elements are rarely used, so IMV don't need a special handling.

     
  • John Thorvald Wodder II

    Attached: patch, round 3. This just tests whether the next node is a paragraph or math block after skipping invisible nodes.

     
  • Günter Milde

    Günter Milde - 2021-01-07

    Thank you for the new patch. I tested, streamlined the implementation, added a fix for images and
    came up with the attached. Please check if this looks and work as you want it.
    This misses the new tests, which I would prefer to be "minimalized" and merged to either one new functional test or added to latex-cornercases. (The aim is to keep running time low while testing the presence/absence of "leavevmode" where it is important.)

     
  • John Thorvald Wodder II

    I am unable to successfully apply the new patch. What Subversion revision was it created against?

     
    • Günter Milde

      Günter Milde - 2021-01-14

      On 2021-01-12, John Thorvald Wodder II via Docutils-develop wrote in gmane.text.docutils.devel:

      [-- Type: text/plain, Encoding: base64 --]

      I am unable to successfully apply the new patch. What Subversion
      revision was it created against?

      Sorry for the inconvenience. It was against "Small fixes." from
      08.01.2021, done from my git-svn checkout.
      A patch against the status as of today 2021-01-14 is below.

      Günter

      From 1651b802181cc82f3618a8e9d66bc57c30a3bad6 Mon Sep 17 00:00:00 2001
      From: milde milde@users.sf.net
      Date: Thu, 7 Jan 2021 13:12:06 +0100
      Subject: [PATCH] latex-writer: add \leavevmode after term or field name if required.


      docutils/docutils/writers/latex2e/init.py | 32 ++++++++++++++++++++++++++++----
      docutils/test/functional/expected/latex_cornercases.tex | 4 ++--
      docutils/test/functional/expected/latex_memoir.tex | 20 ++++++++++----------
      docutils/test/functional/expected/standalone_rst_latex.tex | 20 ++++++++++----------
      docutils/test/functional/expected/standalone_rst_xetex.tex | 20 ++++++++++----------
      5 files changed, 60 insertions(+), 36 deletions(-)

      diff --git a/docutils/docutils/writers/latex2e/init.py b/docutils/docutils/writers/latex2e/init.py
      index 1556d10..e104fa8 100644
      --- a/docutils/docutils/writers/latex2e/init.py
      +++ b/docutils/docutils/writers/latex2e/init.py
      @@ -1577,4 +1577,25 @@ class LaTeXTranslator(nodes.NodeVisitor):
      def pop_output_collector(self):
      self.out = self.out_stack.pop()
      +
      + def term_postfix(self, node):
      + """
      + Return LaTeX code required between term or field name and content.
      +
      + In a LaTeX "description" environment (used for definition
      + lists and non-docinfo field lists), a \\leavevmode
      + between an item's label and content ensures the correct
      + placement of certain block constructs.
      + Images need an additional newline.
      + """
      + visible_children = [child for child in node
      + if not isinstance(child, nodes.Invisible)]

      + if not visible_children:
      + return ''
      + if isinstance(visible_children[0], nodes.image):
      + return '\leavevmode\n'
      + if not isinstance(visible_children[0],
      + (nodes.paragraph, nodes.math_block)):
      + return r'\leavevmode'
      + return ''

       # Visitor methods
      

      @@ -2131,5 +2152,5 @@ class LaTeXTranslator(nodes.NodeVisitor):

       def visit_field_body(self, node):
      
      • pass
      • self.out.append(self.term_postfix(node))

        def depart_field_body(self, node):
        @@ -2971,7 +2992,10 @@ class LaTeXTranslator(nodes.NodeVisitor):

        def depart_term(self, node):
        - # \leavevmode results in a line break if the
        - # term is followed by an item list.
        - self.out.append('}] \leavevmode ')
        + self.out.append('}] ')
        + # Do we need a \leavevmode (line break if the field body begins
        + # with a list or environment)?
        + next_node = node.next_node(descend=False, siblings=True)
        + if not isinstance(next_node, nodes.classifier):
        + self.out.append(self.term_postfix(next_node))

        def visit_tgroup(self, node):
        diff --git a/docutils/test/functional/expected/latex_cornercases.tex b/docutils/test/functional/expected/latex_cornercases.tex
        index bc9ef36..5621bb1 100644
        --- a/docutils/test/functional/expected/latex_cornercases.tex
        +++ b/docutils/test/functional/expected/latex_cornercases.tex
        @@ -438,5 +438,5 @@ cell 1, 2

      \begin{description}
      -\item[{definition:}] \leavevmode
      +\item[{definition:}]
      list

      @@ -491,5 +491,5 @@ cell 1, 2

      \begin{description}
      -\item[{definition:}] \leavevmode
      +\item[{definition:}]
      list

      diff --git a/docutils/test/functional/expected/latex_memoir.tex b/docutils/test/functional/expected/latex_memoir.tex
      index a8da5e0..d5057a0 100644
      --- a/docutils/test/functional/expected/latex_memoir.tex
      +++ b/docutils/test/functional/expected/latex_memoir.tex
      @@ -465,16 +465,16 @@ Paragraph 2 of item 2.

      \begin{description}
      -\item[{Term}] \leavevmode
      +\item[{Term}]
      Definition

      -\item[{Term}] \leavevmode (\textbf{classifier})
      +\item[{Term}] (\textbf{classifier})
      Definition paragraph 1.

      Definition paragraph 2.

      -\item[{Term}] \leavevmode
      +\item[{Term}]
      Definition

      -\item[{Term}] \leavevmode (\textbf{classifier one})(\textbf{classifier two})
      +\item[{Term}] (\textbf{classifier one})(\textbf{classifier two})
      Definition

      @@ -1375,5 +1375,5 @@ list,
      a paragraph,
      \begin{description}
      -\item[{a definition}] \leavevmode
      +\item[{a definition}]
      list,

      @@ -1841,5 +1841,5 @@ physical system changes in time.

      \begin{description}
      -\item[{Math-Accents:}] \leavevmode
      +\item[{Math-Accents:}] \leavevmode
      \setlength{\DUtablewidth}{\linewidth}%
      \begin{longtable*}{p{0.315\DUtablewidth}p{0.315\DUtablewidth}p{0.315\DUtablewidth}}
      @@ -2189,5 +2189,5 @@ is contained in a quote

      \begin{description}
      -\item[{In a definition list:}] \leavevmode
      +\item[{In a definition list:}] \leavevmode
      \begin{DUoptionlist}
      \item[-{}-help] show help
      @@ -2647,8 +2647,8 @@ Compare the items in the following lists:

      \begin{description}
      -\item[{simple}] \leavevmode
      +\item[{simple}]
      description term

      -\item[{{[}bracketed{]}}] \leavevmode
      +\item[{{[}bracketed{]}}]
      description term

      @@ -2720,5 +2720,5 @@ Long URLs should be wrapped in the PDF. This can be achieved with the

      \begin{description}
      -\item[{Example:}] \leavevmode
      +\item[{Example:}]
      a long URL that should wrap in the output
      \url{http://docutils.sourceforge.net/docs/user/latex.html#id79}
      diff --git a/docutils/test/functional/expected/standalone_rst_latex.tex b/docutils/test/functional/expected/standalone_rst_latex.tex
      index bc0098e..efb8d8a 100644
      --- a/docutils/test/functional/expected/standalone_rst_latex.tex
      +++ b/docutils/test/functional/expected/standalone_rst_latex.tex
      @@ -469,16 +469,16 @@ Paragraph 2 of item 2.

      \begin{description}
      -\item[{Term}] \leavevmode
      +\item[{Term}]
      Definition

      -\item[{Term}] \leavevmode (\textbf{classifier})
      +\item[{Term}] (\textbf{classifier})
      Definition paragraph 1.

      Definition paragraph 2.

      -\item[{Term}] \leavevmode
      +\item[{Term}]
      Definition

      -\item[{Term}] \leavevmode (\textbf{classifier one})(\textbf{classifier two})
      +\item[{Term}] (\textbf{classifier one})(\textbf{classifier two})
      Definition

      @@ -1379,5 +1379,5 @@ list,
      a paragraph,
      \begin{description}
      -\item[{a definition}] \leavevmode
      +\item[{a definition}]
      list,

      @@ -1845,5 +1845,5 @@ physical system changes in time.

      \begin{description}
      -\item[{Math-Accents:}] \leavevmode
      +\item[{Math-Accents:}] \leavevmode
      \setlength{\DUtablewidth}{\linewidth}%
      \begin{longtable*}{p{0.315\DUtablewidth}p{0.315\DUtablewidth}p{0.315\DUtablewidth}}
      @@ -2193,5 +2193,5 @@ is contained in a quote

      \begin{description}
      -\item[{In a definition list:}] \leavevmode
      +\item[{In a definition list:}] \leavevmode
      \begin{DUoptionlist}
      \item[-{}-help] show help
      @@ -2651,8 +2651,8 @@ Compare the items in the following lists:

      \begin{description}
      -\item[{simple}] \leavevmode
      +\item[{simple}]
      description term

      -\item[{{[}bracketed{]}}] \leavevmode
      +\item[{{[}bracketed{]}}]
      description term

      @@ -2724,5 +2724,5 @@ Long URLs should be wrapped in the PDF. This can be achieved with the

      \begin{description}
      -\item[{Example:}] \leavevmode
      +\item[{Example:}]
      a long URL that should wrap in the output
      \url{http://docutils.sourceforge.net/docs/user/latex.html#id79}
      diff --git a/docutils/test/functional/expected/standalone_rst_xetex.tex b/docutils/test/functional/expected/standalone_rst_xetex.tex
      index 8150e44..a677b67 100644
      --- a/docutils/test/functional/expected/standalone_rst_xetex.tex
      +++ b/docutils/test/functional/expected/standalone_rst_xetex.tex
      @@ -328,16 +328,16 @@ Paragraph 2 of item 2.

      \begin{description}
      -\item[{Term}] \leavevmode
      +\item[{Term}]
      Definition

      -\item[{Term}] \leavevmode (\textbf{classifier})
      +\item[{Term}] (\textbf{classifier})
      Definition paragraph 1.

      Definition paragraph 2.

      -\item[{Term}] \leavevmode
      +\item[{Term}]
      Definition

      -\item[{Term}] \leavevmode (\textbf{classifier one})(\textbf{classifier two})
      +\item[{Term}] (\textbf{classifier one})(\textbf{classifier two})
      Definition

      @@ -1262,5 +1262,5 @@ list,
      a paragraph,
      \begin{description}
      -\item[{a definition}] \leavevmode
      +\item[{a definition}]
      list,

      @@ -1729,5 +1729,5 @@ physical system changes in time.

      \begin{description}
      -\item[{Math-Accents:}] \leavevmode
      +\item[{Math-Accents:}] \leavevmode
      \setlength{\DUtablewidth}{\linewidth}%
      \begin{longtable*}{p{0.315\DUtablewidth}p{0.315\DUtablewidth}p{0.315\DUtablewidth}}
      @@ -2077,5 +2077,5 @@ is contained in a quote

      \begin{description}
      -\item[{In a definition list:}] \leavevmode
      +\item[{In a definition list:}] \leavevmode
      \begin{DUoptionlist}
      \item[--help] show help
      @@ -2535,8 +2535,8 @@ Compare the items in the following lists:

      \begin{description}
      -\item[{simple}] \leavevmode
      +\item[{simple}]
      description term

      -\item[{{[}bracketed{]}}] \leavevmode
      +\item[{{[}bracketed{]}}]
      description term

      @@ -2608,5 +2608,5 @@ Long URLs should be wrapped in the PDF. This can be achieved with the

      \begin{description}
      -\item[{Example:}] \leavevmode
      +\item[{Example:}]
      a long URL that should wrap in the output
      \url{http://docutils.sourceforge.net/docs/user/latex.html#id79}
      --
      libgit2 0.27.7

       
  • John Thorvald Wodder II

    OK, I finally got around to writing a minimal test case for \leavevmode insertion. I also made a small change to the code so that \leavevmode won't be inserted in docinfo fields. I hope that this patch is satisfactory and that this issue can finally be closed.

     
  • Günter Milde

    Günter Milde - 2021-02-18
    • status: open --> open-accepted
     
  • Günter Milde

    Günter Milde - 2021-02-18

    A modified version of the patch is comitted in r8626.
    The test is now an intermediate between the comprehensive and short versions,
    some additional tweaks were done for ambiguous cases (footnotes, targets, compound).
    Please check if this is what you want.
    Thanks again for your work.

     
  • John Thorvald Wodder II

    Yes, this is just fine.

     
  • Günter Milde

    Günter Milde - 2021-04-03
    • status: open-accepted --> closed-fixed
     
  • Günter Milde

    Günter Milde - 2021-04-03

    Fixed in Docutils 0.17.
    Many thanks for your patience and your contribution.

     

Log in to post a comment.